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