Add a user defined device driver plugin to allow for users to create their own device drivers if they have customized/modded devices.

This commit is contained in:
Kovid Goyal 2011-05-02 09:42:15 -06:00
commit b76509c83f
7 changed files with 287 additions and 1 deletions

View File

@ -595,6 +595,7 @@ from calibre.devices.jetbook.driver import JETBOOK, MIBUK, JETBOOK_MINI
from calibre.devices.kindle.driver import KINDLE, KINDLE2, KINDLE_DX
from calibre.devices.nook.driver import NOOK, NOOK_COLOR
from calibre.devices.prs505.driver import PRS505
from calibre.devices.user_defined.driver import USER_DEFINED
from calibre.devices.android.driver import ANDROID, S60
from calibre.devices.nokia.driver import N770, N810, E71X, E52
from calibre.devices.eslick.driver import ESLICK, EBK52
@ -742,6 +743,7 @@ plugins += [
EEEREADER,
NEXTBOOK,
ITUNES,
USER_DEFINED,
]
plugins += [x for x in list(locals().values()) if isinstance(x, type) and \
x.__name__.endswith('MetadataReader')]

View File

@ -156,3 +156,60 @@ def debug(ioreg_to_tmp=False, buf=None):
sys.stdout = oldo
sys.stderr = olde
def device_info(ioreg_to_tmp=False, buf=None):
from calibre.devices.scanner import DeviceScanner, win_pnp_drives
from calibre.constants import iswindows
import re
res = {}
device_details = {}
device_set = set()
drive_details = {}
drive_set = set()
res['device_set'] = device_set
res['device_details'] = device_details
res['drive_details'] = drive_details
res['drive_set'] = drive_set
try:
s = DeviceScanner()
s.scan()
devices = (s.devices)
if not iswindows:
devices = [list(x) for x in devices]
for dev in devices:
for i in range(3):
dev[i] = hex(dev[i])
d = dev[0] + dev[1] + dev[2]
device_set.add(d)
device_details[d] = dev[0:3]
else:
for dev in devices:
vid = re.search('vid_([0-9a-f]*)&', dev)
if vid:
vid = vid.group(1)
pid = re.search('pid_([0-9a-f]*)&', dev)
if pid:
pid = pid.group(1)
rev = re.search('rev_([0-9a-f]*)$', dev)
if rev:
rev = rev.group(1)
d = vid+pid+rev
device_set.add(d)
device_details[d] = (vid, pid, rev)
drives = win_pnp_drives(debug=False)
for drive,details in drives.iteritems():
order = 'ORD_' + str(drive.order)
ven = re.search('VEN_([^&]*)&', details)
if ven:
ven = ven.group(1)
prod = re.search('PROD_([^&]*)&', details)
if prod:
prod = prod.group(1)
d = (order, ven, prod)
drive_details[drive] = d
drive_set.add(drive)
finally:
pass
return res

View File

@ -0,0 +1,109 @@
# -*- coding: utf-8 -*-
__license__ = 'GPL v3'
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
from calibre.devices.usbms.driver import USBMS
from calibre.ebooks import BOOK_EXTENSIONS
class USER_DEFINED(USBMS):
name = 'User Defined USB driver'
gui_name = 'User Defined USB Device'
author = 'Kovid Goyal'
supported_platforms = ['windows', 'osx', 'linux']
# Ordered list of supported formats
FORMATS = BOOK_EXTENSIONS
VENDOR_ID = 0xFFFF
PRODUCT_ID = 0xFFFF
BCD = None
EBOOK_DIR_MAIN = ''
EBOOK_DIR_CARD_A = ''
VENDOR_NAME = []
WINDOWS_MAIN_MEM = ''
WINDOWS_CARD_A_MEM = ''
OSX_MAIN_MEM = 'Device Main Memory'
MAIN_MEMORY_VOLUME_LABEL = 'Device Main Memory'
SUPPORTS_SUB_DIRS = True
EXTRA_CUSTOMIZATION_MESSAGE = [
_('USB Vendor ID (in hex)') + ':::' +
_('Get this ID using Preferences -> Misc -> Get information to '
'set up the user-defined device'),
_('USB Product ID (in hex)')+ ':::' +
_('Get this ID using Preferences -> Misc -> Get information to '
'set up the user-defined device'),
_('USB Revision ID (in hex)')+ ':::' +
_('Get this ID using Preferences -> Misc -> Get information to '
'set up the user-defined device'),
_('Windows main memory vendor string') + ':::' +
_('This field is used only on windows. '
'Get this ID using Preferences -> Misc -> Get information to '
'set up the user-defined device'),
_('Windows main memory ID string') + ':::' +
_('This field is used only on windows. '
'Get this ID using Preferences -> Misc -> Get information to '
'set up the user-defined device'),
_('Windows card A vendor string') + ':::' +
_('This field is used only on windows. '
'Get this ID using Preferences -> Misc -> Get information to '
'set up the user-defined device'),
_('Windows card A ID string') + ':::' +
_('This field is used only on windows. '
'Get this ID using Preferences -> Misc -> Get information to '
'set up the user-defined device'),
_('Main memory folder') + ':::' +
_('Enter the folder where the books are to be stored. This folder '
'is prepended to any send_to_device template'),
_('Card A folder') + ':::' +
_('Enter the folder where the books are to be stored. This folder '
'is prepended to any send_to_device template'),
]
EXTRA_CUSTOMIZATION_DEFAULT = [
'0x0000',
'0x0000',
'0x0000',
'',
'',
'',
'',
'',
'',
]
OPT_USB_VENDOR_ID = 0
OPT_USB_PRODUCT_ID = 1
OPT_USB_REVISION_ID = 2
OPT_USB_WINDOWS_MM_VEN_ID = 3
OPT_USB_WINDOWS_MM_ID = 4
OPT_USB_WINDOWS_CA_VEN_ID = 5
OPT_USB_WINDOWS_CA_ID = 6
OPT_MAIN_MEM_FOLDER = 7
OPT_CARD_A_FOLDER = 8
def initialize(self):
try:
e = self.settings().extra_customization
self.VENDOR_ID = int(e[self.OPT_USB_VENDOR_ID], 16)
self.PRODUCT_ID = int(e[self.OPT_USB_PRODUCT_ID], 16)
self.BCD = [int(e[self.OPT_USB_REVISION_ID], 16)]
if e[self.OPT_USB_WINDOWS_MM_VEN_ID]:
self.VENDOR_NAME.append(e[self.OPT_USB_WINDOWS_MM_VEN_ID])
if e[self.OPT_USB_WINDOWS_CA_VEN_ID] and \
e[self.OPT_USB_WINDOWS_CA_VEN_ID] not in self.VENDOR_NAME:
self.VENDOR_NAME.append(e[self.OPT_USB_WINDOWS_CA_VEN_ID])
self.WINDOWS_MAIN_MEM = e[self.OPT_USB_WINDOWS_MM_ID] + '&'
self.WINDOWS_CARD_A_MEM = e[self.OPT_USB_WINDOWS_CA_ID] + '&'
self.EBOOK_DIR_MAIN = e[self.OPT_MAIN_MEM_FOLDER]
self.EBOOK_DIR_CARD_A = e[self.OPT_CARD_A_FOLDER]
except:
import traceback
traceback.print_exc()
USBMS.initialize(self)

View File

@ -0,0 +1,105 @@
#!/usr/bin/env python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
from __future__ import with_statement
__license__ = 'GPL v3'
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
from PyQt4.Qt import QDialog, QVBoxLayout, QPlainTextEdit, QTimer, \
QDialogButtonBox, QPushButton, QApplication, QIcon, QMessageBox
from calibre.constants import iswindows
def step_dialog(parent, title, msg, det_msg=''):
d = QMessageBox(parent)
d.setWindowTitle(title)
d.setText(msg)
d.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel)
return d.exec_() & QMessageBox.Cancel
class UserDefinedDevice(QDialog):
def __init__(self, parent=None):
QDialog.__init__(self, parent)
self._layout = QVBoxLayout(self)
self.setLayout(self._layout)
self.log = QPlainTextEdit(self)
self._layout.addWidget(self.log)
self.log.setPlainText(_('Getting device information')+'...')
self.copy = QPushButton(_('Copy to &clipboard'))
self.copy.setDefault(True)
self.setWindowTitle(_('User-defined device information'))
self.setWindowIcon(QIcon(I('debug.png')))
self.copy.clicked.connect(self.copy_to_clipboard)
self.ok = QPushButton('&OK')
self.ok.setAutoDefault(False)
self.ok.clicked.connect(self.accept)
self.bbox = QDialogButtonBox(self)
self.bbox.addButton(self.copy, QDialogButtonBox.ActionRole)
self.bbox.addButton(self.ok, QDialogButtonBox.AcceptRole)
self._layout.addWidget(self.bbox)
self.resize(750, 500)
self.bbox.setEnabled(False)
QTimer.singleShot(1000, self.device_info)
def device_info(self):
try:
from calibre.devices import device_info
r = step_dialog(self.parent(), _('Device Detection'),
_('Ensure your device is disconnected, then press OK'))
if r:
self.close()
return
before = device_info()
r = step_dialog(self.parent(), _('Device Detection'),
_('Ensure your device is connected, then press OK'))
if r:
self.close()
return
after = device_info()
new_drives = after['drive_set'] - before['drive_set']
new_devices = after['device_set'] - before['device_set']
res = ''
if (not iswindows or len(new_drives)) and len(new_devices) == 1:
for d in new_devices:
res = _('USB Vendor ID (in hex)') + ': 0x' + \
after['device_details'][d][0] + '\n'
res += _('USB Product ID (in hex)') + ': 0x' + \
after['device_details'][d][1] + '\n'
res += _('USB Revision ID (in hex)') + ': 0x' + \
after['device_details'][d][2] + '\n'
if iswindows:
# sort the drives by the order number
for i,d in enumerate(sorted(new_drives,
key=lambda x: after['drive_details'][x][0])):
if i == 0:
res += _('Windows main memory ID string') + ': ' + \
after['drive_details'][d][1] + '\n'
res += _('Windows main memory ID string') + ': ' + \
after['drive_details'][d][2] + '\n'
else:
res += _('Windows card A vendor string') + ': ' + \
after['drive_details'][d][1] + '\n'
res += _('Windows card A ID string') + ': ' + \
after['drive_details'][d][2] + '\n'
trailer = _(
'Copy these values to the clipboard, paste them into an '
'editor, then enter them into the USER_DEVICE by '
'customizing the device plugin in Preferences->Plugins. '
'Remember to also enter the folders where you want the books to '
'be put. You must restart calibre for your changes '
'to take effect.\n')
self.log.setPlainText(res + '\n\n' + trailer)
finally:
self.bbox.setEnabled(True)
def copy_to_clipboard(self):
QApplication.clipboard().setText(self.log.toPlainText())
if __name__ == '__main__':
app = QApplication([])
d = UserDefinedDevice()
d.exec_()

View File

@ -30,6 +30,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
r('enforce_cpu_limit', config, restart_required=True)
self.device_detection_button.clicked.connect(self.debug_device_detection)
self.button_open_config_dir.clicked.connect(self.open_config_dir)
self.user_defined_device_button.clicked.connect(self.user_defined_device)
self.button_osx_symlinks.clicked.connect(self.create_symlinks)
self.button_osx_symlinks.setVisible(isosx)
@ -38,6 +39,11 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
d = DebugDevice(self)
d.exec_()
def user_defined_device(self, *args):
from calibre.gui2.preferences.device_user_defined import UserDefinedDevice
d = UserDefinedDevice(self)
d.exec_()
def open_config_dir(self, *args):
from calibre.utils.config import config_dir
open_local_file(config_dir)

View File

@ -58,7 +58,14 @@
</property>
</widget>
</item>
<item row="4" column="0">
<item row="4" column="0" colspan="2">
<widget class="QPushButton" name="user_defined_device_button">
<property name="text">
<string>Get information to setup the &amp;user defined device</string>
</property>
</widget>
</item>
<item row="5" column="0">
<spacer name="verticalSpacer_6">
<property name="orientation">
<enum>Qt::Vertical</enum>