Merge from trunk, adding last_modified to metadata.calibre, and streaming metadata.

This commit is contained in:
Charles Haley 2012-08-23 11:37:05 +02:00
commit 31b63784a5
4 changed files with 68 additions and 48 deletions

View File

@ -171,6 +171,7 @@ 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()
self.client_can_stream_books = False self.client_can_stream_books = False
self.client_can_stream_metadata = False
def _debug(self, *args): def _debug(self, *args):
if not DEBUG: if not DEBUG:
@ -188,7 +189,7 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
printable[k] = 'too long' printable[k] = 'too long'
else: else:
printable[k] = v printable[k] = v
prints('', printable, end=''); prints('', printable, end='')
else: else:
prints('', a, end='') prints('', a, end='')
except: except:
@ -368,7 +369,7 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
if not isinstance(s, bytes): if not isinstance(s, bytes):
self._debug('given a non-byte string!') self._debug('given a non-byte string!')
raise PacketError("Internal error: found a string that isn't bytes") raise PacketError("Internal error: found a string that isn't bytes")
sent_len = 0; sent_len = 0
total_len = len(s) total_len = len(s)
while sent_len < total_len: while sent_len < total_len:
try: try:
@ -377,7 +378,7 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
else: else:
amt_sent = self.device_socket.send(s[sent_len:]) amt_sent = self.device_socket.send(s[sent_len:])
if amt_sent <= 0: if amt_sent <= 0:
raise IOError('Bad write on device socket'); raise IOError('Bad write on device socket')
sent_len += amt_sent sent_len += amt_sent
except socket.error as e: except socket.error as e:
self._debug('socket error', e, e.errno) self._debug('socket error', e, e.errno)
@ -391,9 +392,9 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
extra_debug = self.settings().extra_customization[self.OPT_EXTRA_DEBUG] extra_debug = self.settings().extra_customization[self.OPT_EXTRA_DEBUG]
if print_debug_info or extra_debug: if print_debug_info or extra_debug:
if extra_debug: if extra_debug:
self._debug(op, arg) self._debug(op, 'wfr', wait_for_response, arg)
else: else:
self._debug(op) self._debug(op, 'wfr', wait_for_response)
if self.device_socket is None: if self.device_socket is None:
return None, None return None, None
try: try:
@ -473,7 +474,7 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
b = infile.read(self.max_book_packet_len) b = infile.read(self.max_book_packet_len)
blen = len(b) blen = len(b)
if not b: if not b:
break; break
b = b64encode(b) b = b64encode(b)
opcode, result = self._call_client('BOOK_DATA', opcode, result = self._call_client('BOOK_DATA',
{'lpath': lpath, 'position': pos, 'data': b}, {'lpath': lpath, 'position': pos, 'data': b},
@ -502,29 +503,30 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
else: else:
return None return None
def _compare_metadata(self, mi1, mi2): # def _compare_metadata(self, mi1, mi2):
for key in SERIALIZABLE_FIELDS: # for key in SERIALIZABLE_FIELDS:
if key in ['cover', 'mime']: # if key in ['cover', 'mime']:
continue # continue
if key == 'user_metadata': # if key == 'user_metadata':
meta1 = mi1.get_all_user_metadata(make_copy=False) # meta1 = mi1.get_all_user_metadata(make_copy=False)
meta2 = mi1.get_all_user_metadata(make_copy=False) # meta2 = mi1.get_all_user_metadata(make_copy=False)
if meta1 != meta2: # if meta1 != meta2:
self._debug('custom metadata different') # self._debug('custom metadata different')
return False # return False
for ckey in meta1: # for ckey in meta1:
if mi1.get(ckey) != mi2.get(ckey): # if mi1.get(ckey) != mi2.get(ckey):
self._debug(ckey, mi1.get(ckey), mi2.get(ckey)) # self._debug(ckey, mi1.get(ckey), mi2.get(ckey))
return False # return False
elif mi1.get(key, None) != mi2.get(key, None): # elif mi1.get(key, None) != mi2.get(key, None):
self._debug(key, mi1.get(key), mi2.get(key)) # self._debug(key, mi1.get(key), mi2.get(key))
return False # return False
return True # return True
def _metadata_already_on_device(self, book): def _metadata_already_on_device(self, book):
v = self.known_metadata.get(book.lpath, None) v = self.known_metadata.get(book.lpath, None)
if v is not None: if v is not None:
return self._compare_metadata(book, v) return (v.get('uuid', None) == book.get('uuid', None) and
v.get('last_modified', None) == book.get('last_modified', None))
return False return False
def _set_known_metadata(self, book, remove=False): def _set_known_metadata(self, book, remove=False):
@ -665,8 +667,12 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
self._close_device_socket() self._close_device_socket()
return False return False
self._debug('CC version #:', result.get('ccVersionNumber', 'unknown')) self._debug('CC version #:', result.get('ccVersionNumber', 'unknown'))
self.client_can_stream_books = result.get('canStreamBooks', False) self.client_can_stream_books = result.get('canStreamBooks', False)
self._debug('CC can stream', self.client_can_stream_books); self._debug('CC can stream books', self.client_can_stream_books)
self.client_can_stream_metadata = result.get('canStreamMetadata', False)
self._debug('CC can stream metadata', self.client_can_stream_metadata)
self.max_book_packet_len = result.get('maxBookContentPacketLen', self.max_book_packet_len = result.get('maxBookContentPacketLen',
self.BASE_PACKET_LEN) self.BASE_PACKET_LEN)
exts = result.get('acceptedExtensions', None) exts = result.get('acceptedExtensions', None)
@ -676,7 +682,7 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
return False return False
config = self._configProxy() config = self._configProxy()
config['format_map'] = exts config['format_map'] = exts
self._debug('selected formats', config['format_map']); self._debug('selected formats', config['format_map'])
if password: if password:
returned_hash = result.get('passwordHash', None) returned_hash = result.get('passwordHash', None)
if result.get('passwordHash', None) is None: if result.get('passwordHash', None) is None:
@ -777,8 +783,10 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
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']
will_stream = 'willStream' in result; will_stream = 'willStream' in result
for i in range(0, count): for i in range(0, count):
if (i % 100) == 0:
self._debug('getting book metadata. Done', i, 'of', count)
if will_stream: if will_stream:
opcode, result = self._receive_from_client(print_debug_info=False) opcode, result = self._receive_from_client(print_debug_info=False)
else: else:
@ -792,6 +800,7 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
bl.add_book(book, replace_metadata=True) bl.add_book(book, replace_metadata=True)
else: else:
raise ControlError(desc='book metadata not returned') raise ControlError(desc='book metadata not returned')
self._debug('finished getting book metadata')
return bl return bl
@synchronous('sync_lock') @synchronous('sync_lock')
@ -811,15 +820,27 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
# If we ever do device_db plugboards, this is where it will go. We will # If we ever do device_db plugboards, this is where it will go. We will
# probably need to send two booklists, one with calibre's data that is # probably need to send two booklists, one with calibre's data that is
# given back by "books", and one that has been plugboarded. # given back by "books", and one that has been plugboarded.
self._call_client('SEND_BOOKLISTS', { 'count': len(booklists[0]), books_to_send = []
'collections': coldict}) for book in booklists[0]:
for i,book in enumerate(booklists[0]):
if not self._metadata_already_on_device(book): if not self._metadata_already_on_device(book):
books_to_send.append(book)
count = len(books_to_send)
self._call_client('SEND_BOOKLISTS', { 'count': count,
'collections': coldict,
'willStreamMetadata': self.client_can_stream_metadata},
wait_for_response=not self.client_can_stream_metadata)
if count:
for i,book in enumerate(books_to_send):
self._debug('sending metadata for book', book.lpath)
self._set_known_metadata(book) self._set_known_metadata(book)
opcode, result = self._call_client('SEND_BOOK_METADATA', opcode, result = self._call_client(
{'index': i, 'data': book}, 'SEND_BOOK_METADATA',
print_debug_info=False) {'index': i, 'count': count, 'data': book},
if opcode != 'OK': print_debug_info=False,
wait_for_response=not self.client_can_stream_metadata)
if not self.client_can_stream_metadata and opcode != 'OK':
self._debug('protocol error', opcode, i) self._debug('protocol error', opcode, i)
raise ControlError(desc='sync_booklists') raise ControlError(desc='sync_booklists')
@ -956,6 +977,7 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
self.noop_counter = 0 self.noop_counter = 0
self.connection_attempts = {} self.connection_attempts = {}
self.client_can_stream_books = False self.client_can_stream_books = False
self.client_can_stream_metadata = False
message = None message = None
try: try:
@ -982,7 +1004,7 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
port = self._attach_to_port(opt_port) port = self._attach_to_port(opt_port)
if port == 0: if port == 0:
message = 'Failed to connect to port %d'%opt_port message = 'Failed to connect to port %d'%opt_port
self._debug(message); self._debug(message)
self.listen_socket.close() self.listen_socket.close()
self.listen_socket = None self.listen_socket = None
self.is_connected = False self.is_connected = False
@ -992,10 +1014,10 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
i += 1 i += 1
port = self._attach_to_port(random.randint(8192, 32000)) port = self._attach_to_port(random.randint(8192, 32000))
if port != 0: if port != 0:
break; break
if port == 0: if port == 0:
message = _('Failed to allocate a random port') message = _('Failed to allocate a random port')
self._debug(message); self._debug(message)
self.listen_socket.close() self.listen_socket.close()
self.listen_socket = None self.listen_socket = None
self.is_connected = False self.is_connected = False

View File

@ -42,6 +42,7 @@ PUBLICATION_METADATA_FIELDS = frozenset([
'book_producer', 'book_producer',
'timestamp', # Dates and times must be timezone aware 'timestamp', # Dates and times must be timezone aware
'pubdate', 'pubdate',
'last_modified',
'rights', 'rights',
# So far only known publication type is periodical:calibre # So far only known publication type is periodical:calibre
# If None, means book # If None, means book

View File

@ -1608,6 +1608,9 @@ class DeviceMixin(object): # {{{
if getattr(book, 'uuid', None) in self.db_book_uuid_cache: if getattr(book, 'uuid', None) in self.db_book_uuid_cache:
id_ = db_book_uuid_cache[book.uuid] id_ = db_book_uuid_cache[book.uuid]
if update_metadata: if update_metadata:
mi = db.get_metadata(id_, index_is_id=True,
get_cover=get_covers)
if book.get('last_modified', None) != mi.last_modified:
book.smart_update(db.get_metadata(id_, book.smart_update(db.get_metadata(id_,
index_is_id=True, index_is_id=True,
get_cover=get_covers), get_cover=get_covers),

View File

@ -373,18 +373,13 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
'the file: %s<p>The ' 'the file: %s<p>The '
'log will be displayed automatically.')%self.gui_debug, show=True) 'log will be displayed automatically.')%self.gui_debug, show=True)
smartdevice_action = self.iactions['Connect Share'] self.iactions['Connect Share'].check_smartdevice_menus()
smartdevice_action.check_smartdevice_menus() QTimer.singleShot(1, self.start_smartdevice)
self.sd_timer = QTimer();
self.sd_timer.setSingleShot(True)
self.sd_timer.timeout.connect(self.start_smartdevice, type=Qt.QueuedConnection)
self.sd_timer.start(0)
def esc(self, *args): def esc(self, *args):
self.clear_button.click() self.clear_button.click()
def start_smartdevice(self): def start_smartdevice(self):
self.sd_timer = None
message = None message = None
if self.device_manager.get_option('smartdevice', 'autostart'): if self.device_manager.get_option('smartdevice', 'autostart'):
try: try:
@ -399,8 +394,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
error_dialog(self, _('Problem starting the wireless device'), error_dialog(self, _('Problem starting the wireless device'),
_('The wireless device driver did not start. ' _('The wireless device driver did not start. '
'It said "%s"')%message, show=True) 'It said "%s"')%message, show=True)
smartdevice_action = self.iactions['Connect Share'] self.iactions['Connect Share'].set_smartdevice_action_state()
smartdevice_action.set_smartdevice_action_state()
def start_content_server(self, check_started=True): def start_content_server(self, check_started=True):
from calibre.library.server.main import start_threaded_server from calibre.library.server.main import start_threaded_server