1# -*- coding: utf-8 -*-
2
3
4__license__ = 'GPL 3'
5__copyright__ = '2011, John Schember <john@nachtimwald.com>'
6__docformat__ = 'restructuredtext en'
7
8from calibre.utils.filenames import ascii_filename
9
10
11class StorePlugin:  # {{{
12
13    '''
14    A plugin representing an online ebook repository (store). The store can
15    be a commercial store that sells ebooks or a source of free downloadable
16    ebooks.
17
18    Note that this class is the base class for these plugins, however, to
19    integrate the plugin with calibre's plugin system, you have to make a
20    wrapper class that references the actual plugin. See the
21    :mod:`calibre.customize.builtins` module for examples.
22
23    If two :class:`StorePlugin` objects have the same name, the one with higher
24    priority takes precedence.
25
26    Sub-classes must implement :meth:`open`, and :meth:`search`.
27
28    Regarding :meth:`open`. Most stores only make themselves available
29    though a web site thus most store plugins will open using
30    :class:`calibre.gui2.store.web_store_dialog.WebStoreDialog`. This will
31    open a modal window and display the store website in a QWebView.
32
33    Sub-classes should implement and use the :meth:`genesis` if they require
34    plugin specific initialization. They should not override or otherwise
35    reimplement :meth:`__init__`.
36
37    Once initialized, this plugin has access to the main calibre GUI via the
38    :attr:`gui` member. You can access other plugins by name, for example::
39
40        self.gui.istores['Amazon Kindle']
41
42    Plugin authors can use affiliate programs within their plugin. The
43    distribution of money earned from a store plugin is 70/30. 70% going
44    to the pluin author / maintainer and 30% going to the calibre project.
45
46    The easiest way to handle affiliate money payouts is to randomly select
47    between the author's affiliate id and calibre's affiliate id so that
48    70% of the time the author's id is used.
49
50    See declined.txt for a list of stores that do not want to be included.
51    '''
52
53    minimum_calibre_version = (0, 9, 14)
54
55    def __init__(self, gui, name, config=None, base_plugin=None):
56        self.gui = gui
57        self.name = name
58        self.base_plugin = base_plugin
59        if config is None:
60            from calibre.utils.config import JSONConfig
61            config = JSONConfig('store/stores/' + ascii_filename(self.name))
62        self.config = config
63
64    def create_browser(self):
65        '''
66        If the server requires special headers, such as a particular user agent
67        or a referrer, then implement this method in you plugin to return a
68        customized browser instance. See the Gutenberg plugin for an example.
69
70        Note that if you implement the open() method in your plugin and use the
71        WebStoreDialog class, remember to pass self.createbrowser in the
72        constructor of WebStoreDialog.
73        '''
74        raise NotImplementedError()
75
76    def open(self, gui, parent=None, detail_item=None, external=False):
77        '''
78        Open the store.
79
80        :param gui: The main GUI. This will be used to have the job
81        system start downloading an item from the store.
82
83        :param parent: The parent of the store dialog. This is used
84        to create modal dialogs.
85
86        :param detail_item: A plugin specific reference to an item
87        in the store that the user should be shown.
88
89        :param external: When False open an internal dialog with the
90        store. When True open the users default browser to the store's
91        web site. :param:`detail_item` should still be respected when external
92        is True.
93        '''
94        raise NotImplementedError()
95
96    def search(self, query, max_results=10, timeout=60):
97        '''
98        Searches the store for items matching query. This should
99        return items as a generator.
100
101        Don't be lazy with the search! Load as much data as possible in the
102        :class:`calibre.gui2.store.search_result.SearchResult` object.
103        However, if data (such as cover_url)
104        isn't available because the store does not display cover images then it's okay to
105        ignore it.
106
107        At the very least a :class:`calibre.gui2.store.search_result.SearchResult`
108        returned by this function must have the title, author and id.
109
110        If you have to parse multiple pages to get all of the data then implement
111        :meth:`get_deatils` for retrieving additional information.
112
113        Also, by default search results can only include ebooks. A plugin can offer users
114        an option to include physical books in the search results but this must be
115        disabled by default.
116
117        If a store doesn't provide search on it's own use something like a site specific
118        google search to get search results for this function.
119
120        :param query: The string query search with.
121        :param max_results: The maximum number of results to return.
122        :param timeout: The maximum amount of time in seconds to spend downloading data for search results.
123
124        :return: :class:`calibre.gui2.store.search_result.SearchResult` objects
125        item_data is plugin specific and is used in :meth:`open` to open to a specific place in the store.
126        '''
127        raise NotImplementedError()
128
129    def get_details(self, search_result, timeout=60):
130        '''
131        Delayed search for information about specific search items.
132
133        Typically, this will be used when certain information such as
134        formats, drm status, cover url are not part of the main search
135        results and the information is on another web page.
136
137        Using this function allows for the main information (title, author)
138        to be displayed in the search results while other information can
139        take extra time to load. Splitting retrieving data that takes longer
140        to load into a separate function will give the illusion of the search
141        being faster.
142
143        :param search_result: A search result that need details set.
144        :param timeout: The maximum amount of time in seconds to spend downloading details.
145
146        :return: True if the search_result was modified otherwise False
147        '''
148        return False
149
150    def update_cache(self, parent=None, timeout=60, force=False, suppress_progress=False):
151        '''
152        Some plugins need to keep an local cache of available books. This function
153        is called to update the caches. It is recommended to call this function
154        from :meth:`open`. Especially if :meth:`open` does anything other than
155        open a web page.
156
157        This function can be called at any time. It is up to the plugin to determine
158        if the cache really does need updating. Unless :param:`force` is True, then
159        the plugin must update the cache. The only time force should be True is if
160        this function is called by the plugin's configuration dialog.
161
162        if :param:`suppress_progress` is False it is safe to assume that this function
163        is being called from the main GUI thread so it is safe and recommended to use
164        a QProgressDialog to display what is happening and allow the user to cancel
165        the operation. if :param:`suppress_progress` is True then run the update
166        silently. In this case there is no guarantee what thread is calling this
167        function so no Qt related functionality that requires being run in the main
168        GUI thread should be run. E.G. Open a QProgressDialog.
169
170        :param parent: The parent object to be used by an GUI dialogs.
171
172        :param timeout: The maximum amount of time that should be spent in
173        any given network connection.
174
175        :param force: Force updating the cache even if the plugin has determined
176        it is not necessary.
177
178        :param suppress_progress: Should a progress indicator be shown.
179
180        :return: True if the cache was updated, False otherwise.
181        '''
182        return False
183
184    def do_genesis(self):
185        self.genesis()
186
187    def genesis(self):
188        '''
189        Plugin specific initialization.
190        '''
191        pass
192
193    def config_widget(self):
194        '''
195        See :class:`calibre.customize.Plugin` for details.
196        '''
197        raise NotImplementedError()
198
199    def save_settings(self, config_widget):
200        '''
201        See :class:`calibre.customize.Plugin` for details.
202        '''
203        raise NotImplementedError()
204
205    def customization_help(self, gui=False):
206        '''
207        See :class:`calibre.customize.Plugin` for details.
208        '''
209        raise NotImplementedError()
210
211# }}}
212