mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-06-23 15:30:45 -04:00
Merge from trunk
This commit is contained in:
commit
ae34d84539
@ -12,10 +12,10 @@ class Chronicle(BasicNewsRecipe):
|
|||||||
category = 'news'
|
category = 'news'
|
||||||
encoding = 'UTF-8'
|
encoding = 'UTF-8'
|
||||||
keep_only_tags = [
|
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']}),
|
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']}),
|
dict(name='div', attrs={'id':['section-nav','icon-row', 'enlarge-popup','confirm-popup']}),
|
||||||
dict(name='a', attrs={'class':'show-enlarge enlarge'})]
|
dict(name='a', attrs={'class':'show-enlarge enlarge'})]
|
||||||
no_javascript = True
|
no_javascript = True
|
||||||
no_stylesheets = True
|
no_stylesheets = True
|
||||||
|
@ -124,19 +124,19 @@ class NYTimes(BasicNewsRecipe):
|
|||||||
if headlinesOnly:
|
if headlinesOnly:
|
||||||
title='New York Times Headlines'
|
title='New York Times Headlines'
|
||||||
description = 'Headlines from the New York Times'
|
description = 'Headlines from the New York Times'
|
||||||
needs_subscription = False
|
needs_subscription = True
|
||||||
elif webEdition:
|
elif webEdition:
|
||||||
title='New York Times (Web)'
|
title='New York Times (Web)'
|
||||||
description = 'New York Times on the Web'
|
description = 'New York Times on the Web'
|
||||||
needs_subscription = False
|
needs_subscription = True
|
||||||
elif replaceKindleVersion:
|
elif replaceKindleVersion:
|
||||||
title='The New York Times'
|
title='The New York Times'
|
||||||
description = 'Today\'s New York Times'
|
description = 'Today\'s New York Times'
|
||||||
needs_subscription = False
|
needs_subscription = True
|
||||||
else:
|
else:
|
||||||
title='New York Times'
|
title='New York Times'
|
||||||
description = 'Today\'s New York Times'
|
description = 'Today\'s New York Times'
|
||||||
needs_subscription = False
|
needs_subscription = True
|
||||||
|
|
||||||
def decode_url_date(self,url):
|
def decode_url_date(self,url):
|
||||||
urlitems = url.split('/')
|
urlitems = url.split('/')
|
||||||
@ -359,6 +359,14 @@ class NYTimes(BasicNewsRecipe):
|
|||||||
|
|
||||||
def get_browser(self):
|
def get_browser(self):
|
||||||
br = BasicNewsRecipe.get_browser()
|
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
|
return br
|
||||||
|
|
||||||
cover_tag = 'NY_NYT'
|
cover_tag = 'NY_NYT'
|
||||||
|
@ -48,10 +48,14 @@ class Smithsonian(BasicNewsRecipe):
|
|||||||
link=post.find('a',href=True)
|
link=post.find('a',href=True)
|
||||||
url=link['href']+'?c=y&story=fullstory'
|
url=link['href']+'?c=y&story=fullstory'
|
||||||
if subsection is not None:
|
if subsection is not None:
|
||||||
subsection_title = self.tag_to_string(subsection)
|
subsection_title = self.tag_to_string(subsection).strip()
|
||||||
prefix = (subsection_title+': ')
|
prefix = (subsection_title+': ')
|
||||||
description=self.tag_to_string(post('p', limit=2)[1]).strip()
|
description=self.tag_to_string(post('p', limit=2)[1]).strip()
|
||||||
else:
|
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()
|
description=self.tag_to_string(post.find('p')).strip()
|
||||||
desc=re.sub('\sBy\s.*', '', description, re.DOTALL)
|
desc=re.sub('\sBy\s.*', '', description, re.DOTALL)
|
||||||
author=re.sub('.*By\s', '', description, re.DOTALL)
|
author=re.sub('.*By\s', '', description, re.DOTALL)
|
||||||
@ -64,4 +68,3 @@ class Smithsonian(BasicNewsRecipe):
|
|||||||
feeds[section_title] += articles
|
feeds[section_title] += articles
|
||||||
ans = [(key, val) for key, val in feeds.iteritems()]
|
ans = [(key, val) for key, val in feeds.iteritems()]
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
|
Binary file not shown.
@ -770,13 +770,25 @@ class PocketBook900Output(OutputProfile):
|
|||||||
dpi = 150.0
|
dpi = 150.0
|
||||||
comic_screen_size = screen_size
|
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,
|
output_profiles = [OutputProfile, SonyReaderOutput, SonyReader300Output,
|
||||||
SonyReader900Output, MSReaderOutput, MobipocketOutput, HanlinV3Output,
|
SonyReader900Output, MSReaderOutput, MobipocketOutput, HanlinV3Output,
|
||||||
HanlinV5Output, CybookG3Output, CybookOpusOutput, KindleOutput,
|
HanlinV5Output, CybookG3Output, CybookOpusOutput, KindleOutput,
|
||||||
iPadOutput, iPad3Output, KoboReaderOutput, TabletOutput, SamsungGalaxy,
|
iPadOutput, iPad3Output, KoboReaderOutput, TabletOutput, SamsungGalaxy,
|
||||||
SonyReaderLandscapeOutput, KindleDXOutput, IlliadOutput,
|
SonyReaderLandscapeOutput, KindleDXOutput, IlliadOutput,
|
||||||
IRexDR1000Output, IRexDR800Output, JetBook5Output, NookOutput,
|
IRexDR1000Output, IRexDR800Output, JetBook5Output, NookOutput,
|
||||||
BambookOutput, NookColorOutput, PocketBook900Output, GenericEink,
|
BambookOutput, NookColorOutput, PocketBook900Output, PocketBookPro912Output,
|
||||||
GenericEinkLarge, KindleFireOutput, KindlePaperWhiteOutput]
|
GenericEink, GenericEinkLarge, KindleFireOutput, KindlePaperWhiteOutput]
|
||||||
|
|
||||||
output_profiles.sort(cmp=lambda x,y:cmp(x.name.lower(), y.name.lower()))
|
output_profiles.sort(cmp=lambda x,y:cmp(x.name.lower(), y.name.lower()))
|
||||||
|
@ -234,7 +234,7 @@ class POCKETBOOK301(USBMS):
|
|||||||
class POCKETBOOK602(USBMS):
|
class POCKETBOOK602(USBMS):
|
||||||
|
|
||||||
name = 'PocketBook Pro 602/902 Device Interface'
|
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'
|
author = 'Kovid Goyal'
|
||||||
supported_platforms = ['windows', 'osx', 'linux']
|
supported_platforms = ['windows', 'osx', 'linux']
|
||||||
FORMATS = ['epub', 'fb2', 'prc', 'mobi', 'pdf', 'djvu', 'rtf', 'chm',
|
FORMATS = ['epub', 'fb2', 'prc', 'mobi', 'pdf', 'djvu', 'rtf', 'chm',
|
||||||
@ -249,7 +249,7 @@ class POCKETBOOK602(USBMS):
|
|||||||
|
|
||||||
VENDOR_NAME = ''
|
VENDOR_NAME = ''
|
||||||
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = ['PB602', 'PB603', 'PB902',
|
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = ['PB602', 'PB603', 'PB902',
|
||||||
'PB903', 'PB']
|
'PB903', 'Pocket912', 'PB']
|
||||||
|
|
||||||
class POCKETBOOK622(POCKETBOOK602):
|
class POCKETBOOK622(POCKETBOOK602):
|
||||||
|
|
||||||
|
@ -11,13 +11,17 @@ import struct, os, functools, re
|
|||||||
from urlparse import urldefrag
|
from urlparse import urldefrag
|
||||||
from cStringIO import StringIO
|
from cStringIO import StringIO
|
||||||
from urllib import unquote as urlunquote
|
from urllib import unquote as urlunquote
|
||||||
|
|
||||||
|
from lxml import etree
|
||||||
|
|
||||||
from calibre.ebooks.lit import LitError
|
from calibre.ebooks.lit import LitError
|
||||||
from calibre.ebooks.lit.maps import OPF_MAP, HTML_MAP
|
from calibre.ebooks.lit.maps import OPF_MAP, HTML_MAP
|
||||||
import calibre.ebooks.lit.mssha1 as mssha1
|
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.oeb.reader import OEBReader
|
||||||
from calibre.ebooks import DRMError
|
from calibre.ebooks import DRMError
|
||||||
from calibre import plugins
|
from calibre import plugins
|
||||||
|
|
||||||
lzx, lxzerror = plugins['lzx']
|
lzx, lxzerror = plugins['lzx']
|
||||||
msdes, msdeserror = plugins['msdes']
|
msdes, msdeserror = plugins['msdes']
|
||||||
|
|
||||||
@ -907,3 +911,16 @@ class LitReader(OEBReader):
|
|||||||
Container = LitContainer
|
Container = LitContainer
|
||||||
DEFAULT_PROFILE = 'MSReader'
|
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)
|
||||||
|
|
||||||
|
@ -390,6 +390,10 @@ class MetadataUpdater(object):
|
|||||||
not added_501 and not share_not_sync):
|
not added_501 and not share_not_sync):
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
update_exth_record((113, str(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:
|
if 503 in self.original_exth_records:
|
||||||
update_exth_record((503, mi.title.encode(self.codec, 'replace')))
|
update_exth_record((503, mi.title.encode(self.codec, 'replace')))
|
||||||
|
|
||||||
|
@ -110,6 +110,12 @@ def build_exth(metadata, prefer_author_sort=False, is_periodical=False,
|
|||||||
exth.write(uuid)
|
exth.write(uuid)
|
||||||
nrecs += 1
|
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
|
# Write cdetype
|
||||||
if not is_periodical:
|
if not is_periodical:
|
||||||
if not share_not_sync:
|
if not share_not_sync:
|
||||||
|
@ -43,14 +43,16 @@ class StoreAction(InterfaceAction):
|
|||||||
icon.addFile(I('donate.png'), QSize(16, 16))
|
icon.addFile(I('donate.png'), QSize(16, 16))
|
||||||
for n, p in sorted(self.gui.istores.items(), key=lambda x: x[0].lower()):
|
for n, p in sorted(self.gui.istores.items(), key=lambda x: x[0].lower()):
|
||||||
if p.base_plugin.affiliate:
|
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:
|
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):
|
def do_search(self):
|
||||||
return self.search()
|
return self.search()
|
||||||
|
|
||||||
def search(self, query=''):
|
def search(self, query=''):
|
||||||
|
self.gui.istores.check_for_updates()
|
||||||
self.show_disclaimer()
|
self.show_disclaimer()
|
||||||
from calibre.gui2.store.search.search import SearchDialog
|
from calibre.gui2.store.search.search import SearchDialog
|
||||||
sd = SearchDialog(self.gui, self.gui, query)
|
sd = SearchDialog(self.gui, self.gui, query)
|
||||||
@ -125,9 +127,13 @@ class StoreAction(InterfaceAction):
|
|||||||
self.gui.load_store_plugins()
|
self.gui.load_store_plugins()
|
||||||
self.load_menu()
|
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()
|
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):
|
def show_disclaimer(self):
|
||||||
confirm(('<p>' +
|
confirm(('<p>' +
|
||||||
|
@ -383,12 +383,14 @@ class Series(Base):
|
|||||||
values = list(self.db.all_custom(num=self.col_id))
|
values = list(self.db.all_custom(num=self.col_id))
|
||||||
values.sort(key=sort_key)
|
values.sort(key=sort_key)
|
||||||
val = self.db.get_custom(book_id, num=self.col_id, index_is_id=True)
|
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
|
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)
|
val = self.normalize_db_val(val)
|
||||||
self.name_widget.blockSignals(True)
|
self.name_widget.blockSignals(True)
|
||||||
self.name_widget.update_items_cache(values)
|
self.name_widget.update_items_cache(values)
|
||||||
|
@ -49,13 +49,16 @@ class StorePlugin(object): # {{{
|
|||||||
See declined.txt for a list of stores that do not want to be included.
|
See declined.txt for a list of stores that do not want to be included.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def __init__(self, gui, name):
|
minimum_calibre_version = (0, 9, 14)
|
||||||
from calibre.gui2 import JSONConfig
|
|
||||||
|
|
||||||
|
def __init__(self, gui, name, config=None, base_plugin=None):
|
||||||
self.gui = gui
|
self.gui = gui
|
||||||
self.name = name
|
self.name = name
|
||||||
self.base_plugin = None
|
self.base_plugin = base_plugin
|
||||||
self.config = JSONConfig('store/stores/' + ascii_filename(self.name))
|
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):
|
def open(self, gui, parent=None, detail_item=None, external=False):
|
||||||
'''
|
'''
|
||||||
|
197
src/calibre/gui2/store/loader.py
Normal file
197
src/calibre/gui2/store/loader.py
Normal file
@ -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 <kovid at kovidgoyal.net>'
|
||||||
|
__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))
|
||||||
|
|
||||||
|
|
@ -194,6 +194,7 @@ class SearchDialog(QDialog, Ui_Dialog):
|
|||||||
query = self.clean_query(query)
|
query = self.clean_query(query)
|
||||||
shuffle(store_names)
|
shuffle(store_names)
|
||||||
# Add plugins that the user has checked to the search pool's work queue.
|
# 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:
|
for n in store_names:
|
||||||
if self.store_checks[n].isChecked():
|
if self.store_checks[n].isChecked():
|
||||||
self.search_pool.add_task(query, n, self.gui.istores[n], self.max_results, self.timeout)
|
self.search_pool.add_task(query, n, self.gui.istores[n], self.max_results, self.timeout)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
store_version = 1 # Needed for dynamic plugin loading
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
store_version = 1 # Needed for dynamic plugin loading
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
||||||
@ -21,4 +22,4 @@ class AmazonESKindleStore(AmazonUKKindleStore):
|
|||||||
'&linkCode=ur2&camp=3626&creative=24790')
|
'&linkCode=ur2&camp=3626&creative=24790')
|
||||||
search_url = 'http://www.amazon.es/s/?url=search-alias%3Ddigital-text&field-keywords='
|
search_url = 'http://www.amazon.es/s/?url=search-alias%3Ddigital-text&field-keywords='
|
||||||
|
|
||||||
author_article = 'de '
|
author_article = 'de '
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
store_version = 1 # Needed for dynamic plugin loading
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
store_version = 1 # Needed for dynamic plugin loading
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
||||||
@ -21,4 +22,4 @@ class AmazonITKindleStore(AmazonUKKindleStore):
|
|||||||
'linkCode=ur2&camp=3370&creative=23322')
|
'linkCode=ur2&camp=3370&creative=23322')
|
||||||
search_url = 'http://www.amazon.it/s/?url=search-alias%3Ddigital-text&field-keywords='
|
search_url = 'http://www.amazon.it/s/?url=search-alias%3Ddigital-text&field-keywords='
|
||||||
|
|
||||||
author_article = 'di '
|
author_article = 'di '
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
store_version = 1 # Needed for dynamic plugin loading
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
store_version = 1 # Needed for dynamic plugin loading
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
store_version = 1 # Needed for dynamic plugin loading
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
store_version = 1 # Needed for dynamic plugin loading
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
||||||
@ -22,7 +23,7 @@ from calibre.gui2.store.search_result import SearchResult
|
|||||||
from calibre.gui2.store.web_store_dialog import WebStoreDialog
|
from calibre.gui2.store.web_store_dialog import WebStoreDialog
|
||||||
|
|
||||||
class BaenWebScriptionStore(BasicStoreConfig, StorePlugin):
|
class BaenWebScriptionStore(BasicStoreConfig, StorePlugin):
|
||||||
|
|
||||||
def open(self, parent=None, detail_item=None, external=False):
|
def open(self, parent=None, detail_item=None, external=False):
|
||||||
url = 'http://www.baenebooks.com/'
|
url = 'http://www.baenebooks.com/'
|
||||||
|
|
||||||
@ -41,26 +42,26 @@ class BaenWebScriptionStore(BasicStoreConfig, StorePlugin):
|
|||||||
|
|
||||||
def search(self, query, max_results=10, timeout=60):
|
def search(self, query, max_results=10, timeout=60):
|
||||||
url = 'http://www.baenebooks.com/searchadv.aspx?IsSubmit=true&SearchTerm=' + urllib2.quote(query)
|
url = 'http://www.baenebooks.com/searchadv.aspx?IsSubmit=true&SearchTerm=' + urllib2.quote(query)
|
||||||
|
|
||||||
br = browser()
|
br = browser()
|
||||||
|
|
||||||
counter = max_results
|
counter = max_results
|
||||||
with closing(br.open(url, timeout=timeout)) as f:
|
with closing(br.open(url, timeout=timeout)) as f:
|
||||||
doc = html.fromstring(f.read())
|
doc = html.fromstring(f.read())
|
||||||
for data in doc.xpath('//table//table//table//table//tr'):
|
for data in doc.xpath('//table//table//table//table//tr'):
|
||||||
if counter <= 0:
|
if counter <= 0:
|
||||||
break
|
break
|
||||||
|
|
||||||
id = ''.join(data.xpath('./td[1]/a/@href'))
|
id = ''.join(data.xpath('./td[1]/a/@href'))
|
||||||
if not id or not id.startswith('p-'):
|
if not id or not id.startswith('p-'):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
title = ''.join(data.xpath('./td[1]/a/text()'))
|
title = ''.join(data.xpath('./td[1]/a/text()'))
|
||||||
|
|
||||||
author = ''
|
author = ''
|
||||||
cover_url = ''
|
cover_url = ''
|
||||||
price = ''
|
price = ''
|
||||||
|
|
||||||
with closing(br.open('http://www.baenebooks.com/' + id.strip(), timeout=timeout/4)) as nf:
|
with closing(br.open('http://www.baenebooks.com/' + id.strip(), timeout=timeout/4)) as nf:
|
||||||
idata = html.fromstring(nf.read())
|
idata = html.fromstring(nf.read())
|
||||||
author = ''.join(idata.xpath('//span[@class="ProductNameText"]/../b/text()'))
|
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()'))
|
price = ''.join(idata.xpath('//span[@class="variantprice"]/text()'))
|
||||||
a, b, price = price.partition('$')
|
a, b, price = price.partition('$')
|
||||||
price = b + price
|
price = b + price
|
||||||
|
|
||||||
pnum = ''
|
pnum = ''
|
||||||
mo = re.search(r'p-(?P<num>\d+)-', id.strip())
|
mo = re.search(r'p-(?P<num>\d+)-', id.strip())
|
||||||
if mo:
|
if mo:
|
||||||
pnum = mo.group('num')
|
pnum = mo.group('num')
|
||||||
if pnum:
|
if pnum:
|
||||||
cover_url = 'http://www.baenebooks.com/' + ''.join(idata.xpath('//img[@id="ProductPic%s"]/@src' % pnum))
|
cover_url = 'http://www.baenebooks.com/' + ''.join(idata.xpath('//img[@id="ProductPic%s"]/@src' % pnum))
|
||||||
|
|
||||||
counter -= 1
|
counter -= 1
|
||||||
|
|
||||||
s = SearchResult()
|
s = SearchResult()
|
||||||
s.cover_url = cover_url
|
s.cover_url = cover_url
|
||||||
s.title = title.strip()
|
s.title = title.strip()
|
||||||
@ -86,5 +87,5 @@ class BaenWebScriptionStore(BasicStoreConfig, StorePlugin):
|
|||||||
s.detail_item = id.strip()
|
s.detail_item = id.strip()
|
||||||
s.drm = SearchResult.DRM_UNLOCKED
|
s.drm = SearchResult.DRM_UNLOCKED
|
||||||
s.formats = 'RB, MOBI, EPUB, LIT, LRF, RTF, HTML'
|
s.formats = 'RB, MOBI, EPUB, LIT, LRF, RTF, HTML'
|
||||||
|
|
||||||
yield s
|
yield s
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
store_version = 1 # Needed for dynamic plugin loading
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
||||||
@ -71,7 +72,7 @@ class BeWriteStore(BasicStoreConfig, StorePlugin):
|
|||||||
|
|
||||||
with closing(br.open(search_result.detail_item, timeout=timeout)) as nf:
|
with closing(br.open(search_result.detail_item, timeout=timeout)) as nf:
|
||||||
idata = html.fromstring(nf.read())
|
idata = html.fromstring(nf.read())
|
||||||
|
|
||||||
price = ''.join(idata.xpath('//div[@id="content"]//td[contains(text(), "ePub")]/text()'))
|
price = ''.join(idata.xpath('//div[@id="content"]//td[contains(text(), "ePub")]/text()'))
|
||||||
if not price:
|
if not price:
|
||||||
price = ''.join(idata.xpath('//div[@id="content"]//td[contains(text(), "MOBI")]/text()'))
|
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 = ''.join(idata.xpath('//div[@id="content"]//td[contains(text(), "PDF")]/text()'))
|
||||||
price = '$' + price.split('$')[-1]
|
price = '$' + price.split('$')[-1]
|
||||||
search_result.price = price.strip()
|
search_result.price = price.strip()
|
||||||
|
|
||||||
cover_img = idata.xpath('//div[@id="content"]//img/@src')
|
cover_img = idata.xpath('//div[@id="content"]//img/@src')
|
||||||
if cover_img:
|
if cover_img:
|
||||||
for i in cover_img:
|
for i in cover_img:
|
||||||
@ -87,7 +88,7 @@ class BeWriteStore(BasicStoreConfig, StorePlugin):
|
|||||||
cover_url = 'http://www.bewrite.net/mm5/' + i
|
cover_url = 'http://www.bewrite.net/mm5/' + i
|
||||||
search_result.cover_url = cover_url.strip()
|
search_result.cover_url = cover_url.strip()
|
||||||
break
|
break
|
||||||
|
|
||||||
formats = set([])
|
formats = set([])
|
||||||
if idata.xpath('boolean(//div[@id="content"]//td[contains(text(), "ePub")])'):
|
if idata.xpath('boolean(//div[@id="content"]//td[contains(text(), "ePub")])'):
|
||||||
formats.add('EPUB')
|
formats.add('EPUB')
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
store_version = 1 # Needed for dynamic plugin loading
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2012, Alex Stanev <alex@stanev.org>'
|
__copyright__ = '2012, Alex Stanev <alex@stanev.org>'
|
||||||
@ -26,7 +27,7 @@ class BiblioStore(BasicStoreConfig, OpenSearchOPDSStore):
|
|||||||
|
|
||||||
for s in OpenSearchOPDSStore.search(self, query, max_results, timeout):
|
for s in OpenSearchOPDSStore.search(self, query, max_results, timeout):
|
||||||
yield s
|
yield s
|
||||||
|
|
||||||
def get_details(self, search_result, timeout):
|
def get_details(self, search_result, timeout):
|
||||||
# get format and DRM status
|
# get format and DRM status
|
||||||
from calibre import browser
|
from calibre import browser
|
||||||
@ -39,13 +40,13 @@ class BiblioStore(BasicStoreConfig, OpenSearchOPDSStore):
|
|||||||
search_result.formats = ''
|
search_result.formats = ''
|
||||||
if idata.xpath('.//span[@class="format epub"]'):
|
if idata.xpath('.//span[@class="format epub"]'):
|
||||||
search_result.formats = 'EPUB'
|
search_result.formats = 'EPUB'
|
||||||
|
|
||||||
if idata.xpath('.//span[@class="format pdf"]'):
|
if idata.xpath('.//span[@class="format pdf"]'):
|
||||||
if search_result.formats == '':
|
if search_result.formats == '':
|
||||||
search_result.formats = 'PDF'
|
search_result.formats = 'PDF'
|
||||||
else:
|
else:
|
||||||
search_result.formats.join(', PDF')
|
search_result.formats.join(', PDF')
|
||||||
|
|
||||||
if idata.xpath('.//span[@class="format nodrm-icon"]'):
|
if idata.xpath('.//span[@class="format nodrm-icon"]'):
|
||||||
search_result.drm = SearchResult.DRM_UNLOCKED
|
search_result.drm = SearchResult.DRM_UNLOCKED
|
||||||
else:
|
else:
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
store_version = 1 # Needed for dynamic plugin loading
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
store_version = 1 # Needed for dynamic plugin loading
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2011, Tomasz Długosz <tomek3d@gmail.com>'
|
__copyright__ = '2011, Tomasz Długosz <tomek3d@gmail.com>'
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
store_version = 1 # Needed for dynamic plugin loading
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2011, Alex Stanev <alex@stanev.org>'
|
__copyright__ = '2011, Alex Stanev <alex@stanev.org>'
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
store_version = 1 # Needed for dynamic plugin loading
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
store_version = 1 # Needed for dynamic plugin loading
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
store_version = 1 # Needed for dynamic plugin loading
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2011-2012, Tomasz Długosz <tomek3d@gmail.com>'
|
__copyright__ = '2011-2012, Tomasz Długosz <tomek3d@gmail.com>'
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
store_version = 1 # Needed for dynamic plugin loading
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
|
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
store_version = 1 # Needed for dynamic plugin loading
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2012, Florent FAYOLLE <florent.fayolle69@gmail.com>'
|
__copyright__ = '2012, Florent FAYOLLE <florent.fayolle69@gmail.com>'
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
store_version = 1 # Needed for dynamic plugin loading
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
store_version = 1 # Needed for dynamic plugin loading
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
||||||
@ -36,9 +37,9 @@ class EHarlequinStore(BasicStoreConfig, StorePlugin):
|
|||||||
|
|
||||||
def search(self, query, max_results=10, timeout=60):
|
def search(self, query, max_results=10, timeout=60):
|
||||||
url = 'http://ebooks.eharlequin.com/BANGSearch.dll?Type=FullText&FullTextField=All&FullTextCriteria=' + urllib2.quote(query)
|
url = 'http://ebooks.eharlequin.com/BANGSearch.dll?Type=FullText&FullTextField=All&FullTextCriteria=' + urllib2.quote(query)
|
||||||
|
|
||||||
br = browser()
|
br = browser()
|
||||||
|
|
||||||
counter = max_results
|
counter = max_results
|
||||||
with closing(br.open(url, timeout=timeout)) as f:
|
with closing(br.open(url, timeout=timeout)) as f:
|
||||||
doc = html.fromstring(f.read())
|
doc = html.fromstring(f.read())
|
||||||
@ -64,19 +65,19 @@ class EHarlequinStore(BasicStoreConfig, StorePlugin):
|
|||||||
s.price = price.strip()
|
s.price = price.strip()
|
||||||
s.detail_item = 'http://ebooks.eharlequin.com/' + id.strip()
|
s.detail_item = 'http://ebooks.eharlequin.com/' + id.strip()
|
||||||
s.formats = 'EPUB'
|
s.formats = 'EPUB'
|
||||||
|
|
||||||
yield s
|
yield s
|
||||||
|
|
||||||
def get_details(self, search_result, timeout):
|
def get_details(self, search_result, timeout):
|
||||||
url = 'http://ebooks.eharlequin.com/en/ContentDetails.htm?ID='
|
url = 'http://ebooks.eharlequin.com/en/ContentDetails.htm?ID='
|
||||||
|
|
||||||
mo = re.search(r'\?ID=(?P<id>.+)', search_result.detail_item)
|
mo = re.search(r'\?ID=(?P<id>.+)', search_result.detail_item)
|
||||||
if mo:
|
if mo:
|
||||||
id = mo.group('id')
|
id = mo.group('id')
|
||||||
if not id:
|
if not id:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
br = browser()
|
br = browser()
|
||||||
with closing(br.open(url + id, timeout=timeout)) as nf:
|
with closing(br.open(url + id, timeout=timeout)) as nf:
|
||||||
idata = html.fromstring(nf.read())
|
idata = html.fromstring(nf.read())
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
store_version = 1 # Needed for dynamic plugin loading
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2011, Alex Stanev <alex@stanev.org>'
|
__copyright__ = '2011, Alex Stanev <alex@stanev.org>'
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
store_version = 1 # Needed for dynamic plugin loading
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2011-2012, Tomasz Długosz <tomek3d@gmail.com>'
|
__copyright__ = '2011-2012, Tomasz Długosz <tomek3d@gmail.com>'
|
||||||
@ -68,7 +69,7 @@ class EmpikStore(BasicStoreConfig, StorePlugin):
|
|||||||
counter -= 1
|
counter -= 1
|
||||||
|
|
||||||
s = SearchResult()
|
s = SearchResult()
|
||||||
s.cover_url = cover_url
|
s.cover_url = cover_url
|
||||||
s.title = title.strip() + ' ' + formats
|
s.title = title.strip() + ' ' + formats
|
||||||
s.author = author.strip()
|
s.author = author.strip()
|
||||||
s.price = price
|
s.price = price
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
store_version = 1 # Needed for dynamic plugin loading
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2011, Tomasz Długosz <tomek3d@gmail.com>'
|
__copyright__ = '2011, Tomasz Długosz <tomek3d@gmail.com>'
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
store_version = 1 # Needed for dynamic plugin loading
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
||||||
@ -11,10 +12,10 @@ from calibre.gui2.store.opensearch_store import OpenSearchOPDSStore
|
|||||||
from calibre.gui2.store.search_result import SearchResult
|
from calibre.gui2.store.search_result import SearchResult
|
||||||
|
|
||||||
class FeedbooksStore(BasicStoreConfig, OpenSearchOPDSStore):
|
class FeedbooksStore(BasicStoreConfig, OpenSearchOPDSStore):
|
||||||
|
|
||||||
open_search_url = 'http://assets0.feedbooks.net/opensearch.xml?t=1253087147'
|
open_search_url = 'http://assets0.feedbooks.net/opensearch.xml?t=1253087147'
|
||||||
web_url = 'http://feedbooks.com/'
|
web_url = 'http://feedbooks.com/'
|
||||||
|
|
||||||
# http://www.feedbooks.com/catalog
|
# http://www.feedbooks.com/catalog
|
||||||
|
|
||||||
def search(self, query, max_results=10, timeout=60):
|
def search(self, query, max_results=10, timeout=60):
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
store_version = 1 # Needed for dynamic plugin loading
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
store_version = 1 # Needed for dynamic plugin loading
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
||||||
@ -38,7 +39,7 @@ class GoogleBooksStore(BasicStoreConfig, StorePlugin):
|
|||||||
'ganpub': 'k352583',
|
'ganpub': 'k352583',
|
||||||
'ganclk': 'GOOG_1335335464',
|
'ganclk': 'GOOG_1335335464',
|
||||||
}
|
}
|
||||||
|
|
||||||
url = 'http://gan.doubleclick.net/gan_click?lid=%(lid)s&pubid=%(pubid)s' % aff_id
|
url = 'http://gan.doubleclick.net/gan_click?lid=%(lid)s&pubid=%(pubid)s' % aff_id
|
||||||
if detail_item:
|
if detail_item:
|
||||||
detail_item += '&ganpub=%(ganpub)s&ganclk=%(ganclk)s' % aff_id
|
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):
|
def search(self, query, max_results=10, timeout=60):
|
||||||
url = 'http://www.google.com/search?tbm=bks&q=' + urllib.quote_plus(query)
|
url = 'http://www.google.com/search?tbm=bks&q=' + urllib.quote_plus(query)
|
||||||
|
|
||||||
br = browser()
|
br = browser()
|
||||||
|
|
||||||
counter = max_results
|
counter = max_results
|
||||||
with closing(br.open(url, timeout=timeout)) as f:
|
with closing(br.open(url, timeout=timeout)) as f:
|
||||||
doc = html.fromstring(f.read())
|
doc = html.fromstring(f.read())
|
||||||
@ -76,22 +77,22 @@ class GoogleBooksStore(BasicStoreConfig, StorePlugin):
|
|||||||
author = ', '.join(authors)
|
author = ', '.join(authors)
|
||||||
|
|
||||||
counter -= 1
|
counter -= 1
|
||||||
|
|
||||||
s = SearchResult()
|
s = SearchResult()
|
||||||
s.title = title.strip()
|
s.title = title.strip()
|
||||||
s.author = author.strip()
|
s.author = author.strip()
|
||||||
s.detail_item = id.strip()
|
s.detail_item = id.strip()
|
||||||
s.drm = SearchResult.DRM_UNKNOWN
|
s.drm = SearchResult.DRM_UNKNOWN
|
||||||
|
|
||||||
yield s
|
yield s
|
||||||
|
|
||||||
def get_details(self, search_result, timeout):
|
def get_details(self, search_result, timeout):
|
||||||
br = browser()
|
br = browser()
|
||||||
with closing(br.open(search_result.detail_item, timeout=timeout)) as nf:
|
with closing(br.open(search_result.detail_item, timeout=timeout)) as nf:
|
||||||
doc = html.fromstring(nf.read())
|
doc = html.fromstring(nf.read())
|
||||||
|
|
||||||
search_result.cover_url = ''.join(doc.xpath('//div[@class="sidebarcover"]//img/@src'))
|
search_result.cover_url = ''.join(doc.xpath('//div[@class="sidebarcover"]//img/@src'))
|
||||||
|
|
||||||
# Try to get the set price.
|
# Try to get the set price.
|
||||||
price = ''.join(doc.xpath('//div[@id="gb-get-book-container"]//a/text()'))
|
price = ''.join(doc.xpath('//div[@id="gb-get-book-container"]//a/text()'))
|
||||||
if 'read' in price.lower():
|
if 'read' in price.lower():
|
||||||
@ -101,10 +102,10 @@ class GoogleBooksStore(BasicStoreConfig, StorePlugin):
|
|||||||
elif '-' in price:
|
elif '-' in price:
|
||||||
a, b, price = price.partition(' - ')
|
a, b, price = price.partition(' - ')
|
||||||
search_result.price = price.strip()
|
search_result.price = price.strip()
|
||||||
|
|
||||||
search_result.formats = ', '.join(doc.xpath('//div[contains(@class, "download-panel-div")]//a/text()')).upper()
|
search_result.formats = ', '.join(doc.xpath('//div[contains(@class, "download-panel-div")]//a/text()')).upper()
|
||||||
if not search_result.formats:
|
if not search_result.formats:
|
||||||
search_result.formats = _('Unknown')
|
search_result.formats = _('Unknown')
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
store_version = 1 # Needed for dynamic plugin loading
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
store_version = 1 # Needed for dynamic plugin loading
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
store_version = 1 # Needed for dynamic plugin loading
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2011, Tomasz Długosz <tomek3d@gmail.com>'
|
__copyright__ = '2011, Tomasz Długosz <tomek3d@gmail.com>'
|
||||||
@ -24,7 +25,7 @@ from calibre.gui2.store.web_store_dialog import WebStoreDialog
|
|||||||
class LegimiStore(BasicStoreConfig, StorePlugin):
|
class LegimiStore(BasicStoreConfig, StorePlugin):
|
||||||
|
|
||||||
def open(self, parent=None, detail_item=None, external=False):
|
def open(self, parent=None, detail_item=None, external=False):
|
||||||
|
|
||||||
plain_url = 'http://www.legimi.com/pl/ebooki/'
|
plain_url = 'http://www.legimi.com/pl/ebooki/'
|
||||||
url = 'https://ssl.afiliant.com/affskrypt,,2f9de2,,11483,,,?u=(' + plain_url + ')'
|
url = 'https://ssl.afiliant.com/affskrypt,,2f9de2,,11483,,,?u=(' + plain_url + ')'
|
||||||
detail_url = None
|
detail_url = None
|
||||||
@ -42,17 +43,17 @@ class LegimiStore(BasicStoreConfig, StorePlugin):
|
|||||||
|
|
||||||
def search(self, query, max_results=10, timeout=60):
|
def search(self, query, max_results=10, timeout=60):
|
||||||
url = 'http://www.legimi.com/pl/ebooki/?szukaj=' + urllib.quote_plus(query)
|
url = 'http://www.legimi.com/pl/ebooki/?szukaj=' + urllib.quote_plus(query)
|
||||||
|
|
||||||
br = browser()
|
br = browser()
|
||||||
drm_pattern = re.compile("zabezpieczona DRM")
|
drm_pattern = re.compile("zabezpieczona DRM")
|
||||||
|
|
||||||
counter = max_results
|
counter = max_results
|
||||||
with closing(br.open(url, timeout=timeout)) as f:
|
with closing(br.open(url, timeout=timeout)) as f:
|
||||||
doc = html.fromstring(f.read())
|
doc = html.fromstring(f.read())
|
||||||
for data in doc.xpath('//div[@id="listBooks"]/div'):
|
for data in doc.xpath('//div[@id="listBooks"]/div'):
|
||||||
if counter <= 0:
|
if counter <= 0:
|
||||||
break
|
break
|
||||||
|
|
||||||
id = ''.join(data.xpath('.//a[@class="plainLink"]/@href'))
|
id = ''.join(data.xpath('.//a[@class="plainLink"]/@href'))
|
||||||
if not id:
|
if not id:
|
||||||
continue
|
continue
|
||||||
@ -73,7 +74,7 @@ class LegimiStore(BasicStoreConfig, StorePlugin):
|
|||||||
drm = drm_pattern.search(''.join(idata.xpath('.//div[@id="fullBookFormats"]/p/text()')))
|
drm = drm_pattern.search(''.join(idata.xpath('.//div[@id="fullBookFormats"]/p/text()')))
|
||||||
|
|
||||||
counter -= 1
|
counter -= 1
|
||||||
|
|
||||||
s = SearchResult()
|
s = SearchResult()
|
||||||
s.cover_url = 'http://www.legimi.com/' + cover_url
|
s.cover_url = 'http://www.legimi.com/' + cover_url
|
||||||
s.title = title.strip()
|
s.title = title.strip()
|
||||||
@ -82,5 +83,5 @@ class LegimiStore(BasicStoreConfig, StorePlugin):
|
|||||||
s.detail_item = 'http://www.legimi.com/' + id.strip()
|
s.detail_item = 'http://www.legimi.com/' + id.strip()
|
||||||
s.formats = ', '.join(formats)
|
s.formats = ', '.join(formats)
|
||||||
s.drm = SearchResult.DRM_LOCKED if drm else SearchResult.DRM_UNLOCKED
|
s.drm = SearchResult.DRM_LOCKED if drm else SearchResult.DRM_UNLOCKED
|
||||||
|
|
||||||
yield s
|
yield s
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
store_version = 1 # Needed for dynamic plugin loading
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
store_version = 1 # Needed for dynamic plugin loading
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2011, Roman Mukhin <ramses_ru at hotmail.com>'
|
__copyright__ = '2011, Roman Mukhin <ramses_ru at hotmail.com>'
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
store_version = 1 # Needed for dynamic plugin loading
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
||||||
@ -26,7 +27,7 @@ class ManyBooksStore(BasicStoreConfig, OpenSearchOPDSStore):
|
|||||||
def search(self, query, max_results=10, timeout=60):
|
def search(self, query, max_results=10, timeout=60):
|
||||||
'''
|
'''
|
||||||
Manybooks uses a very strange opds feed. The opds
|
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
|
search result entries give very little information
|
||||||
and requires you to go to a detail link. The detail
|
and requires you to go to a detail link. The detail
|
||||||
link has the wrong type specified (text/html instead
|
link has the wrong type specified (text/html instead
|
||||||
@ -45,7 +46,7 @@ class ManyBooksStore(BasicStoreConfig, OpenSearchOPDSStore):
|
|||||||
oquery.searchTerms = query
|
oquery.searchTerms = query
|
||||||
oquery.count = max_results
|
oquery.count = max_results
|
||||||
url = oquery.url()
|
url = oquery.url()
|
||||||
|
|
||||||
counter = max_results
|
counter = max_results
|
||||||
br = browser()
|
br = browser()
|
||||||
with closing(br.open(url, timeout=timeout)) as f:
|
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"]'):
|
for data in doc.xpath('//*[local-name() = "entry"]'):
|
||||||
if counter <= 0:
|
if counter <= 0:
|
||||||
break
|
break
|
||||||
|
|
||||||
counter -= 1
|
counter -= 1
|
||||||
|
|
||||||
s = SearchResult()
|
s = SearchResult()
|
||||||
|
|
||||||
detail_links = data.xpath('./*[local-name() = "link" and @type = "text/html"]')
|
detail_links = data.xpath('./*[local-name() = "link" and @type = "text/html"]')
|
||||||
if not detail_links:
|
if not detail_links:
|
||||||
continue
|
continue
|
||||||
@ -73,7 +74,7 @@ class ManyBooksStore(BasicStoreConfig, OpenSearchOPDSStore):
|
|||||||
# just in case.
|
# just in case.
|
||||||
s.title = ''.join(data.xpath('./*[local-name() = "title"]//text()')).strip()
|
s.title = ''.join(data.xpath('./*[local-name() = "title"]//text()')).strip()
|
||||||
s.author = ', '.join(data.xpath('./*[local-name() = "author"]//text()')).strip()
|
s.author = ', '.join(data.xpath('./*[local-name() = "author"]//text()')).strip()
|
||||||
|
|
||||||
# Follow the detail link to get the rest of the info.
|
# Follow the detail link to get the rest of the info.
|
||||||
with closing(br.open(detail_href, timeout=timeout/4)) as df:
|
with closing(br.open(detail_href, timeout=timeout/4)) as df:
|
||||||
ddoc = etree.fromstring(df.read())
|
ddoc = etree.fromstring(df.read())
|
||||||
@ -89,9 +90,9 @@ class ManyBooksStore(BasicStoreConfig, OpenSearchOPDSStore):
|
|||||||
s.author = s.author[1:]
|
s.author = s.author[1:]
|
||||||
if s.author.endswith(','):
|
if s.author.endswith(','):
|
||||||
s.author = s.author[:-1]
|
s.author = s.author[:-1]
|
||||||
|
|
||||||
s.cover_url = ''.join(ddata.xpath('./*[local-name() = "link" and @rel = "http://opds-spec.org/thumbnail"][1]/@href')).strip()
|
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"]'):
|
for link in ddata.xpath('./*[local-name() = "link" and @rel = "http://opds-spec.org/acquisition"]'):
|
||||||
type = link.get('type')
|
type = link.get('type')
|
||||||
href = link.get('href')
|
href = link.get('href')
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
store_version = 1 # Needed for dynamic plugin loading
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
store_version = 1 # Needed for dynamic plugin loading
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2011-2012, Tomasz Długosz <tomek3d@gmail.com>'
|
__copyright__ = '2011-2012, Tomasz Długosz <tomek3d@gmail.com>'
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
store_version = 1 # Needed for dynamic plugin loading
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2012, John Schember <john@nachtimwald.com>'
|
__copyright__ = '2012, John Schember <john@nachtimwald.com>'
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
store_version = 1 # Needed for dynamic plugin loading
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
store_version = 1 # Needed for dynamic plugin loading
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2011, Roman Mukhin <ramses_ru at hotmail.com>'
|
__copyright__ = '2011, Roman Mukhin <ramses_ru at hotmail.com>'
|
||||||
@ -24,33 +25,33 @@ from calibre.gui2.store.web_store_dialog import WebStoreDialog
|
|||||||
|
|
||||||
class OzonRUStore(BasicStoreConfig, StorePlugin):
|
class OzonRUStore(BasicStoreConfig, StorePlugin):
|
||||||
shop_url = 'http://www.ozon.ru'
|
shop_url = 'http://www.ozon.ru'
|
||||||
|
|
||||||
def open(self, parent=None, detail_item=None, external=False):
|
def open(self, parent=None, detail_item=None, external=False):
|
||||||
|
|
||||||
aff_id = '?partner=romuk'
|
aff_id = '?partner=romuk'
|
||||||
# Use Kovid's affiliate id 30% of the time.
|
# Use Kovid's affiliate id 30% of the time.
|
||||||
if random.randint(1, 10) in (1, 2, 3):
|
if random.randint(1, 10) in (1, 2, 3):
|
||||||
aff_id = '?partner=kovidgoyal'
|
aff_id = '?partner=kovidgoyal'
|
||||||
|
|
||||||
url = self.shop_url + aff_id
|
url = self.shop_url + aff_id
|
||||||
detail_url = None
|
detail_url = None
|
||||||
if detail_item:
|
if detail_item:
|
||||||
# http://www.ozon.ru/context/detail/id/3037277/
|
# http://www.ozon.ru/context/detail/id/3037277/
|
||||||
detail_url = self.shop_url + '/context/detail/id/' + urllib2.quote(detail_item) + aff_id
|
detail_url = self.shop_url + '/context/detail/id/' + urllib2.quote(detail_item) + aff_id
|
||||||
|
|
||||||
if external or self.config.get('open_external', False):
|
if external or self.config.get('open_external', False):
|
||||||
open_url(QUrl(url_slash_cleaner(detail_url if detail_url else url)))
|
open_url(QUrl(url_slash_cleaner(detail_url if detail_url else url)))
|
||||||
else:
|
else:
|
||||||
d = WebStoreDialog(self.gui, url, parent, detail_url)
|
d = WebStoreDialog(self.gui, url, parent, detail_url)
|
||||||
d.setWindowTitle(self.name)
|
d.setWindowTitle(self.name)
|
||||||
d.set_tags(self.config.get('tags', ''))
|
d.set_tags(self.config.get('tags', ''))
|
||||||
d.exec_()
|
d.exec_()
|
||||||
|
|
||||||
def search(self, query, max_results=15, timeout=60):
|
def search(self, query, max_results=15, timeout=60):
|
||||||
search_url = self.shop_url + '/webservice/webservice.asmx/SearchWebService?'\
|
search_url = self.shop_url + '/webservice/webservice.asmx/SearchWebService?'\
|
||||||
'searchText=%s&searchContext=ebook' % urllib2.quote(query)
|
'searchText=%s&searchContext=ebook' % urllib2.quote(query)
|
||||||
search_urls = [ search_url ]
|
search_urls = [ search_url ]
|
||||||
|
|
||||||
## add this as the fist try if it looks like ozon ID
|
## add this as the fist try if it looks like ozon ID
|
||||||
if re.match("^\d{6,9}$", query):
|
if re.match("^\d{6,9}$", query):
|
||||||
ozon_detail = self.shop_url + '/webservices/OzonWebSvc.asmx/ItemDetail?ID=%s' % 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())'
|
xp_template = 'normalize-space(./*[local-name() = "{0}"]/text())'
|
||||||
counter = max_results
|
counter = max_results
|
||||||
br = browser()
|
br = browser()
|
||||||
|
|
||||||
for url in search_urls:
|
for url in search_urls:
|
||||||
with closing(br.open(url, timeout=timeout)) as f:
|
with closing(br.open(url, timeout=timeout)) as f:
|
||||||
raw = xml_to_unicode(f.read(), strip_encoding_pats=True, assume_utf8=True)[0]
|
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:
|
with closing(br.open(url, timeout=timeout)) as f:
|
||||||
raw = xml_to_unicode(f.read(), verbose=True)[0]
|
raw = xml_to_unicode(f.read(), verbose=True)[0]
|
||||||
doc = html.fromstring(raw)
|
doc = html.fromstring(raw)
|
||||||
|
|
||||||
# example where we are going to find formats
|
# example where we are going to find formats
|
||||||
# <div class="l">
|
# <div class="l">
|
||||||
# <p>
|
# <p>
|
||||||
# Доступно:
|
# Доступно:
|
||||||
# </p>
|
# </p>
|
||||||
# </div>
|
# </div>
|
||||||
@ -104,16 +105,16 @@ class OzonRUStore(BasicStoreConfig, StorePlugin):
|
|||||||
search_result.formats = ', '.join(_parse_ebook_formats(formats))
|
search_result.formats = ', '.join(_parse_ebook_formats(formats))
|
||||||
# unfortunately no direct links to download books (only buy link)
|
# 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)
|
# search_result.downloads['BF2'] = self.shop_url + '/order/digitalorder.aspx?id=' + + urllib2.quote(search_result.detail_item)
|
||||||
|
|
||||||
#<p class="main-cost"><span class="main">215</span><span class="submain">00</span> руб.</p>
|
#<p class="main-cost"><span class="main">215</span><span class="submain">00</span> руб.</p>
|
||||||
#<span itemprop="price" class="hidden">215.00</span>
|
#<span itemprop="price" class="hidden">215.00</span>
|
||||||
#<meta itemprop="priceCurrency" content="RUR " />
|
#<meta itemprop="priceCurrency" content="RUR " />
|
||||||
|
|
||||||
# if the price not in the search result (the ID search case)
|
# if the price not in the search result (the ID search case)
|
||||||
if not search_result.price:
|
if not search_result.price:
|
||||||
price = doc.xpath(u'normalize-space(//*[@itemprop="price"]/text())')
|
price = doc.xpath(u'normalize-space(//*[@itemprop="price"]/text())')
|
||||||
search_result.price = format_price_in_RUR(price)
|
search_result.price = format_price_in_RUR(price)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def format_price_in_RUR(price):
|
def format_price_in_RUR(price):
|
||||||
@ -134,12 +135,12 @@ def format_price_in_RUR(price):
|
|||||||
def _parse_ebook_formats(formatsStr):
|
def _parse_ebook_formats(formatsStr):
|
||||||
'''
|
'''
|
||||||
Creates a list with displayable names of the formats
|
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
|
as it provided by ozon.ru
|
||||||
:return: a list with displayable book formats
|
:return: a list with displayable book formats
|
||||||
'''
|
'''
|
||||||
|
|
||||||
formatsUnstruct = formatsStr.lower()
|
formatsUnstruct = formatsStr.lower()
|
||||||
formats = []
|
formats = []
|
||||||
if 'epub' in formatsUnstruct:
|
if 'epub' in formatsUnstruct:
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
store_version = 1 # Needed for dynamic plugin loading
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
||||||
@ -14,7 +15,7 @@ class PragmaticBookshelfStore(BasicStoreConfig, OpenSearchOPDSStore):
|
|||||||
|
|
||||||
open_search_url = 'http://pragprog.com/catalog/search-description'
|
open_search_url = 'http://pragprog.com/catalog/search-description'
|
||||||
web_url = 'http://pragprog.com/'
|
web_url = 'http://pragprog.com/'
|
||||||
|
|
||||||
# http://pragprog.com/catalog.opds
|
# http://pragprog.com/catalog.opds
|
||||||
|
|
||||||
def search(self, query, max_results=10, timeout=60):
|
def search(self, query, max_results=10, timeout=60):
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
store_version = 1 # Needed for dynamic plugin loading
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2012, Tomasz Długosz <tomek3d@gmail.com>'
|
__copyright__ = '2012, Tomasz Długosz <tomek3d@gmail.com>'
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
store_version = 1 # Needed for dynamic plugin loading
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2011, Tomasz Długosz <tomek3d@gmail.com>'
|
__copyright__ = '2011, Tomasz Długosz <tomek3d@gmail.com>'
|
||||||
@ -73,5 +74,5 @@ class RW2010Store(BasicStoreConfig, StorePlugin):
|
|||||||
s.detail_item = re.sub(r'%3D', '=', id)
|
s.detail_item = re.sub(r'%3D', '=', id)
|
||||||
s.drm = SearchResult.DRM_UNLOCKED
|
s.drm = SearchResult.DRM_UNLOCKED
|
||||||
s.formats = formats[0:-2].upper()
|
s.formats = formats[0:-2].upper()
|
||||||
|
|
||||||
yield s
|
yield s
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
store_version = 1 # Needed for dynamic plugin loading
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
||||||
@ -36,7 +37,7 @@ class SmashwordsStore(BasicStoreConfig, StorePlugin):
|
|||||||
if detail_item:
|
if detail_item:
|
||||||
detail_url = url + detail_item + aff_id
|
detail_url = url + detail_item + aff_id
|
||||||
url = url + aff_id
|
url = url + aff_id
|
||||||
|
|
||||||
if external or self.config.get('open_external', False):
|
if external or self.config.get('open_external', False):
|
||||||
open_url(QUrl(url_slash_cleaner(detail_url if detail_url else url)))
|
open_url(QUrl(url_slash_cleaner(detail_url if detail_url else url)))
|
||||||
else:
|
else:
|
||||||
@ -47,9 +48,9 @@ class SmashwordsStore(BasicStoreConfig, StorePlugin):
|
|||||||
|
|
||||||
def search(self, query, max_results=10, timeout=60):
|
def search(self, query, max_results=10, timeout=60):
|
||||||
url = 'http://www.smashwords.com/books/search?query=' + urllib2.quote(query)
|
url = 'http://www.smashwords.com/books/search?query=' + urllib2.quote(query)
|
||||||
|
|
||||||
br = browser()
|
br = browser()
|
||||||
|
|
||||||
counter = max_results
|
counter = max_results
|
||||||
with closing(br.open(url, timeout=timeout)) as f:
|
with closing(br.open(url, timeout=timeout)) as f:
|
||||||
doc = html.fromstring(f.read())
|
doc = html.fromstring(f.read())
|
||||||
@ -57,7 +58,7 @@ class SmashwordsStore(BasicStoreConfig, StorePlugin):
|
|||||||
if counter <= 0:
|
if counter <= 0:
|
||||||
break
|
break
|
||||||
data = html.fromstring(html.tostring(data))
|
data = html.fromstring(html.tostring(data))
|
||||||
|
|
||||||
id = None
|
id = None
|
||||||
id_a = data.xpath('//a[@class="bookTitle"]')
|
id_a = data.xpath('//a[@class="bookTitle"]')
|
||||||
if id_a:
|
if id_a:
|
||||||
@ -66,14 +67,14 @@ class SmashwordsStore(BasicStoreConfig, StorePlugin):
|
|||||||
id = id.split('/')[-1]
|
id = id.split('/')[-1]
|
||||||
if not id:
|
if not id:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
cover_url = ''
|
cover_url = ''
|
||||||
c_url = data.get('style', None)
|
c_url = data.get('style', None)
|
||||||
if c_url:
|
if c_url:
|
||||||
mo = re.search(r'http://[^\'"]+', c_url)
|
mo = re.search(r'http://[^\'"]+', c_url)
|
||||||
if mo:
|
if mo:
|
||||||
cover_url = mo.group()
|
cover_url = mo.group()
|
||||||
|
|
||||||
title = ''.join(data.xpath('//a[@class="bookTitle"]/text()'))
|
title = ''.join(data.xpath('//a[@class="bookTitle"]/text()'))
|
||||||
subnote = ''.join(data.xpath('//span[@class="subnote"]/text()'))
|
subnote = ''.join(data.xpath('//span[@class="subnote"]/text()'))
|
||||||
author = ''.join(data.xpath('//span[@class="subnote"]//a[1]//text()'))
|
author = ''.join(data.xpath('//span[@class="subnote"]//a[1]//text()'))
|
||||||
@ -85,7 +86,7 @@ class SmashwordsStore(BasicStoreConfig, StorePlugin):
|
|||||||
price = '$0.00'
|
price = '$0.00'
|
||||||
|
|
||||||
counter -= 1
|
counter -= 1
|
||||||
|
|
||||||
s = SearchResult()
|
s = SearchResult()
|
||||||
s.cover_url = cover_url
|
s.cover_url = cover_url
|
||||||
s.title = title.strip()
|
s.title = title.strip()
|
||||||
@ -93,12 +94,12 @@ class SmashwordsStore(BasicStoreConfig, StorePlugin):
|
|||||||
s.price = price.strip()
|
s.price = price.strip()
|
||||||
s.detail_item = '/books/view/' + id.strip()
|
s.detail_item = '/books/view/' + id.strip()
|
||||||
s.drm = SearchResult.DRM_UNLOCKED
|
s.drm = SearchResult.DRM_UNLOCKED
|
||||||
|
|
||||||
yield s
|
yield s
|
||||||
|
|
||||||
def get_details(self, search_result, timeout):
|
def get_details(self, search_result, timeout):
|
||||||
url = 'http://www.smashwords.com/'
|
url = 'http://www.smashwords.com/'
|
||||||
|
|
||||||
br = browser()
|
br = browser()
|
||||||
with closing(br.open(url + search_result.detail_item, timeout=timeout)) as nf:
|
with closing(br.open(url + search_result.detail_item, timeout=timeout)) as nf:
|
||||||
idata = html.fromstring(nf.read())
|
idata = html.fromstring(nf.read())
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:fdm=marker:ai
|
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:fdm=marker:ai
|
||||||
from __future__ import (unicode_literals, division, absolute_import,
|
from __future__ import (unicode_literals, division, absolute_import,
|
||||||
print_function)
|
print_function)
|
||||||
|
store_version = 1 # Needed for dynamic plugin loading
|
||||||
|
|
||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2012, Kovid Goyal <kovid at kovidgoyal.net>'
|
__copyright__ = '2012, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
store_version = 1 # Needed for dynamic plugin loading
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2011, Tomasz Długosz <tomek3d@gmail.com>'
|
__copyright__ = '2011, Tomasz Długosz <tomek3d@gmail.com>'
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
store_version = 1 # Needed for dynamic plugin loading
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
store_version = 1 # Needed for dynamic plugin loading
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
||||||
@ -35,9 +36,9 @@ class WeightlessBooksStore(BasicStoreConfig, StorePlugin):
|
|||||||
|
|
||||||
def search(self, query, max_results=10, timeout=60):
|
def search(self, query, max_results=10, timeout=60):
|
||||||
url = 'http://weightlessbooks.com/?s=' + urllib.quote_plus(query)
|
url = 'http://weightlessbooks.com/?s=' + urllib.quote_plus(query)
|
||||||
|
|
||||||
br = browser()
|
br = browser()
|
||||||
|
|
||||||
counter = max_results
|
counter = max_results
|
||||||
with closing(br.open(url, timeout=timeout)) as f:
|
with closing(br.open(url, timeout=timeout)) as f:
|
||||||
doc = html.fromstring(f.read())
|
doc = html.fromstring(f.read())
|
||||||
@ -50,20 +51,20 @@ class WeightlessBooksStore(BasicStoreConfig, StorePlugin):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
cover_url = ''.join(data.xpath('.//div[@class="cover"]/a/img/@src'))
|
cover_url = ''.join(data.xpath('.//div[@class="cover"]/a/img/@src'))
|
||||||
|
|
||||||
price = ''.join(data.xpath('.//div[@class="buy_buttons"]/b[1]/text()'))
|
price = ''.join(data.xpath('.//div[@class="buy_buttons"]/b[1]/text()'))
|
||||||
if not price:
|
if not price:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
formats = ', '.join(data.xpath('.//select[@class="eStore_variation"]//option//text()'))
|
formats = ', '.join(data.xpath('.//select[@class="eStore_variation"]//option//text()'))
|
||||||
formats = formats.upper()
|
formats = formats.upper()
|
||||||
|
|
||||||
title = ''.join(data.xpath('.//h3/a/text()'))
|
title = ''.join(data.xpath('.//h3/a/text()'))
|
||||||
author = ''.join(data.xpath('.//h3//text()'))
|
author = ''.join(data.xpath('.//h3//text()'))
|
||||||
author = author.replace(title, '')
|
author = author.replace(title, '')
|
||||||
|
|
||||||
counter -= 1
|
counter -= 1
|
||||||
|
|
||||||
s = SearchResult()
|
s = SearchResult()
|
||||||
s.cover_url = cover_url
|
s.cover_url = cover_url
|
||||||
s.title = title.strip()
|
s.title = title.strip()
|
||||||
@ -72,5 +73,5 @@ class WeightlessBooksStore(BasicStoreConfig, StorePlugin):
|
|||||||
s.detail_item = id.strip()
|
s.detail_item = id.strip()
|
||||||
s.drm = SearchResult.DRM_UNLOCKED
|
s.drm = SearchResult.DRM_UNLOCKED
|
||||||
s.formats = formats
|
s.formats = formats
|
||||||
|
|
||||||
yield s
|
yield s
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
store_version = 1 # Needed for dynamic plugin loading
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
store_version = 1 # Needed for dynamic plugin loading
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2011-2012, Tomasz Długosz <tomek3d@gmail.com>'
|
__copyright__ = '2011-2012, Tomasz Długosz <tomek3d@gmail.com>'
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
store_version = 1 # Needed for dynamic plugin loading
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
||||||
@ -20,25 +21,25 @@ class XinXiiStore(BasicStoreConfig, OpenSearchOPDSStore):
|
|||||||
|
|
||||||
open_search_url = 'http://www.xinxii.com/catalog-search/'
|
open_search_url = 'http://www.xinxii.com/catalog-search/'
|
||||||
web_url = 'http://xinxii.com/'
|
web_url = 'http://xinxii.com/'
|
||||||
|
|
||||||
# http://www.xinxii.com/catalog/
|
# http://www.xinxii.com/catalog/
|
||||||
|
|
||||||
def search(self, query, max_results=10, timeout=60):
|
def search(self, query, max_results=10, timeout=60):
|
||||||
'''
|
'''
|
||||||
XinXii's open search url is:
|
XinXii's open search url is:
|
||||||
http://www.xinxii.com/catalog-search/query/?keywords={searchTerms}&pw={startPage?}&doc_lang={docLang}&ff={docFormat},{docFormat},{docFormat}
|
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
|
This url requires the docLang and docFormat. However, the search itself
|
||||||
sent to XinXii does not require them. They can be ignored. We cannot
|
sent to XinXii does not require them. They can be ignored. We cannot
|
||||||
push this into the stanard OpenSearchOPDSStore search because of the
|
push this into the stanard OpenSearchOPDSStore search because of the
|
||||||
required attributes.
|
required attributes.
|
||||||
|
|
||||||
XinXii doesn't return all info supported by OpenSearchOPDSStore search
|
XinXii doesn't return all info supported by OpenSearchOPDSStore search
|
||||||
function so this one is modified to remove parts that are used.
|
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)
|
url = 'http://www.xinxii.com/catalog-search/query/?keywords=' + urllib.quote_plus(query)
|
||||||
|
|
||||||
counter = max_results
|
counter = max_results
|
||||||
br = browser()
|
br = browser()
|
||||||
with closing(br.open(url, timeout=timeout)) as f:
|
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"]'):
|
for data in doc.xpath('//*[local-name() = "entry"]'):
|
||||||
if counter <= 0:
|
if counter <= 0:
|
||||||
break
|
break
|
||||||
|
|
||||||
counter -= 1
|
counter -= 1
|
||||||
|
|
||||||
s = SearchResult()
|
s = SearchResult()
|
||||||
|
|
||||||
s.detail_item = ''.join(data.xpath('./*[local-name() = "id"]/text()')).strip()
|
s.detail_item = ''.join(data.xpath('./*[local-name() = "id"]/text()')).strip()
|
||||||
|
|
||||||
for link in data.xpath('./*[local-name() = "link"]'):
|
for link in data.xpath('./*[local-name() = "link"]'):
|
||||||
rel = link.get('rel')
|
rel = link.get('rel')
|
||||||
href = link.get('href')
|
href = link.get('href')
|
||||||
type = link.get('type')
|
type = link.get('type')
|
||||||
|
|
||||||
if rel and href and type:
|
if rel and href and type:
|
||||||
if rel in ('http://opds-spec.org/thumbnail', 'http://opds-spec.org/image/thumbnail'):
|
if rel in ('http://opds-spec.org/thumbnail', 'http://opds-spec.org/image/thumbnail'):
|
||||||
s.cover_url = href
|
s.cover_url = href
|
||||||
if rel == 'alternate':
|
if rel == 'alternate':
|
||||||
s.detail_item = href
|
s.detail_item = href
|
||||||
|
|
||||||
s.formats = 'EPUB, PDF'
|
s.formats = 'EPUB, PDF'
|
||||||
|
|
||||||
s.title = ' '.join(data.xpath('./*[local-name() = "title"]//text()')).strip()
|
s.title = ' '.join(data.xpath('./*[local-name() = "title"]//text()')).strip()
|
||||||
s.author = ', '.join(data.xpath('./*[local-name() = "author"]//*[local-name() = "name"]//text()')).strip()
|
s.author = ', '.join(data.xpath('./*[local-name() = "author"]//*[local-name() = "name"]//text()')).strip()
|
||||||
|
|
||||||
price_e = data.xpath('.//*[local-name() = "price"][1]')
|
price_e = data.xpath('.//*[local-name() = "price"][1]')
|
||||||
if price_e:
|
if price_e:
|
||||||
price_e = price_e[0]
|
price_e = price_e[0]
|
||||||
@ -76,6 +77,6 @@ class XinXiiStore(BasicStoreConfig, OpenSearchOPDSStore):
|
|||||||
price = ''.join(price_e.xpath('.//text()')).strip()
|
price = ''.join(price_e.xpath('.//text()')).strip()
|
||||||
s.price = currency_code + ' ' + price
|
s.price = currency_code + ' ' + price
|
||||||
s.price = s.price.strip()
|
s.price = s.price.strip()
|
||||||
|
|
||||||
|
|
||||||
yield s
|
yield s
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||||
|
store_version = 1 # Needed for dynamic plugin loading
|
||||||
|
|
||||||
__license__ = 'GPL 3'
|
__license__ = 'GPL 3'
|
||||||
__copyright__ = '2011, Tomasz Długosz <tomek3d@gmail.com>'
|
__copyright__ = '2011, Tomasz Długosz <tomek3d@gmail.com>'
|
||||||
|
@ -155,7 +155,8 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
|
|||||||
acmap[ac.name] = ac
|
acmap[ac.name] = ac
|
||||||
|
|
||||||
def load_store_plugins(self):
|
def load_store_plugins(self):
|
||||||
self.istores = OrderedDict()
|
from calibre.gui2.store.loader import Stores
|
||||||
|
self.istores = Stores()
|
||||||
for store in available_store_plugins():
|
for store in available_store_plugins():
|
||||||
if self.opts.ignore_plugins and store.plugin_path is not None:
|
if self.opts.ignore_plugins and store.plugin_path is not None:
|
||||||
continue
|
continue
|
||||||
@ -169,6 +170,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
|
|||||||
if store.plugin_path is None:
|
if store.plugin_path is None:
|
||||||
raise
|
raise
|
||||||
continue
|
continue
|
||||||
|
self.istores.builtins_loaded()
|
||||||
|
|
||||||
def init_istore(self, store):
|
def init_istore(self, store):
|
||||||
st = store.load_actual_plugin(self)
|
st = store.load_actual_plugin(self)
|
||||||
@ -790,6 +792,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
|
|||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
pass
|
pass
|
||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
|
self.istores.join()
|
||||||
self.hide_windows()
|
self.hide_windows()
|
||||||
# Do not report any errors that happen after the shutdown
|
# Do not report any errors that happen after the shutdown
|
||||||
sys.excepthook = sys.__excepthook__
|
sys.excepthook = sys.__excepthook__
|
||||||
|
@ -245,6 +245,13 @@ class PocketBook900(PocketBook):
|
|||||||
id = 'pocketbook900'
|
id = 'pocketbook900'
|
||||||
output_profile = 'pocketbook_900'
|
output_profile = 'pocketbook_900'
|
||||||
|
|
||||||
|
class PocketBookPro912(PocketBook):
|
||||||
|
|
||||||
|
name = 'PocketBook Pro 912'
|
||||||
|
id = 'pocketbookpro912'
|
||||||
|
output_profile = 'pocketbook_pro_912'
|
||||||
|
|
||||||
|
|
||||||
class iPhone(Device):
|
class iPhone(Device):
|
||||||
|
|
||||||
name = 'iPhone/iTouch'
|
name = 'iPhone/iTouch'
|
||||||
|
Loading…
x
Reference in New Issue
Block a user