mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
1) Change the new sync method to return a list of changed book ids instead of a simple boolean.
2) Make the wireless device driver synchronization more robust in the face of multiple libraries where the sync column might not always exist.
This commit is contained in:
parent
8fe4db7040
commit
deea793c21
@ -724,8 +724,12 @@ class DevicePlugin(Plugin):
|
|||||||
a book in calibre's db. The method is responsible for syncronizing
|
a book in calibre's db. The method is responsible for syncronizing
|
||||||
data from the device to calibre's db (if needed).
|
data from the device to calibre's db (if needed).
|
||||||
|
|
||||||
The method must return True if either calibre's database or the device
|
The method must return a set of calibre book ids changed if calibre's
|
||||||
book's metadata were changed, False otherwise.
|
database was changed, None if the database was not changed. If the
|
||||||
|
method returns an empty set then the metadata for the book on the
|
||||||
|
device is updated with calibre's metadata and given back to the device,
|
||||||
|
but no GUI refresh of that book is done. This is useful when the calire
|
||||||
|
data is correct but must be sent to the device.
|
||||||
|
|
||||||
Extremely important: this method is called on the GUI thread. It must
|
Extremely important: this method is called on the GUI thread. It must
|
||||||
be threadsafe with respect to the device manager's thread.
|
be threadsafe with respect to the device manager's thread.
|
||||||
@ -733,7 +737,7 @@ class DevicePlugin(Plugin):
|
|||||||
book_id: the calibre id for the book in the database.
|
book_id: the calibre id for the book in the database.
|
||||||
book_metadata: the Metadata object for the book coming from the device.
|
book_metadata: the Metadata object for the book coming from the device.
|
||||||
'''
|
'''
|
||||||
return False
|
return None
|
||||||
|
|
||||||
class BookList(list):
|
class BookList(list):
|
||||||
'''
|
'''
|
||||||
|
@ -1295,7 +1295,8 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
|
|||||||
# given back by "books", and one that has been plugboarded.
|
# given back by "books", and one that has been plugboarded.
|
||||||
books_to_send = []
|
books_to_send = []
|
||||||
for book in booklists[0]:
|
for book in booklists[0]:
|
||||||
if not self._metadata_already_on_device(book):
|
if (book.get('_force_send_metadata_', None) or
|
||||||
|
not self._metadata_already_on_device(book)):
|
||||||
books_to_send.append(book)
|
books_to_send.append(book)
|
||||||
|
|
||||||
count = len(books_to_send)
|
count = len(books_to_send)
|
||||||
@ -1459,38 +1460,63 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
|
|||||||
|
|
||||||
@synchronous('sync_lock')
|
@synchronous('sync_lock')
|
||||||
def synchronize_with_db(self, db, id_, book):
|
def synchronize_with_db(self, db, id_, book):
|
||||||
|
if not (self.is_read_sync_col or self.is_read_date_sync_col):
|
||||||
|
# Not syncing
|
||||||
|
return None
|
||||||
|
|
||||||
is_changed = book.get('_is_read_changed_', None);
|
is_changed = book.get('_is_read_changed_', None);
|
||||||
if is_changed:
|
is_read = book.get('_is_read_', None)
|
||||||
made_changes = False
|
|
||||||
# is_read_changed == 1: standard sync. Update calibre with the new value
|
if is_changed == 2 and is_read is None:
|
||||||
# is_read_changed == 2: special first-time sync after specifying the
|
# This is a special case where the user just set the sync column. In
|
||||||
# column. Update calibre if CC's value is not null, else update CC
|
# this case the device value wins if it is not None by falling
|
||||||
# if calibre's value is not null
|
# through to the normal sync situation below, otherwise the calibre
|
||||||
val = book.get('_is_read_', None)
|
# value wins.
|
||||||
if is_changed == 1 or (is_changed == 2 and val):
|
calibre_val = db.new_api.field_for(self.is_read_sync_col,
|
||||||
self._debug('standard update book', book.get('title', 'huh?'), 'to', val)
|
id_, default_value=None)
|
||||||
if self.is_read_sync_col:
|
if calibre_val is not None:
|
||||||
db.new_api.set_field(self.is_read_sync_col, {id_: val})
|
# This will force the metadata for the book to be sent . Note
|
||||||
made_changes = True
|
# that because the devices last_read date is one-way sync, this
|
||||||
if self.is_read_date_sync_col:
|
# could leave an empty date in the device.
|
||||||
db.new_api.set_field(self.is_read_date_sync_col,
|
book.set('_force_send_metadata_', True)
|
||||||
{id_: book.get('_last_read_date_', None)})
|
self._debug('special update book', book.get('title', 'huh?'),
|
||||||
made_changes = True
|
'to', calibre_val)
|
||||||
elif self.is_read_sync_col and is_changed == 2 and not val:
|
return set(id_)
|
||||||
calibre_val = db.new_api.field_for(self.is_read_sync_col,
|
# Both values are None. Do nothing
|
||||||
id_, default_value=None)
|
return None
|
||||||
if calibre_val:
|
|
||||||
from calibre.utils.date import UNDEFINED_DATE
|
orig_is_read = book.get(self.is_read_sync_col, None)
|
||||||
# This will force the metadata for the book to be sent even
|
if is_read != orig_is_read:
|
||||||
# if the last_mod dates matched before. Note that because
|
# The value in the device's is_read checkbox is not the same as the
|
||||||
# CC's last_read date is one-way sync, this could leave an
|
# last one that came to the device from calibre during the last
|
||||||
# empty date in CC.
|
# connect, meaning that the user changed it. Write the one from the
|
||||||
self._debug('special update book', book.get('title', 'huh?'),
|
# checkbox to calibre's db.
|
||||||
'to', calibre_val)
|
changed_books = set()
|
||||||
book.set('last_modified', UNDEFINED_DATE)
|
is_read_date = book.get('_last_read_date_', None);
|
||||||
book.set('_is_read_changed_', None)
|
self._debug('standard update book', book.get('title', 'huh?'), 'to',
|
||||||
return made_changes
|
is_read, is_read_date)
|
||||||
return False
|
if self.is_read_sync_col:
|
||||||
|
try:
|
||||||
|
changed_books = db.new_api.set_field(self.is_read_sync_col,
|
||||||
|
{id_: is_read})
|
||||||
|
except:
|
||||||
|
self._debug('setting read sync col tossed exception',
|
||||||
|
self.is_read_sync_col)
|
||||||
|
if self.is_read_date_sync_col:
|
||||||
|
try:
|
||||||
|
changed_books |= db.new_api.set_field(self.is_read_date_sync_col,
|
||||||
|
{id_: is_read_date})
|
||||||
|
except:
|
||||||
|
self._debug('setting read date sync col tossed exception',
|
||||||
|
self.is_read_date_sync_col)
|
||||||
|
return changed_books
|
||||||
|
|
||||||
|
# The user might have changed the value in calibre. If so, that value
|
||||||
|
# will be sent to the device in the normal way. Note that because any
|
||||||
|
# updated value has already been synced and so will also be sent, the
|
||||||
|
# device should put the calibre value into its checkbox (or whatever it
|
||||||
|
# uses)
|
||||||
|
return None
|
||||||
|
|
||||||
@synchronous('sync_lock')
|
@synchronous('sync_lock')
|
||||||
def startup(self):
|
def startup(self):
|
||||||
|
@ -1751,12 +1751,9 @@ class DeviceMixin(object): # {{{
|
|||||||
self.db_book_title_cache = db_book_title_cache
|
self.db_book_title_cache = db_book_title_cache
|
||||||
self.db_book_uuid_cache = db_book_uuid_cache
|
self.db_book_uuid_cache = db_book_uuid_cache
|
||||||
|
|
||||||
# Now iterate through all the books on the device, setting the
|
# This is really a local variable, but it must be made into a class
|
||||||
# in_library field. If the UUID matches a book in the library, then
|
# attribute so that the updateq function (below) can change it.
|
||||||
# do not consider that book for other matching. In all cases set
|
self.sbil_book_ids_to_refresh = set()
|
||||||
# the application_id to the db_id of the matching book. This value
|
|
||||||
# will be used by books_on_device to indicate matches. While we are
|
|
||||||
# going by, update the metadata for a book if automatic management is on
|
|
||||||
|
|
||||||
def update_book(id_, book) :
|
def update_book(id_, book) :
|
||||||
if not update_metadata:
|
if not update_metadata:
|
||||||
@ -1768,21 +1765,31 @@ class DeviceMixin(object): # {{{
|
|||||||
|
|
||||||
def updateq(id_, book):
|
def updateq(id_, book):
|
||||||
try:
|
try:
|
||||||
return (update_metadata and
|
if not update_metadata:
|
||||||
(
|
return False
|
||||||
(self.device_manager.device is not None and
|
|
||||||
self.device_manager.device.synchronize_with_db(db, id_, book)) or
|
if self.device_manager.device is not None:
|
||||||
(db.metadata_last_modified(id_, index_is_id=True) !=
|
set_of_ids = self.device_manager.device.synchronize_with_db(db, id_, book)
|
||||||
getattr(book, 'last_modified', None) or
|
if set_of_ids is not None:
|
||||||
(isinstance(getattr(book, 'thumbnail', None), (list, tuple))
|
self.sbil_book_ids_to_refresh |= set_of_ids
|
||||||
and max(book.thumbnail[0], book.thumbnail[1]) != desired_thumbnail_height
|
return True
|
||||||
)
|
|
||||||
)
|
return (db.metadata_last_modified(id_, index_is_id=True) !=
|
||||||
|
getattr(book, 'last_modified', None) or
|
||||||
|
(isinstance(getattr(book, 'thumbnail', None), (list, tuple))
|
||||||
|
and max(book.thumbnail[0], book.thumbnail[1]) != desired_thumbnail_height
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
except:
|
except:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
# Now iterate through all the books on the device, setting the
|
||||||
|
# in_library field. If the UUID matches a book in the library, then
|
||||||
|
# do not consider that book for other matching. In all cases set
|
||||||
|
# the application_id to the db_id of the matching book. This value
|
||||||
|
# will be used by books_on_device to indicate matches. While we are
|
||||||
|
# going by, update the metadata for a book if automatic management is on
|
||||||
|
|
||||||
total_book_count = 0
|
total_book_count = 0
|
||||||
for booklist in booklists:
|
for booklist in booklists:
|
||||||
for book in booklist:
|
for book in booklist:
|
||||||
@ -1877,6 +1884,15 @@ class DeviceMixin(object): # {{{
|
|||||||
FunctionDispatcher(self.metadata_synced), booklists,
|
FunctionDispatcher(self.metadata_synced), booklists,
|
||||||
plugboards, add_as_step_to_job)
|
plugboards, add_as_step_to_job)
|
||||||
|
|
||||||
|
if self.sbil_book_ids_to_refresh:
|
||||||
|
try:
|
||||||
|
prints('DeviceJob: set_books_in_library refreshing GUI for ',
|
||||||
|
len(self.sbil_book_ids_to_refresh), 'books')
|
||||||
|
self.library_view.model().refresh_ids(self.sbil_book_ids_to_refresh)
|
||||||
|
except:
|
||||||
|
# This shouldn't ever happen, but just in case ...
|
||||||
|
pass
|
||||||
|
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
prints('DeviceJob: set_books_in_library finished: time=',
|
prints('DeviceJob: set_books_in_library finished: time=',
|
||||||
time.time() - start_time)
|
time.time() - start_time)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user