mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Move store plugins to use a GUI wrapper plugin to keep the plugin system happy. Fix segfault with cover download thread pool.
This commit is contained in:
parent
06e3186391
commit
84d8dc78c1
@ -581,60 +581,41 @@ class PreferencesPlugin(Plugin): # {{{
|
||||
|
||||
# }}}
|
||||
|
||||
|
||||
class StorePlugin(Plugin): # {{{
|
||||
|
||||
class StoreBase(Plugin): # {{{
|
||||
|
||||
supported_platforms = ['windows', 'osx', 'linux']
|
||||
author = 'John Schember'
|
||||
type = _('Store')
|
||||
# This needs to be changed to (0, 8, 0)
|
||||
minimum_calibre_version = (0, 4, 118)
|
||||
|
||||
def open(self, gui, parent=None, detail_item=None, external=False):
|
||||
|
||||
actual_plugin = None
|
||||
actual_plugin_object = None
|
||||
|
||||
def load_actual_plugin(self, gui):
|
||||
'''
|
||||
Open the store.
|
||||
|
||||
:param gui: The main GUI. This will be used to have the job
|
||||
system start downloading an item from the store.
|
||||
|
||||
:param parent: The parent of the store dialog. This is used
|
||||
to create modal dialogs.
|
||||
|
||||
:param detail_item: A plugin specific reference to an item
|
||||
in the store that the user should be shown.
|
||||
|
||||
:param external: When False open an internal dialog with the
|
||||
store. When True open the users default browser to the store's
|
||||
web site. :param:`detail_item` should still be respected when external
|
||||
is True.
|
||||
This method must return the actual interface action plugin object.
|
||||
'''
|
||||
raise NotImplementedError()
|
||||
|
||||
def search(self, query, max_results=10, timeout=60):
|
||||
'''
|
||||
Searches the store for items matching query. This should
|
||||
return items as a generator.
|
||||
mod, cls = self.actual_plugin.split(':')
|
||||
self.actual_plugin_object = getattr(__import__(mod, fromlist=['1'], level=0), cls)(gui, self.name)
|
||||
return self.actual_plugin_object
|
||||
|
||||
def customization_help(self, gui=False):
|
||||
if self.actual_plugin_object:
|
||||
return self.actual_plugin_object.customization_help(gui)
|
||||
else:
|
||||
raise NotImplementedError()
|
||||
|
||||
:param query: The string query search with.
|
||||
:param max_results: The maximum number of results to return.
|
||||
:param timeout: The maximum amount of time in seconds to spend download the search results.
|
||||
|
||||
:return: :class:`calibre.gui2.store.search_result.SearchResult` objects
|
||||
item_data is plugin specific and is used in :meth:`open` to open to a specifc place in the store.
|
||||
'''
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_settings(self):
|
||||
'''
|
||||
This is only useful for plugins that implement
|
||||
:attr:`config_widget` that is the only way to save
|
||||
settings. This is used by plugins to get the saved
|
||||
settings and apply when necessary.
|
||||
|
||||
:return: A dictionary filled with the settings used
|
||||
by this plugin.
|
||||
'''
|
||||
raise NotImplementedError()
|
||||
def config_widget(self):
|
||||
if self.actual_plugin_object:
|
||||
return self.actual_plugin_object.config_widget()
|
||||
else:
|
||||
raise NotImplementedError()
|
||||
|
||||
def save_settings(self, config_widget):
|
||||
if self.actual_plugin_object:
|
||||
return self.actual_plugin_object.save_settings(config_widget)
|
||||
else:
|
||||
raise NotImplementedError()
|
||||
|
||||
# }}}
|
||||
|
||||
|
@ -5,7 +5,7 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
import textwrap, os, glob, functools, re
|
||||
from calibre import guess_type
|
||||
from calibre.customize import FileTypePlugin, MetadataReaderPlugin, \
|
||||
MetadataWriterPlugin, PreferencesPlugin, InterfaceActionBase
|
||||
MetadataWriterPlugin, PreferencesPlugin, InterfaceActionBase, StoreBase
|
||||
from calibre.constants import numeric_version
|
||||
from calibre.ebooks.metadata.archive import ArchiveExtract, get_cbz_metadata
|
||||
from calibre.ebooks.metadata.opf2 import metadata_to_opf
|
||||
@ -1042,12 +1042,32 @@ plugins += [GoogleBooks]
|
||||
# }}}
|
||||
|
||||
# Store plugins {{{
|
||||
from calibre.gui2.store.amazon_plugin import AmazonKindleStore
|
||||
from calibre.gui2.store.gutenberg_plugin import GutenbergStore
|
||||
from calibre.gui2.store.feedbooks_plugin import FeedbooksStore
|
||||
from calibre.gui2.store.manybooks_plugin import ManyBooksStore
|
||||
from calibre.gui2.store.smashwords_plugin import SmashwordsStore
|
||||
class StoreAmazonKindleStore(StoreBase):
|
||||
name = 'Amazon Kindle'
|
||||
description = _('Kindle books from Amazon')
|
||||
actual_plugin = 'calibre.gui2.store.amazon_plugin:AmazonKindleStore'
|
||||
|
||||
plugins += [AmazonKindleStore, GutenbergStore, FeedbooksStore, ManyBooksStore, SmashwordsStore]
|
||||
|
||||
class StoreGutenbergStore(StoreBase):
|
||||
name = 'Project Gutenberg'
|
||||
description = _('The first producer of free ebooks.')
|
||||
actual_plugin = 'calibre.gui2.store.gutenberg_plugin:GutenbergStore'
|
||||
|
||||
class StoreFeedbooksStore(StoreBase):
|
||||
name = 'Feedbooks'
|
||||
description = _('Read anywhere.')
|
||||
actual_plugin = 'calibre.gui2.store.feedbooks_plugin:FeedbooksStore'
|
||||
|
||||
class StoreManyBooksStore(StoreBase):
|
||||
name = 'ManyBooks'
|
||||
description = _('The best ebooks at the best price: free!')
|
||||
actual_plugin = 'calibre.gui2.store.manybooks_plugin:ManyBooksStore'
|
||||
|
||||
class StoreSmashwordsStore(StoreBase):
|
||||
name = 'Smashwords'
|
||||
description = _('Your ebook. Your way.')
|
||||
actual_plugin = 'calibre.gui2.store.smashwords_plugin:SmashwordsStore'
|
||||
|
||||
plugins += [StoreAmazonKindleStore, StoreGutenbergStore, StoreFeedbooksStore, StoreManyBooksStore, StoreSmashwordsStore]
|
||||
|
||||
# }}}
|
||||
|
@ -8,7 +8,7 @@ from contextlib import closing
|
||||
from calibre.customize import Plugin, CatalogPlugin, FileTypePlugin, \
|
||||
MetadataReaderPlugin, MetadataWriterPlugin, \
|
||||
InterfaceActionBase as InterfaceAction, \
|
||||
PreferencesPlugin, StorePlugin
|
||||
PreferencesPlugin, StoreBase
|
||||
from calibre.customize.conversion import InputFormatPlugin, OutputFormatPlugin
|
||||
from calibre.customize.profiles import InputProfile, OutputProfile
|
||||
from calibre.customize.builtins import plugins as builtin_plugins
|
||||
@ -283,7 +283,7 @@ def preferences_plugins():
|
||||
def store_plugins():
|
||||
customization = config['plugin_customization']
|
||||
for plugin in _initialized_plugins:
|
||||
if isinstance(plugin, StorePlugin):
|
||||
if isinstance(plugin, StoreBase):
|
||||
if not is_disabled(plugin):
|
||||
plugin.site_customization = customization.get(plugin.name, '')
|
||||
yield plugin
|
||||
|
@ -8,7 +8,6 @@ from functools import partial
|
||||
|
||||
from PyQt4.Qt import Qt, QMenu, QToolButton, QDialog, QVBoxLayout
|
||||
|
||||
from calibre.customize.ui import store_plugins
|
||||
from calibre.gui2.actions import InterfaceAction
|
||||
|
||||
class StoreAction(InterfaceAction):
|
||||
@ -21,14 +20,14 @@ class StoreAction(InterfaceAction):
|
||||
self.store_menu = QMenu()
|
||||
self.store_menu.addAction(_('Search'), self.search)
|
||||
self.store_menu.addSeparator()
|
||||
for x in store_plugins():
|
||||
self.store_menu.addAction(x.name, partial(self.open_store, x))
|
||||
for n, p in self.gui.istores.items():
|
||||
self.store_menu.addAction(n, partial(self.open_store, p))
|
||||
self.qaction.setMenu(self.store_menu)
|
||||
|
||||
def search(self):
|
||||
from calibre.gui2.store.search import SearchDialog
|
||||
sd = SearchDialog(self.gui, self.gui)
|
||||
sd = SearchDialog(self.gui.istores, self.gui)
|
||||
sd.exec_()
|
||||
|
||||
def open_store(self, store_plugin):
|
||||
store_plugin.open(self.gui, self.gui)
|
||||
store_plugin.open(self.gui)
|
||||
|
@ -0,0 +1,75 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
__license__ = 'GPL 3'
|
||||
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
class StorePlugin(object): # {{{
|
||||
|
||||
def __init__(self, gui, name):
|
||||
self.gui = gui
|
||||
self.name = name
|
||||
self.base_plugin = None
|
||||
|
||||
def open(self, gui, parent=None, detail_item=None, external=False):
|
||||
'''
|
||||
Open the store.
|
||||
|
||||
:param gui: The main GUI. This will be used to have the job
|
||||
system start downloading an item from the store.
|
||||
|
||||
:param parent: The parent of the store dialog. This is used
|
||||
to create modal dialogs.
|
||||
|
||||
:param detail_item: A plugin specific reference to an item
|
||||
in the store that the user should be shown.
|
||||
|
||||
:param external: When False open an internal dialog with the
|
||||
store. When True open the users default browser to the store's
|
||||
web site. :param:`detail_item` should still be respected when external
|
||||
is True.
|
||||
'''
|
||||
raise NotImplementedError()
|
||||
|
||||
def search(self, query, max_results=10, timeout=60):
|
||||
'''
|
||||
Searches the store for items matching query. This should
|
||||
return items as a generator.
|
||||
|
||||
:param query: The string query search with.
|
||||
:param max_results: The maximum number of results to return.
|
||||
:param timeout: The maximum amount of time in seconds to spend download the search results.
|
||||
|
||||
:return: :class:`calibre.gui2.store.search_result.SearchResult` objects
|
||||
item_data is plugin specific and is used in :meth:`open` to open to a specifc place in the store.
|
||||
'''
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_settings(self):
|
||||
'''
|
||||
This is only useful for plugins that implement
|
||||
:attr:`config_widget` that is the only way to save
|
||||
settings. This is used by plugins to get the saved
|
||||
settings and apply when necessary.
|
||||
|
||||
:return: A dictionary filled with the settings used
|
||||
by this plugin.
|
||||
'''
|
||||
raise NotImplementedError()
|
||||
|
||||
def do_genesis(self):
|
||||
self.genesis()
|
||||
|
||||
def genesis(self):
|
||||
pass
|
||||
|
||||
def config_widget(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
def save_settings(self, config_widget):
|
||||
raise NotImplementedError()
|
||||
|
||||
def customization_help(self, gui=False):
|
||||
raise NotImplementedError()
|
||||
|
||||
# }}}
|
@ -14,15 +14,13 @@ from lxml import html
|
||||
from PyQt4.Qt import QUrl
|
||||
|
||||
from calibre import browser
|
||||
from calibre.customize import StorePlugin
|
||||
from calibre.gui2 import open_url
|
||||
from calibre.gui2.store import StorePlugin
|
||||
from calibre.gui2.store.search_result import SearchResult
|
||||
|
||||
class AmazonKindleStore(StorePlugin):
|
||||
|
||||
name = 'Amazon Kindle'
|
||||
description = _('Kindle books from Amazon')
|
||||
|
||||
def open(self, gui, parent=None, detail_item=None):
|
||||
def open(self, parent=None, detail_item=None, external=False):
|
||||
'''
|
||||
Amazon comes with a number of difficulties.
|
||||
|
||||
@ -106,7 +104,6 @@ class AmazonKindleStore(StorePlugin):
|
||||
The best (I use the term lightly here) solution is to open Amazon.com
|
||||
in the users default browser and set the affiliate id as part of the url.
|
||||
'''
|
||||
from calibre.gui2 import open_url
|
||||
aff_id = {'tag': 'josbl0e-cpb-20'}
|
||||
# Use Kovid's affiliate id 30% of the time.
|
||||
if random.randint(1, 10) in (1, 2, 3):
|
||||
|
@ -10,18 +10,14 @@ from contextlib import closing
|
||||
from lxml import html
|
||||
|
||||
from calibre import browser
|
||||
from calibre.customize import StorePlugin
|
||||
from calibre.gui2.store.search_result import SearchResult
|
||||
from calibre.gui2.store import StorePlugin
|
||||
from calibre.gui2.store.basic_config import BasicStoreConfig
|
||||
from calibre.gui2.store.web_store_dialog import WebStoreDialog
|
||||
|
||||
class FeedbooksStore(StorePlugin):
|
||||
class FeedbooksStore(BasicStoreConfig, StorePlugin):
|
||||
|
||||
name = 'Feedbooks'
|
||||
description = _('Read anywhere.')
|
||||
|
||||
|
||||
def open(self, gui, parent=None, detail_item=None):
|
||||
from calibre.gui2.store.web_store_dialog import WebStoreDialog
|
||||
d = WebStoreDialog(gui, 'http://m.feedbooks.com/', parent, detail_item)
|
||||
def open(self, parent=None, detail_item=None, external=False):
|
||||
d = WebStoreDialog(self.gui, 'http://m.feedbooks.com/', parent, detail_item)
|
||||
d.setWindowTitle(self.name)
|
||||
d.set_tags(self.name + ',' + _('store'))
|
||||
d = d.exec_()
|
||||
@ -76,22 +72,3 @@ class FeedbooksStore(StorePlugin):
|
||||
s.detail_item = id.strip()
|
||||
|
||||
yield s
|
||||
|
||||
def customization_help(self, gui=False):
|
||||
return 'Customize the behavior of this store.'
|
||||
|
||||
def config_widget(self):
|
||||
from calibre.gui2.store.basic_config_widget import BasicStoreConfigWidget
|
||||
return BasicStoreConfigWidget(self)
|
||||
|
||||
def save_settings(self, config_widget):
|
||||
from calibre.gui2.store.basic_config_widget import save_settings
|
||||
save_settings(config_widget)
|
||||
|
||||
def get_settings(self):
|
||||
from calibre.gui2 import gprefs
|
||||
settings = {}
|
||||
|
||||
settings[self.name + '_tags'] = gprefs.get(self.name + '_tags', self.name + ', store, download')
|
||||
|
||||
return settings
|
||||
|
@ -10,18 +10,16 @@ from contextlib import closing
|
||||
from lxml import html
|
||||
|
||||
from calibre import browser
|
||||
from calibre.customize import StorePlugin
|
||||
from calibre.gui2.store import StorePlugin
|
||||
from calibre.gui2.store.basic_config import BasicStoreConfig
|
||||
from calibre.gui2.store.search_result import SearchResult
|
||||
from calibre.gui2.store.web_store_dialog import WebStoreDialog
|
||||
|
||||
class GutenbergStore(StorePlugin):
|
||||
|
||||
name = 'Project Gutenberg'
|
||||
description = _('The first producer of free ebooks.')
|
||||
class GutenbergStore(BasicStoreConfig, StorePlugin):
|
||||
|
||||
def open(self, gui, parent=None, detail_item=None):
|
||||
def open(self, parent=None, detail_item=None, external=False):
|
||||
settings = self.get_settings()
|
||||
from calibre.gui2.store.web_store_dialog import WebStoreDialog
|
||||
d = WebStoreDialog(gui, 'http://m.gutenberg.org/', parent, detail_item)
|
||||
d = WebStoreDialog(self.gui, 'http://m.gutenberg.org/', parent, detail_item)
|
||||
d.setWindowTitle(self.name)
|
||||
d.set_tags(settings.get(self.name + '_tags', ''))
|
||||
d = d.exec_()
|
||||
@ -51,8 +49,9 @@ class GutenbergStore(StorePlugin):
|
||||
continue
|
||||
id = url.split('/')[-1]
|
||||
|
||||
heading = ''.join(url_a.xpath('text()'))
|
||||
title, _, author = heading.partition('by ')
|
||||
url_a = html.fromstring(html.tostring(url_a))
|
||||
heading = ''.join(url_a.xpath('//text()'))
|
||||
title, _, author = heading.rpartition('by ')
|
||||
author = author.split('-')[0]
|
||||
price = '$0.00'
|
||||
|
||||
@ -66,22 +65,3 @@ class GutenbergStore(StorePlugin):
|
||||
s.detail_item = '/ebooks/' + id.strip()
|
||||
|
||||
yield s
|
||||
|
||||
def customization_help(self, gui=False):
|
||||
return 'Customize the behavior of this store.'
|
||||
|
||||
def config_widget(self):
|
||||
from calibre.gui2.store.basic_config_widget import BasicStoreConfigWidget
|
||||
return BasicStoreConfigWidget(self)
|
||||
|
||||
def save_settings(self, config_widget):
|
||||
from calibre.gui2.store.basic_config_widget import save_settings
|
||||
save_settings(config_widget)
|
||||
|
||||
def get_settings(self):
|
||||
from calibre.gui2 import gprefs
|
||||
settings = {}
|
||||
|
||||
settings[self.name + '_tags'] = gprefs.get(self.name + '_tags', self.name + ', store, download')
|
||||
|
||||
return settings
|
||||
|
@ -11,18 +11,15 @@ from contextlib import closing
|
||||
from lxml import html
|
||||
|
||||
from calibre import browser
|
||||
from calibre.customize import StorePlugin
|
||||
from calibre.gui2.store import StorePlugin
|
||||
from calibre.gui2.store.basic_config import BasicStoreConfig
|
||||
from calibre.gui2.store.search_result import SearchResult
|
||||
|
||||
class ManyBooksStore(StorePlugin):
|
||||
|
||||
name = 'ManyBooks'
|
||||
description = _('The best ebooks at the best price: free!')
|
||||
|
||||
from calibre.gui2.store.web_store_dialog import WebStoreDialog
|
||||
|
||||
def open(self, gui, parent=None, detail_item=None):
|
||||
from calibre.gui2.store.web_store_dialog import WebStoreDialog
|
||||
d = WebStoreDialog(gui, 'http://manybooks.net/', parent, detail_item)
|
||||
class ManyBooksStore(BasicStoreConfig, StorePlugin):
|
||||
|
||||
def open(self, parent=None, detail_item=None, external=False):
|
||||
d = WebStoreDialog(self.gui, 'http://manybooks.net/', parent, detail_item)
|
||||
d.setWindowTitle(self.name)
|
||||
d.set_tags(self.name + ',' + _('store'))
|
||||
d = d.exec_()
|
||||
@ -55,8 +52,9 @@ class ManyBooksStore(StorePlugin):
|
||||
id = url.split('/')[-1]
|
||||
id = id.strip()
|
||||
|
||||
heading = ''.join(url_a.xpath('text()'))
|
||||
title, _, author = heading.partition('by ')
|
||||
url_a = html.fromstring(html.tostring(url_a))
|
||||
heading = ''.join(url_a.xpath('//text()'))
|
||||
title, _, author = heading.rpartition('by ')
|
||||
author = author.split('-')[0]
|
||||
price = '$0.00'
|
||||
|
||||
@ -78,22 +76,3 @@ class ManyBooksStore(StorePlugin):
|
||||
s.detail_item = '/titles/' + id
|
||||
|
||||
yield s
|
||||
|
||||
def customization_help(self, gui=False):
|
||||
return 'Customize the behavior of this store.'
|
||||
|
||||
def config_widget(self):
|
||||
from calibre.gui2.store.basic_config_widget import BasicStoreConfigWidget
|
||||
return BasicStoreConfigWidget(self)
|
||||
|
||||
def save_settings(self, config_widget):
|
||||
from calibre.gui2.store.basic_config_widget import save_settings
|
||||
save_settings(config_widget)
|
||||
|
||||
def get_settings(self):
|
||||
from calibre.gui2 import gprefs
|
||||
settings = {}
|
||||
|
||||
settings[self.name + '_tags'] = gprefs.get(self.name + '_tags', self.name + ', store, download')
|
||||
|
||||
return settings
|
||||
|
@ -16,7 +16,6 @@ from PyQt4.Qt import Qt, QAbstractItemModel, QDialog, QTimer, QVariant, \
|
||||
QPushButton
|
||||
|
||||
from calibre import browser
|
||||
from calibre.customize.ui import store_plugins
|
||||
from calibre.gui2 import NONE
|
||||
from calibre.gui2.store.search_ui import Ui_Dialog
|
||||
from calibre.utils.icu import sort_key
|
||||
@ -29,17 +28,12 @@ COVER_DOWNLOAD_THREAD_TOTAL = 2
|
||||
|
||||
class SearchDialog(QDialog, Ui_Dialog):
|
||||
|
||||
def __init__(self, gui, *args):
|
||||
def __init__(self, istores, *args):
|
||||
QDialog.__init__(self, *args)
|
||||
self.setupUi(self)
|
||||
|
||||
# We pass this on to the store plugins so they can
|
||||
# tell the gui's job system to start downloading an
|
||||
# item.
|
||||
self.gui = gui
|
||||
|
||||
# We keep a cache of store plugins and reference them by name.
|
||||
self.store_plugins = {}
|
||||
self.store_plugins = istores
|
||||
self.search_pool = SearchThreadPool(SearchThread, SEARCH_THREAD_TOTAL)
|
||||
# Check for results and hung threads.
|
||||
self.checker = QTimer()
|
||||
@ -53,12 +47,11 @@ class SearchDialog(QDialog, Ui_Dialog):
|
||||
# per search basis.
|
||||
stores_group_layout = QVBoxLayout()
|
||||
self.stores_group.setLayout(stores_group_layout)
|
||||
for x in store_plugins():
|
||||
self.store_plugins[x.name] = x
|
||||
cbox = QCheckBox(x.name)
|
||||
for x in self.store_plugins:
|
||||
cbox = QCheckBox(x)
|
||||
cbox.setChecked(True)
|
||||
stores_group_layout.addWidget(cbox)
|
||||
setattr(self, 'store_check_' + x.name, cbox)
|
||||
setattr(self, 'store_check_' + x, cbox)
|
||||
stores_group_layout.addStretch()
|
||||
|
||||
self.search.clicked.connect(self.do_search)
|
||||
@ -122,7 +115,7 @@ class SearchDialog(QDialog, Ui_Dialog):
|
||||
self.checker.stop()
|
||||
else:
|
||||
# Stop the checker if not threads are running.
|
||||
if not self.search_pool.threads_running():
|
||||
if not self.search_pool.threads_running() and not self.search_pool.has_tasks():
|
||||
self.checker.stop()
|
||||
|
||||
while self.search_pool.has_results():
|
||||
@ -132,7 +125,7 @@ class SearchDialog(QDialog, Ui_Dialog):
|
||||
|
||||
def open_store(self, index):
|
||||
result = self.results_view.model().get_result(index)
|
||||
self.store_plugins[result.store_name].open(self.gui, self, result.detail_item)
|
||||
self.store_plugins[result.store_name].open(self, result.detail_item)
|
||||
|
||||
def get_store_checks(self):
|
||||
'''
|
||||
@ -156,6 +149,10 @@ class SearchDialog(QDialog, Ui_Dialog):
|
||||
def stores_select_none(self):
|
||||
for check in self.get_store_checks():
|
||||
check.setChecked(False)
|
||||
|
||||
def closeEvent(self, e):
|
||||
self.model.closing()
|
||||
QDialog.closeEvent(self, e)
|
||||
|
||||
|
||||
class GenericDownloadThreadPool(object):
|
||||
@ -305,6 +302,9 @@ class Matches(QAbstractItemModel):
|
||||
self.matches = []
|
||||
self.cover_pool = CoverThreadPool(CoverThread, 2)
|
||||
self.cover_pool.start_threads()
|
||||
|
||||
def closing(self):
|
||||
self.cover_pool.abort()
|
||||
|
||||
def clear_results(self):
|
||||
self.matches = []
|
||||
@ -315,7 +315,6 @@ class Matches(QAbstractItemModel):
|
||||
def add_result(self, result):
|
||||
self.layoutAboutToBeChanged.emit()
|
||||
self.matches.append(result)
|
||||
|
||||
self.cover_pool.add_task(result, self.update_result)
|
||||
self.layoutChanged.emit()
|
||||
|
||||
|
@ -12,22 +12,19 @@ from contextlib import closing
|
||||
from lxml import html
|
||||
|
||||
from calibre import browser
|
||||
from calibre.customize import StorePlugin
|
||||
from calibre.gui2.store import StorePlugin
|
||||
from calibre.gui2.store.basic_config import BasicStoreConfig
|
||||
from calibre.gui2.store.search_result import SearchResult
|
||||
from calibre.gui2.store.web_store_dialog import WebStoreDialog
|
||||
|
||||
class SmashwordsStore(StorePlugin):
|
||||
|
||||
name = 'Smashwords'
|
||||
description = _('Your ebook. Your way.')
|
||||
|
||||
|
||||
def open(self, gui, parent=None, detail_item=None):
|
||||
from calibre.gui2.store.web_store_dialog import WebStoreDialog
|
||||
class SmashwordsStore(BasicStoreConfig, StorePlugin):
|
||||
|
||||
def open(self, parent=None, detail_item=None, external=False):
|
||||
aff_id = 'usernone'
|
||||
# Use Kovid's affiliate id 30% of the time.
|
||||
if random.randint(1, 10) in (1, 2, 3):
|
||||
aff_id = 'kovidgoyal'
|
||||
d = WebStoreDialog(gui, 'http://www.smashwords.com/?ref=%s' % aff_id, parent, detail_item)
|
||||
d = WebStoreDialog(self.gui, 'http://www.smashwords.com/?ref=%s' % aff_id, parent, detail_item)
|
||||
d.setWindowTitle(self.name)
|
||||
d.set_tags(self.name + ',' + _('store'))
|
||||
d = d.exec_()
|
||||
@ -78,22 +75,3 @@ class SmashwordsStore(StorePlugin):
|
||||
s.detail_item = '/books/view/' + id.strip()
|
||||
|
||||
yield s
|
||||
|
||||
def customization_help(self, gui=False):
|
||||
return 'Customize the behavior of this store.'
|
||||
|
||||
def config_widget(self):
|
||||
from calibre.gui2.store.basic_config_widget import BasicStoreConfigWidget
|
||||
return BasicStoreConfigWidget(self)
|
||||
|
||||
def save_settings(self, config_widget):
|
||||
from calibre.gui2.store.basic_config_widget import save_settings
|
||||
save_settings(config_widget)
|
||||
|
||||
def get_settings(self):
|
||||
from calibre.gui2 import gprefs
|
||||
settings = {}
|
||||
|
||||
settings[self.name + '_tags'] = gprefs.get(self.name + '_tags', self.name + ', store, download')
|
||||
|
||||
return settings
|
||||
|
@ -22,7 +22,7 @@ from calibre.ptempfile import PersistentTemporaryFile
|
||||
from calibre.utils.config import prefs, dynamic
|
||||
from calibre.utils.ipc.server import Server
|
||||
from calibre.library.database2 import LibraryDatabase2
|
||||
from calibre.customize.ui import interface_actions
|
||||
from calibre.customize.ui import interface_actions, store_plugins
|
||||
from calibre.gui2 import error_dialog, GetMetadata, open_local_file, \
|
||||
gprefs, max_available_height, config, info_dialog, Dispatcher, \
|
||||
question_dialog
|
||||
@ -102,6 +102,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
|
||||
self.device_connected = None
|
||||
self.gui_debug = gui_debug
|
||||
self.iactions = OrderedDict()
|
||||
# Actions
|
||||
for action in interface_actions():
|
||||
if opts.ignore_plugins and action.plugin_path is not None:
|
||||
continue
|
||||
@ -114,11 +115,24 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
|
||||
if action.plugin_path is None:
|
||||
raise
|
||||
continue
|
||||
|
||||
ac.plugin_path = action.plugin_path
|
||||
ac.interface_action_base_plugin = action
|
||||
|
||||
self.add_iaction(ac)
|
||||
# Stores
|
||||
self.istores = OrderedDict()
|
||||
for store in store_plugins():
|
||||
if opts.ignore_plugins and store.plugin_path is not None:
|
||||
continue
|
||||
try:
|
||||
st = self.init_istore(store)
|
||||
self.add_istore(st)
|
||||
except:
|
||||
# Ignore errors in loading user supplied plugins
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
if store.plugin_path is None:
|
||||
raise
|
||||
continue
|
||||
|
||||
def init_iaction(self, action):
|
||||
ac = action.load_actual_plugin(self)
|
||||
@ -126,6 +140,13 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
|
||||
ac.interface_action_base_plugin = action
|
||||
action.actual_iaction_plugin_loaded = True
|
||||
return ac
|
||||
|
||||
def init_istore(self, store):
|
||||
st = store.load_actual_plugin(self)
|
||||
st.plugin_path = store.plugin_path
|
||||
st.base_plugin = store
|
||||
store.actual_istore_plugin_loaded = True
|
||||
return st
|
||||
|
||||
def add_iaction(self, ac):
|
||||
acmap = self.iactions
|
||||
@ -134,6 +155,14 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
|
||||
acmap[ac.name] = ac
|
||||
else:
|
||||
acmap[ac.name] = ac
|
||||
|
||||
def add_istore(self, st):
|
||||
stmap = self.istores
|
||||
if st.name in stmap:
|
||||
if st.priority >= stmap[st.name].priority:
|
||||
stmap[st.name] = st
|
||||
else:
|
||||
stmap[st.name] = st
|
||||
|
||||
|
||||
def initialize(self, library_path, db, listener, actions, show_gui=True):
|
||||
@ -155,6 +184,8 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
|
||||
|
||||
for ac in self.iactions.values():
|
||||
ac.do_genesis()
|
||||
for st in self.istores.values():
|
||||
st.do_genesis()
|
||||
MainWindowMixin.__init__(self, db)
|
||||
|
||||
# Jobs Button {{{
|
||||
|
Loading…
x
Reference in New Issue
Block a user