From 5008cc5d92b561c849417d510d4bca3a6970fb0a Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 12 Sep 2012 15:26:08 +0530 Subject: [PATCH] MTP: only populate driveinfo during the fetching of the book lists so that the get_device_information() job is not slow --- src/calibre/devices/interface.py | 16 ++++++++++++++++ src/calibre/devices/mtp/driver.py | 17 ++++++++++++----- src/calibre/devices/mtp/filesystem_cache.py | 3 +++ src/calibre/devices/mtp/unix/driver.py | 3 ++- src/calibre/devices/mtp/windows/driver.py | 3 ++- src/calibre/gui2/device.py | 10 ++++++++++ 6 files changed, 45 insertions(+), 7 deletions(-) diff --git a/src/calibre/devices/interface.py b/src/calibre/devices/interface.py index 58b097f951..c345045e7e 100644 --- a/src/calibre/devices/interface.py +++ b/src/calibre/devices/interface.py @@ -95,6 +95,10 @@ class DevicePlugin(Plugin): #: call post_yank_cleanup(). MANAGES_DEVICE_PRESENCE = False + #: If set the True, calibre will call the :method:`get_driveinfo()` method + #: after the books lists have been loaded to get the driveinfo. + SLOW_DRIVEINFO = False + @classmethod def get_gui_name(cls): if hasattr(cls, 'gui_name'): @@ -352,6 +356,18 @@ class DevicePlugin(Plugin): """ raise NotImplementedError() + def get_driveinfo(self): + ''' + Return the driveinfo dictionary. Usually called from + get_device_information(), but if loading the driveinfo is slow for this + driver, then it should set SLOW_DRIVEINFO. In this case, this method + will be called by calibre after the book lists have been loaded. Note + that it is not called on the device thread, so the driver should cache + the drive info in the books() method and this function should return + the cached data. + ''' + return {} + def card_prefix(self, end_session=True): ''' Return a 2 element list of the prefix to paths on the cards. diff --git a/src/calibre/devices/mtp/driver.py b/src/calibre/devices/mtp/driver.py index caf3174a11..de06955bfe 100644 --- a/src/calibre/devices/mtp/driver.py +++ b/src/calibre/devices/mtp/driver.py @@ -35,6 +35,7 @@ class MTP_DEVICE(BASE): MANAGES_DEVICE_PRESENCE = True FORMATS = ['epub', 'azw3', 'mobi', 'pdf'] DEVICE_PLUGBOARD_NAME = 'MTP_DEVICE' + SLOW_DRIVEINFO = True def __init__(self, *args, **kwargs): BASE.__init__(self, *args, **kwargs) @@ -76,6 +77,7 @@ class MTP_DEVICE(BASE): def open(self, devices, library_uuid): self.current_library_uuid = library_uuid self.location_paths = None + self.driveinfo = {} BASE.open(self, devices, library_uuid) h = self.prefs['history'] if self.current_serial_num: @@ -109,13 +111,17 @@ class MTP_DEVICE(BASE): self.put_file(storage, self.DRIVEINFO, BytesIO(raw), len(raw)) self.driveinfo[location_code] = dinfo + def get_driveinfo(self): + if not self.driveinfo: + self.driveinfo = {} + for sid, location_code in ( (self._main_id, 'main'), (self._carda_id, + 'A'), (self._cardb_id, 'B')): + if sid is None: continue + self._update_drive_info(self.filesystem_cache.storage(sid), location_code) + return self.driveinfo + def get_device_information(self, end_session=True): self.report_progress(1.0, _('Get device information...')) - self.driveinfo = {} - for sid, location_code in ( (self._main_id, 'main'), (self._carda_id, - 'A'), (self._cardb_id, 'B')): - if sid is None: continue - self._update_drive_info(self.filesystem_cache.storage(sid), location_code) dinfo = self.get_basic_device_information() return tuple( list(dinfo) + [self.driveinfo] ) @@ -135,6 +141,7 @@ class MTP_DEVICE(BASE): def books(self, oncard=None, end_session=True): from calibre.devices.mtp.books import JSONCodec from calibre.devices.mtp.books import BookList, Book + self.get_driveinfo() # Ensure driveinfo is loaded sid = {'carda':self._carda_id, 'cardb':self._cardb_id}.get(oncard, self._main_id) if sid is None: diff --git a/src/calibre/devices/mtp/filesystem_cache.py b/src/calibre/devices/mtp/filesystem_cache.py index 8f4d20ae18..b1c2828b8c 100644 --- a/src/calibre/devices/mtp/filesystem_cache.py +++ b/src/calibre/devices/mtp/filesystem_cache.py @@ -230,6 +230,9 @@ class FilesystemCache(object): continue # Ignore .txt files in the root yield x + def __len__(self): + return len(self.id_map) + def resolve_mtp_id_path(self, path): if not path.startswith('mtp:::'): raise ValueError('%s is not a valid MTP path'%path) diff --git a/src/calibre/devices/mtp/unix/driver.py b/src/calibre/devices/mtp/unix/driver.py index 6e5d91c0a0..4b9ed9e928 100644 --- a/src/calibre/devices/mtp/unix/driver.py +++ b/src/calibre/devices/mtp/unix/driver.py @@ -222,7 +222,8 @@ class MTP_DEVICE(MTPDeviceBase): self.current_friendly_name, self.format_errorstack(all_errs))) self._filesystem_cache = FilesystemCache(storage, all_items) - debug('Filesystem metadata loaded in %g seconds'%(time.time()-st)) + debug('Filesystem metadata loaded in %g seconds (%d objects)'%( + time.time()-st, len(self._filesystem_cache))) return self._filesystem_cache @synchronous diff --git a/src/calibre/devices/mtp/windows/driver.py b/src/calibre/devices/mtp/windows/driver.py index 3290115028..3f79e7d991 100644 --- a/src/calibre/devices/mtp/windows/driver.py +++ b/src/calibre/devices/mtp/windows/driver.py @@ -220,7 +220,8 @@ class MTP_DEVICE(MTPDeviceBase): all_storage.append(storage) items.append(id_map.itervalues()) self._filesystem_cache = FilesystemCache(all_storage, chain(*items)) - debug('Filesystem metadata loaded in %g seconds'%(time.time()-st)) + debug('Filesystem metadata loaded in %g seconds (%d objects)'%( + time.time()-st, len(self._filesystem_cache))) return self._filesystem_cache @same_thread diff --git a/src/calibre/gui2/device.py b/src/calibre/gui2/device.py index 8466fe9320..553532e95d 100644 --- a/src/calibre/gui2/device.py +++ b/src/calibre/gui2/device.py @@ -433,6 +433,15 @@ class DeviceManager(Thread): # {{{ return self.create_job_step(self._get_device_information, done, description=_('Get device information'), to_job=add_as_step_to_job) + def slow_driveinfo(self): + ''' Update the stored device information with the driveinfo if the + device indicates that getting driveinfo is slow ''' + info = self._device_information['info'] + if (not info[4] and self.device.SLOW_DRIVEINFO): + info = list(info) + info[4] = self.device.get_driveinfo() + self._device_information['info'] = tuple(info) + def get_current_device_information(self): return self._device_information @@ -1023,6 +1032,7 @@ class DeviceMixin(object): # {{{ if job.failed: self.device_job_exception(job) return + self.device_manager.slow_driveinfo() # set_books_in_library might schedule a sync_booklists job self.set_books_in_library(job.result, reset=True, add_as_step_to_job=job) mainlist, cardalist, cardblist = job.result