Store: Include changes from lp:~cbhaley/calibre/charles_store

This commit is contained in:
John Schember 2011-05-28 15:37:58 -04:00
commit a7296da58e
5 changed files with 235 additions and 23 deletions

View File

@ -1143,6 +1143,7 @@ class StoreArchiveOrgStore(StoreBase):
drm_free_only = True drm_free_only = True
headquarters = 'US' headquarters = 'US'
formats = ['DAISY', 'DJVU', 'EPUB', 'MOBI', 'PDF', 'TXT'] formats = ['DAISY', 'DJVU', 'EPUB', 'MOBI', 'PDF', 'TXT']
affiliate = False
class StoreBaenWebScriptionStore(StoreBase): class StoreBaenWebScriptionStore(StoreBase):
name = 'Baen WebScription' name = 'Baen WebScription'
@ -1152,6 +1153,7 @@ class StoreBaenWebScriptionStore(StoreBase):
drm_free_only = True drm_free_only = True
headquarters = 'US' headquarters = 'US'
formats = ['EPUB', 'LIT', 'LRF', 'MOBI', 'RB', 'RTF', 'ZIP'] formats = ['EPUB', 'LIT', 'LRF', 'MOBI', 'RB', 'RTF', 'ZIP']
affiliate = False
class StoreBNStore(StoreBase): class StoreBNStore(StoreBase):
name = 'Barnes and Noble' name = 'Barnes and Noble'
@ -1161,6 +1163,7 @@ class StoreBNStore(StoreBase):
drm_free_only = False drm_free_only = False
headquarters = 'US' headquarters = 'US'
formats = ['NOOK'] formats = ['NOOK']
affiliate = True
class StoreBeamEBooksDEStore(StoreBase): class StoreBeamEBooksDEStore(StoreBase):
name = 'Beam EBooks DE' name = 'Beam EBooks DE'
@ -1181,6 +1184,7 @@ class StoreBeWriteStore(StoreBase):
drm_free_only = True drm_free_only = True
headquarters = 'US' headquarters = 'US'
formats = ['EPUB', 'MOBI', 'PDF'] formats = ['EPUB', 'MOBI', 'PDF']
affiliate = False
class StoreDieselEbooksStore(StoreBase): class StoreDieselEbooksStore(StoreBase):
name = 'Diesel eBooks' name = 'Diesel eBooks'
@ -1190,6 +1194,7 @@ class StoreDieselEbooksStore(StoreBase):
drm_free_only = False drm_free_only = False
headquarters = 'US' headquarters = 'US'
formats = ['EPUB', 'PDF'] formats = ['EPUB', 'PDF']
affiliate = True
class StoreEbookscomStore(StoreBase): class StoreEbookscomStore(StoreBase):
name = 'eBooks.com' name = 'eBooks.com'
@ -1199,6 +1204,18 @@ class StoreEbookscomStore(StoreBase):
drm_free_only = False drm_free_only = False
headquarters = 'US' headquarters = 'US'
formats = ['EPUB', 'LIT', 'MOBI', 'PDF'] formats = ['EPUB', 'LIT', 'MOBI', 'PDF']
affiliate = True
class StoreEPubBuyDEStore(StoreBase):
name = 'EPUBBuy DE'
author = 'Charles Haley'
description = u'Bei EPUBBuy.com finden Sie ausschliesslich eBooks im weitverbreiteten EPUB-Format und ohne DRM. So haben Sie die freie Wahl, wo Sie Ihr eBook lesen: Tablet, eBook-Reader, Smartphone oder einfach auf Ihrem PC. So macht eBook-Lesen Spaß!'
actual_plugin = 'calibre.gui2.store.epubbuy_de_plugin:EPubBuyDEStore'
drm_free_only = True
headquarters = 'DE'
formats = ['EPUB']
affiliate = True
class StoreEBookShoppeUKStore(StoreBase): class StoreEBookShoppeUKStore(StoreBase):
name = 'ebookShoppe UK' name = 'ebookShoppe UK'
@ -1211,16 +1228,6 @@ class StoreEBookShoppeUKStore(StoreBase):
formats = ['EPUB', 'PDF'] formats = ['EPUB', 'PDF']
affiliate = True affiliate = True
class StoreEPubBuyDEStore(StoreBase):
name = 'EPUBBuy DE'
author = 'Charles Haley'
description = u'Bei EPUBBuy.com finden Sie ausschliesslich eBooks im weitverbreiteten EPUB-Format und ohne DRM. So haben Sie die freie Wahl, wo Sie Ihr eBook lesen: Tablet, eBook-Reader, Smartphone oder einfach auf Ihrem PC. So macht eBook-Lesen Spaß!'
actual_plugin = 'calibre.gui2.store.epubbuy_de_plugin:EPubBuyDEStore'
drm_free_only = True
headquarters = 'DE'
formats = ['EPUB']
class StoreEHarlequinStore(StoreBase): class StoreEHarlequinStore(StoreBase):
name = 'eHarlequin' name = 'eHarlequin'
description = u'A global leader in series romance and one of the world\'s leading publishers of books for women. Offers women a broad range of reading from romance to bestseller fiction, from young adult novels to erotic literature, from nonfiction to fantasy, from African-American novels to inspirational romance, and more.' description = u'A global leader in series romance and one of the world\'s leading publishers of books for women. Offers women a broad range of reading from romance to bestseller fiction, from young adult novels to erotic literature, from nonfiction to fantasy, from African-American novels to inspirational romance, and more.'
@ -1229,6 +1236,7 @@ class StoreEHarlequinStore(StoreBase):
drm_free_only = False drm_free_only = False
headquarters = 'CA' headquarters = 'CA'
formats = ['EPUB', 'PDF'] formats = ['EPUB', 'PDF']
affiliate = True
class StoreFeedbooksStore(StoreBase): class StoreFeedbooksStore(StoreBase):
name = 'Feedbooks' name = 'Feedbooks'
@ -1238,6 +1246,7 @@ class StoreFeedbooksStore(StoreBase):
drm_free_only = False drm_free_only = False
headquarters = 'FR' headquarters = 'FR'
formats = ['EPUB', 'MOBI', 'PDF'] formats = ['EPUB', 'MOBI', 'PDF']
affiliate = False
class StoreFoylesUKStore(StoreBase): class StoreFoylesUKStore(StoreBase):
name = 'Foyles UK' name = 'Foyles UK'
@ -1259,6 +1268,7 @@ class StoreGandalfStore(StoreBase):
drm_free_only = False drm_free_only = False
headquarters = 'PL' headquarters = 'PL'
formats = ['EPUB', 'PDF'] formats = ['EPUB', 'PDF']
affiliate = False
class StoreGoogleBooksStore(StoreBase): class StoreGoogleBooksStore(StoreBase):
name = 'Google Books' name = 'Google Books'
@ -1268,6 +1278,7 @@ class StoreGoogleBooksStore(StoreBase):
drm_free_only = False drm_free_only = False
headquarters = 'US' headquarters = 'US'
formats = ['EPUB', 'PDF', 'TXT'] formats = ['EPUB', 'PDF', 'TXT']
affiliate = False
class StoreGutenbergStore(StoreBase): class StoreGutenbergStore(StoreBase):
name = 'Project Gutenberg' name = 'Project Gutenberg'
@ -1277,6 +1288,7 @@ class StoreGutenbergStore(StoreBase):
drm_free_only = True drm_free_only = True
headquarters = 'US' headquarters = 'US'
formats = ['EPUB', 'HTML', 'MOBI', 'PDB', 'TXT'] formats = ['EPUB', 'HTML', 'MOBI', 'PDB', 'TXT']
affiliate = False
class StoreKoboStore(StoreBase): class StoreKoboStore(StoreBase):
name = 'Kobo' name = 'Kobo'
@ -1286,6 +1298,7 @@ class StoreKoboStore(StoreBase):
drm_free_only = False drm_free_only = False
headquarters = 'CA' headquarters = 'CA'
formats = ['EPUB'] formats = ['EPUB']
affiliate = True
class StoreLegimiStore(StoreBase): class StoreLegimiStore(StoreBase):
name = 'Legimi' name = 'Legimi'
@ -1296,6 +1309,7 @@ class StoreLegimiStore(StoreBase):
drm_free_only = False drm_free_only = False
headquarters = 'PL' headquarters = 'PL'
formats = ['EPUB'] formats = ['EPUB']
affiliate = False
class StoreManyBooksStore(StoreBase): class StoreManyBooksStore(StoreBase):
name = 'ManyBooks' name = 'ManyBooks'
@ -1305,6 +1319,7 @@ class StoreManyBooksStore(StoreBase):
drm_free_only = True drm_free_only = True
headquarters = 'US' headquarters = 'US'
formats = ['EPUB', 'FB2', 'JAR', 'LIT', 'LRF', 'MOBI', 'PDB', 'PDF', 'RB', 'RTF', 'TCR', 'TXT', 'ZIP'] formats = ['EPUB', 'FB2', 'JAR', 'LIT', 'LRF', 'MOBI', 'PDB', 'PDF', 'RB', 'RTF', 'TCR', 'TXT', 'ZIP']
affiliate = False
class StoreMobileReadStore(StoreBase): class StoreMobileReadStore(StoreBase):
name = 'MobileRead' name = 'MobileRead'
@ -1314,6 +1329,7 @@ class StoreMobileReadStore(StoreBase):
drm_free_only = True drm_free_only = True
headquarters = 'CH' headquarters = 'CH'
formats = ['EPUB', 'IMP', 'LRF', 'LIT', 'MOBI', 'PDF'] formats = ['EPUB', 'IMP', 'LRF', 'LIT', 'MOBI', 'PDF']
affiliate = False
class StoreNextoStore(StoreBase): class StoreNextoStore(StoreBase):
name = 'Nexto' name = 'Nexto'
@ -1324,6 +1340,7 @@ class StoreNextoStore(StoreBase):
drm_free_only = False drm_free_only = False
headquarters = 'PL' headquarters = 'PL'
formats = ['EPUB', 'PDF'] formats = ['EPUB', 'PDF']
affiliate = True
class StoreOpenLibraryStore(StoreBase): class StoreOpenLibraryStore(StoreBase):
name = 'Open Library' name = 'Open Library'
@ -1333,6 +1350,7 @@ class StoreOpenLibraryStore(StoreBase):
drm_free_only = True drm_free_only = True
headquarters = 'US' headquarters = 'US'
formats = ['DAISY', 'DJVU', 'EPUB', 'MOBI', 'PDF', 'TXT'] formats = ['DAISY', 'DJVU', 'EPUB', 'MOBI', 'PDF', 'TXT']
affiliate = False
class StoreOReillyStore(StoreBase): class StoreOReillyStore(StoreBase):
name = 'OReilly' name = 'OReilly'
@ -1342,6 +1360,7 @@ class StoreOReillyStore(StoreBase):
drm_free_only = True drm_free_only = True
headquarters = 'US' headquarters = 'US'
formats = ['APK', 'DAISY', 'EPUB', 'MOBI', 'PDF'] formats = ['APK', 'DAISY', 'EPUB', 'MOBI', 'PDF']
affiliate = False
class StorePragmaticBookshelfStore(StoreBase): class StorePragmaticBookshelfStore(StoreBase):
name = 'Pragmatic Bookshelf' name = 'Pragmatic Bookshelf'
@ -1351,6 +1370,7 @@ class StorePragmaticBookshelfStore(StoreBase):
drm_free_only = True drm_free_only = True
headquarters = 'US' headquarters = 'US'
formats = ['EPUB', 'MOBI', 'PDF'] formats = ['EPUB', 'MOBI', 'PDF']
affiliate = False
class StoreSmashwordsStore(StoreBase): class StoreSmashwordsStore(StoreBase):
name = 'Smashwords' name = 'Smashwords'
@ -1360,6 +1380,7 @@ class StoreSmashwordsStore(StoreBase):
drm_free_only = True drm_free_only = True
headquarters = 'US' headquarters = 'US'
formats = ['EPUB', 'HTML', 'LRF', 'MOBI', 'PDB', 'RTF', 'TXT'] formats = ['EPUB', 'HTML', 'LRF', 'MOBI', 'PDB', 'RTF', 'TXT']
affiliate = True
class StoreVirtualoStore(StoreBase): class StoreVirtualoStore(StoreBase):
name = 'Virtualo' name = 'Virtualo'
@ -1370,6 +1391,7 @@ class StoreVirtualoStore(StoreBase):
drm_free_only = False drm_free_only = False
headquarters = 'PL' headquarters = 'PL'
formats = ['EPUB', 'PDF'] formats = ['EPUB', 'PDF']
affiliate = False
class StoreWaterstonesUKStore(StoreBase): class StoreWaterstonesUKStore(StoreBase):
name = 'Waterstones UK' name = 'Waterstones UK'
@ -1380,6 +1402,7 @@ class StoreWaterstonesUKStore(StoreBase):
drm_free_only = False drm_free_only = False
headquarters = 'UK' headquarters = 'UK'
formats = ['EPUB', 'PDF'] formats = ['EPUB', 'PDF']
affiliate = False
class StoreWeightlessBooksStore(StoreBase): class StoreWeightlessBooksStore(StoreBase):
name = 'Weightless Books' name = 'Weightless Books'
@ -1389,6 +1412,18 @@ class StoreWeightlessBooksStore(StoreBase):
drm_free_only = True drm_free_only = True
headquarters = 'US' headquarters = 'US'
formats = ['EPUB', 'HTML', 'LIT', 'MOBI', 'PDF'] formats = ['EPUB', 'HTML', 'LIT', 'MOBI', 'PDF']
affiliate = False
class StoreWHSmithUKStore(StoreBase):
name = 'WH Smith UK'
author = 'Charles Haley'
description = u"Shop for savings on Books, discounted Magazine subscriptions and great prices on Stationery, Toys & Games"
actual_plugin = 'calibre.gui2.store.whsmith_uk_plugin:WHSmithUKStore'
drm_free_only = False
headquarters = 'UK'
formats = ['EPUB', 'PDF']
affiliate = False
class StoreWizardsTowerBooksStore(StoreBase): class StoreWizardsTowerBooksStore(StoreBase):
name = 'Wizards Tower Books' name = 'Wizards Tower Books'
@ -1398,6 +1433,7 @@ class StoreWizardsTowerBooksStore(StoreBase):
drm_free_only = True drm_free_only = True
headquarters = 'UK' headquarters = 'UK'
formats = ['EPUB', 'MOBI'] formats = ['EPUB', 'MOBI']
affiliate = False
class StoreWoblinkStore(StoreBase): class StoreWoblinkStore(StoreBase):
name = 'Woblink' name = 'Woblink'
@ -1408,6 +1444,7 @@ class StoreWoblinkStore(StoreBase):
drm_free_only = False drm_free_only = False
headquarters = 'PL' headquarters = 'PL'
formats = ['EPUB'] formats = ['EPUB']
affiliate = False
plugins += [ plugins += [
StoreArchiveOrgStore, StoreArchiveOrgStore,
@ -1440,6 +1477,7 @@ plugins += [
StoreVirtualoStore, StoreVirtualoStore,
StoreWaterstonesUKStore, StoreWaterstonesUKStore,
StoreWeightlessBooksStore, StoreWeightlessBooksStore,
StoreWHSmithUKStore,
StoreWizardsTowerBooksStore, StoreWizardsTowerBooksStore,
StoreWoblinkStore StoreWoblinkStore
] ]

View File

@ -6,11 +6,17 @@ __license__ = 'GPL 3'
__copyright__ = '2011, John Schember <john@nachtimwald.com>' __copyright__ = '2011, John Schember <john@nachtimwald.com>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
import urllib
from contextlib import closing
from lxml import html
from PyQt4.Qt import QUrl from PyQt4.Qt import QUrl
from calibre import browser
from calibre.gui2 import open_url from calibre.gui2 import open_url
from calibre.gui2.store.amazon_plugin import AmazonKindleStore from calibre.gui2.store.amazon_plugin import AmazonKindleStore
from calibre.gui2.store.search_result import SearchResult
class AmazonUKKindleStore(AmazonKindleStore): class AmazonUKKindleStore(AmazonKindleStore):
''' '''
@ -28,3 +34,81 @@ class AmazonUKKindleStore(AmazonKindleStore):
aff_id['asin'] = detail_item aff_id['asin'] = detail_item
store_link = 'http://www.amazon.co.uk/gp/redirect.html?ie=UTF8&location=http://www.amazon.co.uk/dp/%(asin)s&tag=%(tag)s&linkCode=ur2&camp=1634&creative=6738' % aff_id store_link = 'http://www.amazon.co.uk/gp/redirect.html?ie=UTF8&location=http://www.amazon.co.uk/dp/%(asin)s&tag=%(tag)s&linkCode=ur2&camp=1634&creative=6738' % aff_id
open_url(QUrl(store_link)) open_url(QUrl(store_link))
def search(self, query, max_results=10, timeout=60):
url = self.search_url + urllib.quote_plus(query)
br = browser()
counter = max_results
with closing(br.open(url, timeout=timeout)) as f:
doc = html.fromstring(f.read())
# Amazon has two results pages.
is_shot = doc.xpath('boolean(//div[@id="shotgunMainResults"])')
# Horizontal grid of books.
if is_shot:
data_xpath = '//div[contains(@class, "result")]'
cover_xpath = './/div[@class="productTitle"]//img/@src'
# Vertical list of books.
else:
data_xpath = '//div[contains(@class, "product")]'
cover_xpath = './div[@class="productImage"]/a/img/@src'
for data in doc.xpath(data_xpath):
if counter <= 0:
break
# We must have an asin otherwise we can't easily reference the
# book later.
asin = ''.join(data.xpath('./@name'))
if not asin:
continue
cover_url = ''.join(data.xpath(cover_xpath))
title = ''.join(data.xpath('.//div[@class="productTitle"]/a/text()'))
price = ''.join(data.xpath('.//div[@class="newPrice"]/span/text()'))
counter -= 1
s = SearchResult()
s.cover_url = cover_url.strip()
s.title = title.strip()
s.price = price.strip()
s.detail_item = asin.strip()
s.formats = 'Kindle'
if is_shot:
# Amazon UK does not include the author on the grid layout
s.author = ''
self.get_details(s, timeout)
else:
author = ''.join(data.xpath('.//div[@class="productTitle"]/span[@class="ptBrand"]/text()'))
s.author = author.split(' by ')[-1].strip()
yield s
def get_details(self, search_result, timeout):
# We might already have been called.
if search_result.drm:
return
url = self.details_url
br = browser()
with closing(br.open(url + search_result.detail_item, timeout=timeout)) as nf:
idata = html.fromstring(nf.read())
if not search_result.author:
search_result.author = ''.join(idata.xpath('//div[@class="buying" and contains(., "Author")]/a/text()'))
if idata.xpath('boolean(//div[@class="content"]//li/b[contains(text(), "' +
self.drm_search_text + '")])'):
if idata.xpath('boolean(//div[@class="content"]//li[contains(., "' +
self.drm_free_text + '") and contains(b, "' +
self.drm_search_text + '")])'):
search_result.drm = SearchResult.DRM_UNLOCKED
else:
search_result.drm = SearchResult.DRM_UNKNOWN
else:
search_result.drm = SearchResult.DRM_LOCKED
return True

View File

@ -65,14 +65,14 @@ class EBookShoppeUKStore(BasicStoreConfig, StorePlugin):
s.price = price s.price = price
s.drm = SearchResult.DRM_UNLOCKED s.drm = SearchResult.DRM_UNLOCKED
s.detail_item = id s.detail_item = id
self.my_get_details(s, timeout) self.get_author_and_formats(s, timeout)
if not s.author: if not s.author:
continue continue
yield s yield s
def my_get_details(self, search_result, timeout): def get_author_and_formats(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:
idata = html.fromstring(nf.read()) idata = html.fromstring(nf.read())

View File

@ -9,8 +9,8 @@ __docformat__ = 'restructuredtext en'
import re import re
from random import shuffle from random import shuffle
from PyQt4.Qt import (Qt, QDialog, QDialogButtonBox, QTimer, QCheckBox, from PyQt4.Qt import (Qt, QDialog, QDialogButtonBox, QTimer, QCheckBox, QLabel,
QVBoxLayout, QIcon, QWidget, QTabWidget) QVBoxLayout, QIcon, QWidget, QTabWidget, QGridLayout)
from calibre.gui2 import JSONConfig, info_dialog from calibre.gui2 import JSONConfig, info_dialog
from calibre.gui2.progress_indicator import ProgressIndicator from calibre.gui2.progress_indicator import ProgressIndicator
@ -80,7 +80,7 @@ class SearchDialog(QDialog, Ui_Dialog):
self.progress_checker.start(100) self.progress_checker.start(100)
self.restore_state() self.restore_state()
def setup_store_checks(self): def setup_store_checks(self):
# Add check boxes for each store so the user # Add check boxes for each store so the user
# can disable searching specific stores on a # can disable searching specific stores on a
@ -88,18 +88,25 @@ class SearchDialog(QDialog, Ui_Dialog):
existing = {} existing = {}
for n in self.store_checks: for n in self.store_checks:
existing[n] = self.store_checks[n].isChecked() existing[n] = self.store_checks[n].isChecked()
self.store_checks = {} self.store_checks = {}
stores_check_widget = QWidget() stores_check_widget = QWidget()
store_list_layout = QVBoxLayout() store_list_layout = QGridLayout()
stores_check_widget.setLayout(store_list_layout) stores_check_widget.setLayout(store_list_layout)
for x in sorted(self.gui.istores.keys(), key=lambda x: x.lower()):
icon = QIcon(I('donate.png'))
for i, x in enumerate(sorted(self.gui.istores.keys(), key=lambda x: x.lower())):
cbox = QCheckBox(x) cbox = QCheckBox(x)
cbox.setChecked(existing.get(x, False)) cbox.setChecked(existing.get(x, False))
store_list_layout.addWidget(cbox) store_list_layout.addWidget(cbox, i, 0, 1, 1)
if self.gui.istores[x].base_plugin.affiliate:
iw = QLabel(self)
iw.setToolTip(_('Buying from this store supports a calibre developer'))
iw.setPixmap(icon.pixmap(16, 16))
store_list_layout.addWidget(iw, i, 1, 1, 1)
self.store_checks[x] = cbox self.store_checks[x] = cbox
store_list_layout.addStretch() store_list_layout.setRowStretch(store_list_layout.rowCount(), 10)
self.store_list.setWidget(stores_check_widget) self.store_list.setWidget(stores_check_widget)
def build_adv_search(self): def build_adv_search(self):
@ -250,14 +257,14 @@ class SearchDialog(QDialog, Ui_Dialog):
button_box.accepted.connect(d.accept) button_box.accepted.connect(d.accept)
button_box.rejected.connect(d.reject) button_box.rejected.connect(d.reject)
d.setWindowTitle(_('Customize get books search')) d.setWindowTitle(_('Customize get books search'))
tab_widget = QTabWidget(d) tab_widget = QTabWidget(d)
v.addWidget(tab_widget) v.addWidget(tab_widget)
v.addWidget(button_box) v.addWidget(button_box)
chooser_config_widget = StoreChooserWidget() chooser_config_widget = StoreChooserWidget()
search_config_widget = StoreConfigWidget(self.config) search_config_widget = StoreConfigWidget(self.config)
tab_widget.addTab(chooser_config_widget, _('Choose stores')) tab_widget.addTab(chooser_config_widget, _('Choose stores'))
tab_widget.addTab(search_config_widget, _('Configure search')) tab_widget.addTab(search_config_widget, _('Configure search'))

View File

@ -0,0 +1,83 @@
# -*- coding: utf-8 -*-
from __future__ import (unicode_literals, division, absolute_import, print_function)
__license__ = 'GPL 3'
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
__docformat__ = 'restructuredtext en'
import urllib2
from contextlib import closing
from lxml import html
from PyQt4.Qt import QUrl
from calibre import browser
from calibre.gui2 import open_url
from calibre.gui2.store import StorePlugin
from calibre.gui2.store.basic_config import BasicStoreConfig
from calibre.gui2.store.search_result import SearchResult
from calibre.gui2.store.web_store_dialog import WebStoreDialog
class WHSmithUKStore(BasicStoreConfig, StorePlugin):
def open(self, parent=None, detail_item=None, external=False):
url = 'http://www.whsmith.co.uk/'
url_details = ''
if external or self.config.get('open_external', False):
if detail_item:
url = url_details + detail_item
open_url(QUrl(url))
else:
detail_url = None
if detail_item:
detail_url = url_details + detail_item
d = WebStoreDialog(self.gui, url, parent, detail_url)
d.setWindowTitle(self.name)
d.set_tags(self.config.get('tags', ''))
d.exec_()
def search(self, query, max_results=10, timeout=60):
url = ('http://www.whsmith.co.uk/CatalogAndSearch/SearchWithinCategory.aspx'
'?cat=\Books\eb_eBooks&gq=' + urllib2.quote(query))
br = browser()
counter = max_results
with closing(br.open(url, timeout=timeout)) as f:
doc = html.fromstring(f.read())
for data in doc.xpath('//div[@class="product-search"]/'
'div[contains(@id, "whsSearchResultItem")]'):
if counter <= 0:
break
id = ''.join(data.xpath('.//a[contains(@id, "labelProductTitle")]/@href'))
if not id:
continue
cover_url = ''.join(data.xpath('.//a[contains(@id, "hlinkProductImage")]/img/@src'))
title = ''.join(data.xpath('.//a[contains(@id, "labelProductTitle")]/text()'))
author = ', '.join(data.xpath('.//div[@class="author"]/h3/span/text()'))
price = ''.join(data.xpath('.//span[contains(@id, "labelProductPrice")]/text()'))
pdf = data.xpath('boolean(.//span[contains(@id, "labelFormatText") and '
'contains(., "PDF")])')
epub = data.xpath('boolean(.//span[contains(@id, "labelFormatText") and '
'contains(., "ePub")])')
counter -= 1
s = SearchResult()
s.cover_url = cover_url
s.title = title.strip()
s.author = author.strip()
s.price = price
s.drm = SearchResult.DRM_LOCKED
s.detail_item = id
formats = []
if epub:
formats.append('ePub')
if pdf:
formats.append('PDF')
s.formats = ', '.join(formats)
yield s