diff --git a/recipes/chronicle_higher_ed.recipe b/recipes/chronicle_higher_ed.recipe
index 15b284cd7a..66b17cafcf 100644
--- a/recipes/chronicle_higher_ed.recipe
+++ b/recipes/chronicle_higher_ed.recipe
@@ -12,10 +12,10 @@ class Chronicle(BasicNewsRecipe):
category = 'news'
encoding = 'UTF-8'
keep_only_tags = [
- dict(name='div', attrs={'class':'article'}),
+ dict(name='div', attrs={'class':['article','blog-mod']}),
]
- remove_tags = [dict(name='div',attrs={'class':['related module1','maintitle']}),
- dict(name='div', attrs={'id':['section-nav','icon-row', 'enlarge-popup']}),
+ remove_tags = [dict(name='div',attrs={'class':['related module1','maintitle','entry-utility','object-meta']}),
+ dict(name='div', attrs={'id':['section-nav','icon-row', 'enlarge-popup','confirm-popup']}),
dict(name='a', attrs={'class':'show-enlarge enlarge'})]
no_javascript = True
no_stylesheets = True
diff --git a/recipes/nytimes_sub.recipe b/recipes/nytimes_sub.recipe
index df44856293..35f7b46517 100644
--- a/recipes/nytimes_sub.recipe
+++ b/recipes/nytimes_sub.recipe
@@ -124,19 +124,19 @@ class NYTimes(BasicNewsRecipe):
if headlinesOnly:
title='New York Times Headlines'
description = 'Headlines from the New York Times'
- needs_subscription = False
+ needs_subscription = True
elif webEdition:
title='New York Times (Web)'
description = 'New York Times on the Web'
- needs_subscription = False
+ needs_subscription = True
elif replaceKindleVersion:
title='The New York Times'
description = 'Today\'s New York Times'
- needs_subscription = False
+ needs_subscription = True
else:
title='New York Times'
description = 'Today\'s New York Times'
- needs_subscription = False
+ needs_subscription = True
def decode_url_date(self,url):
urlitems = url.split('/')
@@ -359,6 +359,14 @@ class NYTimes(BasicNewsRecipe):
def get_browser(self):
br = BasicNewsRecipe.get_browser()
+ if self.username is not None and self.password is not None:
+ br.open('http://www.nytimes.com/auth/login')
+ br.form = br.forms().next()
+ br['userid'] = self.username
+ br['password'] = self.password
+ raw = br.submit().read()
+ if 'Please try again' in raw:
+ raise Exception('Your username and password are incorrect')
return br
cover_tag = 'NY_NYT'
diff --git a/recipes/smith.recipe b/recipes/smith.recipe
index 3d6a95c494..cd0c94ab35 100644
--- a/recipes/smith.recipe
+++ b/recipes/smith.recipe
@@ -48,10 +48,14 @@ class Smithsonian(BasicNewsRecipe):
link=post.find('a',href=True)
url=link['href']+'?c=y&story=fullstory'
if subsection is not None:
- subsection_title = self.tag_to_string(subsection)
+ subsection_title = self.tag_to_string(subsection).strip()
prefix = (subsection_title+': ')
description=self.tag_to_string(post('p', limit=2)[1]).strip()
else:
+ if post.find('img') is not None:
+ subsection_title = self.tag_to_string(post.findPrevious('div', attrs={'class':'departments plainModule'}).find('p', attrs={'class':'article-cat'})).strip()
+ prefix = (subsection_title+': ')
+
description=self.tag_to_string(post.find('p')).strip()
desc=re.sub('\sBy\s.*', '', description, re.DOTALL)
author=re.sub('.*By\s', '', description, re.DOTALL)
@@ -64,4 +68,3 @@ class Smithsonian(BasicNewsRecipe):
feeds[section_title] += articles
ans = [(key, val) for key, val in feeds.iteritems()]
return ans
-
diff --git a/resources/quick_start.epub b/resources/quick_start.epub
index a3f74213a6..23728547cd 100644
Binary files a/resources/quick_start.epub and b/resources/quick_start.epub differ
diff --git a/src/calibre/customize/profiles.py b/src/calibre/customize/profiles.py
index 00ec6294fb..7eba099bd2 100644
--- a/src/calibre/customize/profiles.py
+++ b/src/calibre/customize/profiles.py
@@ -770,13 +770,25 @@ class PocketBook900Output(OutputProfile):
dpi = 150.0
comic_screen_size = screen_size
+class PocketBookPro912Output(OutputProfile):
+
+ author = 'Daniele Pizzolli'
+ name = 'PocketBook Pro 912'
+ short_name = 'pocketbook_pro_912'
+ description = _('This profile is intended for the PocketBook Pro 912 series of devices.')
+
+ # According to http://download.pocketbook-int.com/user-guides/E_Ink/912/User_Guide_PocketBook_912(EN).pdf
+ screen_size = (825, 1200)
+ dpi = 155.0
+ comic_screen_size = screen_size
+
output_profiles = [OutputProfile, SonyReaderOutput, SonyReader300Output,
SonyReader900Output, MSReaderOutput, MobipocketOutput, HanlinV3Output,
HanlinV5Output, CybookG3Output, CybookOpusOutput, KindleOutput,
iPadOutput, iPad3Output, KoboReaderOutput, TabletOutput, SamsungGalaxy,
SonyReaderLandscapeOutput, KindleDXOutput, IlliadOutput,
IRexDR1000Output, IRexDR800Output, JetBook5Output, NookOutput,
- BambookOutput, NookColorOutput, PocketBook900Output, GenericEink,
- GenericEinkLarge, KindleFireOutput, KindlePaperWhiteOutput]
+ BambookOutput, NookColorOutput, PocketBook900Output, PocketBookPro912Output,
+ GenericEink, GenericEinkLarge, KindleFireOutput, KindlePaperWhiteOutput]
output_profiles.sort(cmp=lambda x,y:cmp(x.name.lower(), y.name.lower()))
diff --git a/src/calibre/devices/eb600/driver.py b/src/calibre/devices/eb600/driver.py
index 04501d193a..045eb2b4b7 100644
--- a/src/calibre/devices/eb600/driver.py
+++ b/src/calibre/devices/eb600/driver.py
@@ -234,7 +234,7 @@ class POCKETBOOK301(USBMS):
class POCKETBOOK602(USBMS):
name = 'PocketBook Pro 602/902 Device Interface'
- description = _('Communicate with the PocketBook 602/603/902/903 reader.')
+ description = _('Communicate with the PocketBook 602/603/902/903/Pro 912 reader.')
author = 'Kovid Goyal'
supported_platforms = ['windows', 'osx', 'linux']
FORMATS = ['epub', 'fb2', 'prc', 'mobi', 'pdf', 'djvu', 'rtf', 'chm',
@@ -249,7 +249,7 @@ class POCKETBOOK602(USBMS):
VENDOR_NAME = ''
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = ['PB602', 'PB603', 'PB902',
- 'PB903', 'PB']
+ 'PB903', 'Pocket912', 'PB']
class POCKETBOOK622(POCKETBOOK602):
diff --git a/src/calibre/ebooks/lit/reader.py b/src/calibre/ebooks/lit/reader.py
index a673de87d7..98b230e5bb 100644
--- a/src/calibre/ebooks/lit/reader.py
+++ b/src/calibre/ebooks/lit/reader.py
@@ -11,13 +11,17 @@ import struct, os, functools, re
from urlparse import urldefrag
from cStringIO import StringIO
from urllib import unquote as urlunquote
+
+from lxml import etree
+
from calibre.ebooks.lit import LitError
from calibre.ebooks.lit.maps import OPF_MAP, HTML_MAP
import calibre.ebooks.lit.mssha1 as mssha1
-from calibre.ebooks.oeb.base import urlnormalize
+from calibre.ebooks.oeb.base import urlnormalize, xpath
from calibre.ebooks.oeb.reader import OEBReader
from calibre.ebooks import DRMError
from calibre import plugins
+
lzx, lxzerror = plugins['lzx']
msdes, msdeserror = plugins['msdes']
@@ -907,3 +911,16 @@ class LitReader(OEBReader):
Container = LitContainer
DEFAULT_PROFILE = 'MSReader'
+ def _spine_from_opf(self, opf):
+ manifest = self.oeb.manifest
+ for elem in xpath(opf, '/o2:package/o2:spine/o2:itemref'):
+ idref = elem.get('idref')
+ if idref not in manifest.ids:
+ continue
+ item = manifest.ids[idref]
+ if (item.media_type.lower() == 'application/xml' and
+ hasattr(item.data, 'xpath') and item.data.xpath('/html')):
+ item.media_type = 'application/xhtml+xml'
+ item.data = item._parse_xhtml(etree.tostring(item.data))
+ super(LitReader, self)._spine_from_opf(opf)
+
diff --git a/src/calibre/ebooks/metadata/mobi.py b/src/calibre/ebooks/metadata/mobi.py
index e701946c01..e223dcac23 100644
--- a/src/calibre/ebooks/metadata/mobi.py
+++ b/src/calibre/ebooks/metadata/mobi.py
@@ -390,6 +390,10 @@ class MetadataUpdater(object):
not added_501 and not share_not_sync):
from uuid import uuid4
update_exth_record((113, str(uuid4())))
+ # Add a 112 record with actual UUID
+ if getattr(mi, 'uuid', None):
+ update_exth_record((112,
+ (u"calibre:%s" % mi.uuid).encode(self.codec, 'replace')))
if 503 in self.original_exth_records:
update_exth_record((503, mi.title.encode(self.codec, 'replace')))
diff --git a/src/calibre/ebooks/mobi/writer8/exth.py b/src/calibre/ebooks/mobi/writer8/exth.py
index a060e338d1..31792d2156 100644
--- a/src/calibre/ebooks/mobi/writer8/exth.py
+++ b/src/calibre/ebooks/mobi/writer8/exth.py
@@ -110,6 +110,12 @@ def build_exth(metadata, prefer_author_sort=False, is_periodical=False,
exth.write(uuid)
nrecs += 1
+ # Write UUID as SOURCE
+ c_uuid = b'calibre:%s' % uuid
+ exth.write(pack(b'>II', 112, len(c_uuid) + 8))
+ exth.write(c_uuid)
+ nrecs += 1
+
# Write cdetype
if not is_periodical:
if not share_not_sync:
diff --git a/src/calibre/gui2/actions/store.py b/src/calibre/gui2/actions/store.py
index adc66edea4..b84836c465 100644
--- a/src/calibre/gui2/actions/store.py
+++ b/src/calibre/gui2/actions/store.py
@@ -43,14 +43,16 @@ class StoreAction(InterfaceAction):
icon.addFile(I('donate.png'), QSize(16, 16))
for n, p in sorted(self.gui.istores.items(), key=lambda x: x[0].lower()):
if p.base_plugin.affiliate:
- self.store_list_menu.addAction(icon, n, partial(self.open_store, p))
+ self.store_list_menu.addAction(icon, n,
+ partial(self.open_store, n))
else:
- self.store_list_menu.addAction(n, partial(self.open_store, p))
+ self.store_list_menu.addAction(n, partial(self.open_store, n))
def do_search(self):
return self.search()
def search(self, query=''):
+ self.gui.istores.check_for_updates()
self.show_disclaimer()
from calibre.gui2.store.search.search import SearchDialog
sd = SearchDialog(self.gui, self.gui, query)
@@ -125,9 +127,13 @@ class StoreAction(InterfaceAction):
self.gui.load_store_plugins()
self.load_menu()
- def open_store(self, store_plugin):
+ def open_store(self, store_plugin_name):
+ self.gui.istores.check_for_updates()
self.show_disclaimer()
- store_plugin.open(self.gui)
+ # It's not too important that the updated plugin have finished loading
+ # at this point
+ self.gui.istores.join(1.0)
+ self.gui.istores[store_plugin_name].open(self.gui)
def show_disclaimer(self):
confirm(('
' +
diff --git a/src/calibre/gui2/custom_column_widgets.py b/src/calibre/gui2/custom_column_widgets.py
index f4c92529e3..0f5bb0a1c9 100644
--- a/src/calibre/gui2/custom_column_widgets.py
+++ b/src/calibre/gui2/custom_column_widgets.py
@@ -383,12 +383,14 @@ class Series(Base):
values = list(self.db.all_custom(num=self.col_id))
values.sort(key=sort_key)
val = self.db.get_custom(book_id, num=self.col_id, index_is_id=True)
- s_index = self.db.get_custom_extra(book_id, num=self.col_id, index_is_id=True)
- if s_index is None:
- s_index = 0.0
- self.idx_widget.setValue(s_index)
- self.initial_index = s_index
self.initial_val = val
+ s_index = self.db.get_custom_extra(book_id, num=self.col_id, index_is_id=True)
+ self.initial_index = s_index
+ try:
+ s_index = float(s_index)
+ except (ValueError, TypeError):
+ s_index = 1.0
+ self.idx_widget.setValue(s_index)
val = self.normalize_db_val(val)
self.name_widget.blockSignals(True)
self.name_widget.update_items_cache(values)
diff --git a/src/calibre/gui2/store/__init__.py b/src/calibre/gui2/store/__init__.py
index ae42d82032..3af0a14cda 100644
--- a/src/calibre/gui2/store/__init__.py
+++ b/src/calibre/gui2/store/__init__.py
@@ -49,13 +49,16 @@ class StorePlugin(object): # {{{
See declined.txt for a list of stores that do not want to be included.
'''
- def __init__(self, gui, name):
- from calibre.gui2 import JSONConfig
+ minimum_calibre_version = (0, 9, 14)
+ def __init__(self, gui, name, config=None, base_plugin=None):
self.gui = gui
self.name = name
- self.base_plugin = None
- self.config = JSONConfig('store/stores/' + ascii_filename(self.name))
+ self.base_plugin = base_plugin
+ if config is None:
+ from calibre.gui2 import JSONConfig
+ config = JSONConfig('store/stores/' + ascii_filename(self.name))
+ self.config = config
def open(self, gui, parent=None, detail_item=None, external=False):
'''
diff --git a/src/calibre/gui2/store/loader.py b/src/calibre/gui2/store/loader.py
new file mode 100644
index 0000000000..45c258a915
--- /dev/null
+++ b/src/calibre/gui2/store/loader.py
@@ -0,0 +1,197 @@
+#!/usr/bin/env python
+# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:fdm=marker:ai
+from __future__ import (unicode_literals, division, absolute_import,
+ print_function)
+
+__license__ = 'GPL v3'
+__copyright__ = '2013, Kovid Goyal '
+__docformat__ = 'restructuredtext en'
+
+import sys, time, io, re
+from zlib import decompressobj
+from collections import OrderedDict
+from threading import Thread
+from urllib import urlencode
+
+from calibre import prints, browser
+from calibre.constants import numeric_version, DEBUG
+from calibre.gui2.store import StorePlugin
+from calibre.utils.config import JSONConfig
+
+class VersionMismatch(ValueError):
+ def __init__(self, ver):
+ ValueError.__init__(self, 'calibre too old')
+ self.ver = ver
+
+def download_updates(ver_map={}, server='http://status.calibre-ebook.com'):
+ data = {k:type(u'')(v) for k, v in ver_map.iteritems()}
+ data['ver'] = '1'
+ url = '%s/stores?%s'%(server, urlencode(data))
+ br = browser()
+ # We use a timeout here to ensure the non-daemonic update thread does not
+ # cause calibre to hang indefinitely during shutdown
+ raw = br.open(url, timeout=4.0).read()
+
+ while raw:
+ name, raw = raw.partition(b'\0')[0::2]
+ name = name.decode('utf-8')
+ d = decompressobj()
+ src = d.decompress(raw)
+ src = src.decode('utf-8')
+ # Python complains if there is a coding declaration in a unicode string
+ src = re.sub(r'^#.*coding\s*[:=]\s*([-\w.]+)', '#', src, flags=re.MULTILINE)
+ # Translate newlines to \n
+ src = io.StringIO(src, newline=None).getvalue()
+ yield name, src
+ raw = d.unused_data
+
+class Stores(OrderedDict):
+
+ CHECK_INTERVAL = 24 * 60 * 60
+
+ def builtins_loaded(self):
+ self.last_check_time = 0
+ self.version_map = {}
+ self.cached_version_map = {}
+ self.name_rmap = {}
+ for key, val in self.iteritems():
+ prefix, name = val.__module__.rpartition('.')[0::2]
+ if prefix == 'calibre.gui2.store.stores' and name.endswith('_plugin'):
+ module = sys.modules[val.__module__]
+ sv = getattr(module, 'store_version', None)
+ if sv is not None:
+ name = name.rpartition('_')[0]
+ self.version_map[name] = sv
+ self.name_rmap[name] = key
+ self.cache_file = JSONConfig('store/plugin_cache')
+ self.load_cache()
+
+ def load_cache(self):
+ # Load plugins from on disk cache
+ remove = set()
+ pat = re.compile(r'^store_version\s*=\s*(\d+)', re.M)
+ for name, src in self.cache_file.iteritems():
+ try:
+ key = self.name_rmap[name]
+ except KeyError:
+ # Plugin has been disabled
+ m = pat.search(src[:512])
+ if m is not None:
+ try:
+ self.cached_version_map[name] = int(m.group(1))
+ except (TypeError, ValueError):
+ pass
+ continue
+
+ try:
+ obj, ver = self.load_object(src, key)
+ except VersionMismatch as e:
+ self.cached_version_map[name] = e.ver
+ continue
+ except:
+ import traceback
+ prints('Failed to load cached store:', name)
+ traceback.print_exc()
+ else:
+ if not self.replace_plugin(ver, name, obj, 'cached'):
+ # Builtin plugin is newer than cached
+ remove.add(name)
+
+ if remove:
+ with self.cache_file:
+ for name in remove:
+ del self.cache_file[name]
+
+ def check_for_updates(self):
+ if hasattr(self, 'update_thread') and self.update_thread.is_alive():
+ return
+ if time.time() - self.last_check_time < self.CHECK_INTERVAL:
+ return
+ self.last_check_time = time.time()
+ try:
+ self.update_thread.start()
+ except (RuntimeError, AttributeError):
+ self.update_thread = Thread(target=self.do_update)
+ self.update_thread.start()
+
+ def join(self, timeout=None):
+ hasattr(self, 'update_thread') and self.update_thread.join(timeout)
+
+ def download_updates(self):
+ ver_map = {name:max(ver, self.cached_version_map.get(name, -1))
+ for name, ver in self.version_map.iteritems()}
+ try:
+ updates = download_updates(ver_map)
+ except:
+ import traceback
+ traceback.print_exc()
+ else:
+ for name, code in updates:
+ yield name, code
+
+ def do_update(self):
+ replacements = {}
+
+ for name, src in self.download_updates():
+ try:
+ key = self.name_rmap[name]
+ except KeyError:
+ # Plugin has been disabled
+ replacements[name] = src
+ continue
+ try:
+ obj, ver = self.load_object(src, key)
+ except VersionMismatch as e:
+ self.cached_version_map[name] = e.ver
+ replacements[name] = src
+ continue
+ except:
+ import traceback
+ prints('Failed to load downloaded store:', name)
+ traceback.print_exc()
+ else:
+ if self.replace_plugin(ver, name, obj, 'downloaded'):
+ replacements[name] = src
+
+ if replacements:
+ with self.cache_file:
+ for name, src in replacements.iteritems():
+ self.cache_file[name] = src
+
+ def replace_plugin(self, ver, name, obj, source):
+ if ver > self.version_map[name]:
+ if DEBUG:
+ prints('Loaded', source, 'store plugin for:',
+ self.name_rmap[name], 'at version:', ver)
+ self[self.name_rmap[name]] = obj
+ self.version_map[name] = ver
+ return True
+ return False
+
+ def load_object(self, src, key):
+ namespace = {}
+ builtin = self[key]
+ exec src in namespace
+ ver = namespace['store_version']
+ cls = None
+ for x in namespace.itervalues():
+ if (isinstance(x, type) and issubclass(x, StorePlugin) and x is not
+ StorePlugin):
+ cls = x
+ break
+ if cls is None:
+ raise ValueError('No store plugin found')
+ if cls.minimum_calibre_version > numeric_version:
+ raise VersionMismatch(ver)
+ return cls(builtin.gui, builtin.name, config=builtin.config,
+ base_plugin=builtin.base_plugin), ver
+
+if __name__ == '__main__':
+ st = time.time()
+ for name, code in download_updates():
+ print(name)
+ print(code)
+ print('\n', '_'*80, '\n', sep='')
+ print ('Time to download all plugins: %.2f'%( time.time() - st))
+
+
diff --git a/src/calibre/gui2/store/search/search.py b/src/calibre/gui2/store/search/search.py
index b4ae0bc943..20c6c09a03 100644
--- a/src/calibre/gui2/store/search/search.py
+++ b/src/calibre/gui2/store/search/search.py
@@ -194,6 +194,7 @@ class SearchDialog(QDialog, Ui_Dialog):
query = self.clean_query(query)
shuffle(store_names)
# Add plugins that the user has checked to the search pool's work queue.
+ self.gui.istores.join(4.0) # Wait for updated plugins to load
for n in store_names:
if self.store_checks[n].isChecked():
self.search_pool.add_task(query, n, self.gui.istores[n], self.max_results, self.timeout)
diff --git a/src/calibre/gui2/store/stores/amazon_de_plugin.py b/src/calibre/gui2/store/stores/amazon_de_plugin.py
index 3ccbef0b6e..58c67122e1 100644
--- a/src/calibre/gui2/store/stores/amazon_de_plugin.py
+++ b/src/calibre/gui2/store/stores/amazon_de_plugin.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
+store_version = 1 # Needed for dynamic plugin loading
__license__ = 'GPL 3'
__copyright__ = '2011, John Schember '
diff --git a/src/calibre/gui2/store/stores/amazon_es_plugin.py b/src/calibre/gui2/store/stores/amazon_es_plugin.py
index 131f77c7e9..427927a5a6 100644
--- a/src/calibre/gui2/store/stores/amazon_es_plugin.py
+++ b/src/calibre/gui2/store/stores/amazon_es_plugin.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
+store_version = 1 # Needed for dynamic plugin loading
__license__ = 'GPL 3'
__copyright__ = '2011, John Schember '
@@ -21,4 +22,4 @@ class AmazonESKindleStore(AmazonUKKindleStore):
'&linkCode=ur2&camp=3626&creative=24790')
search_url = 'http://www.amazon.es/s/?url=search-alias%3Ddigital-text&field-keywords='
- author_article = 'de '
\ No newline at end of file
+ author_article = 'de '
diff --git a/src/calibre/gui2/store/stores/amazon_fr_plugin.py b/src/calibre/gui2/store/stores/amazon_fr_plugin.py
index cd59be0313..e3eff50450 100644
--- a/src/calibre/gui2/store/stores/amazon_fr_plugin.py
+++ b/src/calibre/gui2/store/stores/amazon_fr_plugin.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
+store_version = 1 # Needed for dynamic plugin loading
__license__ = 'GPL 3'
__copyright__ = '2011, John Schember '
diff --git a/src/calibre/gui2/store/stores/amazon_it_plugin.py b/src/calibre/gui2/store/stores/amazon_it_plugin.py
index ad028bf963..669831f89d 100644
--- a/src/calibre/gui2/store/stores/amazon_it_plugin.py
+++ b/src/calibre/gui2/store/stores/amazon_it_plugin.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
+store_version = 1 # Needed for dynamic plugin loading
__license__ = 'GPL 3'
__copyright__ = '2011, John Schember '
@@ -21,4 +22,4 @@ class AmazonITKindleStore(AmazonUKKindleStore):
'linkCode=ur2&camp=3370&creative=23322')
search_url = 'http://www.amazon.it/s/?url=search-alias%3Ddigital-text&field-keywords='
- author_article = 'di '
\ No newline at end of file
+ author_article = 'di '
diff --git a/src/calibre/gui2/store/stores/amazon_plugin.py b/src/calibre/gui2/store/stores/amazon_plugin.py
index e26aa2a133..cf29e19fa4 100644
--- a/src/calibre/gui2/store/stores/amazon_plugin.py
+++ b/src/calibre/gui2/store/stores/amazon_plugin.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
+store_version = 1 # Needed for dynamic plugin loading
__license__ = 'GPL 3'
__copyright__ = '2011, John Schember '
diff --git a/src/calibre/gui2/store/stores/amazon_uk_plugin.py b/src/calibre/gui2/store/stores/amazon_uk_plugin.py
index 486671c729..ab67fc477c 100644
--- a/src/calibre/gui2/store/stores/amazon_uk_plugin.py
+++ b/src/calibre/gui2/store/stores/amazon_uk_plugin.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
+store_version = 1 # Needed for dynamic plugin loading
__license__ = 'GPL 3'
__copyright__ = '2011, John Schember '
diff --git a/src/calibre/gui2/store/stores/archive_org_plugin.py b/src/calibre/gui2/store/stores/archive_org_plugin.py
index 7439056baa..ed83b1c433 100644
--- a/src/calibre/gui2/store/stores/archive_org_plugin.py
+++ b/src/calibre/gui2/store/stores/archive_org_plugin.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
+store_version = 1 # Needed for dynamic plugin loading
__license__ = 'GPL 3'
__copyright__ = '2011, John Schember '
diff --git a/src/calibre/gui2/store/stores/baen_webscription_plugin.py b/src/calibre/gui2/store/stores/baen_webscription_plugin.py
index a2a4e63d74..63203693a5 100644
--- a/src/calibre/gui2/store/stores/baen_webscription_plugin.py
+++ b/src/calibre/gui2/store/stores/baen_webscription_plugin.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
+store_version = 1 # Needed for dynamic plugin loading
__license__ = 'GPL 3'
__copyright__ = '2011, John Schember '
@@ -22,7 +23,7 @@ from calibre.gui2.store.search_result import SearchResult
from calibre.gui2.store.web_store_dialog import WebStoreDialog
class BaenWebScriptionStore(BasicStoreConfig, StorePlugin):
-
+
def open(self, parent=None, detail_item=None, external=False):
url = 'http://www.baenebooks.com/'
@@ -41,26 +42,26 @@ class BaenWebScriptionStore(BasicStoreConfig, StorePlugin):
def search(self, query, max_results=10, timeout=60):
url = 'http://www.baenebooks.com/searchadv.aspx?IsSubmit=true&SearchTerm=' + urllib2.quote(query)
-
+
br = browser()
-
+
counter = max_results
with closing(br.open(url, timeout=timeout)) as f:
doc = html.fromstring(f.read())
for data in doc.xpath('//table//table//table//table//tr'):
if counter <= 0:
break
-
+
id = ''.join(data.xpath('./td[1]/a/@href'))
if not id or not id.startswith('p-'):
continue
-
+
title = ''.join(data.xpath('./td[1]/a/text()'))
-
+
author = ''
cover_url = ''
price = ''
-
+
with closing(br.open('http://www.baenebooks.com/' + id.strip(), timeout=timeout/4)) as nf:
idata = html.fromstring(nf.read())
author = ''.join(idata.xpath('//span[@class="ProductNameText"]/../b/text()'))
@@ -68,16 +69,16 @@ class BaenWebScriptionStore(BasicStoreConfig, StorePlugin):
price = ''.join(idata.xpath('//span[@class="variantprice"]/text()'))
a, b, price = price.partition('$')
price = b + price
-
+
pnum = ''
mo = re.search(r'p-(?P\d+)-', id.strip())
if mo:
pnum = mo.group('num')
if pnum:
cover_url = 'http://www.baenebooks.com/' + ''.join(idata.xpath('//img[@id="ProductPic%s"]/@src' % pnum))
-
+
counter -= 1
-
+
s = SearchResult()
s.cover_url = cover_url
s.title = title.strip()
@@ -86,5 +87,5 @@ class BaenWebScriptionStore(BasicStoreConfig, StorePlugin):
s.detail_item = id.strip()
s.drm = SearchResult.DRM_UNLOCKED
s.formats = 'RB, MOBI, EPUB, LIT, LRF, RTF, HTML'
-
+
yield s
diff --git a/src/calibre/gui2/store/stores/bewrite_plugin.py b/src/calibre/gui2/store/stores/bewrite_plugin.py
index b702f15623..3ccd28d976 100644
--- a/src/calibre/gui2/store/stores/bewrite_plugin.py
+++ b/src/calibre/gui2/store/stores/bewrite_plugin.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
+store_version = 1 # Needed for dynamic plugin loading
__license__ = 'GPL 3'
__copyright__ = '2011, John Schember '
@@ -71,7 +72,7 @@ class BeWriteStore(BasicStoreConfig, StorePlugin):
with closing(br.open(search_result.detail_item, timeout=timeout)) as nf:
idata = html.fromstring(nf.read())
-
+
price = ''.join(idata.xpath('//div[@id="content"]//td[contains(text(), "ePub")]/text()'))
if not price:
price = ''.join(idata.xpath('//div[@id="content"]//td[contains(text(), "MOBI")]/text()'))
@@ -79,7 +80,7 @@ class BeWriteStore(BasicStoreConfig, StorePlugin):
price = ''.join(idata.xpath('//div[@id="content"]//td[contains(text(), "PDF")]/text()'))
price = '$' + price.split('$')[-1]
search_result.price = price.strip()
-
+
cover_img = idata.xpath('//div[@id="content"]//img/@src')
if cover_img:
for i in cover_img:
@@ -87,7 +88,7 @@ class BeWriteStore(BasicStoreConfig, StorePlugin):
cover_url = 'http://www.bewrite.net/mm5/' + i
search_result.cover_url = cover_url.strip()
break
-
+
formats = set([])
if idata.xpath('boolean(//div[@id="content"]//td[contains(text(), "ePub")])'):
formats.add('EPUB')
diff --git a/src/calibre/gui2/store/stores/biblio_plugin.py b/src/calibre/gui2/store/stores/biblio_plugin.py
index 5a40ec57cc..db7d909b3b 100644
--- a/src/calibre/gui2/store/stores/biblio_plugin.py
+++ b/src/calibre/gui2/store/stores/biblio_plugin.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
+store_version = 1 # Needed for dynamic plugin loading
__license__ = 'GPL 3'
__copyright__ = '2012, Alex Stanev '
@@ -26,7 +27,7 @@ class BiblioStore(BasicStoreConfig, OpenSearchOPDSStore):
for s in OpenSearchOPDSStore.search(self, query, max_results, timeout):
yield s
-
+
def get_details(self, search_result, timeout):
# get format and DRM status
from calibre import browser
@@ -39,13 +40,13 @@ class BiblioStore(BasicStoreConfig, OpenSearchOPDSStore):
search_result.formats = ''
if idata.xpath('.//span[@class="format epub"]'):
search_result.formats = 'EPUB'
-
+
if idata.xpath('.//span[@class="format pdf"]'):
if search_result.formats == '':
search_result.formats = 'PDF'
else:
search_result.formats.join(', PDF')
-
+
if idata.xpath('.//span[@class="format nodrm-icon"]'):
search_result.drm = SearchResult.DRM_UNLOCKED
else:
diff --git a/src/calibre/gui2/store/stores/bn_plugin.py b/src/calibre/gui2/store/stores/bn_plugin.py
index 8f2f988974..6138181fde 100644
--- a/src/calibre/gui2/store/stores/bn_plugin.py
+++ b/src/calibre/gui2/store/stores/bn_plugin.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
+store_version = 1 # Needed for dynamic plugin loading
__license__ = 'GPL 3'
__copyright__ = '2011, John Schember '
diff --git a/src/calibre/gui2/store/stores/bookoteka_plugin.py b/src/calibre/gui2/store/stores/bookoteka_plugin.py
index 4df22060ed..7c3b2e8242 100644
--- a/src/calibre/gui2/store/stores/bookoteka_plugin.py
+++ b/src/calibre/gui2/store/stores/bookoteka_plugin.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
+store_version = 1 # Needed for dynamic plugin loading
__license__ = 'GPL 3'
__copyright__ = '2011, Tomasz Długosz '
diff --git a/src/calibre/gui2/store/stores/chitanka_plugin.py b/src/calibre/gui2/store/stores/chitanka_plugin.py
index 58ef109dba..30fc543849 100644
--- a/src/calibre/gui2/store/stores/chitanka_plugin.py
+++ b/src/calibre/gui2/store/stores/chitanka_plugin.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
+store_version = 1 # Needed for dynamic plugin loading
__license__ = 'GPL 3'
__copyright__ = '2011, Alex Stanev '
diff --git a/src/calibre/gui2/store/stores/diesel_ebooks_plugin.py b/src/calibre/gui2/store/stores/diesel_ebooks_plugin.py
index 309ee98e4c..eebd1376ba 100644
--- a/src/calibre/gui2/store/stores/diesel_ebooks_plugin.py
+++ b/src/calibre/gui2/store/stores/diesel_ebooks_plugin.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
+store_version = 1 # Needed for dynamic plugin loading
__license__ = 'GPL 3'
__copyright__ = '2011, John Schember '
diff --git a/src/calibre/gui2/store/stores/ebook_nl_plugin.py b/src/calibre/gui2/store/stores/ebook_nl_plugin.py
index 0a79026dbb..6f895f1325 100644
--- a/src/calibre/gui2/store/stores/ebook_nl_plugin.py
+++ b/src/calibre/gui2/store/stores/ebook_nl_plugin.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
+store_version = 1 # Needed for dynamic plugin loading
__license__ = 'GPL 3'
__copyright__ = '2011, John Schember '
diff --git a/src/calibre/gui2/store/stores/ebookpoint_plugin.py b/src/calibre/gui2/store/stores/ebookpoint_plugin.py
index 94e6cc73ca..d0306a45ee 100644
--- a/src/calibre/gui2/store/stores/ebookpoint_plugin.py
+++ b/src/calibre/gui2/store/stores/ebookpoint_plugin.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
+store_version = 1 # Needed for dynamic plugin loading
__license__ = 'GPL 3'
__copyright__ = '2011-2012, Tomasz Długosz '
diff --git a/src/calibre/gui2/store/stores/ebooks_com_plugin.py b/src/calibre/gui2/store/stores/ebooks_com_plugin.py
index 826b59d41d..5c901bd65e 100644
--- a/src/calibre/gui2/store/stores/ebooks_com_plugin.py
+++ b/src/calibre/gui2/store/stores/ebooks_com_plugin.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
+store_version = 1 # Needed for dynamic plugin loading
__license__ = 'GPL 3'
__copyright__ = '2011, John Schember '
diff --git a/src/calibre/gui2/store/stores/ebooksgratuits_plugin.py b/src/calibre/gui2/store/stores/ebooksgratuits_plugin.py
index add4bb2d40..2f13e0be86 100644
--- a/src/calibre/gui2/store/stores/ebooksgratuits_plugin.py
+++ b/src/calibre/gui2/store/stores/ebooksgratuits_plugin.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
+store_version = 1 # Needed for dynamic plugin loading
__license__ = 'GPL 3'
__copyright__ = '2012, Florent FAYOLLE '
diff --git a/src/calibre/gui2/store/stores/ebookshoppe_uk_plugin.py b/src/calibre/gui2/store/stores/ebookshoppe_uk_plugin.py
index 804279d3fd..77801d8584 100644
--- a/src/calibre/gui2/store/stores/ebookshoppe_uk_plugin.py
+++ b/src/calibre/gui2/store/stores/ebookshoppe_uk_plugin.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
+store_version = 1 # Needed for dynamic plugin loading
__license__ = 'GPL 3'
__copyright__ = '2011, John Schember '
diff --git a/src/calibre/gui2/store/stores/eharlequin_plugin.py b/src/calibre/gui2/store/stores/eharlequin_plugin.py
index ec85ccf1d3..5c863af856 100644
--- a/src/calibre/gui2/store/stores/eharlequin_plugin.py
+++ b/src/calibre/gui2/store/stores/eharlequin_plugin.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
+store_version = 1 # Needed for dynamic plugin loading
__license__ = 'GPL 3'
__copyright__ = '2011, John Schember '
@@ -36,9 +37,9 @@ class EHarlequinStore(BasicStoreConfig, StorePlugin):
def search(self, query, max_results=10, timeout=60):
url = 'http://ebooks.eharlequin.com/BANGSearch.dll?Type=FullText&FullTextField=All&FullTextCriteria=' + urllib2.quote(query)
-
+
br = browser()
-
+
counter = max_results
with closing(br.open(url, timeout=timeout)) as f:
doc = html.fromstring(f.read())
@@ -64,19 +65,19 @@ class EHarlequinStore(BasicStoreConfig, StorePlugin):
s.price = price.strip()
s.detail_item = 'http://ebooks.eharlequin.com/' + id.strip()
s.formats = 'EPUB'
-
+
yield s
-
+
def get_details(self, search_result, timeout):
url = 'http://ebooks.eharlequin.com/en/ContentDetails.htm?ID='
-
+
mo = re.search(r'\?ID=(?P.+)', search_result.detail_item)
if mo:
id = mo.group('id')
if not id:
return
-
-
+
+
br = browser()
with closing(br.open(url + id, timeout=timeout)) as nf:
idata = html.fromstring(nf.read())
diff --git a/src/calibre/gui2/store/stores/eknigi_plugin.py b/src/calibre/gui2/store/stores/eknigi_plugin.py
index 7d88465f62..7cafba421e 100644
--- a/src/calibre/gui2/store/stores/eknigi_plugin.py
+++ b/src/calibre/gui2/store/stores/eknigi_plugin.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
+store_version = 1 # Needed for dynamic plugin loading
__license__ = 'GPL 3'
__copyright__ = '2011, Alex Stanev '
diff --git a/src/calibre/gui2/store/stores/empik_plugin.py b/src/calibre/gui2/store/stores/empik_plugin.py
index 16a7ee13e3..08b1cdcb64 100644
--- a/src/calibre/gui2/store/stores/empik_plugin.py
+++ b/src/calibre/gui2/store/stores/empik_plugin.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
+store_version = 1 # Needed for dynamic plugin loading
__license__ = 'GPL 3'
__copyright__ = '2011-2012, Tomasz Długosz '
@@ -68,7 +69,7 @@ class EmpikStore(BasicStoreConfig, StorePlugin):
counter -= 1
s = SearchResult()
- s.cover_url = cover_url
+ s.cover_url = cover_url
s.title = title.strip() + ' ' + formats
s.author = author.strip()
s.price = price
diff --git a/src/calibre/gui2/store/stores/escapemagazine_plugin.py b/src/calibre/gui2/store/stores/escapemagazine_plugin.py
index 7f3f24e7d6..e3b1ef335a 100644
--- a/src/calibre/gui2/store/stores/escapemagazine_plugin.py
+++ b/src/calibre/gui2/store/stores/escapemagazine_plugin.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
+store_version = 1 # Needed for dynamic plugin loading
__license__ = 'GPL 3'
__copyright__ = '2011, Tomasz Długosz '
diff --git a/src/calibre/gui2/store/stores/feedbooks_plugin.py b/src/calibre/gui2/store/stores/feedbooks_plugin.py
index cac44fd8df..36521406bb 100644
--- a/src/calibre/gui2/store/stores/feedbooks_plugin.py
+++ b/src/calibre/gui2/store/stores/feedbooks_plugin.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
+store_version = 1 # Needed for dynamic plugin loading
__license__ = 'GPL 3'
__copyright__ = '2011, John Schember '
@@ -11,10 +12,10 @@ from calibre.gui2.store.opensearch_store import OpenSearchOPDSStore
from calibre.gui2.store.search_result import SearchResult
class FeedbooksStore(BasicStoreConfig, OpenSearchOPDSStore):
-
+
open_search_url = 'http://assets0.feedbooks.net/opensearch.xml?t=1253087147'
web_url = 'http://feedbooks.com/'
-
+
# http://www.feedbooks.com/catalog
def search(self, query, max_results=10, timeout=60):
diff --git a/src/calibre/gui2/store/stores/foyles_uk_plugin.py b/src/calibre/gui2/store/stores/foyles_uk_plugin.py
index 819c412758..7c224f4f70 100644
--- a/src/calibre/gui2/store/stores/foyles_uk_plugin.py
+++ b/src/calibre/gui2/store/stores/foyles_uk_plugin.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
+store_version = 1 # Needed for dynamic plugin loading
__license__ = 'GPL 3'
__copyright__ = '2011, John Schember '
diff --git a/src/calibre/gui2/store/stores/google_books_plugin.py b/src/calibre/gui2/store/stores/google_books_plugin.py
index 6ffeab517c..f6f91fd81d 100644
--- a/src/calibre/gui2/store/stores/google_books_plugin.py
+++ b/src/calibre/gui2/store/stores/google_books_plugin.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
+store_version = 1 # Needed for dynamic plugin loading
__license__ = 'GPL 3'
__copyright__ = '2011, John Schember '
@@ -38,7 +39,7 @@ class GoogleBooksStore(BasicStoreConfig, StorePlugin):
'ganpub': 'k352583',
'ganclk': 'GOOG_1335335464',
}
-
+
url = 'http://gan.doubleclick.net/gan_click?lid=%(lid)s&pubid=%(pubid)s' % aff_id
if detail_item:
detail_item += '&ganpub=%(ganpub)s&ganclk=%(ganclk)s' % aff_id
@@ -53,9 +54,9 @@ class GoogleBooksStore(BasicStoreConfig, StorePlugin):
def search(self, query, max_results=10, timeout=60):
url = 'http://www.google.com/search?tbm=bks&q=' + urllib.quote_plus(query)
-
+
br = browser()
-
+
counter = max_results
with closing(br.open(url, timeout=timeout)) as f:
doc = html.fromstring(f.read())
@@ -76,22 +77,22 @@ class GoogleBooksStore(BasicStoreConfig, StorePlugin):
author = ', '.join(authors)
counter -= 1
-
+
s = SearchResult()
s.title = title.strip()
s.author = author.strip()
s.detail_item = id.strip()
s.drm = SearchResult.DRM_UNKNOWN
-
+
yield s
-
+
def get_details(self, search_result, timeout):
br = browser()
with closing(br.open(search_result.detail_item, timeout=timeout)) as nf:
doc = html.fromstring(nf.read())
-
+
search_result.cover_url = ''.join(doc.xpath('//div[@class="sidebarcover"]//img/@src'))
-
+
# Try to get the set price.
price = ''.join(doc.xpath('//div[@id="gb-get-book-container"]//a/text()'))
if 'read' in price.lower():
@@ -101,10 +102,10 @@ class GoogleBooksStore(BasicStoreConfig, StorePlugin):
elif '-' in price:
a, b, price = price.partition(' - ')
search_result.price = price.strip()
-
+
search_result.formats = ', '.join(doc.xpath('//div[contains(@class, "download-panel-div")]//a/text()')).upper()
if not search_result.formats:
search_result.formats = _('Unknown')
-
+
return True
diff --git a/src/calibre/gui2/store/stores/gutenberg_plugin.py b/src/calibre/gui2/store/stores/gutenberg_plugin.py
index cbf3a2f565..422165f263 100644
--- a/src/calibre/gui2/store/stores/gutenberg_plugin.py
+++ b/src/calibre/gui2/store/stores/gutenberg_plugin.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
+store_version = 1 # Needed for dynamic plugin loading
__license__ = 'GPL 3'
__copyright__ = '2011, John Schember '
diff --git a/src/calibre/gui2/store/stores/kobo_plugin.py b/src/calibre/gui2/store/stores/kobo_plugin.py
index 5a8b5618d5..44f4f4001c 100644
--- a/src/calibre/gui2/store/stores/kobo_plugin.py
+++ b/src/calibre/gui2/store/stores/kobo_plugin.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
+store_version = 1 # Needed for dynamic plugin loading
__license__ = 'GPL 3'
__copyright__ = '2011, John Schember '
diff --git a/src/calibre/gui2/store/stores/legimi_plugin.py b/src/calibre/gui2/store/stores/legimi_plugin.py
index 509ca88104..85561c63f3 100644
--- a/src/calibre/gui2/store/stores/legimi_plugin.py
+++ b/src/calibre/gui2/store/stores/legimi_plugin.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
+store_version = 1 # Needed for dynamic plugin loading
__license__ = 'GPL 3'
__copyright__ = '2011, Tomasz Długosz '
@@ -24,7 +25,7 @@ from calibre.gui2.store.web_store_dialog import WebStoreDialog
class LegimiStore(BasicStoreConfig, StorePlugin):
def open(self, parent=None, detail_item=None, external=False):
-
+
plain_url = 'http://www.legimi.com/pl/ebooki/'
url = 'https://ssl.afiliant.com/affskrypt,,2f9de2,,11483,,,?u=(' + plain_url + ')'
detail_url = None
@@ -42,17 +43,17 @@ class LegimiStore(BasicStoreConfig, StorePlugin):
def search(self, query, max_results=10, timeout=60):
url = 'http://www.legimi.com/pl/ebooki/?szukaj=' + urllib.quote_plus(query)
-
+
br = browser()
drm_pattern = re.compile("zabezpieczona DRM")
-
+
counter = max_results
with closing(br.open(url, timeout=timeout)) as f:
doc = html.fromstring(f.read())
for data in doc.xpath('//div[@id="listBooks"]/div'):
if counter <= 0:
break
-
+
id = ''.join(data.xpath('.//a[@class="plainLink"]/@href'))
if not id:
continue
@@ -73,7 +74,7 @@ class LegimiStore(BasicStoreConfig, StorePlugin):
drm = drm_pattern.search(''.join(idata.xpath('.//div[@id="fullBookFormats"]/p/text()')))
counter -= 1
-
+
s = SearchResult()
s.cover_url = 'http://www.legimi.com/' + cover_url
s.title = title.strip()
@@ -82,5 +83,5 @@ class LegimiStore(BasicStoreConfig, StorePlugin):
s.detail_item = 'http://www.legimi.com/' + id.strip()
s.formats = ', '.join(formats)
s.drm = SearchResult.DRM_LOCKED if drm else SearchResult.DRM_UNLOCKED
-
+
yield s
diff --git a/src/calibre/gui2/store/stores/libri_de_plugin.py b/src/calibre/gui2/store/stores/libri_de_plugin.py
index 60f7471272..d7d0807a99 100644
--- a/src/calibre/gui2/store/stores/libri_de_plugin.py
+++ b/src/calibre/gui2/store/stores/libri_de_plugin.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
+store_version = 1 # Needed for dynamic plugin loading
__license__ = 'GPL 3'
__copyright__ = '2011, John Schember '
diff --git a/src/calibre/gui2/store/stores/litres_plugin.py b/src/calibre/gui2/store/stores/litres_plugin.py
index 6f4c386dda..5a1d2271fe 100644
--- a/src/calibre/gui2/store/stores/litres_plugin.py
+++ b/src/calibre/gui2/store/stores/litres_plugin.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
+store_version = 1 # Needed for dynamic plugin loading
__license__ = 'GPL 3'
__copyright__ = '2011, Roman Mukhin '
diff --git a/src/calibre/gui2/store/stores/manybooks_plugin.py b/src/calibre/gui2/store/stores/manybooks_plugin.py
index 2b06798630..2344193b47 100644
--- a/src/calibre/gui2/store/stores/manybooks_plugin.py
+++ b/src/calibre/gui2/store/stores/manybooks_plugin.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
+store_version = 1 # Needed for dynamic plugin loading
__license__ = 'GPL 3'
__copyright__ = '2011, John Schember '
@@ -26,7 +27,7 @@ class ManyBooksStore(BasicStoreConfig, OpenSearchOPDSStore):
def search(self, query, max_results=10, timeout=60):
'''
Manybooks uses a very strange opds feed. The opds
- main feed is structured like a stanza feed. The
+ main feed is structured like a stanza feed. The
search result entries give very little information
and requires you to go to a detail link. The detail
link has the wrong type specified (text/html instead
@@ -45,7 +46,7 @@ class ManyBooksStore(BasicStoreConfig, OpenSearchOPDSStore):
oquery.searchTerms = query
oquery.count = max_results
url = oquery.url()
-
+
counter = max_results
br = browser()
with closing(br.open(url, timeout=timeout)) as f:
@@ -55,11 +56,11 @@ class ManyBooksStore(BasicStoreConfig, OpenSearchOPDSStore):
for data in doc.xpath('//*[local-name() = "entry"]'):
if counter <= 0:
break
-
+
counter -= 1
-
+
s = SearchResult()
-
+
detail_links = data.xpath('./*[local-name() = "link" and @type = "text/html"]')
if not detail_links:
continue
@@ -73,7 +74,7 @@ class ManyBooksStore(BasicStoreConfig, OpenSearchOPDSStore):
# just in case.
s.title = ''.join(data.xpath('./*[local-name() = "title"]//text()')).strip()
s.author = ', '.join(data.xpath('./*[local-name() = "author"]//text()')).strip()
-
+
# Follow the detail link to get the rest of the info.
with closing(br.open(detail_href, timeout=timeout/4)) as df:
ddoc = etree.fromstring(df.read())
@@ -89,9 +90,9 @@ class ManyBooksStore(BasicStoreConfig, OpenSearchOPDSStore):
s.author = s.author[1:]
if s.author.endswith(','):
s.author = s.author[:-1]
-
+
s.cover_url = ''.join(ddata.xpath('./*[local-name() = "link" and @rel = "http://opds-spec.org/thumbnail"][1]/@href')).strip()
-
+
for link in ddata.xpath('./*[local-name() = "link" and @rel = "http://opds-spec.org/acquisition"]'):
type = link.get('type')
href = link.get('href')
diff --git a/src/calibre/gui2/store/stores/mills_boon_uk_plugin.py b/src/calibre/gui2/store/stores/mills_boon_uk_plugin.py
index 6aa4b4b0b7..b8969beaed 100644
--- a/src/calibre/gui2/store/stores/mills_boon_uk_plugin.py
+++ b/src/calibre/gui2/store/stores/mills_boon_uk_plugin.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
+store_version = 1 # Needed for dynamic plugin loading
__license__ = 'GPL 3'
__copyright__ = '2011, John Schember '
diff --git a/src/calibre/gui2/store/stores/nexto_plugin.py b/src/calibre/gui2/store/stores/nexto_plugin.py
index 79cb1be2f1..e5f9e31980 100644
--- a/src/calibre/gui2/store/stores/nexto_plugin.py
+++ b/src/calibre/gui2/store/stores/nexto_plugin.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
+store_version = 1 # Needed for dynamic plugin loading
__license__ = 'GPL 3'
__copyright__ = '2011-2012, Tomasz Długosz '
diff --git a/src/calibre/gui2/store/stores/nook_uk_plugin.py b/src/calibre/gui2/store/stores/nook_uk_plugin.py
index 1ff8b688bb..cc97d5cf93 100644
--- a/src/calibre/gui2/store/stores/nook_uk_plugin.py
+++ b/src/calibre/gui2/store/stores/nook_uk_plugin.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
+store_version = 1 # Needed for dynamic plugin loading
__license__ = 'GPL 3'
__copyright__ = '2012, John Schember '
diff --git a/src/calibre/gui2/store/stores/open_books_plugin.py b/src/calibre/gui2/store/stores/open_books_plugin.py
index 99b68656e9..66642ab679 100644
--- a/src/calibre/gui2/store/stores/open_books_plugin.py
+++ b/src/calibre/gui2/store/stores/open_books_plugin.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
+store_version = 1 # Needed for dynamic plugin loading
__license__ = 'GPL 3'
__copyright__ = '2011, John Schember '
diff --git a/src/calibre/gui2/store/stores/ozon_ru_plugin.py b/src/calibre/gui2/store/stores/ozon_ru_plugin.py
index b54bf01daf..9a3c2dabaa 100644
--- a/src/calibre/gui2/store/stores/ozon_ru_plugin.py
+++ b/src/calibre/gui2/store/stores/ozon_ru_plugin.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
+store_version = 1 # Needed for dynamic plugin loading
__license__ = 'GPL 3'
__copyright__ = '2011, Roman Mukhin '
@@ -24,33 +25,33 @@ from calibre.gui2.store.web_store_dialog import WebStoreDialog
class OzonRUStore(BasicStoreConfig, StorePlugin):
shop_url = 'http://www.ozon.ru'
-
+
def open(self, parent=None, detail_item=None, external=False):
-
+
aff_id = '?partner=romuk'
# Use Kovid's affiliate id 30% of the time.
if random.randint(1, 10) in (1, 2, 3):
aff_id = '?partner=kovidgoyal'
-
+
url = self.shop_url + aff_id
detail_url = None
if detail_item:
# http://www.ozon.ru/context/detail/id/3037277/
detail_url = self.shop_url + '/context/detail/id/' + urllib2.quote(detail_item) + aff_id
-
+
if external or self.config.get('open_external', False):
open_url(QUrl(url_slash_cleaner(detail_url if detail_url else url)))
else:
d = WebStoreDialog(self.gui, url, parent, detail_url)
d.setWindowTitle(self.name)
d.set_tags(self.config.get('tags', ''))
- d.exec_()
-
+ d.exec_()
+
def search(self, query, max_results=15, timeout=60):
search_url = self.shop_url + '/webservice/webservice.asmx/SearchWebService?'\
'searchText=%s&searchContext=ebook' % urllib2.quote(query)
search_urls = [ search_url ]
-
+
## add this as the fist try if it looks like ozon ID
if re.match("^\d{6,9}$", query):
ozon_detail = self.shop_url + '/webservices/OzonWebSvc.asmx/ItemDetail?ID=%s' % query
@@ -59,7 +60,7 @@ class OzonRUStore(BasicStoreConfig, StorePlugin):
xp_template = 'normalize-space(./*[local-name() = "{0}"]/text())'
counter = max_results
br = browser()
-
+
for url in search_urls:
with closing(br.open(url, timeout=timeout)) as f:
raw = xml_to_unicode(f.read(), strip_encoding_pats=True, assume_utf8=True)[0]
@@ -86,10 +87,10 @@ class OzonRUStore(BasicStoreConfig, StorePlugin):
with closing(br.open(url, timeout=timeout)) as f:
raw = xml_to_unicode(f.read(), verbose=True)[0]
doc = html.fromstring(raw)
-
+
# example where we are going to find formats
#
@@ -104,16 +105,16 @@ class OzonRUStore(BasicStoreConfig, StorePlugin):
search_result.formats = ', '.join(_parse_ebook_formats(formats))
# unfortunately no direct links to download books (only buy link)
# search_result.downloads['BF2'] = self.shop_url + '/order/digitalorder.aspx?id=' + + urllib2.quote(search_result.detail_item)
-
+
#21500 руб.
#215.00
#
-
+
# if the price not in the search result (the ID search case)
if not search_result.price:
price = doc.xpath(u'normalize-space(//*[@itemprop="price"]/text())')
search_result.price = format_price_in_RUR(price)
-
+
return result
def format_price_in_RUR(price):
@@ -134,12 +135,12 @@ def format_price_in_RUR(price):
def _parse_ebook_formats(formatsStr):
'''
Creates a list with displayable names of the formats
-
- :param formatsStr: string with comma separated book formats
+
+ :param formatsStr: string with comma separated book formats
as it provided by ozon.ru
:return: a list with displayable book formats
'''
-
+
formatsUnstruct = formatsStr.lower()
formats = []
if 'epub' in formatsUnstruct:
diff --git a/src/calibre/gui2/store/stores/pragmatic_bookshelf_plugin.py b/src/calibre/gui2/store/stores/pragmatic_bookshelf_plugin.py
index 99b94778bf..544fa06fe8 100644
--- a/src/calibre/gui2/store/stores/pragmatic_bookshelf_plugin.py
+++ b/src/calibre/gui2/store/stores/pragmatic_bookshelf_plugin.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
+store_version = 1 # Needed for dynamic plugin loading
__license__ = 'GPL 3'
__copyright__ = '2011, John Schember '
@@ -14,7 +15,7 @@ class PragmaticBookshelfStore(BasicStoreConfig, OpenSearchOPDSStore):
open_search_url = 'http://pragprog.com/catalog/search-description'
web_url = 'http://pragprog.com/'
-
+
# http://pragprog.com/catalog.opds
def search(self, query, max_results=10, timeout=60):
diff --git a/src/calibre/gui2/store/stores/publio_plugin.py b/src/calibre/gui2/store/stores/publio_plugin.py
index eb00f231ea..44f3267b35 100644
--- a/src/calibre/gui2/store/stores/publio_plugin.py
+++ b/src/calibre/gui2/store/stores/publio_plugin.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
+store_version = 1 # Needed for dynamic plugin loading
__license__ = 'GPL 3'
__copyright__ = '2012, Tomasz Długosz '
diff --git a/src/calibre/gui2/store/stores/rw2010_plugin.py b/src/calibre/gui2/store/stores/rw2010_plugin.py
index ed4d5a53f7..fc86ae4967 100644
--- a/src/calibre/gui2/store/stores/rw2010_plugin.py
+++ b/src/calibre/gui2/store/stores/rw2010_plugin.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
+store_version = 1 # Needed for dynamic plugin loading
__license__ = 'GPL 3'
__copyright__ = '2011, Tomasz Długosz '
@@ -73,5 +74,5 @@ class RW2010Store(BasicStoreConfig, StorePlugin):
s.detail_item = re.sub(r'%3D', '=', id)
s.drm = SearchResult.DRM_UNLOCKED
s.formats = formats[0:-2].upper()
-
+
yield s
diff --git a/src/calibre/gui2/store/stores/smashwords_plugin.py b/src/calibre/gui2/store/stores/smashwords_plugin.py
index 983067ab51..580e3c2907 100644
--- a/src/calibre/gui2/store/stores/smashwords_plugin.py
+++ b/src/calibre/gui2/store/stores/smashwords_plugin.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
+store_version = 1 # Needed for dynamic plugin loading
__license__ = 'GPL 3'
__copyright__ = '2011, John Schember '
@@ -36,7 +37,7 @@ class SmashwordsStore(BasicStoreConfig, StorePlugin):
if detail_item:
detail_url = url + detail_item + aff_id
url = url + aff_id
-
+
if external or self.config.get('open_external', False):
open_url(QUrl(url_slash_cleaner(detail_url if detail_url else url)))
else:
@@ -47,9 +48,9 @@ class SmashwordsStore(BasicStoreConfig, StorePlugin):
def search(self, query, max_results=10, timeout=60):
url = 'http://www.smashwords.com/books/search?query=' + urllib2.quote(query)
-
+
br = browser()
-
+
counter = max_results
with closing(br.open(url, timeout=timeout)) as f:
doc = html.fromstring(f.read())
@@ -57,7 +58,7 @@ class SmashwordsStore(BasicStoreConfig, StorePlugin):
if counter <= 0:
break
data = html.fromstring(html.tostring(data))
-
+
id = None
id_a = data.xpath('//a[@class="bookTitle"]')
if id_a:
@@ -66,14 +67,14 @@ class SmashwordsStore(BasicStoreConfig, StorePlugin):
id = id.split('/')[-1]
if not id:
continue
-
+
cover_url = ''
c_url = data.get('style', None)
if c_url:
mo = re.search(r'http://[^\'"]+', c_url)
if mo:
cover_url = mo.group()
-
+
title = ''.join(data.xpath('//a[@class="bookTitle"]/text()'))
subnote = ''.join(data.xpath('//span[@class="subnote"]/text()'))
author = ''.join(data.xpath('//span[@class="subnote"]//a[1]//text()'))
@@ -85,7 +86,7 @@ class SmashwordsStore(BasicStoreConfig, StorePlugin):
price = '$0.00'
counter -= 1
-
+
s = SearchResult()
s.cover_url = cover_url
s.title = title.strip()
@@ -93,12 +94,12 @@ class SmashwordsStore(BasicStoreConfig, StorePlugin):
s.price = price.strip()
s.detail_item = '/books/view/' + id.strip()
s.drm = SearchResult.DRM_UNLOCKED
-
+
yield s
def get_details(self, search_result, timeout):
url = 'http://www.smashwords.com/'
-
+
br = browser()
with closing(br.open(url + search_result.detail_item, timeout=timeout)) as nf:
idata = html.fromstring(nf.read())
diff --git a/src/calibre/gui2/store/stores/sony_plugin.py b/src/calibre/gui2/store/stores/sony_plugin.py
index aa0c65bcde..030919c925 100644
--- a/src/calibre/gui2/store/stores/sony_plugin.py
+++ b/src/calibre/gui2/store/stores/sony_plugin.py
@@ -2,6 +2,7 @@
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:fdm=marker:ai
from __future__ import (unicode_literals, division, absolute_import,
print_function)
+store_version = 1 # Needed for dynamic plugin loading
__license__ = 'GPL v3'
__copyright__ = '2012, Kovid Goyal '
diff --git a/src/calibre/gui2/store/stores/virtualo_plugin.py b/src/calibre/gui2/store/stores/virtualo_plugin.py
index e6b60fbe91..02396b7f19 100644
--- a/src/calibre/gui2/store/stores/virtualo_plugin.py
+++ b/src/calibre/gui2/store/stores/virtualo_plugin.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
+store_version = 1 # Needed for dynamic plugin loading
__license__ = 'GPL 3'
__copyright__ = '2011, Tomasz Długosz '
diff --git a/src/calibre/gui2/store/stores/waterstones_uk_plugin.py b/src/calibre/gui2/store/stores/waterstones_uk_plugin.py
index df17372d0a..397b8ee53f 100644
--- a/src/calibre/gui2/store/stores/waterstones_uk_plugin.py
+++ b/src/calibre/gui2/store/stores/waterstones_uk_plugin.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
+store_version = 1 # Needed for dynamic plugin loading
__license__ = 'GPL 3'
__copyright__ = '2011, John Schember '
diff --git a/src/calibre/gui2/store/stores/weightless_books_plugin.py b/src/calibre/gui2/store/stores/weightless_books_plugin.py
index 330f3fdf0f..cc18cf5807 100644
--- a/src/calibre/gui2/store/stores/weightless_books_plugin.py
+++ b/src/calibre/gui2/store/stores/weightless_books_plugin.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
+store_version = 1 # Needed for dynamic plugin loading
__license__ = 'GPL 3'
__copyright__ = '2011, John Schember '
@@ -35,9 +36,9 @@ class WeightlessBooksStore(BasicStoreConfig, StorePlugin):
def search(self, query, max_results=10, timeout=60):
url = 'http://weightlessbooks.com/?s=' + urllib.quote_plus(query)
-
+
br = browser()
-
+
counter = max_results
with closing(br.open(url, timeout=timeout)) as f:
doc = html.fromstring(f.read())
@@ -50,20 +51,20 @@ class WeightlessBooksStore(BasicStoreConfig, StorePlugin):
continue
cover_url = ''.join(data.xpath('.//div[@class="cover"]/a/img/@src'))
-
+
price = ''.join(data.xpath('.//div[@class="buy_buttons"]/b[1]/text()'))
if not price:
continue
-
+
formats = ', '.join(data.xpath('.//select[@class="eStore_variation"]//option//text()'))
formats = formats.upper()
-
+
title = ''.join(data.xpath('.//h3/a/text()'))
author = ''.join(data.xpath('.//h3//text()'))
author = author.replace(title, '')
counter -= 1
-
+
s = SearchResult()
s.cover_url = cover_url
s.title = title.strip()
@@ -72,5 +73,5 @@ class WeightlessBooksStore(BasicStoreConfig, StorePlugin):
s.detail_item = id.strip()
s.drm = SearchResult.DRM_UNLOCKED
s.formats = formats
-
+
yield s
diff --git a/src/calibre/gui2/store/stores/whsmith_uk_plugin.py b/src/calibre/gui2/store/stores/whsmith_uk_plugin.py
index 5d78340517..6f2de93523 100644
--- a/src/calibre/gui2/store/stores/whsmith_uk_plugin.py
+++ b/src/calibre/gui2/store/stores/whsmith_uk_plugin.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
+store_version = 1 # Needed for dynamic plugin loading
__license__ = 'GPL 3'
__copyright__ = '2011, John Schember '
diff --git a/src/calibre/gui2/store/stores/woblink_plugin.py b/src/calibre/gui2/store/stores/woblink_plugin.py
index 37861766f7..63ec259dbf 100644
--- a/src/calibre/gui2/store/stores/woblink_plugin.py
+++ b/src/calibre/gui2/store/stores/woblink_plugin.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
+store_version = 1 # Needed for dynamic plugin loading
__license__ = 'GPL 3'
__copyright__ = '2011-2012, Tomasz Długosz '
diff --git a/src/calibre/gui2/store/stores/xinxii_plugin.py b/src/calibre/gui2/store/stores/xinxii_plugin.py
index e8721a79b8..4be0a410be 100644
--- a/src/calibre/gui2/store/stores/xinxii_plugin.py
+++ b/src/calibre/gui2/store/stores/xinxii_plugin.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
+store_version = 1 # Needed for dynamic plugin loading
__license__ = 'GPL 3'
__copyright__ = '2011, John Schember '
@@ -20,25 +21,25 @@ class XinXiiStore(BasicStoreConfig, OpenSearchOPDSStore):
open_search_url = 'http://www.xinxii.com/catalog-search/'
web_url = 'http://xinxii.com/'
-
+
# http://www.xinxii.com/catalog/
def search(self, query, max_results=10, timeout=60):
'''
XinXii's open search url is:
http://www.xinxii.com/catalog-search/query/?keywords={searchTerms}&pw={startPage?}&doc_lang={docLang}&ff={docFormat},{docFormat},{docFormat}
-
+
This url requires the docLang and docFormat. However, the search itself
sent to XinXii does not require them. They can be ignored. We cannot
push this into the stanard OpenSearchOPDSStore search because of the
required attributes.
-
+
XinXii doesn't return all info supported by OpenSearchOPDSStore search
function so this one is modified to remove parts that are used.
'''
-
+
url = 'http://www.xinxii.com/catalog-search/query/?keywords=' + urllib.quote_plus(query)
-
+
counter = max_results
br = browser()
with closing(br.open(url, timeout=timeout)) as f:
@@ -46,29 +47,29 @@ class XinXiiStore(BasicStoreConfig, OpenSearchOPDSStore):
for data in doc.xpath('//*[local-name() = "entry"]'):
if counter <= 0:
break
-
+
counter -= 1
-
+
s = SearchResult()
-
+
s.detail_item = ''.join(data.xpath('./*[local-name() = "id"]/text()')).strip()
for link in data.xpath('./*[local-name() = "link"]'):
rel = link.get('rel')
href = link.get('href')
type = link.get('type')
-
+
if rel and href and type:
if rel in ('http://opds-spec.org/thumbnail', 'http://opds-spec.org/image/thumbnail'):
s.cover_url = href
if rel == 'alternate':
s.detail_item = href
-
+
s.formats = 'EPUB, PDF'
-
+
s.title = ' '.join(data.xpath('./*[local-name() = "title"]//text()')).strip()
s.author = ', '.join(data.xpath('./*[local-name() = "author"]//*[local-name() = "name"]//text()')).strip()
-
+
price_e = data.xpath('.//*[local-name() = "price"][1]')
if price_e:
price_e = price_e[0]
@@ -76,6 +77,6 @@ class XinXiiStore(BasicStoreConfig, OpenSearchOPDSStore):
price = ''.join(price_e.xpath('.//text()')).strip()
s.price = currency_code + ' ' + price
s.price = s.price.strip()
-
+
yield s
diff --git a/src/calibre/gui2/store/stores/zixo_plugin.py b/src/calibre/gui2/store/stores/zixo_plugin.py
index b4e54736c0..98bbdf3155 100644
--- a/src/calibre/gui2/store/stores/zixo_plugin.py
+++ b/src/calibre/gui2/store/stores/zixo_plugin.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
+store_version = 1 # Needed for dynamic plugin loading
__license__ = 'GPL 3'
__copyright__ = '2011, Tomasz Długosz '
diff --git a/src/calibre/gui2/ui.py b/src/calibre/gui2/ui.py
index db64969179..65993ff31c 100644
--- a/src/calibre/gui2/ui.py
+++ b/src/calibre/gui2/ui.py
@@ -155,7 +155,8 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
acmap[ac.name] = ac
def load_store_plugins(self):
- self.istores = OrderedDict()
+ from calibre.gui2.store.loader import Stores
+ self.istores = Stores()
for store in available_store_plugins():
if self.opts.ignore_plugins and store.plugin_path is not None:
continue
@@ -169,6 +170,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
if store.plugin_path is None:
raise
continue
+ self.istores.builtins_loaded()
def init_istore(self, store):
st = store.load_actual_plugin(self)
@@ -790,6 +792,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
except KeyboardInterrupt:
pass
time.sleep(2)
+ self.istores.join()
self.hide_windows()
# Do not report any errors that happen after the shutdown
sys.excepthook = sys.__excepthook__
diff --git a/src/calibre/gui2/wizard/__init__.py b/src/calibre/gui2/wizard/__init__.py
index 208a986888..1cdcb85d4c 100644
--- a/src/calibre/gui2/wizard/__init__.py
+++ b/src/calibre/gui2/wizard/__init__.py
@@ -245,6 +245,13 @@ class PocketBook900(PocketBook):
id = 'pocketbook900'
output_profile = 'pocketbook_900'
+class PocketBookPro912(PocketBook):
+
+ name = 'PocketBook Pro 912'
+ id = 'pocketbookpro912'
+ output_profile = 'pocketbook_pro_912'
+
+
class iPhone(Device):
name = 'iPhone/iTouch'