mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-08 02:34:06 -04:00
Wireless driver: Remove old non-streaming netowrking code. Apps that still use it must be updated. A message will popup notifying the user of the need to update if such an app is encountered.
Wireless driver: Fix a bug that could cause the wrong path information to be sent to the device in some circumstances. Merge branch 'master' of https://github.com/cbhaley/calibre
This commit is contained in:
commit
5161246eb2
@ -13,14 +13,13 @@ from collections import defaultdict
|
|||||||
import hashlib, threading
|
import hashlib, threading
|
||||||
import Queue
|
import Queue
|
||||||
|
|
||||||
from base64 import b64encode, b64decode
|
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from errno import EAGAIN, EINTR
|
from errno import EAGAIN, EINTR
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
|
|
||||||
from calibre import prints
|
from calibre import prints
|
||||||
from calibre.constants import numeric_version, DEBUG, cache_dir
|
from calibre.constants import numeric_version, DEBUG, cache_dir
|
||||||
from calibre.devices.errors import (OpenFailed, ControlError, TimeoutError,
|
from calibre.devices.errors import (OpenFailed, OpenFeedback, ControlError, TimeoutError,
|
||||||
InitialConnectionError, PacketError)
|
InitialConnectionError, PacketError)
|
||||||
from calibre.devices.interface import DevicePlugin
|
from calibre.devices.interface import DevicePlugin
|
||||||
from calibre.devices.usbms.books import Book, CollectionsBookList
|
from calibre.devices.usbms.books import Book, CollectionsBookList
|
||||||
@ -159,6 +158,7 @@ class ConnectionListener(Thread):
|
|||||||
|
|
||||||
|
|
||||||
class SDBook(Book):
|
class SDBook(Book):
|
||||||
|
|
||||||
def __init__(self, prefix, lpath, size=None, other=None):
|
def __init__(self, prefix, lpath, size=None, other=None):
|
||||||
Book.__init__(self, prefix, lpath, size=size, other=other)
|
Book.__init__(self, prefix, lpath, size=size, other=other)
|
||||||
path = getattr(self, 'path', lpath)
|
path = getattr(self, 'path', lpath)
|
||||||
@ -255,6 +255,9 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
|
|||||||
}
|
}
|
||||||
reverse_opcodes = dict([(v, k) for k,v in opcodes.iteritems()])
|
reverse_opcodes = dict([(v, k) for k,v in opcodes.iteritems()])
|
||||||
|
|
||||||
|
MESSAGE_PASSWORD_ERROR = 1
|
||||||
|
MESSAGE_UPDATE_NEEDED = 2
|
||||||
|
|
||||||
ALL_BY_TITLE = _('All by title')
|
ALL_BY_TITLE = _('All by title')
|
||||||
ALL_BY_AUTHOR = _('All by author')
|
ALL_BY_AUTHOR = _('All by author')
|
||||||
ALL_BY_SOMETHING = _('All by something')
|
ALL_BY_SOMETHING = _('All by something')
|
||||||
@ -344,8 +347,6 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
|
|||||||
self.noop_counter = 0
|
self.noop_counter = 0
|
||||||
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_metadata = False
|
|
||||||
|
|
||||||
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
|
||||||
@ -405,8 +406,9 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
|
|||||||
try:
|
try:
|
||||||
# If we have already seen this book's UUID, use the existing path
|
# If we have already seen this book's UUID, use the existing path
|
||||||
if self.settings().extra_customization[self.OPT_OVERWRITE_BOOKS_UUID]:
|
if self.settings().extra_customization[self.OPT_OVERWRITE_BOOKS_UUID]:
|
||||||
existing_book = self._uuid_already_on_device(mdata.uuid, ext)
|
existing_book = self._uuid_in_cache(mdata.uuid, ext)
|
||||||
if existing_book and existing_book.lpath:
|
if (existing_book and existing_book.lpath and
|
||||||
|
self.known_metadata.get(existing_book.lpath, None)):
|
||||||
return existing_book.lpath
|
return existing_book.lpath
|
||||||
|
|
||||||
# If the device asked for it, try to use the UUID as the file name.
|
# If the device asked for it, try to use the UUID as the file name.
|
||||||
@ -628,7 +630,7 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
|
|||||||
raise
|
raise
|
||||||
raise ControlError(desc='Device responded with incorrect information')
|
raise ControlError(desc='Device responded with incorrect information')
|
||||||
|
|
||||||
# Write a file as a series of base64-encoded strings.
|
# Write a file to the device as a series of binary strings.
|
||||||
def _put_file(self, infile, lpath, book_metadata, this_book, total_books):
|
def _put_file(self, infile, lpath, book_metadata, this_book, total_books):
|
||||||
close_ = False
|
close_ = False
|
||||||
if not hasattr(infile, 'read'):
|
if not hasattr(infile, 'read'):
|
||||||
@ -641,10 +643,10 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
|
|||||||
opcode, result = self._call_client('SEND_BOOK', {'lpath': lpath, 'length': length,
|
opcode, result = self._call_client('SEND_BOOK', {'lpath': lpath, 'length': length,
|
||||||
'metadata': book_metadata, 'thisBook': this_book,
|
'metadata': book_metadata, 'thisBook': this_book,
|
||||||
'totalBooks': total_books,
|
'totalBooks': total_books,
|
||||||
'willStreamBooks': self.client_can_stream_books,
|
'willStreamBooks': True,
|
||||||
'willStreamBinary' : self.client_can_receive_book_binary},
|
'willStreamBinary' : True},
|
||||||
print_debug_info=False,
|
print_debug_info=False,
|
||||||
wait_for_response=(not self.client_can_stream_books))
|
wait_for_response=False)
|
||||||
|
|
||||||
self._set_known_metadata(book_metadata)
|
self._set_known_metadata(book_metadata)
|
||||||
pos = 0
|
pos = 0
|
||||||
@ -655,21 +657,8 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
|
|||||||
blen = len(b)
|
blen = len(b)
|
||||||
if not b:
|
if not b:
|
||||||
break
|
break
|
||||||
if self.client_can_stream_books and self.client_can_receive_book_binary:
|
|
||||||
self._send_byte_string(self.device_socket, b)
|
self._send_byte_string(self.device_socket, b)
|
||||||
else:
|
|
||||||
b = b64encode(b)
|
|
||||||
opcode, result = self._call_client('BOOK_DATA',
|
|
||||||
{'lpath': lpath, 'position': pos, 'data': b},
|
|
||||||
print_debug_info=False,
|
|
||||||
wait_for_response=(not self.client_can_stream_books))
|
|
||||||
pos += blen
|
pos += blen
|
||||||
if not self.client_can_stream_books and opcode != 'OK':
|
|
||||||
self._debug('protocol error', opcode)
|
|
||||||
failed = True
|
|
||||||
break
|
|
||||||
if not (self.client_can_stream_books and self.client_can_receive_book_binary):
|
|
||||||
self._call_client('BOOK_DONE', {'lpath': lpath})
|
|
||||||
self.time = None
|
self.time = None
|
||||||
if close_:
|
if close_:
|
||||||
infile.close()
|
infile.close()
|
||||||
@ -722,7 +711,7 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
|
|||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def _uuid_already_on_device(self, uuid, ext):
|
def _uuid_in_cache(self, uuid, ext):
|
||||||
try:
|
try:
|
||||||
return self.known_uuids[uuid + ext]['book']
|
return self.known_uuids[uuid + ext]['book']
|
||||||
except:
|
except:
|
||||||
@ -774,7 +763,6 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
|
|||||||
except:
|
except:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
|
||||||
|
|
||||||
def _write_metadata_cache(self):
|
def _write_metadata_cache(self):
|
||||||
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(),
|
||||||
@ -968,18 +956,28 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
|
|||||||
self._debug('Protocol error - bogus book packet length')
|
self._debug('Protocol error - bogus book packet length')
|
||||||
self._close_device_socket()
|
self._close_device_socket()
|
||||||
return False
|
return False
|
||||||
self._debug('App version #:', result.get('ccVersionNumber', 'unknown'))
|
|
||||||
|
|
||||||
self.client_can_stream_books = result.get('canStreamBooks', False)
|
client_can_stream_books = result.get('canStreamBooks', False)
|
||||||
self._debug('Device can stream books', self.client_can_stream_books)
|
self._debug('Device can stream books', client_can_stream_books)
|
||||||
self.client_can_stream_metadata = result.get('canStreamMetadata', False)
|
client_can_stream_metadata = result.get('canStreamMetadata', False)
|
||||||
self._debug('Device can stream metadata', self.client_can_stream_metadata)
|
self._debug('Device can stream metadata', client_can_stream_metadata)
|
||||||
self.client_can_receive_book_binary = result.get('canReceiveBookBinary', False)
|
client_can_receive_book_binary = result.get('canReceiveBookBinary', False)
|
||||||
self._debug('Device can receive book binary', self.client_can_stream_metadata)
|
self._debug('Device can receive book binary', client_can_receive_book_binary)
|
||||||
self.client_can_delete_multiple = result.get('canDeleteMultipleBooks', False)
|
client_can_delete_multiple = result.get('canDeleteMultipleBooks', False)
|
||||||
self._debug('Device can delete multiple books', self.client_can_delete_multiple)
|
self._debug('Device can delete multiple books', client_can_delete_multiple)
|
||||||
|
|
||||||
|
if not (client_can_stream_books and
|
||||||
|
client_can_stream_metadata and
|
||||||
|
client_can_receive_book_binary and
|
||||||
|
client_can_delete_multiple):
|
||||||
|
self._debug('Software on device too old')
|
||||||
|
self._close_device_socket()
|
||||||
|
raise OpenFeedback(_('The app on your device is too old and is no '
|
||||||
|
'longer supported. Update it to a newer version.'))
|
||||||
|
|
||||||
self.client_can_use_metadata_cache = result.get('canUseCachedMetadata', False)
|
self.client_can_use_metadata_cache = result.get('canUseCachedMetadata', False)
|
||||||
self._debug('Device can use cached metadata', self.client_can_use_metadata_cache)
|
self._debug('Device can use cached metadata', self.client_can_use_metadata_cache)
|
||||||
|
|
||||||
if not self.settings().extra_customization[self.OPT_USE_METADATA_CACHE]:
|
if not self.settings().extra_customization[self.OPT_USE_METADATA_CACHE]:
|
||||||
self.client_can_use_metadata_cache = False
|
self.client_can_use_metadata_cache = False
|
||||||
self._debug('metadata caching disabled by option')
|
self._debug('metadata caching disabled by option')
|
||||||
@ -992,6 +990,18 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
|
|||||||
|
|
||||||
self.client_app_name = result.get('appName', "")
|
self.client_app_name = result.get('appName', "")
|
||||||
self._debug('Client app name', self.client_app_name)
|
self._debug('Client app name', self.client_app_name)
|
||||||
|
self.app_version_number = result.get('ccVersionNumber', '0')
|
||||||
|
self._debug('App version #:', self.app_version_number)
|
||||||
|
|
||||||
|
try:
|
||||||
|
if (self.client_app_name == 'CalibreCompanion' and
|
||||||
|
self.app_version_number < 62):
|
||||||
|
self._debug('Telling client to update')
|
||||||
|
self._call_client("DISPLAY_MESSAGE",
|
||||||
|
{'messageKind': self.MESSAGE_UPDATE_NEEDED,
|
||||||
|
'lastestKnownAppVersion': 62})
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
self.max_book_packet_len = result.get('maxBookContentPacketLen',
|
self.max_book_packet_len = result.get('maxBookContentPacketLen',
|
||||||
self.BASE_PACKET_LEN)
|
self.BASE_PACKET_LEN)
|
||||||
@ -1035,7 +1045,7 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
|
|||||||
self._debug('password mismatch')
|
self._debug('password mismatch')
|
||||||
try:
|
try:
|
||||||
self._call_client("DISPLAY_MESSAGE",
|
self._call_client("DISPLAY_MESSAGE",
|
||||||
{'messageKind':1,
|
{'messageKind': self.MESSAGE_PASSWORD_ERROR,
|
||||||
'currentLibraryName': self.current_library_name,
|
'currentLibraryName': self.current_library_name,
|
||||||
'currentLibraryUUID': library_uuid})
|
'currentLibraryUUID': library_uuid})
|
||||||
except:
|
except:
|
||||||
@ -1141,8 +1151,6 @@ 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_scan = 'willScan' in result
|
|
||||||
will_use_cache = self.client_can_use_metadata_cache
|
will_use_cache = self.client_can_use_metadata_cache
|
||||||
|
|
||||||
if will_use_cache:
|
if will_use_cache:
|
||||||
@ -1172,11 +1180,7 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
|
|||||||
for i in range(0, count):
|
for i in range(0, count):
|
||||||
if (i % 100) == 0:
|
if (i % 100) == 0:
|
||||||
self._debug('getting book metadata. Done', i, 'of', count)
|
self._debug('getting book metadata. Done', i, 'of', count)
|
||||||
if will_stream:
|
|
||||||
opcode, result = self._receive_from_client(print_debug_info=False)
|
opcode, result = self._receive_from_client(print_debug_info=False)
|
||||||
else:
|
|
||||||
opcode, result = self._call_client('GET_BOOK_METADATA', {'index': i},
|
|
||||||
print_debug_info=False)
|
|
||||||
if opcode == 'OK':
|
if opcode == 'OK':
|
||||||
if '_series_sort_' in result:
|
if '_series_sort_' in result:
|
||||||
del result['_series_sort_']
|
del result['_series_sort_']
|
||||||
@ -1189,7 +1193,6 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
|
|||||||
else:
|
else:
|
||||||
raise ControlError(desc='book metadata not returned')
|
raise ControlError(desc='book metadata not returned')
|
||||||
|
|
||||||
if will_scan:
|
|
||||||
total = 0
|
total = 0
|
||||||
for book in bl:
|
for book in bl:
|
||||||
if book.get('_new_book_', None):
|
if book.get('_new_book_', None):
|
||||||
@ -1231,8 +1234,8 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
|
|||||||
count = len(books_to_send)
|
count = len(books_to_send)
|
||||||
self._call_client('SEND_BOOKLISTS', {'count': count,
|
self._call_client('SEND_BOOKLISTS', {'count': count,
|
||||||
'collections': coldict,
|
'collections': coldict,
|
||||||
'willStreamMetadata': self.client_can_stream_metadata},
|
'willStreamMetadata': True},
|
||||||
wait_for_response=not self.client_can_stream_metadata)
|
wait_for_response=False)
|
||||||
|
|
||||||
if count:
|
if count:
|
||||||
for i,book in enumerate(books_to_send):
|
for i,book in enumerate(books_to_send):
|
||||||
@ -1242,10 +1245,7 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
|
|||||||
'SEND_BOOK_METADATA',
|
'SEND_BOOK_METADATA',
|
||||||
{'index': i, 'count': count, 'data': book},
|
{'index': i, 'count': count, 'data': book},
|
||||||
print_debug_info=False,
|
print_debug_info=False,
|
||||||
wait_for_response=not self.client_can_stream_metadata)
|
wait_for_response=False)
|
||||||
if not self.client_can_stream_metadata and opcode != 'OK':
|
|
||||||
self._debug('protocol error', opcode, i)
|
|
||||||
raise ControlError(desc='sync_booklists')
|
|
||||||
|
|
||||||
@synchronous('sync_lock')
|
@synchronous('sync_lock')
|
||||||
def eject(self):
|
def eject(self):
|
||||||
@ -1315,7 +1315,6 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
|
|||||||
else:
|
else:
|
||||||
self._debug()
|
self._debug()
|
||||||
|
|
||||||
if self.client_can_delete_multiple:
|
|
||||||
new_paths = []
|
new_paths = []
|
||||||
for path in paths:
|
for path in paths:
|
||||||
new_paths.append(self._strip_prefix(path))
|
new_paths.append(self._strip_prefix(path))
|
||||||
@ -1324,15 +1323,6 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
|
|||||||
opcode, result = self._receive_from_client(False)
|
opcode, result = self._receive_from_client(False)
|
||||||
self._debug('removed book with UUID', result['uuid'])
|
self._debug('removed book with UUID', result['uuid'])
|
||||||
self._debug('removed', len(new_paths), 'books')
|
self._debug('removed', len(new_paths), 'books')
|
||||||
else:
|
|
||||||
for path in paths:
|
|
||||||
# the path has the prefix on it (I think)
|
|
||||||
path = self._strip_prefix(path)
|
|
||||||
opcode, result = self._call_client('DELETE_BOOK', {'lpath': path})
|
|
||||||
if opcode == 'OK':
|
|
||||||
self._debug('removed book with UUID', result['uuid'])
|
|
||||||
else:
|
|
||||||
raise ControlError(desc='Protocol error - delete books')
|
|
||||||
|
|
||||||
@synchronous('sync_lock')
|
@synchronous('sync_lock')
|
||||||
def remove_books_from_metadata(self, paths, booklists):
|
def remove_books_from_metadata(self, paths, booklists):
|
||||||
@ -1368,10 +1358,6 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
|
|||||||
'canStream':True, 'canStreamBinary': True},
|
'canStream':True, 'canStreamBinary': True},
|
||||||
print_debug_info=False)
|
print_debug_info=False)
|
||||||
if opcode == 'OK':
|
if opcode == 'OK':
|
||||||
client_will_stream = 'willStream' in result
|
|
||||||
client_will_stream_binary = 'willStreamBinary' in result
|
|
||||||
|
|
||||||
if (client_will_stream_binary):
|
|
||||||
length = result.get('fileLength')
|
length = result.get('fileLength')
|
||||||
remaining = length
|
remaining = length
|
||||||
|
|
||||||
@ -1380,19 +1366,6 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
|
|||||||
outfile.write(v)
|
outfile.write(v)
|
||||||
remaining -= len(v)
|
remaining -= len(v)
|
||||||
eof = True
|
eof = True
|
||||||
else:
|
|
||||||
while not eof:
|
|
||||||
if not result['eof']:
|
|
||||||
data = b64decode(result['data'])
|
|
||||||
if len(data) != result['next_position'] - position:
|
|
||||||
self._debug('position mismatch', result['next_position'], position)
|
|
||||||
position = result['next_position']
|
|
||||||
outfile.write(data)
|
|
||||||
opcode, result = self._receive_from_client(print_debug_info=True)
|
|
||||||
else:
|
|
||||||
eof = True
|
|
||||||
if not client_will_stream:
|
|
||||||
break
|
|
||||||
else:
|
else:
|
||||||
raise ControlError(desc='request for book data failed')
|
raise ControlError(desc='request for book data failed')
|
||||||
|
|
||||||
@ -1438,8 +1411,6 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin):
|
|||||||
self.max_book_packet_len = 0
|
self.max_book_packet_len = 0
|
||||||
self.noop_counter = 0
|
self.noop_counter = 0
|
||||||
self.connection_attempts = {}
|
self.connection_attempts = {}
|
||||||
self.client_can_stream_books = False
|
|
||||||
self.client_can_stream_metadata = False
|
|
||||||
self.client_wants_uuid_file_names = False
|
self.client_wants_uuid_file_names = False
|
||||||
|
|
||||||
compression_quality_ok = True
|
compression_quality_ok = True
|
||||||
|
Loading…
x
Reference in New Issue
Block a user