mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Three changes:
1) make get_metadata return an unverified list of formats. Avoids a file system operation per format 2) enhancement request #2845 3) permit composite fields as search/replace source fields.
This commit is contained in:
parent
2c1debbe79
commit
7893c01807
@ -32,24 +32,30 @@ class Worker(Thread):
|
|||||||
remove, add, au, aus, do_aus, rating, pub, do_series, \
|
remove, add, au, aus, do_aus, rating, pub, do_series, \
|
||||||
do_autonumber, do_remove_format, remove_format, do_swap_ta, \
|
do_autonumber, do_remove_format, remove_format, do_swap_ta, \
|
||||||
do_remove_conv, do_auto_author, series, do_series_restart, \
|
do_remove_conv, do_auto_author, series, do_series_restart, \
|
||||||
series_start_value = self.args
|
series_start_value, do_title_case = self.args
|
||||||
|
|
||||||
# first loop: do author and title. These will commit at the end of each
|
# first loop: do author and title. These will commit at the end of each
|
||||||
# operation, because each operation modifies the file system. We want to
|
# operation, because each operation modifies the file system. We want to
|
||||||
# try hard to keep the DB and the file system in sync, even in the face
|
# try hard to keep the DB and the file system in sync, even in the face
|
||||||
# of exceptions or forced exits.
|
# of exceptions or forced exits.
|
||||||
for id in self.ids:
|
for id in self.ids:
|
||||||
|
title_set = False
|
||||||
if do_swap_ta:
|
if do_swap_ta:
|
||||||
title = self.db.title(id, index_is_id=True)
|
title = self.db.title(id, index_is_id=True)
|
||||||
aum = self.db.authors(id, index_is_id=True)
|
aum = self.db.authors(id, index_is_id=True)
|
||||||
if aum:
|
if aum:
|
||||||
aum = [a.strip().replace('|', ',') for a in aum.split(',')]
|
aum = [a.strip().replace('|', ',') for a in aum.split(',')]
|
||||||
new_title = authors_to_string(aum)
|
new_title = authors_to_string(aum)
|
||||||
|
if do_title_case:
|
||||||
|
new_title = new_title.title()
|
||||||
self.db.set_title(id, new_title, notify=False)
|
self.db.set_title(id, new_title, notify=False)
|
||||||
|
title_set = True
|
||||||
if title:
|
if title:
|
||||||
new_authors = string_to_authors(title)
|
new_authors = string_to_authors(title)
|
||||||
self.db.set_authors(id, new_authors, notify=False)
|
self.db.set_authors(id, new_authors, notify=False)
|
||||||
|
if do_title_case and not title_set:
|
||||||
|
title = self.db.title(id, index_is_id=True)
|
||||||
|
self.db.set_title(id, title.title(), notify=False)
|
||||||
if au:
|
if au:
|
||||||
self.db.set_authors(id, string_to_authors(au), notify=False)
|
self.db.set_authors(id, string_to_authors(au), notify=False)
|
||||||
|
|
||||||
@ -182,19 +188,22 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
|||||||
self.search_for.initialize('bulk_edit_search_for')
|
self.search_for.initialize('bulk_edit_search_for')
|
||||||
self.replace_with.initialize('bulk_edit_replace_with')
|
self.replace_with.initialize('bulk_edit_replace_with')
|
||||||
self.test_text.initialize('bulk_edit_test_test')
|
self.test_text.initialize('bulk_edit_test_test')
|
||||||
fields = ['']
|
self.all_fields = ['']
|
||||||
|
self.writable_fields = ['']
|
||||||
fm = self.db.field_metadata
|
fm = self.db.field_metadata
|
||||||
for f in fm:
|
for f in fm:
|
||||||
if (f in ['author_sort'] or (
|
if (f in ['author_sort'] or (
|
||||||
fm[f]['datatype'] == 'text' or fm[f]['datatype'] == 'series')
|
fm[f]['datatype'] in ['text', 'series'])
|
||||||
and fm[f].get('search_terms', None)
|
and fm[f].get('search_terms', None)
|
||||||
and f not in ['formats', 'ondevice']):
|
and f not in ['formats', 'ondevice']):
|
||||||
fields.append(f)
|
self.all_fields.append(f)
|
||||||
fields.sort()
|
self.writable_fields.append(f)
|
||||||
self.search_field.addItems(fields)
|
if fm[f]['datatype'] == 'composite':
|
||||||
self.search_field.setMaxVisibleItems(min(len(fields), 20))
|
self.all_fields.append(f)
|
||||||
self.destination_field.addItems(fields)
|
self.all_fields.sort()
|
||||||
self.destination_field.setMaxVisibleItems(min(len(fields), 20))
|
self.writable_fields.sort()
|
||||||
|
self.search_field.setMaxVisibleItems(20)
|
||||||
|
self.destination_field.setMaxVisibleItems(20)
|
||||||
offset = 10
|
offset = 10
|
||||||
self.s_r_number_of_books = min(10, len(self.ids))
|
self.s_r_number_of_books = min(10, len(self.ids))
|
||||||
for i in range(1,self.s_r_number_of_books+1):
|
for i in range(1,self.s_r_number_of_books+1):
|
||||||
@ -262,7 +271,7 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
|||||||
|
|
||||||
self.replace_func.addItems(sorted(self.s_r_functions.keys()))
|
self.replace_func.addItems(sorted(self.s_r_functions.keys()))
|
||||||
self.search_mode.currentIndexChanged[int].connect(self.s_r_search_mode_changed)
|
self.search_mode.currentIndexChanged[int].connect(self.s_r_search_mode_changed)
|
||||||
self.search_field.currentIndexChanged[str].connect(self.s_r_search_field_changed)
|
self.search_field.currentIndexChanged[int].connect(self.s_r_search_field_changed)
|
||||||
self.destination_field.currentIndexChanged[str].connect(self.s_r_destination_field_changed)
|
self.destination_field.currentIndexChanged[str].connect(self.s_r_destination_field_changed)
|
||||||
|
|
||||||
self.replace_mode.currentIndexChanged[int].connect(self.s_r_paint_results)
|
self.replace_mode.currentIndexChanged[int].connect(self.s_r_paint_results)
|
||||||
@ -293,15 +302,18 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
|||||||
val = []
|
val = []
|
||||||
return val
|
return val
|
||||||
|
|
||||||
def s_r_search_field_changed(self, txt):
|
def s_r_search_field_changed(self, idx):
|
||||||
txt = unicode(txt)
|
|
||||||
for i in range(0, self.s_r_number_of_books):
|
for i in range(0, self.s_r_number_of_books):
|
||||||
w = getattr(self, 'book_%d_text'%(i+1))
|
w = getattr(self, 'book_%d_text'%(i+1))
|
||||||
mi = self.db.get_metadata(self.ids[i], index_is_id=True)
|
mi = self.db.get_metadata(self.ids[i], index_is_id=True)
|
||||||
src = unicode(self.search_field.currentText())
|
src = unicode(self.search_field.currentText())
|
||||||
t = self.s_r_get_field(mi, src)
|
t = self.s_r_get_field(mi, src)
|
||||||
w.setText(''.join(t[0:1]))
|
w.setText(''.join(t[0:1]))
|
||||||
self.s_r_paint_results(None)
|
|
||||||
|
if self.search_mode.currentIndex() == 0:
|
||||||
|
self.destination_field.setCurrentIndex(idx)
|
||||||
|
else:
|
||||||
|
self.s_r_paint_results(None)
|
||||||
|
|
||||||
def s_r_destination_field_changed(self, txt):
|
def s_r_destination_field_changed(self, txt):
|
||||||
txt = unicode(txt)
|
txt = unicode(txt)
|
||||||
@ -314,7 +326,11 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
|||||||
self.s_r_paint_results(None)
|
self.s_r_paint_results(None)
|
||||||
|
|
||||||
def s_r_search_mode_changed(self, val):
|
def s_r_search_mode_changed(self, val):
|
||||||
|
self.search_field.clear()
|
||||||
|
self.destination_field.clear()
|
||||||
if val == 0:
|
if val == 0:
|
||||||
|
self.search_field.addItems(self.writable_fields)
|
||||||
|
self.destination_field.addItems(self.writable_fields)
|
||||||
self.destination_field.setCurrentIndex(0)
|
self.destination_field.setCurrentIndex(0)
|
||||||
self.destination_field.setVisible(False)
|
self.destination_field.setVisible(False)
|
||||||
self.destination_field_label.setVisible(False)
|
self.destination_field_label.setVisible(False)
|
||||||
@ -324,6 +340,8 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
|||||||
self.comma_separated.setVisible(False)
|
self.comma_separated.setVisible(False)
|
||||||
self.s_r_heading.setText('<p>'+self.main_heading + self.character_heading)
|
self.s_r_heading.setText('<p>'+self.main_heading + self.character_heading)
|
||||||
else:
|
else:
|
||||||
|
self.search_field.addItems(self.all_fields)
|
||||||
|
self.destination_field.addItems(self.writable_fields)
|
||||||
self.destination_field.setVisible(True)
|
self.destination_field.setVisible(True)
|
||||||
self.destination_field_label.setVisible(True)
|
self.destination_field_label.setVisible(True)
|
||||||
self.replace_mode.setVisible(True)
|
self.replace_mode.setVisible(True)
|
||||||
@ -367,6 +385,8 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
|||||||
return ''
|
return ''
|
||||||
dest = unicode(self.destination_field.currentText())
|
dest = unicode(self.destination_field.currentText())
|
||||||
if dest == '':
|
if dest == '':
|
||||||
|
if self.db.metadata_for_field(src)['datatype'] == 'composite':
|
||||||
|
raise Exception(_('You must specify a destination when source is a composite field'))
|
||||||
dest = src
|
dest = src
|
||||||
dest_mode = self.replace_mode.currentIndex()
|
dest_mode = self.replace_mode.currentIndex()
|
||||||
|
|
||||||
@ -433,8 +453,6 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
|||||||
t = self.s_r_replace_mode_separator().join(t)
|
t = self.s_r_replace_mode_separator().join(t)
|
||||||
wr.setText(t)
|
wr.setText(t)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
import traceback
|
|
||||||
traceback.print_exc()
|
|
||||||
self.s_r_error = e
|
self.s_r_error = e
|
||||||
self.s_r_set_colors()
|
self.s_r_set_colors()
|
||||||
break
|
break
|
||||||
@ -592,11 +610,12 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
|||||||
do_swap_ta = self.swap_title_and_author.isChecked()
|
do_swap_ta = self.swap_title_and_author.isChecked()
|
||||||
do_remove_conv = self.remove_conversion_settings.isChecked()
|
do_remove_conv = self.remove_conversion_settings.isChecked()
|
||||||
do_auto_author = self.auto_author_sort.isChecked()
|
do_auto_author = self.auto_author_sort.isChecked()
|
||||||
|
do_title_case = self.change_title_to_title_case.isChecked()
|
||||||
|
|
||||||
args = (remove, add, au, aus, do_aus, rating, pub, do_series,
|
args = (remove, add, au, aus, do_aus, rating, pub, do_series,
|
||||||
do_autonumber, do_remove_format, remove_format, do_swap_ta,
|
do_autonumber, do_remove_format, remove_format, do_swap_ta,
|
||||||
do_remove_conv, do_auto_author, series, do_series_restart,
|
do_remove_conv, do_auto_author, series, do_series_restart,
|
||||||
series_start_value)
|
series_start_value, do_title_case)
|
||||||
|
|
||||||
bb = BlockingBusy(_('Applying changes to %d books. This may take a while.')
|
bb = BlockingBusy(_('Applying changes to %d books. This may take a while.')
|
||||||
%len(self.ids), parent=self)
|
%len(self.ids), parent=self)
|
||||||
|
@ -270,6 +270,17 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="12" column="0" colspan="2">
|
||||||
|
<widget class="QCheckBox" name="change_title_to_title_case">
|
||||||
|
<property name="text">
|
||||||
|
<string>Change title to title case</string>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Force the title to be in title case. If both this and swap authors are checked,
|
||||||
|
title and author are swapped before the title case is set</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item row="8" column="1" colspan="2">
|
<item row="8" column="1" colspan="2">
|
||||||
<layout class="QHBoxLayout" name="HLayout_3">
|
<layout class="QHBoxLayout" name="HLayout_3">
|
||||||
<item>
|
<item>
|
||||||
@ -340,7 +351,7 @@ Future conversion of these books will use the default settings.</string>
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="12" column="0" colspan="3">
|
<item row="15" column="0" colspan="3">
|
||||||
<spacer name="verticalSpacer_2">
|
<spacer name="verticalSpacer_2">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Vertical</enum>
|
<enum>Qt::Vertical</enum>
|
||||||
|
@ -535,7 +535,8 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
|||||||
### The field-style interface. These use field keys.
|
### The field-style interface. These use field keys.
|
||||||
|
|
||||||
def get_field(self, idx, key, default=None, index_is_id=False):
|
def get_field(self, idx, key, default=None, index_is_id=False):
|
||||||
mi = self.get_metadata(idx, index_is_id=index_is_id, get_cover=True)
|
mi = self.get_metadata(idx, index_is_id=index_is_id,
|
||||||
|
get_cover=key == 'cover')
|
||||||
return mi.get(key, default)
|
return mi.get(key, default)
|
||||||
|
|
||||||
def standard_field_keys(self):
|
def standard_field_keys(self):
|
||||||
@ -590,7 +591,8 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
|||||||
mi.pubdate = self.pubdate(idx, index_is_id=index_is_id)
|
mi.pubdate = self.pubdate(idx, index_is_id=index_is_id)
|
||||||
mi.uuid = self.uuid(idx, index_is_id=index_is_id)
|
mi.uuid = self.uuid(idx, index_is_id=index_is_id)
|
||||||
mi.title_sort = self.title_sort(idx, index_is_id=index_is_id)
|
mi.title_sort = self.title_sort(idx, index_is_id=index_is_id)
|
||||||
mi.formats = self.formats(idx, index_is_id=index_is_id)
|
mi.formats = self.formats(idx, index_is_id=index_is_id,
|
||||||
|
verify_formats=False)
|
||||||
if hasattr(mi.formats, 'split'):
|
if hasattr(mi.formats, 'split'):
|
||||||
mi.formats = mi.formats.split(',')
|
mi.formats = mi.formats.split(',')
|
||||||
else:
|
else:
|
||||||
@ -731,7 +733,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
|||||||
return set([])
|
return set([])
|
||||||
return set([f[0] for f in formats])
|
return set([f[0] for f in formats])
|
||||||
|
|
||||||
def formats(self, index, index_is_id=False):
|
def formats(self, index, index_is_id=False, verify_formats=True):
|
||||||
''' Return available formats as a comma separated list or None if there are no available formats '''
|
''' Return available formats as a comma separated list or None if there are no available formats '''
|
||||||
id = index if index_is_id else self.id(index)
|
id = index if index_is_id else self.id(index)
|
||||||
try:
|
try:
|
||||||
@ -739,6 +741,8 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
|
|||||||
formats = map(lambda x:x[0], formats)
|
formats = map(lambda x:x[0], formats)
|
||||||
except:
|
except:
|
||||||
return None
|
return None
|
||||||
|
if not verify_formats:
|
||||||
|
return formats
|
||||||
ans = []
|
ans = []
|
||||||
for format in formats:
|
for format in formats:
|
||||||
if self.format_abspath(id, format, index_is_id=True) is not None:
|
if self.format_abspath(id, format, index_is_id=True) is not None:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user