mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
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:
commit
b76509c83f
@ -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')]
|
||||
|
@ -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
|
||||
|
0
src/calibre/devices/user_defined/__init__.py
Normal file
0
src/calibre/devices/user_defined/__init__.py
Normal file
109
src/calibre/devices/user_defined/driver.py
Normal file
109
src/calibre/devices/user_defined/driver.py
Normal 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)
|
105
src/calibre/gui2/preferences/device_user_defined.py
Normal file
105
src/calibre/gui2/preferences/device_user_defined.py
Normal 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_()
|
@ -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)
|
||||
|
@ -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 &user defined device</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<spacer name="verticalSpacer_6">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
|
Loading…
x
Reference in New Issue
Block a user