From 6627f20c2cd4b628954adbb05ce52ee161c8e4f0 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 31 Aug 2012 11:56:47 +0530 Subject: [PATCH] MTP: Implement get_device_information() --- src/calibre/devices/mtp/base.py | 3 +- src/calibre/devices/mtp/driver.py | 76 ++++++++++++++++++++- src/calibre/devices/mtp/filesystem_cache.py | 28 ++++++++ src/calibre/devices/mtp/unix/driver.py | 20 +----- src/calibre/devices/mtp/windows/driver.py | 16 +---- src/calibre/devices/usbms/driver.py | 2 +- 6 files changed, 109 insertions(+), 36 deletions(-) diff --git a/src/calibre/devices/mtp/base.py b/src/calibre/devices/mtp/base.py index 3e7dc63f87..4f8bbc991f 100644 --- a/src/calibre/devices/mtp/base.py +++ b/src/calibre/devices/mtp/base.py @@ -35,13 +35,14 @@ class MTPDeviceBase(DevicePlugin): DevicePlugin.__init__(self, *args, **kwargs) self.progress_reporter = None self.current_friendly_name = None + self.report_progress = lambda x, y: None def reset(self, key='-1', log_packets=False, report_progress=None, detected_device=None): pass def set_progress_reporter(self, report_progress): - self.progress_reporter = report_progress + self.report_progress = report_progress def get_gui_name(self): return self.current_friendly_name or self.name diff --git a/src/calibre/devices/mtp/driver.py b/src/calibre/devices/mtp/driver.py index 54827d234a..53a1fe36db 100644 --- a/src/calibre/devices/mtp/driver.py +++ b/src/calibre/devices/mtp/driver.py @@ -7,14 +7,86 @@ __license__ = 'GPL v3' __copyright__ = '2012, Kovid Goyal ' __docformat__ = 'restructuredtext en' -from calibre.constants import iswindows +import json, pprint +from io import BytesIO + +from calibre.constants import iswindows, numeric_version +from calibre.utils.config import from_json, to_json +from calibre.utils.date import now, isoformat if iswindows: from calibre.devices.mtp.windows.driver import MTP_DEVICE as BASE BASE else: from calibre.devices.mtp.unix.driver import MTP_DEVICE as BASE +pprint class MTP_DEVICE(BASE): - pass + + METADATA_CACHE = 'metadata.calibre' + DRIVEINFO = 'driveinfo.calibre' + + def _update_drive_info(self, storage, location_code, name=None): + import uuid + f = storage.find_path((self.DRIVEINFO,)) + dinfo = {} + if f is not None: + stream = self.get_file(f) + try: + dinfo = json.load(stream, object_hook=from_json) + except: + dinfo = None + if dinfo.get('device_store_uuid', None) is None: + dinfo['device_store_uuid'] = unicode(uuid.uuid4()) + if dinfo.get('device_name', None) is None: + dinfo['device_name'] = self.current_friendly_name + if name is not None: + dinfo['device_name'] = name + dinfo['location_code'] = location_code + dinfo['last_library_uuid'] = getattr(self, 'current_library_uuid', None) + dinfo['calibre_version'] = '.'.join([unicode(i) for i in numeric_version]) + dinfo['date_last_connected'] = isoformat(now()) + dinfo['mtp_prefix'] = storage.storage_prefix + raw = json.dumps(dinfo, default=to_json) + self.put_file(storage, self.DRIVEINFO, BytesIO(raw), len(raw)) + self.driveinfo = dinfo + + def open(self, devices, library_uuid): + self.current_library_uuid = library_uuid + BASE.open(self, devices, library_uuid) + + 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] ) + + def card_prefix(self, end_session=True): + ans = [None, None] + if self._carda_id is not None: + ans[0] = self.filesystem_cache.storage(self._carda_id).storage_prefix + if self._cardb_id is not None: + ans[1] = self.filesystem_cache.storage(self._cardb_id).storage_prefix + return tuple(ans) + +if __name__ == '__main__': + dev = MTP_DEVICE(None) + dev.startup() + try: + from calibre.devices.scanner import DeviceScanner + scanner = DeviceScanner() + scanner.scan() + devs = scanner.devices + cd = dev.detect_managed_devices(devs) + if cd is None: + raise ValueError('Failed to detect MTP device') + dev.open(cd, None) + pprint.pprint(dev.get_device_information()) + finally: + dev.shutdown() + diff --git a/src/calibre/devices/mtp/filesystem_cache.py b/src/calibre/devices/mtp/filesystem_cache.py index 3370967054..cd97c5c2ed 100644 --- a/src/calibre/devices/mtp/filesystem_cache.py +++ b/src/calibre/devices/mtp/filesystem_cache.py @@ -47,6 +47,9 @@ class FileOrFolder(object): self.fs_cache = weakref.ref(fs_cache) self.deleted = False + if self.storage_id == self.object_id: + self.storage_prefix = 'mtp:::%s:::'%self.persistent_id + def __repr__(self): name = 'Folder' if self.is_folder else 'File' try: @@ -125,6 +128,26 @@ class FileOrFolder(object): return e return None + def find_path(self, path): + ''' + Find a path in this folder, where path is a + tuple of folder and file names like ('eBooks', 'newest', + 'calibre.epub'). Finding is case-insensitive. + ''' + parent = self + components = list(path) + while components: + child = components[0] + components = components[1:] + c = parent.folder_named(child) + if c is None: + c = parent.file_named(child) + if c is None: + return None + parent = c + return parent + + class FilesystemCache(object): def __init__(self, all_storage, entries): @@ -164,4 +187,9 @@ class FilesystemCache(object): for e in self.entries: e.dump(out=out) + def storage(self, storage_id): + for e in self.entries: + if e.storage_id == storage_id: + return e + diff --git a/src/calibre/devices/mtp/unix/driver.py b/src/calibre/devices/mtp/unix/driver.py index e179647629..5d7d767a9b 100644 --- a/src/calibre/devices/mtp/unix/driver.py +++ b/src/calibre/devices/mtp/unix/driver.py @@ -46,14 +46,6 @@ class MTP_DEVICE(MTPDeviceBase): def set_debug_level(self, lvl): self.libmtp.set_debug_level(lvl) - def report_progress(self, sent, total): - try: - p = int(sent/total * 100) - except ZeroDivisionError: - p = 100 - if self.progress_reporter is not None: - self.progress_reporter(p) - @synchronous def detect_managed_devices(self, devices_on_system, force_refresh=False): if self.libmtp is None: return None @@ -212,19 +204,10 @@ class MTP_DEVICE(MTPDeviceBase): return self._filesystem_cache @synchronous - def get_device_information(self, end_session=True): + def get_basic_device_information(self): d = self.dev return (self.current_friendly_name, d.device_version, d.device_version, '') - @synchronous - def card_prefix(self, end_session=True): - ans = [None, None] - if self._carda_id is not None: - ans[0] = 'mtp:::%d:::'%self._carda_id - if self._cardb_id is not None: - ans[1] = 'mtp:::%d:::'%self._cardb_id - return tuple(ans) - @synchronous def total_space(self, end_session=True): ans = [0, 0, 0] @@ -298,6 +281,7 @@ class MTP_DEVICE(MTPDeviceBase): if not ok: raise DeviceError('Failed to get file: %s with errors: %s'%( f.full_path, self.format_errorstack(errs))) + stream.seek(0) return stream @synchronous diff --git a/src/calibre/devices/mtp/windows/driver.py b/src/calibre/devices/mtp/windows/driver.py index 63eef1df66..0506f63054 100644 --- a/src/calibre/devices/mtp/windows/driver.py +++ b/src/calibre/devices/mtp/windows/driver.py @@ -203,24 +203,11 @@ class MTP_DEVICE(MTPDeviceBase): self.current_friendly_name = devdata.get('friendly_name', None) @same_thread - def get_device_information(self, end_session=True): + def get_basic_device_information(self): d = self.dev.data dv = d.get('device_version', '') - for sid, location_code in ( (self._main_id, 'main'), (self._carda_id, - 'A'), (self._cardb_id, 'B')): - if sid is None: continue - # TODO: Implement the drive info dict return (self.current_friendly_name, dv, dv, '') - @same_thread - def card_prefix(self, end_session=True): - ans = [None, None] - if self._carda_id is not None: - ans[0] = 'mtp:::%s:::'%self._carda_id - if self._cardb_id is not None: - ans[1] = 'mtp:::%s:::'%self._cardb_id - return tuple(ans) - @same_thread def total_space(self, end_session=True): ans = [0, 0, 0] @@ -260,6 +247,7 @@ class MTP_DEVICE(MTPDeviceBase): except Exception as e: raise DeviceError('Failed to fetch the file %s with error: %s'% f.full_path, as_unicode(e)) + stream.seek(0) return stream @same_thread diff --git a/src/calibre/devices/usbms/driver.py b/src/calibre/devices/usbms/driver.py index b86d61182d..12e30073ac 100644 --- a/src/calibre/devices/usbms/driver.py +++ b/src/calibre/devices/usbms/driver.py @@ -63,7 +63,7 @@ class USBMS(CLI, Device): dinfo = {} if dinfo.get('device_store_uuid', None) is None: dinfo['device_store_uuid'] = unicode(uuid.uuid4()) - if dinfo.get('device_name') is None: + if dinfo.get('device_name', None) is None: dinfo['device_name'] = self.get_gui_name() if name is not None: dinfo['device_name'] = name