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
|
||||
def configure_for_kindle_app(cls):
|
||||
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['extra_customization'] = [
|
||||
','.join(['kindle']+cls.EBOOK_DIR_MAIN), '']
|
||||
|
@ -46,9 +46,8 @@ class MTPDeviceBase(DevicePlugin):
|
||||
def set_progress_reporter(self, report_progress):
|
||||
self.report_progress = report_progress
|
||||
|
||||
@classmethod
|
||||
def get_gui_name(cls):
|
||||
return getattr(cls, 'current_friendly_name', cls.gui_name)
|
||||
def get_gui_name(self):
|
||||
return getattr(self, 'current_friendly_name', self.gui_name)
|
||||
|
||||
def is_usb_connected(self, devices_on_system, debug=False,
|
||||
only_presence=False):
|
||||
@ -60,13 +59,4 @@ class MTPDeviceBase(DevicePlugin):
|
||||
from calibre.devices.utils import build_template_regexp
|
||||
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.devices.mtp.base import debug
|
||||
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
|
||||
|
||||
BASE = importlib.import_module('calibre.devices.mtp.%s.driver'%(
|
||||
@ -39,9 +39,40 @@ class MTP_DEVICE(BASE):
|
||||
def __init__(self, *args, **kwargs):
|
||||
BASE.__init__(self, *args, **kwargs)
|
||||
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):
|
||||
self.current_library_uuid = library_uuid
|
||||
self.location_paths = None
|
||||
BASE.open(self, devices, library_uuid)
|
||||
|
||||
# Device information {{{
|
||||
@ -248,8 +279,24 @@ class MTP_DEVICE(BASE):
|
||||
return tuple(x for x in filepath.split('/'))
|
||||
|
||||
def prefix_for_location(self, on_card):
|
||||
# TODO: Implement this
|
||||
return 'calibre'
|
||||
if self.location_paths is None:
|
||||
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):
|
||||
parent = storage
|
||||
@ -366,14 +413,28 @@ class MTP_DEVICE(BASE):
|
||||
# }}}
|
||||
|
||||
# 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):
|
||||
# TODO: Implement this
|
||||
class Opts(object):
|
||||
def __init__(s):
|
||||
s.format_map = self.FORMATS
|
||||
s.format_map = self.get_pref('format_map')
|
||||
return Opts()
|
||||
|
||||
@property
|
||||
def save_template(self):
|
||||
return self.prefs['send_template']
|
||||
|
||||
# }}}
|
||||
|
||||
if __name__ == '__main__':
|
||||
@ -390,6 +451,7 @@ if __name__ == '__main__':
|
||||
dev.set_progress_reporter(prints)
|
||||
dev.open(cd, None)
|
||||
dev.filesystem_cache.dump()
|
||||
print ('Prefix for main mem:', dev.prefix_for_location(None))
|
||||
finally:
|
||||
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