mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Merge from trunk, adding last_modified to metadata.calibre, and streaming metadata.
This commit is contained in:
commit
31b63784a5
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -1608,7 +1608,10 @@ 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:
|
||||||
book.smart_update(db.get_metadata(id_,
|
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_,
|
||||||
index_is_id=True,
|
index_is_id=True,
|
||||||
get_cover=get_covers),
|
get_cover=get_covers),
|
||||||
replace_metadata=True)
|
replace_metadata=True)
|
||||||
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user