From 59907f91c7fc684013d0a67326de9b4bd9276a16 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 5 Jan 2011 14:32:39 -0700 Subject: [PATCH 1/7] When downloading metadata from isbndb.com, download a maximum of 30 results rather than 1000 --- src/calibre/ebooks/metadata/isbndb.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/calibre/ebooks/metadata/isbndb.py b/src/calibre/ebooks/metadata/isbndb.py index 9169227326..1c5f706593 100644 --- a/src/calibre/ebooks/metadata/isbndb.py +++ b/src/calibre/ebooks/metadata/isbndb.py @@ -17,10 +17,10 @@ BASE_URL = 'http://isbndb.com/api/books.xml?access_key=%(key)s&page_number=1&res class ISBNDBError(Exception): pass -def fetch_metadata(url, max=100, timeout=5.): +def fetch_metadata(url, max=3, timeout=5.): books = [] page_number = 1 - total_results = sys.maxint + total_results = 31 br = browser() while len(books) < total_results and max > 0: try: From 8a70e2dc3a11e075ff83ccbc4213a2b57ff37d0c Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 5 Jan 2011 14:39:46 -0700 Subject: [PATCH 2/7] ... --- src/calibre/ebooks/metadata/xisbn.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/calibre/ebooks/metadata/xisbn.py b/src/calibre/ebooks/metadata/xisbn.py index 21ea0ee79f..2ee74396c7 100644 --- a/src/calibre/ebooks/metadata/xisbn.py +++ b/src/calibre/ebooks/metadata/xisbn.py @@ -18,7 +18,6 @@ class xISBN(object): self._data = [] self._map = {} - self.br = browser() self.isbn_pat = re.compile(r'[^0-9X]', re.IGNORECASE) def purify(self, isbn): @@ -26,7 +25,7 @@ class xISBN(object): def fetch_data(self, isbn): url = self.QUERY%isbn - data = self.br.open_novisit(url).read() + data = browser().open_novisit(url).read() data = json.loads(data) if data.get('stat', None) != 'ok': return [] From f42cda6573e2380e8911e1a7ddee8fb976547abf Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 5 Jan 2011 14:53:48 -0700 Subject: [PATCH 3/7] ... --- src/calibre/ebooks/oeb/transforms/cover.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/ebooks/oeb/transforms/cover.py b/src/calibre/ebooks/oeb/transforms/cover.py index a3cf9e9f83..0d82e9ad73 100644 --- a/src/calibre/ebooks/oeb/transforms/cover.py +++ b/src/calibre/ebooks/oeb/transforms/cover.py @@ -103,7 +103,7 @@ class CoverManager(object): from calibre.ebooks import calibre_cover img_data = calibre_cover(title, authors_to_string(authors), series_string=series_string) - id, href = self.oeb.manifest.generate('cover_image', + id, href = self.oeb.manifest.generate('cover', 'cover_image.jpg') item = self.oeb.manifest.add(id, href, guess_type('t.jpg')[0], data=img_data) From 847422e9b0eadaf23327167433930114103cd3e2 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 5 Jan 2011 14:54:14 -0700 Subject: [PATCH 4/7] Fix #8202 (Wrong Folder on device dr900) --- src/calibre/devices/misc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/devices/misc.py b/src/calibre/devices/misc.py index 2a0fdf6433..ecd12ac61d 100644 --- a/src/calibre/devices/misc.py +++ b/src/calibre/devices/misc.py @@ -259,7 +259,7 @@ class EEEREADER(USBMS): PRODUCT_ID = [0x178f] BCD = [0x0319] - EBOOK_DIR_MAIN = 'Books' + EBOOK_DIR_MAIN = EBOOK_DIR_CARD_A = 'Book' VENDOR_NAME = 'LINUX' WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = 'FILE-STOR_GADGET' From 51314ce5d8b6af02751b714606017a0f28b36bc7 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 5 Jan 2011 15:26:21 -0700 Subject: [PATCH 5/7] Content server: When serving OPDS feeds handle html descriptions taht have namespaced attributes. Fixes #7938 (Stanza shows some authors as "catalog is empty") --- src/calibre/library/server/opds.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/calibre/library/server/opds.py b/src/calibre/library/server/opds.py index fd8c50c594..ab0853add9 100644 --- a/src/calibre/library/server/opds.py +++ b/src/calibre/library/server/opds.py @@ -101,7 +101,19 @@ def html_to_lxml(raw): root = html.fragment_fromstring(raw) root.set('xmlns', "http://www.w3.org/1999/xhtml") raw = etree.tostring(root, encoding=None) - return etree.fromstring(raw) + try: + return etree.fromstring(raw) + except: + for x in root.iterdescendants(): + remove = [] + for attr in x.attrib: + if ':' in attr: + remove.append(attr) + for a in remove: + del x.attrib[a] + raw = etree.tostring(root, encoding=None) + return etree.fromstring(raw) + def CATALOG_ENTRY(item, item_kind, base_href, version, updated, ignore_count=False, add_kind=False): From 67c9bec212c88406e04bf31855ee28744c735525 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 5 Jan 2011 19:04:40 -0700 Subject: [PATCH 6/7] Fix #8176 (Expose configuration dialog for a plugin via API) --- src/calibre/customize/__init__.py | 78 +++++++++++++++++++++++++ src/calibre/gui2/preferences/plugins.py | 49 +--------------- 2 files changed, 81 insertions(+), 46 deletions(-) diff --git a/src/calibre/customize/__init__.py b/src/calibre/customize/__init__.py index 770d405203..a00a2cd5e2 100644 --- a/src/calibre/customize/__init__.py +++ b/src/calibre/customize/__init__.py @@ -80,6 +80,84 @@ class Plugin(object): # {{{ ''' pass + def config_widget(self): + ''' + Implement this method and :meth:`save_settings` in your plugin to + use a custom configuration dialog, rather then relying on the simple + string based default customization. + + This method, if implemented, must return a QWidget. The widget can have + an optional method validate() that takes no arguments and is called + immediately after the user clicks OK. Changes are applied if and only + if the method returns True. + ''' + raise NotImplementedError() + + def save_settings(self, config_widget): + ''' + Save the settings specified by the user with config_widget. + + :param config_widget: The widget returned by :meth:`config_widget`. + + ''' + raise NotImplementedError() + + def do_user_config(self, parent=None): + ''' + This method shows a configuration dialog for this plugin. It returns + True if the user clicks OK, False otherwise. The changes are + automatically applied. + ''' + from PyQt4.Qt import QDialog, QDialogButtonBox, QVBoxLayout, \ + QLabel, Qt, QLineEdit + config_dialog = QDialog(parent) + button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) + v = QVBoxLayout(config_dialog) + + button_box.accepted.connect(config_dialog.accept) + button_box.rejected.connect(config_dialog.reject) + config_dialog.setWindowTitle(_('Customize') + ' ' + self.name) + try: + config_widget = self.config_widget() + except NotImplementedError: + config_widget = None + + if config_widget is not None: + v.addWidget(config_widget) + v.addWidget(button_box) + config_dialog.exec_() + + if config_dialog.result() == QDialog.Accepted: + if hasattr(config_widget, 'validate'): + if config_widget.validate(): + self.save_settings(config_widget) + else: + self.save_settings(config_widget) + else: + from calibre.customize.ui import plugin_customization, \ + customize_plugin + help_text = self.customization_help(gui=True) + help_text = QLabel(help_text, config_dialog) + help_text.setWordWrap(True) + help_text.setTextInteractionFlags(Qt.LinksAccessibleByMouse + | Qt.LinksAccessibleByKeyboard) + help_text.setOpenExternalLinks(True) + v.addWidget(help_text) + sc = plugin_customization(self) + if not sc: + sc = '' + sc = sc.strip() + sc = QLineEdit(sc, config_dialog) + v.addWidget(sc) + v.addWidget(button_box) + config_dialog.exec_() + + if config_dialog.result() == QDialog.Accepted: + sc = unicode(sc.text()).strip() + customize_plugin(self, sc) + + return config_dialog.result() + def load_resources(self, names): ''' If this plugin comes in a ZIP file (user added plugin), this method diff --git a/src/calibre/gui2/preferences/plugins.py b/src/calibre/gui2/preferences/plugins.py index 3d714e388e..2fe2b3bf01 100644 --- a/src/calibre/gui2/preferences/plugins.py +++ b/src/calibre/gui2/preferences/plugins.py @@ -8,13 +8,12 @@ __docformat__ = 'restructuredtext en' import textwrap, os from PyQt4.Qt import Qt, QModelIndex, QAbstractItemModel, QVariant, QIcon, \ - QBrush, QDialog, QDialogButtonBox, QVBoxLayout, QLabel, QLineEdit + QBrush from calibre.gui2.preferences import ConfigWidgetBase, test_widget from calibre.gui2.preferences.plugins_ui import Ui_Form from calibre.customize.ui import initialized_plugins, is_disabled, enable_plugin, \ - disable_plugin, customize_plugin, \ - plugin_customization, add_plugin, \ + disable_plugin, plugin_customization, add_plugin, \ remove_plugin from calibre.gui2 import NONE, error_dialog, info_dialog, choose_files @@ -189,49 +188,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): _('Plugin: %s does not need customization')%plugin.name).exec_() return self.changed_signal.emit() - - config_dialog = QDialog(self) - button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) - v = QVBoxLayout(config_dialog) - - button_box.accepted.connect(config_dialog.accept) - button_box.rejected.connect(config_dialog.reject) - config_dialog.setWindowTitle(_('Customize') + ' ' + plugin.name) - - if hasattr(plugin, 'config_widget'): - config_widget = plugin.config_widget() - v.addWidget(config_widget) - v.addWidget(button_box) - config_dialog.exec_() - - if config_dialog.result() == QDialog.Accepted: - if hasattr(config_widget, 'validate'): - if config_widget.validate(): - plugin.save_settings(config_widget) - else: - plugin.save_settings(config_widget) - self._plugin_model.refresh_plugin(plugin) - else: - help_text = plugin.customization_help(gui=True) - help_text = QLabel(help_text, config_dialog) - help_text.setWordWrap(True) - help_text.setTextInteractionFlags(Qt.LinksAccessibleByMouse - | Qt.LinksAccessibleByKeyboard) - help_text.setOpenExternalLinks(True) - v.addWidget(help_text) - sc = plugin_customization(plugin) - if not sc: - sc = '' - sc = sc.strip() - sc = QLineEdit(sc, config_dialog) - v.addWidget(sc) - v.addWidget(button_box) - config_dialog.exec_() - - if config_dialog.result() == QDialog.Accepted: - sc = unicode(sc.text()).strip() - customize_plugin(plugin, sc) - + if plugin.do_user_config(): self._plugin_model.refresh_plugin(plugin) elif op == 'remove': if remove_plugin(plugin): From 5c6a17b25b04297692e953f20c09d3da62c0a26b Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 5 Jan 2011 19:11:59 -0700 Subject: [PATCH 7/7] Fix #8174 (Persist plugin configuration dialog size/position) --- src/calibre/customize/__init__.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/calibre/customize/__init__.py b/src/calibre/customize/__init__.py index a00a2cd5e2..13e1f20a2d 100644 --- a/src/calibre/customize/__init__.py +++ b/src/calibre/customize/__init__.py @@ -110,10 +110,21 @@ class Plugin(object): # {{{ ''' from PyQt4.Qt import QDialog, QDialogButtonBox, QVBoxLayout, \ QLabel, Qt, QLineEdit + from calibre.gui2 import gprefs + + prefname = 'plugin config dialog:'+self.type + ':' + self.name + geom = gprefs.get(prefname, None) + config_dialog = QDialog(parent) button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) v = QVBoxLayout(config_dialog) + def size_dialog(): + if geom is None: + config_dialog.resize(config_dialog.sizeHint()) + else: + config_dialog.restoreGeometry(geom) + button_box.accepted.connect(config_dialog.accept) button_box.rejected.connect(config_dialog.reject) config_dialog.setWindowTitle(_('Customize') + ' ' + self.name) @@ -125,6 +136,7 @@ class Plugin(object): # {{{ if config_widget is not None: v.addWidget(config_widget) v.addWidget(button_box) + size_dialog() config_dialog.exec_() if config_dialog.result() == QDialog.Accepted: @@ -150,12 +162,16 @@ class Plugin(object): # {{{ sc = QLineEdit(sc, config_dialog) v.addWidget(sc) v.addWidget(button_box) + size_dialog() config_dialog.exec_() if config_dialog.result() == QDialog.Accepted: sc = unicode(sc.text()).strip() customize_plugin(self, sc) + geom = bytearray(config_dialog.saveGeometry()) + gprefs[prefname] = geom + return config_dialog.result() def load_resources(self, names):