This commit is contained in:
Kovid Goyal 2014-03-03 08:43:25 +05:30
commit 2d406eb2a1

View File

@ -355,8 +355,6 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
self.debug_start_time = time.time() self.debug_start_time = time.time()
self.debug_time = time.time() self.debug_time = time.time()
# This must be protected by a lock because it is called from three threads
@synchronous('sync_lock')
def _debug(self, *args): def _debug(self, *args):
# manual synchronization so we don't lose the calling method name # manual synchronization so we don't lose the calling method name
import inspect import inspect
@ -694,8 +692,8 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
return None return None
def _metadata_in_cache(self, uuid, ext_or_lpath, lastmod): def _metadata_in_cache(self, uuid, ext_or_lpath, lastmod):
from calibre.utils.date import now, parse_date
try: try:
from calibre.utils.date import parse_date, now
key = self._make_metadata_cache_key(uuid, ext_or_lpath) key = self._make_metadata_cache_key(uuid, ext_or_lpath)
if isinstance(lastmod, unicode): if isinstance(lastmod, unicode):
if lastmod == 'None': if lastmod == 'None':
@ -795,8 +793,8 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
traceback.print_exc() traceback.print_exc()
def _write_metadata_cache(self): def _write_metadata_cache(self):
self._debug()
from calibre.utils.date import now from calibre.utils.date import now
self._debug()
from calibre.utils.config import to_json from calibre.utils.config import to_json
cache_file_name = os.path.join(cache_dir(), cache_file_name = os.path.join(cache_dir(),
'wireless_device_' + self.device_uuid + 'wireless_device_' + self.device_uuid +
@ -990,7 +988,8 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
'currentLibraryUUID': library_uuid, 'currentLibraryUUID': library_uuid,
'pubdateFormat': tweaks['gui_pubdate_display_format'], 'pubdateFormat': tweaks['gui_pubdate_display_format'],
'timestampFormat': tweaks['gui_timestamp_display_format'], 'timestampFormat': tweaks['gui_timestamp_display_format'],
'lastModifiedFormat': tweaks['gui_last_modified_display_format']}) 'lastModifiedFormat': tweaks['gui_last_modified_display_format'],
'calibre_version': numeric_version})
if opcode != 'OK': if opcode != 'OK':
# Something wrong with the return. Close the socket # Something wrong with the return. Close the socket
# and continue. # and continue.
@ -1089,7 +1088,7 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
self.is_read_sync_col = result.get('isReadSyncCol', None) self.is_read_sync_col = result.get('isReadSyncCol', None)
self._debug('Device is_read sync col', self.is_read_sync_col) self._debug('Device is_read sync col', self.is_read_sync_col)
self.is_read_date_sync_col = result.get('isReadDateSyncCol', False) self.is_read_date_sync_col = result.get('isReadDateSyncCol', None)
self._debug('Device is_read_date sync col', self.is_read_date_sync_col) self._debug('Device is_read_date sync col', self.is_read_date_sync_col)
if password: if password:
@ -1209,7 +1208,8 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
{'canStream':True, {'canStream':True,
'canScan':True, 'canScan':True,
'willUseCachedMetadata': self.client_can_use_metadata_cache, 'willUseCachedMetadata': self.client_can_use_metadata_cache,
'supportsSync': True}) 'supportsSync': (self.is_read_sync_col or
self.is_read_date_sync_col)})
bl = CollectionsBookList(None, self.PREFIX, self.settings) bl = CollectionsBookList(None, self.PREFIX, self.settings)
if opcode == 'OK': if opcode == 'OK':
count = result['count'] count = result['count']
@ -1257,7 +1257,7 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
book = self.json_codec.raw_to_book(result, SDBook, self.PREFIX) book = self.json_codec.raw_to_book(result, SDBook, self.PREFIX)
book.set('_is_read_', result.get('_is_read_', None)) book.set('_is_read_', result.get('_is_read_', None))
book.set('_is_read_changed_', result.get('_is_read_changed_', None)) book.set('_is_read_changed_', result.get('_is_read_changed_', None))
book.set('_last_read_date_', r.get('_last_read_date_', None)) book.set('_last_read_date_', result.get('_last_read_date_', None))
bl.add_book(book, replace_metadata=True) bl.add_book(book, replace_metadata=True)
if '_new_book_' in result: if '_new_book_' in result:
book.set('_new_book_', True) book.set('_new_book_', True)
@ -1309,7 +1309,8 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
self._call_client('SEND_BOOKLISTS', {'count': count, self._call_client('SEND_BOOKLISTS', {'count': count,
'collections': coldict, 'collections': coldict,
'willStreamMetadata': True, 'willStreamMetadata': True,
'supportsSync': True}, 'supportsSync': (self.is_read_sync_col or
self.is_read_date_sync_col)},
wait_for_response=False) wait_for_response=False)
if count: if count:
@ -1318,7 +1319,9 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
self._set_known_metadata(book) self._set_known_metadata(book)
opcode, result = self._call_client( opcode, result = self._call_client(
'SEND_BOOK_METADATA', 'SEND_BOOK_METADATA',
{'index': i, 'count': count, 'data': book, 'supportsSync': True}, {'index': i, 'count': count, 'data': book,
'supportsSync': (self.is_read_sync_col or
self.is_read_date_sync_col)},
print_debug_info=False, print_debug_info=False,
wait_for_response=False) wait_for_response=False)
@ -1466,86 +1469,123 @@ 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):
from calibre.utils.date import parse_date, UNDEFINED_DATE
def show_message(message): def show_message(message):
self._call_client("DISPLAY_MESSAGE", self._call_client("DISPLAY_MESSAGE",
{'messageKind': self.MESSAGE_SHOW_TOAST, {'messageKind': self.MESSAGE_SHOW_TOAST,
'message': message}) 'message': message})
if not (self.is_read_sync_col or self.is_read_date_sync_col): if self.have_bad_sync_columns or not (self.is_read_sync_col or
# Not syncing self.is_read_date_sync_col):
# Not syncing or sync columns are invalid
return None return None
# Check the validity of the columns once per connection. We do it
# here because we have access to the db to get field_metadata
if not self.have_checked_sync_columns: if not self.have_checked_sync_columns:
# Check the validity of the columns once per connection. We do it
# here because we have access to the db to get field_metadata
fm = db.field_metadata.custom_field_metadata() fm = db.field_metadata.custom_field_metadata()
if self.is_read_sync_col: if self.is_read_sync_col:
if self.is_read_sync_col not in fm: if self.is_read_sync_col not in fm:
self._debug('is_read_sync_col not in field_metadata') self._debug('is_read_sync_col not in field_metadata')
show_message(_("The read sync column %s is " show_message(_("The read sync column %s is "
"not in calibre's library")%self.is_read_sync_col) "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': elif fm[self.is_read_sync_col]['datatype'] != 'bool':
self._debug('is_read_sync_col not bool type') self._debug('is_read_sync_col not bool type')
show_message(_("The read sync column %s is " show_message(_("The read sync column %s is "
"not a Yes/No column")%self.is_read_sync_col) "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:
if self.is_read_date_sync_col not in fm: if self.is_read_date_sync_col not in fm:
self._debug('is_read_date_sync_col not in field_metadata') self._debug('is_read_date_sync_col not in field_metadata')
show_message(_("The read date sync column %s is " show_message(_("The read date sync column %s is "
"not in calibre's library")%self.is_read_date_sync_col) "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': elif fm[self.is_read_date_sync_col]['datatype'] != 'datetime':
self._debug('is_read_date_sync_col not date type') self._debug('is_read_date_sync_col not date type')
show_message(_("The read date sync column %s is " show_message(_("The read date sync column %s is "
"not a Date column")%self.is_read_date_sync_col) "not a Date column")%self.is_read_date_sync_col)
self.have_bad_sync_columns = True
self.have_checked_sync_columns = True self.have_checked_sync_columns = True
if self.have_bad_sync_columns:
return None
is_changed = book.get('_is_read_changed_', None); is_changed = book.get('_is_read_changed_', None);
is_read = book.get('_is_read_', None) is_read = book.get('_is_read_', None)
# This returns UNDEFINED_DATE if the value is None
is_read_date = parse_date(book.get('_last_read_date_', None));
value_to_return = None
if is_changed == 2 and is_read is None: if is_changed == 2:
# This is a special case where the user just set the sync column. In # This is a special case where the user just set the sync column. In
# this case the device value wins if it is not None by falling # this case the device value wins if it is not None by falling
# through to the normal sync situation below, otherwise the calibre # through to the normal sync situation below, otherwise the calibre
# value wins. # value wins. The orig_* values are set to None to force the normal
calibre_val = db.new_api.field_for(self.is_read_sync_col, # sync code to actually sync because the values are different
id_, default_value=None) orig_is_read_date = UNDEFINED_DATE
if calibre_val is not None: orig_is_read = None
# This will force the metadata for the book to be sent . Note if is_read is None:
# that because the devices last_read date is one-way sync, this calibre_val = db.new_api.field_for(self.is_read_sync_col,
# could leave an empty date in the device. id_, default_value=None)
book.set('_force_send_metadata_', True) if calibre_val is not None:
self._debug('special update book', book.get('title', 'huh?'), # This forces the metadata for the book to be sent to the
'to', calibre_val) # device even if the mod dates haven't changed.
return set(id_) book.set('_force_send_metadata_', True)
# Both values are None. Do nothing self._debug('special update is_read', book.get('title', 'huh?'),
return None 'to', calibre_val)
value_to_return = set()
orig_is_read = book.get(self.is_read_sync_col, None) if is_read_date == UNDEFINED_DATE:
if is_read != orig_is_read: calibre_val = db.new_api.field_for(self.is_read_date_sync_col,
# The value in the device's is_read checkbox is not the same as the id_, default_value=None)
# last one that came to the device from calibre during the last if calibre_val != UNDEFINED_DATE:
# connect, meaning that the user changed it. Write the one from the book.set('_force_send_metadata_', True)
# checkbox to calibre's db. self._debug('special update is_read_date', book.get('title', 'huh?'),
changed_books = set() 'to', calibre_val)
is_read_date = book.get('_last_read_date_', None); value_to_return = set()
self._debug('standard update book', book.get('title', 'huh?'), 'to', # Fall through to the normal sync. At this point either the is_read*
is_read, is_read_date) # values are different from the orig_is_read* which will cause a
if self.is_read_sync_col: # sync below, or they are both None which will cause the code below
try: # to do nothing. If either of the calibre data fields were set, the
# method will return set(), which will force updated metadata to be
# given back to the device, effectively forcing the sync of the
# calibre values back to the device.
else:
orig_is_read = book.get(self.is_read_sync_col, None)
orig_is_read_date = book.get(self.is_read_date_sync_col, None)
changed_books = set()
try:
if is_read != orig_is_read:
# The value in the device's is_read checkbox is not the same as the
# last one that came to the device from calibre during the last
# connect, meaning that the user changed it. Write the one from the
# device to calibre's db.
self._debug('standard update book is_read', book.get('title', 'huh?'),
'to', is_read)
if self.is_read_sync_col:
changed_books = db.new_api.set_field(self.is_read_sync_col, changed_books = db.new_api.set_field(self.is_read_sync_col,
{id_: is_read}) {id_: is_read})
except: except:
self._debug('setting read sync col tossed exception', self._debug('exception syncing is_read col', self.is_read_sync_col)
self.is_read_sync_col) traceback.print_exc()
if self.is_read_date_sync_col:
try: try:
if is_read_date != orig_is_read_date:
self._debug('standard update book is_read_date', book.get('title', 'huh?'),
'to', is_read_date)
if self.is_read_date_sync_col:
changed_books |= db.new_api.set_field(self.is_read_date_sync_col, changed_books |= db.new_api.set_field(self.is_read_date_sync_col,
{id_: is_read_date}) {id_: is_read_date})
except: except:
self._debug('setting read date sync col tossed exception', self._debug('Exception while syncing is_read_date', self.is_read_date_sync_col)
self.is_read_date_sync_col) traceback.print_exc()
if changed_books:
# One of the two values was synced, giving a list of changed books.
# Return that.
return changed_books return changed_books
# The user might have changed the value in calibre. If so, that value # The user might have changed the value in calibre. If so, that value
@ -1553,7 +1593,7 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
# updated value has already been synced and so will also be sent, the # 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 # device should put the calibre value into its checkbox (or whatever it
# uses) # uses)
return None return value_to_return
@synchronous('sync_lock') @synchronous('sync_lock')
def startup(self): def startup(self):
@ -1581,6 +1621,7 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
self.is_read_sync_col = None self.is_read_sync_col = None
self.is_read_date_sync_col = None self.is_read_date_sync_col = None
self.have_checked_sync_columns = False self.have_checked_sync_columns = False
self.have_bad_sync_columns = False
message = None message = None
compression_quality_ok = True compression_quality_ok = True