mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Make the metadata download plugins customizable
This commit is contained in:
parent
87c7c91ab2
commit
69fc173ff5
@ -101,8 +101,6 @@ def metadata_sources(metadata_type='basic', customize=True, isbndb_key=None):
|
||||
plugin.site_customization = customization.get(plugin.name, None)
|
||||
if plugin.name == 'IsbnDB' and isbndb_key is not None:
|
||||
plugin.site_customization = isbndb_key
|
||||
if not plugin.is_ok():
|
||||
continue
|
||||
yield plugin
|
||||
|
||||
def get_isbndb_key():
|
||||
|
@ -9,9 +9,11 @@ from threading import Thread
|
||||
from calibre import prints
|
||||
from calibre.utils.config import OptionParser
|
||||
from calibre.utils.logging import default_log
|
||||
|
||||
from calibre.ebooks.metadata import MetaInformation
|
||||
from calibre.customize import Plugin
|
||||
|
||||
metadata_config = None
|
||||
|
||||
class MetadataSource(Plugin):
|
||||
|
||||
author = 'Kovid Goyal'
|
||||
@ -23,11 +25,17 @@ class MetadataSource(Plugin):
|
||||
#: tags/rating/reviews/etc.
|
||||
metadata_type = 'basic'
|
||||
|
||||
#: If not None, the customization dialog will allow for string
|
||||
#: based customization as well the default customization. The
|
||||
#: string customization will be saved in the site_customization
|
||||
#: member.
|
||||
string_customization_help = None
|
||||
|
||||
type = _('Metadata download')
|
||||
|
||||
def __call__(self, title, author, publisher, isbn, verbose, log=None,
|
||||
extra=None):
|
||||
self.worker = Thread(target=self.fetch)
|
||||
self.worker = Thread(target=self._fetch)
|
||||
self.worker.daemon = True
|
||||
self.title = title
|
||||
self.verbose = verbose
|
||||
@ -39,23 +47,87 @@ class MetadataSource(Plugin):
|
||||
self.exception, self.tb, self.results = None, None, []
|
||||
self.worker.start()
|
||||
|
||||
def _fetch(self):
|
||||
try:
|
||||
self.fetch()
|
||||
if self.results:
|
||||
c = self.config_store().get(self.name, {})
|
||||
res = self.results
|
||||
if isinstance(res, MetaInformation):
|
||||
res = [res]
|
||||
for mi in res:
|
||||
if not c.get('rating', True):
|
||||
mi.rating = None
|
||||
if not c.get('comments', True):
|
||||
mi.comments = None
|
||||
if not c.get('tags', True):
|
||||
mi.tags = []
|
||||
|
||||
except Exception, e:
|
||||
self.exception = e
|
||||
self.tb = traceback.format_exc()
|
||||
|
||||
def fetch(self):
|
||||
'''
|
||||
All the actual work is done here.
|
||||
'''
|
||||
raise NotImplementedError
|
||||
|
||||
def is_ok(self):
|
||||
'''
|
||||
Used to check if the plugin has been correctly customized.
|
||||
For example: The isbndb plugin checks to see if the site_customization
|
||||
has been set with an isbndb.com access key.
|
||||
'''
|
||||
return True
|
||||
|
||||
def join(self):
|
||||
return self.worker.join()
|
||||
|
||||
def is_customizable(self):
|
||||
return True
|
||||
|
||||
def config_store(self):
|
||||
global metadata_config
|
||||
if metadata_config is None:
|
||||
from calibre.utils.config import XMLConfig
|
||||
metadata_config = XMLConfig('plugins/metadata_download')
|
||||
return metadata_config
|
||||
|
||||
def config_widget(self):
|
||||
from PyQt4.Qt import QWidget, QVBoxLayout, QLabel, Qt, QLineEdit, \
|
||||
QCheckBox
|
||||
from calibre.customize.ui import config
|
||||
w = QWidget()
|
||||
w._layout = QVBoxLayout(w)
|
||||
w.setLayout(w._layout)
|
||||
if self.string_customization_help is not None:
|
||||
w._sc_label = QLabel(self.string_customization_help, w)
|
||||
w._layout.addWidget(w._sc_label)
|
||||
customization = config['plugin_customization']
|
||||
def_sc = customization.get(self.name, '')
|
||||
if not def_sc:
|
||||
def_sc = ''
|
||||
w._sc = QLineEdit(def_sc, w)
|
||||
w._layout.addWidget(w._sc)
|
||||
w._sc_label.setWordWrap(True)
|
||||
w._sc_label.setTextInteractionFlags(Qt.LinksAccessibleByMouse
|
||||
| Qt.LinksAccessibleByKeyboard)
|
||||
w._sc_label.setOpenExternalLinks(True)
|
||||
c = self.config_store()
|
||||
c = c.get(self.name, {})
|
||||
for x, l in {'rating':_('ratings'), 'tags':_('tags'),
|
||||
'comments':_('description/reviews')}.items():
|
||||
cb = QCheckBox(_('Download %s from %s')%(l,
|
||||
self.name))
|
||||
setattr(w, '_'+x, cb)
|
||||
cb.setChecked(c.get(x, True))
|
||||
w._layout.addWidget(cb)
|
||||
return w
|
||||
|
||||
def save_settings(self, w):
|
||||
dl_settings = {}
|
||||
for x in ('rating', 'tags', 'comments'):
|
||||
dl_settings[x] = getattr(w, '_'+x).isChecked()
|
||||
c = self.config_store()
|
||||
c.set(self.name, dl_settings)
|
||||
if hasattr(w, '_sc'):
|
||||
sc = unicode(w._sc.text()).strip()
|
||||
from calibre.customize.ui import customize_plugin
|
||||
customize_plugin(self, sc)
|
||||
|
||||
|
||||
class GoogleBooks(MetadataSource):
|
||||
|
||||
@ -102,14 +174,11 @@ class ISBNDB(MetadataSource):
|
||||
self.exception = e
|
||||
self.tb = traceback.format_exc()
|
||||
|
||||
def customization_help(self, gui=False):
|
||||
@property
|
||||
def string_customization_help(self):
|
||||
ans = _('To use isbndb.com you must sign up for a %sfree account%s '
|
||||
'and enter your access key below.')
|
||||
if gui:
|
||||
ans = '<p>'+ans%('<a href="http://www.isbndb.com">', '</a>')
|
||||
else:
|
||||
ans = ans.replace('%s', '')
|
||||
return ans
|
||||
return '<p>'+ans%('<a href="http://www.isbndb.com">', '</a>')
|
||||
|
||||
class Amazon(MetadataSource):
|
||||
|
||||
|
@ -81,7 +81,7 @@ Device Integration
|
||||
|
||||
What devices does |app| support?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
At the moment |app| has full support for the SONY PRS 300/500/505/600/700, Cybook Gen 3/Opus, Amazon Kindle 1/2/DX, Netronix EB600, Ectaco Jetbook, BeBook/BeBook Mini, Irex Illiad/DR1000, Foxit eSlick, Android phones and the iPhone. In addition, using the :guilabel:`Save to disk` function you can use it with any ebook reader that exports itself as a USB disk.
|
||||
At the moment |app| has full support for the SONY PRS 300/500/505/600/700, Cybook Gen 3/Opus, Amazon Kindle 1/2/DX, Netronix EB600, Ectaco Jetbook, BeBook/BeBook Mini, Irex Illiad/DR1000, Foxit eSlick, PocketBook 360, Android phones and the iPhone. In addition, using the :guilabel:`Save to disk` function you can use it with any ebook reader that exports itself as a USB disk.
|
||||
|
||||
How can I help get my device supported in |app|?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -108,7 +108,7 @@ Metadata download plugins
|
||||
.. class:: calibre.ebooks.metadata.fetch.MetadataSource
|
||||
|
||||
Represents a source to query for metadata. Subclasses must implement
|
||||
at least the fetch method and optionally the is_ok method.
|
||||
at least the fetch method.
|
||||
|
||||
When :meth:`fetch` is called, the `self` object will have the following
|
||||
useful attributes (each of which may be None)::
|
||||
@ -124,8 +124,9 @@ Metadata download plugins
|
||||
|
||||
.. automember:: calibre.ebooks.metadata.fetch.MetadataSource.metadata_type
|
||||
|
||||
.. automember:: calibre.ebooks.metadata.fetch.MetadataSource.string_customization_help
|
||||
|
||||
.. automethod:: calibre.ebooks.metadata.fetch.MetadataSource.fetch
|
||||
|
||||
.. automethod:: calibre.ebooks.metadata.fetch.MetadataSource.is_ok
|
||||
|
||||
|
||||
|
@ -6,7 +6,7 @@ __docformat__ = 'restructuredtext en'
|
||||
'''
|
||||
Manage application-wide preferences.
|
||||
'''
|
||||
import os, re, cPickle, textwrap, traceback
|
||||
import os, re, cPickle, textwrap, traceback, plistlib
|
||||
from copy import deepcopy
|
||||
from functools import partial
|
||||
from optparse import OptionParser as _OptionParser
|
||||
@ -34,9 +34,11 @@ else:
|
||||
|
||||
plugin_dir = os.path.join(config_dir, 'plugins')
|
||||
|
||||
CONFIG_DIR_MODE = 0700
|
||||
|
||||
def make_config_dir():
|
||||
if not os.path.exists(plugin_dir):
|
||||
os.makedirs(plugin_dir, mode=448) # 0700 == 448
|
||||
os.makedirs(plugin_dir, mode=CONFIG_DIR_MODE)
|
||||
|
||||
def check_config_write_access():
|
||||
return os.access(config_dir, os.W_OK) and os.access(config_dir, os.X_OK)
|
||||
@ -552,6 +554,72 @@ class DynamicConfig(dict):
|
||||
|
||||
dynamic = DynamicConfig()
|
||||
|
||||
class XMLConfig(dict):
|
||||
|
||||
'''
|
||||
Similar to :class:`DynamicConfig`, except that it uses an XML storage
|
||||
backend instead of a pickle file.
|
||||
|
||||
See `http://docs.python.org/dev/library/plistlib.html`_ for the supported
|
||||
data types.
|
||||
'''
|
||||
|
||||
def __init__(self, rel_path_to_cf_file):
|
||||
dict.__init__(self)
|
||||
self.file_path = os.path.join(config_dir,
|
||||
*(rel_path_to_cf_file.split('/')))
|
||||
self.file_path = os.path.abspath(self.file_path)
|
||||
if not self.file_path.endswith('.plist'):
|
||||
self.file_path += '.plist'
|
||||
|
||||
self.refresh()
|
||||
|
||||
def refresh(self):
|
||||
d = {}
|
||||
if os.path.exists(self.file_path):
|
||||
with ExclusiveFile(self.file_path) as f:
|
||||
raw = f.read()
|
||||
try:
|
||||
d = plistlib.readPlistFromString(raw) if raw.strip() else {}
|
||||
except SystemError:
|
||||
pass
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
d = {}
|
||||
self.clear()
|
||||
self.update(d)
|
||||
|
||||
def __getitem__(self, key):
|
||||
try:
|
||||
ans = dict.__getitem__(self, key)
|
||||
if isinstance(ans, plistlib.Data):
|
||||
ans = ans.data
|
||||
return ans
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
def __setitem__(self, key, val):
|
||||
if isinstance(val, (bytes, str)):
|
||||
val = plistlib.Data(val)
|
||||
dict.__setitem__(self, key, val)
|
||||
self.commit()
|
||||
|
||||
def set(self, key, val):
|
||||
self.__setitem__(key, val)
|
||||
|
||||
def commit(self):
|
||||
if hasattr(self, 'file_path') and self.file_path:
|
||||
dpath = os.path.dirname(self.file_path)
|
||||
if not os.path.exists(dpath):
|
||||
os.makedirs(dpath, mode=CONFIG_DIR_MODE)
|
||||
with ExclusiveFile(self.file_path) as f:
|
||||
raw = plistlib.writePlistToString(self)
|
||||
f.seek(0)
|
||||
f.truncate()
|
||||
f.write(raw)
|
||||
|
||||
|
||||
def _prefs():
|
||||
c = Config('global', 'calibre wide preferences')
|
||||
c.add_opt('database_path',
|
||||
|
Loading…
x
Reference in New Issue
Block a user