mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Wireless device driver: detect future-dated books in the calibre library and refuse to sync them.
This commit is contained in:
parent
295441d9b2
commit
77c4c45612
@ -726,7 +726,7 @@ class DevicePlugin(Plugin):
|
||||
'''
|
||||
return False
|
||||
|
||||
def synchronize_with_db(self, db, book_id, book_metadata):
|
||||
def synchronize_with_db(self, db, book_id, book_metadata, first_call):
|
||||
'''
|
||||
Called during book matching when a book on the device is matched with
|
||||
a book in calibre's db. The method is responsible for syncronizing
|
||||
@ -740,23 +740,28 @@ class DevicePlugin(Plugin):
|
||||
This is useful when the calire data is correct but must be sent to the
|
||||
device.
|
||||
|
||||
The second value in the tuple specifies whether a book format should be
|
||||
sent to the device. The intent is to permit verifying that the book on
|
||||
the device is the same as the book in calibre. Return None if no book is
|
||||
to be sent, otherwise return the base file name on the device (a string
|
||||
like foobar.epub). Be sure to include the extension in the name. The
|
||||
device subsystem will construct a send_books job for all books with not-
|
||||
None returned values. Note: other than to later retrieve the extension,
|
||||
the name is ignored in cases where the device uses a template to
|
||||
generate the file name, which most do.
|
||||
The second value is itself a 2-value tuple. The first value in the tuple
|
||||
specifies whether a book format should be sent to the device. The intent
|
||||
is to permit verifying that the book on the device is the same as the
|
||||
book in calibre. This value must be None if no book is to be sent,
|
||||
otherwise return the base file name on the device (a string like
|
||||
foobar.epub). Be sure to include the extension in the name. The device
|
||||
subsystem will construct a send_books job for all books with not- None
|
||||
returned values. Note: other than to later retrieve the extension, the
|
||||
name is ignored in cases where the device uses a template to generate
|
||||
the file name, which most do. The second value in the returned tuple
|
||||
indicated whether the format is future-dated. Return True if it is,
|
||||
otherwise return False. Calibre will display a dialog to the user
|
||||
listing all future dated books.
|
||||
|
||||
Extremely important: this method is called on the GUI thread. It must
|
||||
be threadsafe with respect to the device manager's thread.
|
||||
|
||||
book_id: the calibre id for the book in the database.
|
||||
book_metadata: the Metadata object for the book coming from the device.
|
||||
first_call: True if this is the first call during a sync, False otherwise
|
||||
'''
|
||||
return None, None
|
||||
return (None, (None, False))
|
||||
|
||||
class BookList(list):
|
||||
'''
|
||||
|
@ -1546,36 +1546,49 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
|
||||
def specialize_global_preferences(self, device_prefs):
|
||||
device_prefs.set_overrides(manage_device_metadata='on_connect')
|
||||
|
||||
def _show_message(self, message):
|
||||
self._call_client("DISPLAY_MESSAGE",
|
||||
{'messageKind': self.MESSAGE_SHOW_TOAST,
|
||||
'message': message})
|
||||
|
||||
def _check_if_format_send_needed(self, db, id_, book):
|
||||
if not self.will_ask_for_update_books:
|
||||
return None
|
||||
|
||||
from calibre.utils.date import parse_date, now, isoformat
|
||||
from calibre.utils.date import parse_date, isoformat
|
||||
try:
|
||||
if not hasattr(book, '_format_mtime_'):
|
||||
return None
|
||||
|
||||
cc_mtime = parse_date(book.get('_format_mtime_'), as_utc=False)
|
||||
ext = posixpath.splitext(book.lpath)[1][1:]
|
||||
fmt_metadata = db.new_api.format_metadata(id_, ext)
|
||||
if fmt_metadata:
|
||||
calibre_mtime = fmt_metadata['mtime']
|
||||
if calibre_mtime > self.now:
|
||||
if not self.have_sent_future_dated_book_message:
|
||||
self.have_sent_future_dated_book_message = True
|
||||
self._show_message(_('You have book formats in your library '
|
||||
'with dates in the future. See calibre '
|
||||
'for details'))
|
||||
return (None, True)
|
||||
|
||||
cc_mtime = parse_date(book.get('_format_mtime_'), as_utc=False)
|
||||
self._debug(book.title, 'cal_mtime', calibre_mtime, 'cc_mtime', cc_mtime)
|
||||
if cc_mtime < calibre_mtime:
|
||||
book.set('_format_mtime_', isoformat(now()))
|
||||
return posixpath.basename(book.lpath)
|
||||
book.set('_format_mtime_', isoformat(self.now))
|
||||
return (posixpath.basename(book.lpath), False)
|
||||
except:
|
||||
self._debug('exception checking if must send format', book.title)
|
||||
traceback.print_exc()
|
||||
return None
|
||||
return (None, False)
|
||||
|
||||
@synchronous('sync_lock')
|
||||
def synchronize_with_db(self, db, id_, book):
|
||||
from calibre.utils.date import parse_date, is_date_undefined
|
||||
def show_message(message):
|
||||
self._call_client("DISPLAY_MESSAGE",
|
||||
{'messageKind': self.MESSAGE_SHOW_TOAST,
|
||||
'message': message})
|
||||
def synchronize_with_db(self, db, id_, book, first_call):
|
||||
from calibre.utils.date import parse_date, is_date_undefined, now
|
||||
|
||||
if first_call:
|
||||
self.have_sent_future_dated_book_message = False
|
||||
self.now = now()
|
||||
|
||||
if self.have_bad_sync_columns or not (self.is_read_sync_col or
|
||||
self.is_read_date_sync_col):
|
||||
@ -1589,24 +1602,24 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
|
||||
if self.is_read_sync_col:
|
||||
if self.is_read_sync_col not in fm:
|
||||
self._debug('is_read_sync_col not in field_metadata')
|
||||
show_message(_("The read sync column %s is "
|
||||
self._show_message(_("The read sync column %s is "
|
||||
"not in calibre's library")%self.is_read_sync_col)
|
||||
self.have_bad_sync_columns = True
|
||||
elif fm[self.is_read_sync_col]['datatype'] != 'bool':
|
||||
self._debug('is_read_sync_col not bool type')
|
||||
show_message(_("The read sync column %s is "
|
||||
self._show_message(_("The read sync column %s is "
|
||||
"not a Yes/No column")%self.is_read_sync_col)
|
||||
self.have_bad_sync_columns = True
|
||||
|
||||
if self.is_read_date_sync_col:
|
||||
if self.is_read_date_sync_col not in fm:
|
||||
self._debug('is_read_date_sync_col not in field_metadata')
|
||||
show_message(_("The read date sync column %s is "
|
||||
self._show_message(_("The read date sync column %s is "
|
||||
"not in calibre's library")%self.is_read_date_sync_col)
|
||||
self.have_bad_sync_columns = True
|
||||
elif fm[self.is_read_date_sync_col]['datatype'] != 'datetime':
|
||||
self._debug('is_read_date_sync_col not date type')
|
||||
show_message(_("The read date sync column %s is "
|
||||
self._show_message(_("The read date sync column %s is "
|
||||
"not a Date column")%self.is_read_date_sync_col)
|
||||
self.have_bad_sync_columns = True
|
||||
|
||||
@ -1786,6 +1799,8 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
|
||||
self.is_read_date_sync_col = None
|
||||
self.have_checked_sync_columns = False
|
||||
self.have_bad_sync_columns = False
|
||||
self.have_sent_future_dated_book_message = False
|
||||
self.now = None
|
||||
|
||||
message = None
|
||||
compression_quality_ok = True
|
||||
|
@ -1776,6 +1776,8 @@ class DeviceMixin(object): # {{{
|
||||
|
||||
book_ids_to_refresh = set()
|
||||
book_formats_to_send = []
|
||||
books_with_future_dates = []
|
||||
self._first_call_to_synchronize_with_db = True
|
||||
|
||||
def update_book(id_, book) :
|
||||
if not update_metadata:
|
||||
@ -1791,9 +1793,13 @@ class DeviceMixin(object): # {{{
|
||||
return False
|
||||
|
||||
if self.device_manager.device is not None:
|
||||
set_of_ids, fmt_name = \
|
||||
self.device_manager.device.synchronize_with_db(db, id_, book)
|
||||
if fmt_name is not None:
|
||||
set_of_ids, (fmt_name, date_bad) = \
|
||||
self.device_manager.device.synchronize_with_db(db, id_, book,
|
||||
self._first_call_to_synchronize_with_db)
|
||||
self._first_call_to_synchronize_with_db = False
|
||||
if date_bad:
|
||||
books_with_future_dates.append(book.title)
|
||||
elif fmt_name is not None:
|
||||
book_formats_to_send.append((id_, fmt_name))
|
||||
if set_of_ids is not None:
|
||||
book_ids_to_refresh.update(set_of_ids)
|
||||
@ -1946,6 +1952,19 @@ class DeviceMixin(object): # {{{
|
||||
# Shouldn't ever happen, but just in case
|
||||
traceback.print_exc()
|
||||
|
||||
# Inform user about future-dated books
|
||||
try:
|
||||
if books_with_future_dates:
|
||||
d = error_dialog(self, _('Book format sync problem'),
|
||||
_('Some book formats in your library cannot be '
|
||||
'synced because they have dates in the future'),
|
||||
det_msg='\n'.join(books_with_future_dates),
|
||||
show=False,
|
||||
show_copy_button=True)
|
||||
d.show()
|
||||
except:
|
||||
traceback.print_exc()
|
||||
|
||||
if DEBUG:
|
||||
prints('DeviceJob: set_books_in_library finished: time=',
|
||||
time.time() - start_time)
|
||||
|
Loading…
x
Reference in New Issue
Block a user