From 14537228158d25572f1e9ca7aa67922cf31e44f2 Mon Sep 17 00:00:00 2001
From: Charles Haley <>
Date: Sun, 26 Sep 2010 11:00:32 +0100
Subject: [PATCH 01/11] 1) Fix problem with editing bool custom column metadata
2) make on-send and manual metadata managment work with sonys
---
src/calibre/devices/usbms/books.py | 31 +++++++++++++++++--
.../gui2/preferences/create_custom_column.py | 2 +-
2 files changed, 29 insertions(+), 4 deletions(-)
diff --git a/src/calibre/devices/usbms/books.py b/src/calibre/devices/usbms/books.py
index 13fcb90b49..915d937379 100644
--- a/src/calibre/devices/usbms/books.py
+++ b/src/calibre/devices/usbms/books.py
@@ -111,6 +111,12 @@ class CollectionsBookList(BookList):
from calibre.devices.usbms.driver import debug_print
debug_print('Starting get_collections:', prefs['manage_device_metadata'])
debug_print('Renaming rules:', tweaks['sony_collection_renaming_rules'])
+
+ # Complexity: we can use renaming rules only when using automatic
+ # management. Otherwise we don't always have the metadata to make the
+ # right decisions
+ use_renaming_rules = prefs['manage_device_metadata'] == 'on_connect'
+
collections = {}
# This map of sets is used to avoid linear searches when testing for
# book equality
@@ -139,7 +145,16 @@ class CollectionsBookList(BookList):
attrs = collection_attributes
for attr in attrs:
attr = attr.strip()
- ign, val, orig_val, fm = book.format_field_extended(attr)
+ # If attr is device_collections, then we cannot use
+ # format_field, because we don't know the fields where the
+ # values came from.
+ if attr == 'device_collections':
+ doing_dc = True
+ val = book.device_collections # is a list
+ else:
+ doing_dc = False
+ ign, val, orig_val, fm = book.format_field_extended(attr)
+
if not val: continue
if isbytestring(val):
val = val.decode(preferred_encoding, 'replace')
@@ -151,9 +166,15 @@ class CollectionsBookList(BookList):
val = orig_val
else:
val = [val]
+
for category in val:
is_series = False
- if fm['is_custom']: # is a custom field
+ if doing_dc:
+ # Attempt to determine if this value is a series by
+ # comparing it to the series name.
+ if category == book.series:
+ is_series = True
+ elif fm['is_custom']: # is a custom field
if fm['datatype'] == 'text' and len(category) > 1 and \
category[0] == '[' and category[-1] == ']':
continue
@@ -167,7 +188,11 @@ class CollectionsBookList(BookList):
('series' in collection_attributes and
book.get('series', None) == category):
is_series = True
- cat_name = self.compute_category_name(attr, category, fm)
+ if use_renaming_rules:
+ cat_name = self.compute_category_name(attr, category, fm)
+ else:
+ cat_name = category
+
if cat_name not in collections:
collections[cat_name] = []
collections_lpaths[cat_name] = set()
diff --git a/src/calibre/gui2/preferences/create_custom_column.py b/src/calibre/gui2/preferences/create_custom_column.py
index bec21270df..ebf33784d4 100644
--- a/src/calibre/gui2/preferences/create_custom_column.py
+++ b/src/calibre/gui2/preferences/create_custom_column.py
@@ -38,7 +38,7 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
'is_multiple':False},
8:{'datatype':'bool',
'text':_('Yes/No'), 'is_multiple':False},
- 8:{'datatype':'composite',
+ 9:{'datatype':'composite',
'text':_('Column built from other columns'), 'is_multiple':False},
}
From e3781d0f15e4b7fa9dec8f55348dae129172c52f Mon Sep 17 00:00:00 2001
From: Charles Haley <>
Date: Sun, 26 Sep 2010 12:38:34 +0100
Subject: [PATCH 02/11] 1) add force renumber series to custom series bulk
editing 2) add clear series to both standard and custom series bulk editing
---
src/calibre/gui2/custom_column_widgets.py | 44 +++++--
src/calibre/gui2/dialogs/metadata_bulk.py | 8 +-
src/calibre/gui2/dialogs/metadata_bulk.ui | 137 +++++++++++++---------
3 files changed, 124 insertions(+), 65 deletions(-)
diff --git a/src/calibre/gui2/custom_column_widgets.py b/src/calibre/gui2/custom_column_widgets.py
index 90abfc2474..1d265fea1e 100644
--- a/src/calibre/gui2/custom_column_widgets.py
+++ b/src/calibre/gui2/custom_column_widgets.py
@@ -452,9 +452,25 @@ class BulkSeries(BulkBase):
self.name_widget = w
self.widgets = [QLabel('&'+self.col_metadata['name']+':', parent), w]
- self.widgets.append(QLabel(_('Automatically number books in this series'), parent))
- self.idx_widget=QCheckBox(parent)
- self.widgets.append(self.idx_widget)
+ self.widgets.append(QLabel('', parent))
+ w = QWidget(parent)
+ layout = QHBoxLayout(w)
+ layout.setContentsMargins(0, 0, 0, 0)
+ self.remove_series = QCheckBox(parent)
+ self.remove_series.setText(_('Remove series'))
+ layout.addWidget(self.remove_series)
+ self.idx_widget = QCheckBox(parent)
+ self.idx_widget.setText(_('Automatically number books'))
+ layout.addWidget(self.idx_widget)
+ self.force_number = QCheckBox(parent)
+ self.force_number.setText(_('Force numbers to start with '))
+ layout.addWidget(self.force_number)
+ self.series_start_number = QSpinBox(parent)
+ self.series_start_number.setMinimum(1)
+ self.series_start_number.setProperty("value", 1)
+ layout.addWidget(self.series_start_number)
+ layout.addItem(QSpacerItem(20, 10, QSizePolicy.Expanding, QSizePolicy.Minimum))
+ self.widgets.append(w)
def initialize(self, book_id):
self.idx_widget.setChecked(False)
@@ -465,17 +481,27 @@ class BulkSeries(BulkBase):
def getter(self):
n = unicode(self.name_widget.currentText()).strip()
i = self.idx_widget.checkState()
- return n, i
+ f = self.force_number.checkState()
+ s = self.series_start_number.value()
+ r = self.remove_series.checkState()
+ return n, i, f, s, r
def commit(self, book_ids, notify=False):
- val, update_indices = self.gui_val
- val = self.normalize_ui_val(val)
- if val != '':
+ val, update_indices, force_start, at_value, clear = self.gui_val
+ val = '' if clear else self.normalize_ui_val(val)
+ if clear or val != '':
extras = []
next_index = self.db.get_next_cc_series_num_for(val, num=self.col_id)
+ print 'cc commit next index', next_index
for book_id in book_ids:
+ if clear:
+ extras.append(None)
+ continue
if update_indices:
- if tweaks['series_index_auto_increment'] == 'next':
+ if force_start:
+ s_index = at_value
+ at_value += 1
+ elif tweaks['series_index_auto_increment'] == 'next':
s_index = next_index
next_index += 1
else:
@@ -483,6 +509,8 @@ class BulkSeries(BulkBase):
else:
s_index = self.db.get_custom_extra(book_id, num=self.col_id,
index_is_id=True)
+ if s_index is None:
+ s_index = 1.0
extras.append(s_index)
self.db.set_custom_bulk(book_ids, val, extras=extras,
num=self.col_id, notify=notify)
diff --git a/src/calibre/gui2/dialogs/metadata_bulk.py b/src/calibre/gui2/dialogs/metadata_bulk.py
index b14390e001..9c83b3aee5 100644
--- a/src/calibre/gui2/dialogs/metadata_bulk.py
+++ b/src/calibre/gui2/dialogs/metadata_bulk.py
@@ -32,7 +32,7 @@ class Worker(Thread):
remove, add, au, aus, do_aus, rating, pub, do_series, \
do_autonumber, do_remove_format, remove_format, do_swap_ta, \
do_remove_conv, do_auto_author, series, do_series_restart, \
- series_start_value, do_title_case = self.args
+ series_start_value, do_title_case, clear_series = self.args
# 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
@@ -75,6 +75,9 @@ class Worker(Thread):
if pub:
self.db.set_publisher(id, pub, notify=False, commit=False)
+ if clear_series:
+ self.db.set_series(id, '', notify=False, commit=False)
+
if do_series:
if do_series_restart:
next = series_start_value
@@ -592,6 +595,7 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
rating = self.rating.value()
pub = unicode(self.publisher.text())
do_series = self.write_series
+ clear_series = self.clear_series.isChecked()
series = unicode(self.series.currentText()).strip()
do_autonumber = self.autonumber_series.isChecked()
do_series_restart = self.series_numbering_restarts.isChecked()
@@ -606,7 +610,7 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
args = (remove, add, au, aus, do_aus, rating, pub, do_series,
do_autonumber, do_remove_format, remove_format, do_swap_ta,
do_remove_conv, do_auto_author, series, do_series_restart,
- series_start_value, do_title_case)
+ series_start_value, do_title_case, clear_series)
bb = BlockingBusy(_('Applying changes to %d books. This may take a while.')
%len(self.ids), parent=self)
diff --git a/src/calibre/gui2/dialogs/metadata_bulk.ui b/src/calibre/gui2/dialogs/metadata_bulk.ui
index e03a59b7ea..60e24dbceb 100644
--- a/src/calibre/gui2/dialogs/metadata_bulk.ui
+++ b/src/calibre/gui2/dialogs/metadata_bulk.ui
@@ -225,61 +225,50 @@
-
-
-
- List of known series. You can add new series.
-
-
- List of known series. You can add new series.
-
-
- true
-
-
- QComboBox::InsertAlphabetically
-
-
- QComboBox::AdjustToContents
-
-
-
- -
-
-
- Remove &format:
-
-
- remove_format
-
-
-
- -
-
-
- -
-
-
- true
-
-
-
- -
-
-
- &Swap title and author
-
-
-
- -
-
-
- Change title to title case
-
-
- 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
-
-
+
+
-
+
+
+ List of known series. You can add new series.
+
+
+ List of known series. You can add new series.
+
+
+ true
+
+
+ QComboBox::InsertAlphabetically
+
+
+ QComboBox::AdjustToContents
+
+
+
+ -
+
+
+ If checked, the series will be cleared
+
+
+ Clear series
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 20
+ 00
+
+
+
+
+
-
@@ -339,6 +328,44 @@ from the value in the box
+ -
+
+
+ Remove &format:
+
+
+ remove_format
+
+
+
+ -
+
+
+ -
+
+
+ true
+
+
+
+ -
+
+
+ &Swap title and author
+
+
+
+ -
+
+
+ Change title to title case
+
+
+ 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
+
+
+
-
From 3e1cb3b5e08c98105f7ce1636f454619fdde3879 Mon Sep 17 00:00:00 2001
From: Charles Haley <>
Date: Sun, 26 Sep 2010 13:07:22 +0100
Subject: [PATCH 03/11] Make covercache and backup stoppable.
---
src/calibre/gui2/library/models.py | 2 ++
src/calibre/library/caches.py | 4 ++--
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/src/calibre/gui2/library/models.py b/src/calibre/gui2/library/models.py
index a2555cfc56..ef251a884a 100644
--- a/src/calibre/gui2/library/models.py
+++ b/src/calibre/gui2/library/models.py
@@ -156,6 +156,8 @@ class BooksModel(QAbstractTableModel): # {{{
self.cover_cache.stop()
self.cover_cache = CoverCache(db, FunctionDispatcher(self.db.cover))
self.cover_cache.start()
+ if self.metadata_backup is not None:
+ self.metadata_backup.stop()
self.metadata_backup = MetadataBackup(db)
self.metadata_backup.start()
def refresh_cover(event, ids):
diff --git a/src/calibre/library/caches.py b/src/calibre/library/caches.py
index bc16681f81..8d449974a5 100644
--- a/src/calibre/library/caches.py
+++ b/src/calibre/library/caches.py
@@ -43,7 +43,7 @@ class MetadataBackup(Thread): # {{{
def run(self):
while self.keep_running:
try:
- id_ = self.db.dirtied_queue.get()
+ id_ = self.db.dirtied_queue.get(True, 2)
except Empty:
continue
except:
@@ -122,7 +122,7 @@ class CoverCache(Thread): # {{{
def run(self):
while self.keep_running:
try:
- id_ = self.load_queue.get()
+ id_ = self.load_queue.get(True, 2)
except Empty:
continue
except:
From c8dbd705467ed6d15bc443939c06997fcee5b91b Mon Sep 17 00:00:00 2001
From: Charles Haley <>
Date: Sun, 26 Sep 2010 13:48:30 +0100
Subject: [PATCH 04/11] metadata backup that gets metadata on the GUI thread,
computes the OPF on a separate thread, then writes the file on the GUI
thread.
---
src/calibre/gui2/library/models.py | 1 +
src/calibre/library/caches.py | 26 ++++++++++++++++++--------
src/calibre/library/database2.py | 23 +++++++++++++++++++++++
3 files changed, 42 insertions(+), 8 deletions(-)
diff --git a/src/calibre/gui2/library/models.py b/src/calibre/gui2/library/models.py
index ef251a884a..b908019bcb 100644
--- a/src/calibre/gui2/library/models.py
+++ b/src/calibre/gui2/library/models.py
@@ -89,6 +89,7 @@ class BooksModel(QAbstractTableModel): # {{{
self.alignment_map = {}
self.buffer_size = buffer
self.cover_cache = None
+ self.metadata_backup = None
self.bool_yes_icon = QIcon(I('ok.png'))
self.bool_no_icon = QIcon(I('list_remove.png'))
self.bool_blank_icon = QIcon(I('blank.png'))
diff --git a/src/calibre/library/caches.py b/src/calibre/library/caches.py
index 8d449974a5..7d8a8624a9 100644
--- a/src/calibre/library/caches.py
+++ b/src/calibre/library/caches.py
@@ -19,6 +19,7 @@ from calibre.utils.date import parse_date, now, UNDEFINED_DATE
from calibre.utils.search_query_parser import SearchQueryParser
from calibre.utils.pyparsing import ParseException
from calibre.ebooks.metadata import title_sort
+from calibre.ebooks.metadata.opf2 import metadata_to_opf
from calibre import fit_image, prints
class MetadataBackup(Thread): # {{{
@@ -36,6 +37,8 @@ class MetadataBackup(Thread): # {{{
self.keep_running = True
from calibre.gui2 import FunctionDispatcher
self.do_write = FunctionDispatcher(self.write)
+ self.get_metadata_for_dump = FunctionDispatcher(db.get_metadata_for_dump)
+ self.clear_dirtied = FunctionDispatcher(db.clear_dirtied)
def stop(self):
self.keep_running = False
@@ -43,6 +46,7 @@ class MetadataBackup(Thread): # {{{
def run(self):
while self.keep_running:
try:
+ time.sleep(0.5) # Limit to two per second
id_ = self.db.dirtied_queue.get(True, 2)
except Empty:
continue
@@ -50,25 +54,27 @@ class MetadataBackup(Thread): # {{{
# Happens during interpreter shutdown
break
- dump = []
try:
- self.db.dump_metadata([id_], dump_to=dump)
+ path, mi = self.get_metadata_for_dump(id_)
except:
prints('Failed to get backup metadata for id:', id_, 'once')
import traceback
traceback.print_exc()
time.sleep(2)
- dump = []
try:
- self.db.dump_metadata([id_], dump_to=dump)
+ path, mi = self.get_metadata_for_dump(id_)
except:
prints('Failed to get backup metadata for id:', id_, 'again, giving up')
traceback.print_exc()
continue
try:
- path, raw = dump[0]
+ print 'now do metadata'
+ raw = metadata_to_opf(mi)
except:
- break
+ prints('Failed to convert to opf for id:', id_)
+ traceback.print_exc()
+ continue
+
try:
self.do_write(path, raw)
except:
@@ -79,8 +85,12 @@ class MetadataBackup(Thread): # {{{
except:
prints('Failed to write backup metadata for id:', id_,
'again, giving up')
+ continue
- time.sleep(0.5) # Limit to two per second
+ try:
+ self.clear_dirtied([id_])
+ except:
+ prints('Failed to clear dirtied for id:', id_)
def write(self, path, raw):
with open(path, 'wb') as f:
@@ -106,7 +116,6 @@ class CoverCache(Thread): # {{{
self.keep_running = False
def _image_for_id(self, id_):
- time.sleep(0.050) # Limit 20/second to not overwhelm the GUI
img = self.cover_func(id_, index_is_id=True, as_image=True)
if img is None:
img = QImage()
@@ -122,6 +131,7 @@ class CoverCache(Thread): # {{{
def run(self):
while self.keep_running:
try:
+ time.sleep(0.050) # Limit 20/second to not overwhelm the GUI
id_ = self.load_queue.get(True, 2)
except Empty:
continue
diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py
index a6f3f286bc..e6587f06a2 100644
--- a/src/calibre/library/database2.py
+++ b/src/calibre/library/database2.py
@@ -566,6 +566,24 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
def metadata_for_field(self, key):
return self.field_metadata[key]
+ def clear_dirtied(self, book_ids=None):
+ '''
+ Clear the dirtied indicator for the books. This is used when fetching
+ metadata, creating an OPF, and writing a file are separated into steps.
+ The last step is clearing the indicator
+ '''
+ for book_id in book_ids:
+ if not self.data.has_id(book_id):
+ continue
+ self.conn.execute('DELETE FROM metadata_dirtied WHERE book=?',
+ (book_id,))
+ # if a later exception prevents the commit, then the dirtied
+ # table will still have the book. No big deal, because the OPF
+ # is there and correct. We will simply do it again on next
+ # start
+ self.dirtied_cache.discard(book_id)
+ self.conn.commit()
+
def dump_metadata(self, book_ids=None, remove_from_dirtied=True,
commit=True, dump_to=None):
'''
@@ -638,6 +656,11 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
self.dirtied_cache = set()
self.dirtied(book_ids)
+ def get_metadata_for_dump(self, idx):
+ path = os.path.join(self.abspath(idx, index_is_id=True), 'metadata.opf')
+ mi = self.get_metadata(idx, index_is_id=True)
+ return ((path, mi))
+
def get_metadata(self, idx, index_is_id=False, get_cover=False):
'''
Convenience method to return metadata as a :class:`Metadata` object.
From 12f75ddaf86e89c03013c75531aa1b5f25f7409a Mon Sep 17 00:00:00 2001
From: Charles Haley <>
Date: Sun, 26 Sep 2010 13:56:31 +0100
Subject: [PATCH 05/11] Note why we don't do a join where we should.
---
src/calibre/gui2/library/models.py | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/src/calibre/gui2/library/models.py b/src/calibre/gui2/library/models.py
index b908019bcb..ff6d8b70f0 100644
--- a/src/calibre/gui2/library/models.py
+++ b/src/calibre/gui2/library/models.py
@@ -155,10 +155,14 @@ class BooksModel(QAbstractTableModel): # {{{
self.database_changed.emit(db)
if self.cover_cache is not None:
self.cover_cache.stop()
+ # Would like to to a join here, but the thread might be waiting to
+ # do something on the GUI thread. Deadlock.
self.cover_cache = CoverCache(db, FunctionDispatcher(self.db.cover))
self.cover_cache.start()
if self.metadata_backup is not None:
self.metadata_backup.stop()
+ # Would like to to a join here, but the thread might be waiting to
+ # do something on the GUI thread. Deadlock.
self.metadata_backup = MetadataBackup(db)
self.metadata_backup.start()
def refresh_cover(event, ids):
From f6870bd14b6cdf9041401a7f7c2ac02385bb4ae1 Mon Sep 17 00:00:00 2001
From: Charles Haley <>
Date: Sun, 26 Sep 2010 14:06:56 +0100
Subject: [PATCH 06/11] Introduce scheduling opportunities into the backup
thread
---
src/calibre/library/caches.py | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/src/calibre/library/caches.py b/src/calibre/library/caches.py
index 7d8a8624a9..a9ef6a4281 100644
--- a/src/calibre/library/caches.py
+++ b/src/calibre/library/caches.py
@@ -67,6 +67,11 @@ class MetadataBackup(Thread): # {{{
prints('Failed to get backup metadata for id:', id_, 'again, giving up')
traceback.print_exc()
continue
+
+ # Give the GUI thread a chance to do something. Python threads don't
+ # have priorities, so this thread would naturally keep the processor
+ # until some scheduling event happens. The sleep makes such an event
+ time.sleep(0.010)
try:
print 'now do metadata'
raw = metadata_to_opf(mi)
@@ -75,6 +80,7 @@ class MetadataBackup(Thread): # {{{
traceback.print_exc()
continue
+ time.sleep(0.010) # Give the GUI thread a chance to do something
try:
self.do_write(path, raw)
except:
@@ -87,6 +93,7 @@ class MetadataBackup(Thread): # {{{
'again, giving up')
continue
+ time.sleep(0.010) # Give the GUI thread a chance to do something
try:
self.clear_dirtied([id_])
except:
From 66ed343b1185f4a42976be447cfe0313e5223803 Mon Sep 17 00:00:00 2001
From: Charles Haley <>
Date: Sun, 26 Sep 2010 14:51:25 +0100
Subject: [PATCH 07/11] 1) Remove a print statement 2) fix series formatting
---
src/calibre/ebooks/metadata/book/base.py | 4 ++++
src/calibre/library/caches.py | 9 ++++-----
2 files changed, 8 insertions(+), 5 deletions(-)
diff --git a/src/calibre/ebooks/metadata/book/base.py b/src/calibre/ebooks/metadata/book/base.py
index fd7ce8a6c3..0526de96a0 100644
--- a/src/calibre/ebooks/metadata/book/base.py
+++ b/src/calibre/ebooks/metadata/book/base.py
@@ -455,6 +455,8 @@ class Metadata(object):
res = format_date(res, cmeta['display'].get('date_format','dd MMM yyyy'))
elif datatype == 'bool':
res = _('Yes') if res else _('No')
+ elif datatype == 'float' and key.endswith('_index'):
+ res = self.format_series_index(res)
return (name, unicode(res), orig_res, cmeta)
if key in field_metadata and field_metadata[key]['kind'] == 'field':
@@ -468,6 +470,8 @@ class Metadata(object):
datatype = fmeta['datatype']
if key == 'authors':
res = authors_to_string(res)
+ elif key == 'series_index':
+ res = self.format_series_index(res)
elif datatype == 'text' and fmeta['is_multiple']:
res = u', '.join(res)
elif datatype == 'series' and series_with_index:
diff --git a/src/calibre/library/caches.py b/src/calibre/library/caches.py
index a9ef6a4281..74be3cd594 100644
--- a/src/calibre/library/caches.py
+++ b/src/calibre/library/caches.py
@@ -53,7 +53,7 @@ class MetadataBackup(Thread): # {{{
except:
# Happens during interpreter shutdown
break
-
+ print 'doing id', id_
try:
path, mi = self.get_metadata_for_dump(id_)
except:
@@ -71,16 +71,15 @@ class MetadataBackup(Thread): # {{{
# Give the GUI thread a chance to do something. Python threads don't
# have priorities, so this thread would naturally keep the processor
# until some scheduling event happens. The sleep makes such an event
- time.sleep(0.010)
+ time.sleep(0.1)
try:
- print 'now do metadata'
raw = metadata_to_opf(mi)
except:
prints('Failed to convert to opf for id:', id_)
traceback.print_exc()
continue
- time.sleep(0.010) # Give the GUI thread a chance to do something
+ time.sleep(0.1) # Give the GUI thread a chance to do something
try:
self.do_write(path, raw)
except:
@@ -93,7 +92,7 @@ class MetadataBackup(Thread): # {{{
'again, giving up')
continue
- time.sleep(0.010) # Give the GUI thread a chance to do something
+ time.sleep(0.1) # Give the GUI thread a chance to do something
try:
self.clear_dirtied([id_])
except:
From 87020e38be2441b7352a9c04dc8587547fdc6f53 Mon Sep 17 00:00:00 2001
From: Charles Haley <>
Date: Sun, 26 Sep 2010 15:49:15 +0100
Subject: [PATCH 08/11] Ensure that cached Metadata copies contain valid cover
info when get_metadata is called with get_cover = True
---
src/calibre/library/database2.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py
index e6587f06a2..0943c86e43 100644
--- a/src/calibre/library/database2.py
+++ b/src/calibre/library/database2.py
@@ -670,6 +670,8 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
mi = self.data.get(idx, self.FIELD_MAP['all_metadata'],
row_is_id = index_is_id)
if mi is not None:
+ if get_cover and mi.cover is None:
+ mi.cover = self.cover(idx, index_is_id=index_is_id, as_path=True)
return mi
self.gm_missed += 1
From 88cb23d952bdb2578418808feb6cc823da000e16 Mon Sep 17 00:00:00 2001
From: Charles Haley <>
Date: Sun, 26 Sep 2010 15:54:29 +0100
Subject: [PATCH 09/11] Take out print statement
---
src/calibre/library/caches.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/calibre/library/caches.py b/src/calibre/library/caches.py
index 74be3cd594..cdf0c1fce6 100644
--- a/src/calibre/library/caches.py
+++ b/src/calibre/library/caches.py
@@ -53,7 +53,6 @@ class MetadataBackup(Thread): # {{{
except:
# Happens during interpreter shutdown
break
- print 'doing id', id_
try:
path, mi = self.get_metadata_for_dump(id_)
except:
From 1169eaef9959fab39d0631bbb77c6f86c3c23cd8 Mon Sep 17 00:00:00 2001
From: Charles Haley <>
Date: Sun, 26 Sep 2010 16:26:45 +0100
Subject: [PATCH 10/11] Fix problem trying to back up metadata for a deleted
book Add a 'backup all' button
---
src/calibre/gui2/preferences/misc.py | 6 ++++++
src/calibre/gui2/preferences/misc.ui | 11 +++++++++--
src/calibre/library/caches.py | 7 ++++++-
src/calibre/library/database2.py | 7 +++++--
4 files changed, 26 insertions(+), 5 deletions(-)
diff --git a/src/calibre/gui2/preferences/misc.py b/src/calibre/gui2/preferences/misc.py
index eae79fdfc0..e749a6fb98 100644
--- a/src/calibre/gui2/preferences/misc.py
+++ b/src/calibre/gui2/preferences/misc.py
@@ -88,10 +88,16 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
r('enforce_cpu_limit', config, restart_required=True)
self.device_detection_button.clicked.connect(self.debug_device_detection)
self.compact_button.clicked.connect(self.compact)
+ self.button_all_books_dirty.clicked.connect(self.mark_dirty)
self.button_open_config_dir.clicked.connect(self.open_config_dir)
self.button_osx_symlinks.clicked.connect(self.create_symlinks)
self.button_osx_symlinks.setVisible(isosx)
+ def mark_dirty(self):
+ db = self.gui.library_view.model().db
+ ids = [id for id in db.data.iterallids()]
+ db.dirtied(ids)
+
def debug_device_detection(self, *args):
from calibre.gui2.preferences.device_debug import DebugDevice
d = DebugDevice(self)
diff --git a/src/calibre/gui2/preferences/misc.ui b/src/calibre/gui2/preferences/misc.ui
index f8582a3675..573c61aba5 100644
--- a/src/calibre/gui2/preferences/misc.ui
+++ b/src/calibre/gui2/preferences/misc.ui
@@ -124,7 +124,14 @@
- -
+
-
+
+
+ Force saving metadata of all books
+
+
+
+ -
Qt::Vertical
@@ -132,7 +139,7 @@
20
- 18
+ 1000
diff --git a/src/calibre/library/caches.py b/src/calibre/library/caches.py
index cdf0c1fce6..9d6f87324f 100644
--- a/src/calibre/library/caches.py
+++ b/src/calibre/library/caches.py
@@ -44,6 +44,7 @@ class MetadataBackup(Thread): # {{{
self.keep_running = False
def run(self):
+ import traceback
while self.keep_running:
try:
time.sleep(0.5) # Limit to two per second
@@ -53,11 +54,11 @@ class MetadataBackup(Thread): # {{{
except:
# Happens during interpreter shutdown
break
+
try:
path, mi = self.get_metadata_for_dump(id_)
except:
prints('Failed to get backup metadata for id:', id_, 'once')
- import traceback
traceback.print_exc()
time.sleep(2)
try:
@@ -67,6 +68,10 @@ class MetadataBackup(Thread): # {{{
traceback.print_exc()
continue
+ if mi is None:
+ self.clear_dirtied([id_])
+ continue
+
# Give the GUI thread a chance to do something. Python threads don't
# have priorities, so this thread would naturally keep the processor
# until some scheduling event happens. The sleep makes such an event
diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py
index 0943c86e43..6f628d8454 100644
--- a/src/calibre/library/database2.py
+++ b/src/calibre/library/database2.py
@@ -657,8 +657,11 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
self.dirtied(book_ids)
def get_metadata_for_dump(self, idx):
- path = os.path.join(self.abspath(idx, index_is_id=True), 'metadata.opf')
- mi = self.get_metadata(idx, index_is_id=True)
+ try:
+ path = os.path.join(self.abspath(idx, index_is_id=True), 'metadata.opf')
+ mi = self.get_metadata(idx, index_is_id=True)
+ except:
+ return ((None, None))
return ((path, mi))
def get_metadata(self, idx, index_is_id=False, get_cover=False):
From 2ea859906ded2ef2980502cccf07df1bb5d1f80c Mon Sep 17 00:00:00 2001
From: Charles Haley <>
Date: Sun, 26 Sep 2010 16:40:46 +0100
Subject: [PATCH 11/11] Change text on backup button
---
src/calibre/gui2/preferences/misc.ui | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/calibre/gui2/preferences/misc.ui b/src/calibre/gui2/preferences/misc.ui
index 573c61aba5..492540901d 100644
--- a/src/calibre/gui2/preferences/misc.ui
+++ b/src/calibre/gui2/preferences/misc.ui
@@ -127,7 +127,7 @@
-
- Force saving metadata of all books
+ Back up metadata of all books (while you are working)