Merge from trunk

This commit is contained in:
Charles Haley 2013-01-15 10:01:33 +01:00
commit ae34d84539
65 changed files with 446 additions and 128 deletions

View File

@ -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

View File

@ -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'

View File

@ -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.

View File

@ -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()))

View File

@ -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):

View File

@ -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)

View File

@ -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')))

View File

@ -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:

View File

@ -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>' +

View File

@ -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)

View File

@ -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):
''' '''

View 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))

View File

@ -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)

View File

@ -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>'

View File

@ -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 '

View File

@ -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>'

View File

@ -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 '

View File

@ -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>'

View File

@ -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>'

View File

@ -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>'

View File

@ -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

View File

@ -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')

View File

@ -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:

View File

@ -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>'

View File

@ -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>'

View File

@ -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>'

View File

@ -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>'

View File

@ -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>'

View File

@ -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>'

View File

@ -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>'

View File

@ -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>'

View File

@ -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>'

View File

@ -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())

View File

@ -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>'

View File

@ -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

View File

@ -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>'

View File

@ -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):

View File

@ -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>'

View File

@ -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

View File

@ -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>'

View File

@ -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>'

View File

@ -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

View File

@ -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>'

View File

@ -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>'

View File

@ -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')

View File

@ -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>'

View File

@ -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>'

View File

@ -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>'

View File

@ -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>'

View File

@ -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:

View File

@ -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):

View File

@ -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>'

View File

@ -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

View File

@ -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())

View File

@ -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>'

View File

@ -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>'

View File

@ -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>'

View File

@ -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

View File

@ -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>'

View File

@ -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>'

View File

@ -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}&amp;pw={startPage?}&amp;doc_lang={docLang}&amp;ff={docFormat},{docFormat},{docFormat} http://www.xinxii.com/catalog-search/query/?keywords={searchTerms}&amp;pw={startPage?}&amp;doc_lang={docLang}&amp;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

View File

@ -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>'

View File

@ -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__

View File

@ -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'