diff --git a/src/calibre/devices/interface.py b/src/calibre/devices/interface.py index fbdcbed564..1336e3c782 100644 --- a/src/calibre/devices/interface.py +++ b/src/calibre/devices/interface.py @@ -228,7 +228,7 @@ class DevicePlugin(Plugin): """ raise NotImplementedError() - def can_handle_windows(self, device_id, debug=False): + def can_handle_windows(self, usbdevice, debug=False): ''' Optional method to perform further checks on a device to see if this driver is capable of handling it. If it is not it should return False. This method @@ -237,15 +237,17 @@ class DevicePlugin(Plugin): returns True. This method is called only on windows. See also :meth:`can_handle`. - :param device_info: On windows a device ID string. On Unix a tuple of - ``(vendor_id, product_id, bcd)``. + Note that for devices based on USBMS this method by default delegates + to :meth:`can_handle`. So you only need to override :meth:`can_handle` + in your subclass of USBMS. + :param usbdevice: A usbdevice as returned by :func:`calibre.devices.winusb.scan_usb_devices` ''' return True def can_handle(self, device_info, debug=False): ''' - Unix version of :meth:`can_handle_windows` + Unix version of :meth:`can_handle_windows`. :param device_info: Is a tuple of (vid, pid, bcd, manufacturer, product, serial number) diff --git a/src/calibre/devices/usbms/device.py b/src/calibre/devices/usbms/device.py index 0e4d47e885..46b8974165 100644 --- a/src/calibre/devices/usbms/device.py +++ b/src/calibre/devices/usbms/device.py @@ -13,6 +13,7 @@ device. This class handles device detection. import os, subprocess, time, re, sys, glob from itertools import repeat +from collections import namedtuple from calibre import prints, as_unicode from calibre.constants import DEBUG @@ -25,6 +26,9 @@ from calibre.utils.filenames import ascii_filename as sanitize if isosx: usbobserver, usbobserver_err = plugins['usbobserver'] +if iswindows: + usb_info_cache = {} + def eject_exe(): base = sys.extensions_location if hasattr(sys, 'new_app_layout') else os.path.dirname(sys.executable) return os.path.join(base, 'calibre-eject.exe') @@ -196,6 +200,30 @@ class Device(DeviceConfig, DevicePlugin): ''' return drives + def can_handle_windows(self, usbdevice, debug=False): + from calibre.devices.interface import DevicePlugin + if self.can_handle.im_func is DevicePlugin.can_handle.im_func: + # No custom can_handle implementation + return True + # Delegate to the unix can_handle function, creating a unix like + # USBDevice object + from calibre.devices.winusb import get_usb_info + dev = usb_info_cache.get(usbdevice) + if dev is None: + try: + data = get_usb_info(usbdevice, debug=debug) + except Exception: + time.sleep(0.1) + try: + data = get_usb_info(usbdevice, debug=debug) + except Exception: + data = {} + dev = usb_info_cache[usbdevice] = namedtuple( + 'USBDevice', 'vendor_id product_id bcd manufacturer product serial')( + usbdevice.vendor_id, usbdevice.product_id, usbdevice.bcd, + data.get('manufacturer') or '', data.get('product') or '', data.get('serial_number') or '') + return self.can_handle(dev, debug=debug) + def open_windows(self): from calibre.devices.scanner import drive_is_ok from calibre.devices.winusb import get_drive_letters_for_device