mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Add basic per-device config for MTP devices
This commit is contained in:
parent
7a5049d2a1
commit
8288da34fc
@ -291,7 +291,7 @@ class ANDROID(USBMS):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def configure_for_kindle_app(cls):
|
def configure_for_kindle_app(cls):
|
||||||
proxy = cls._configProxy()
|
proxy = cls._configProxy()
|
||||||
proxy['format_map'] = ['mobi', 'azw', 'azw1', 'azw4', 'pdf']
|
proxy['format_map'] = ['azw3', 'mobi', 'azw', 'azw1', 'azw4', 'pdf']
|
||||||
proxy['use_subdirs'] = False
|
proxy['use_subdirs'] = False
|
||||||
proxy['extra_customization'] = [
|
proxy['extra_customization'] = [
|
||||||
','.join(['kindle']+cls.EBOOK_DIR_MAIN), '']
|
','.join(['kindle']+cls.EBOOK_DIR_MAIN), '']
|
||||||
|
@ -46,9 +46,8 @@ class MTPDeviceBase(DevicePlugin):
|
|||||||
def set_progress_reporter(self, report_progress):
|
def set_progress_reporter(self, report_progress):
|
||||||
self.report_progress = report_progress
|
self.report_progress = report_progress
|
||||||
|
|
||||||
@classmethod
|
def get_gui_name(self):
|
||||||
def get_gui_name(cls):
|
return getattr(self, 'current_friendly_name', self.gui_name)
|
||||||
return getattr(cls, 'current_friendly_name', cls.gui_name)
|
|
||||||
|
|
||||||
def is_usb_connected(self, devices_on_system, debug=False,
|
def is_usb_connected(self, devices_on_system, debug=False,
|
||||||
only_presence=False):
|
only_presence=False):
|
||||||
@ -60,13 +59,4 @@ class MTPDeviceBase(DevicePlugin):
|
|||||||
from calibre.devices.utils import build_template_regexp
|
from calibre.devices.utils import build_template_regexp
|
||||||
return build_template_regexp(self.save_template)
|
return build_template_regexp(self.save_template)
|
||||||
|
|
||||||
@property
|
|
||||||
def default_save_template(cls):
|
|
||||||
from calibre.library.save_to_disk import config
|
|
||||||
return config().parse().send_template
|
|
||||||
|
|
||||||
@property
|
|
||||||
def save_template(self):
|
|
||||||
# TODO: Use the device specific template here
|
|
||||||
return self.default_save_template
|
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ from calibre import prints
|
|||||||
from calibre.constants import iswindows, numeric_version
|
from calibre.constants import iswindows, numeric_version
|
||||||
from calibre.devices.mtp.base import debug
|
from calibre.devices.mtp.base import debug
|
||||||
from calibre.ptempfile import SpooledTemporaryFile, PersistentTemporaryDirectory
|
from calibre.ptempfile import SpooledTemporaryFile, PersistentTemporaryDirectory
|
||||||
from calibre.utils.config import from_json, to_json
|
from calibre.utils.config import from_json, to_json, JSONConfig
|
||||||
from calibre.utils.date import now, isoformat
|
from calibre.utils.date import now, isoformat
|
||||||
|
|
||||||
BASE = importlib.import_module('calibre.devices.mtp.%s.driver'%(
|
BASE = importlib.import_module('calibre.devices.mtp.%s.driver'%(
|
||||||
@ -39,9 +39,40 @@ class MTP_DEVICE(BASE):
|
|||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
BASE.__init__(self, *args, **kwargs)
|
BASE.__init__(self, *args, **kwargs)
|
||||||
self.plugboards = self.plugboard_func = None
|
self.plugboards = self.plugboard_func = None
|
||||||
|
self._prefs = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def prefs(self):
|
||||||
|
if self._prefs is None:
|
||||||
|
from calibre.library.save_to_disk import config
|
||||||
|
self._prefs = p = JSONConfig('mtp_devices')
|
||||||
|
p.defaults['format_map'] = self.FORMATS
|
||||||
|
p.defaults['send_to'] = ['eBooks/import',
|
||||||
|
'wordplayer/calibretransfer', 'Books', 'sdcard/ebooks',
|
||||||
|
'eBooks', 'kindle']
|
||||||
|
p.defaults['send_template'] = config().parse().send_template
|
||||||
|
|
||||||
|
return self._prefs
|
||||||
|
|
||||||
|
def configure_for_kindle_app(self):
|
||||||
|
proxy = self.prefs
|
||||||
|
with proxy:
|
||||||
|
proxy['format_map'] = ['azw3', 'mobi', 'azw', 'azw1', 'azw4', 'pdf']
|
||||||
|
proxy['send_template'] = '{title} - {authors}'
|
||||||
|
orig = list(proxy['send_to'])
|
||||||
|
if 'kindle' in orig:
|
||||||
|
orig.remove('kindle')
|
||||||
|
orig.insert(0, 'kindle')
|
||||||
|
proxy['send_to'] = orig
|
||||||
|
|
||||||
|
def configure_for_generic_epub_app(self):
|
||||||
|
with self.prefs:
|
||||||
|
for x in ('format_map', 'send_template', 'send_to'):
|
||||||
|
del self.prefs[x]
|
||||||
|
|
||||||
def open(self, devices, library_uuid):
|
def open(self, devices, library_uuid):
|
||||||
self.current_library_uuid = library_uuid
|
self.current_library_uuid = library_uuid
|
||||||
|
self.location_paths = None
|
||||||
BASE.open(self, devices, library_uuid)
|
BASE.open(self, devices, library_uuid)
|
||||||
|
|
||||||
# Device information {{{
|
# Device information {{{
|
||||||
@ -248,8 +279,24 @@ class MTP_DEVICE(BASE):
|
|||||||
return tuple(x for x in filepath.split('/'))
|
return tuple(x for x in filepath.split('/'))
|
||||||
|
|
||||||
def prefix_for_location(self, on_card):
|
def prefix_for_location(self, on_card):
|
||||||
# TODO: Implement this
|
if self.location_paths is None:
|
||||||
return 'calibre'
|
self.location_paths = {}
|
||||||
|
for sid, loc in ( (self._main_id, None), (self._carda_id, 'carda'),
|
||||||
|
(self._cardb_id, 'cardb') ):
|
||||||
|
if sid is not None:
|
||||||
|
storage = self.filesystem_cache.storage(sid)
|
||||||
|
prefixes = self.get_pref('send_to')
|
||||||
|
p = None
|
||||||
|
for path in prefixes:
|
||||||
|
path = path.replace(os.sep, '/')
|
||||||
|
if storage.find_path(path.split('/')) is not None:
|
||||||
|
p = path
|
||||||
|
break
|
||||||
|
if p is None:
|
||||||
|
p = 'eBooks'
|
||||||
|
self.location_paths[loc] = p
|
||||||
|
|
||||||
|
return self.location_paths[on_card]
|
||||||
|
|
||||||
def ensure_parent(self, storage, path):
|
def ensure_parent(self, storage, path):
|
||||||
parent = storage
|
parent = storage
|
||||||
@ -366,14 +413,28 @@ class MTP_DEVICE(BASE):
|
|||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
# Settings {{{
|
# Settings {{{
|
||||||
@classmethod
|
|
||||||
|
def get_pref(self, key):
|
||||||
|
return self.prefs.get('device-%s'%self.current_serial_num, {}).get(key,
|
||||||
|
self.prefs[key])
|
||||||
|
|
||||||
|
def config_widget(self):
|
||||||
|
from calibre.gui2.device_drivers.mtp_config import MTPConfig
|
||||||
|
return MTPConfig(self)
|
||||||
|
|
||||||
|
def save_settings(self, cw):
|
||||||
|
cw.commit()
|
||||||
|
|
||||||
def settings(self):
|
def settings(self):
|
||||||
# TODO: Implement this
|
|
||||||
class Opts(object):
|
class Opts(object):
|
||||||
def __init__(s):
|
def __init__(s):
|
||||||
s.format_map = self.FORMATS
|
s.format_map = self.get_pref('format_map')
|
||||||
return Opts()
|
return Opts()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def save_template(self):
|
||||||
|
return self.prefs['send_template']
|
||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
@ -390,6 +451,7 @@ if __name__ == '__main__':
|
|||||||
dev.set_progress_reporter(prints)
|
dev.set_progress_reporter(prints)
|
||||||
dev.open(cd, None)
|
dev.open(cd, None)
|
||||||
dev.filesystem_cache.dump()
|
dev.filesystem_cache.dump()
|
||||||
|
print ('Prefix for main mem:', dev.prefix_for_location(None))
|
||||||
finally:
|
finally:
|
||||||
dev.shutdown()
|
dev.shutdown()
|
||||||
|
|
||||||
|
225
src/calibre/gui2/device_drivers/mtp_config.py
Normal file
225
src/calibre/gui2/device_drivers/mtp_config.py
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:fdm=marker:ai
|
||||||
|
from __future__ import (unicode_literals, division, absolute_import,
|
||||||
|
print_function)
|
||||||
|
|
||||||
|
__license__ = 'GPL v3'
|
||||||
|
__copyright__ = '2012, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
import weakref
|
||||||
|
|
||||||
|
from PyQt4.Qt import (QWidget, QListWidgetItem, Qt, QToolButton, QLabel,
|
||||||
|
QTabWidget, QGridLayout, QListWidget, QIcon, QLineEdit, QVBoxLayout)
|
||||||
|
|
||||||
|
from calibre.ebooks import BOOK_EXTENSIONS
|
||||||
|
from calibre.gui2 import error_dialog
|
||||||
|
|
||||||
|
class FormatsConfig(QWidget): # {{{
|
||||||
|
|
||||||
|
def __init__(self, all_formats, format_map):
|
||||||
|
QWidget.__init__(self)
|
||||||
|
self.l = l = QGridLayout()
|
||||||
|
self.setLayout(l)
|
||||||
|
|
||||||
|
self.f = f = QListWidget(self)
|
||||||
|
l.addWidget(f, 0, 0, 3, 1)
|
||||||
|
unchecked_formats = sorted(all_formats - set(format_map))
|
||||||
|
for fmt in format_map + unchecked_formats:
|
||||||
|
item = QListWidgetItem(fmt, f)
|
||||||
|
item.setData(Qt.UserRole, fmt)
|
||||||
|
item.setFlags(Qt.ItemIsEnabled|Qt.ItemIsUserCheckable|Qt.ItemIsSelectable)
|
||||||
|
item.setCheckState(Qt.Checked if fmt in format_map else Qt.Unchecked)
|
||||||
|
|
||||||
|
self.button_up = b = QToolButton(self)
|
||||||
|
b.setIcon(QIcon(I('arrow-up.png')))
|
||||||
|
l.addWidget(b, 0, 1)
|
||||||
|
b.clicked.connect(self.up)
|
||||||
|
|
||||||
|
self.button_down = b = QToolButton(self)
|
||||||
|
b.setIcon(QIcon(I('arrow-down.png')))
|
||||||
|
l.addWidget(b, 2, 1)
|
||||||
|
b.clicked.connect(self.down)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def format_map(self):
|
||||||
|
return [unicode(self.f.item(i).data(Qt.UserRole).toString()) for i in
|
||||||
|
xrange(self.f.count()) if self.f.item(i).checkState()==Qt.Checked]
|
||||||
|
|
||||||
|
def validate(self):
|
||||||
|
if not self.format_map:
|
||||||
|
error_dialog(self, _('No formats selected'),
|
||||||
|
_('You must choose at least one format to send to the'
|
||||||
|
' device'), show=True)
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def up(self):
|
||||||
|
idx = self.f.currentRow()
|
||||||
|
if idx > 0:
|
||||||
|
self.f.insertItem(idx-1, self.f.takeItem(idx))
|
||||||
|
self.f.setCurrentRow(idx-1)
|
||||||
|
|
||||||
|
def down(self):
|
||||||
|
idx = self.f.currentRow()
|
||||||
|
if idx < self.f.count()-1:
|
||||||
|
self.f.insertItem(idx+1, self.f.takeItem(idx))
|
||||||
|
self.f.setCurrentRow(idx+1)
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
class TemplateConfig(QWidget): # {{{
|
||||||
|
|
||||||
|
def __init__(self, val):
|
||||||
|
QWidget.__init__(self)
|
||||||
|
self.t = t = QLineEdit(self)
|
||||||
|
t.setText(val or '')
|
||||||
|
t.setCursorPosition(0)
|
||||||
|
self.setMinimumWidth(400)
|
||||||
|
self.l = l = QVBoxLayout(self)
|
||||||
|
self.setLayout(l)
|
||||||
|
self.m = m = QLabel('<p>'+_('''<b>Save &template</b> to control the filename and
|
||||||
|
location of files sent to the device:'''))
|
||||||
|
m.setWordWrap(True)
|
||||||
|
m.setBuddy(t)
|
||||||
|
l.addWidget(m)
|
||||||
|
l.addWidget(t)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def template(self):
|
||||||
|
return unicode(self.t.text()).strip()
|
||||||
|
|
||||||
|
def validate(self):
|
||||||
|
from calibre.utils.formatter import validation_formatter
|
||||||
|
tmpl = self.template
|
||||||
|
try:
|
||||||
|
validation_formatter.validate(tmpl)
|
||||||
|
return True
|
||||||
|
except Exception as err:
|
||||||
|
error_dialog(self, _('Invalid template'),
|
||||||
|
'<p>'+_('The template %s is invalid:')%tmpl + \
|
||||||
|
'<br>'+unicode(err), show=True)
|
||||||
|
|
||||||
|
return False
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
class SendToConfig(QWidget): # {{{
|
||||||
|
|
||||||
|
def __init__(self, val):
|
||||||
|
QWidget.__init__(self)
|
||||||
|
self.t = t = QLineEdit(self)
|
||||||
|
t.setText(', '.join(val or []))
|
||||||
|
t.setCursorPosition(0)
|
||||||
|
self.l = l = QVBoxLayout(self)
|
||||||
|
self.setLayout(l)
|
||||||
|
self.m = m = QLabel('<p>'+_('''A <b>list of &folders</b> on the device to
|
||||||
|
which to send ebooks. The first one that exists will be used:'''))
|
||||||
|
m.setWordWrap(True)
|
||||||
|
m.setBuddy(t)
|
||||||
|
l.addWidget(m)
|
||||||
|
l.addWidget(t)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def value(self):
|
||||||
|
ans = [x.strip() for x in unicode(self.t.text()).strip().split(',')]
|
||||||
|
return [x for x in ans if x]
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
class MTPConfig(QTabWidget):
|
||||||
|
|
||||||
|
def __init__(self, device, parent=None):
|
||||||
|
QTabWidget.__init__(self, parent)
|
||||||
|
self._device = weakref.ref(device)
|
||||||
|
|
||||||
|
cd = msg = None
|
||||||
|
if device.current_friendly_name is not None:
|
||||||
|
if device.current_serial_num is None:
|
||||||
|
msg = '<p>' + _('The <b>%s</b> device has no serial number, '
|
||||||
|
'it cannot be configured'%device.current_friendly_name)
|
||||||
|
else:
|
||||||
|
cd = 'device-'+device.current_serial_num
|
||||||
|
else:
|
||||||
|
msg = '<p>' + _('<b>No MTP device connected.</b><p>'
|
||||||
|
' You can only configure the MTP device plugin when a device'
|
||||||
|
' is connected.')
|
||||||
|
|
||||||
|
self.current_device_key = cd
|
||||||
|
|
||||||
|
if msg:
|
||||||
|
msg += '<p>' + _('If you want to un-ignore a previously'
|
||||||
|
' ignored MTP device, use the "Ignored devices" tab.')
|
||||||
|
l = QLabel(msg)
|
||||||
|
l.setWordWrap(True)
|
||||||
|
l.setStyleSheet('QLabel { margin-left: 2em }')
|
||||||
|
self.insertTab(0, l, _('Cannot configure'))
|
||||||
|
else:
|
||||||
|
self.base = QWidget(self)
|
||||||
|
self.insertTab(0, self.base, _('Configure %s')%self.device.current_friendly_name)
|
||||||
|
l = self.base.l = QGridLayout(self.base)
|
||||||
|
self.base.setLayout(l)
|
||||||
|
|
||||||
|
self.formats = FormatsConfig(set(BOOK_EXTENSIONS),
|
||||||
|
self.get_pref('format_map'))
|
||||||
|
self.send_to = SendToConfig(self.get_pref('send_to'))
|
||||||
|
self.template = TemplateConfig(self.get_pref('send_template'))
|
||||||
|
l.addWidget(self.formats, 0, 0, 3, 1)
|
||||||
|
l.addWidget(self.send_to, 0, 1, 1, 1)
|
||||||
|
l.addWidget(self.template, 1, 1, 1, 1)
|
||||||
|
l.setRowStretch(2, 10)
|
||||||
|
|
||||||
|
self.setCurrentIndex(0)
|
||||||
|
|
||||||
|
def get_pref(self, key):
|
||||||
|
p = self.device.prefs.get(self.current_device_key, {})
|
||||||
|
if not p:
|
||||||
|
self.device.prefs[self.current_device_key] = p
|
||||||
|
return p.get(key, self.device.prefs[key])
|
||||||
|
|
||||||
|
@property
|
||||||
|
def device(self):
|
||||||
|
return self._device()
|
||||||
|
|
||||||
|
def validate(self):
|
||||||
|
if not self.formats.validate():
|
||||||
|
return False
|
||||||
|
if not self.template.validate():
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def commit(self):
|
||||||
|
p = self.device.prefs.get(self.current_device_key, {})
|
||||||
|
|
||||||
|
p.pop('format_map', None)
|
||||||
|
f = self.formats.format_map
|
||||||
|
if f and f != self.device.prefs['format_map']:
|
||||||
|
p['format_map'] = f
|
||||||
|
|
||||||
|
p.pop('send_template', None)
|
||||||
|
t = self.template.template
|
||||||
|
if t and t != self.device.prefs['send_template']:
|
||||||
|
p['send_template'] = t
|
||||||
|
|
||||||
|
p.pop('send_to', None)
|
||||||
|
s = self.send_to.value
|
||||||
|
if s and s != self.device.prefs['send_to']:
|
||||||
|
p['send_to'] = s
|
||||||
|
|
||||||
|
self.device.prefs[self.current_device_key] = p
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
from calibre.gui2 import Application
|
||||||
|
from calibre.devices.mtp.driver import MTP_DEVICE
|
||||||
|
from calibre.devices.scanner import DeviceScanner
|
||||||
|
s = DeviceScanner()
|
||||||
|
s.scan()
|
||||||
|
app = Application([])
|
||||||
|
dev = MTP_DEVICE(None)
|
||||||
|
dev.startup()
|
||||||
|
cd = dev.detect_managed_devices(s.devices)
|
||||||
|
dev.open(cd, 'test')
|
||||||
|
cw = dev.config_widget()
|
||||||
|
cw.show()
|
||||||
|
app.exec_()
|
||||||
|
dev.shutdown()
|
||||||
|
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user