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
headquarters = 'US'
formats = ['DAISY', 'DJVU', 'EPUB', 'MOBI', 'PDF', 'TXT']
affiliate = False
class StoreBaenWebScriptionStore(StoreBase):
name = 'Baen WebScription'
@ -1152,6 +1153,7 @@ class StoreBaenWebScriptionStore(StoreBase):
drm_free_only = True
headquarters = 'US'
formats = ['EPUB', 'LIT', 'LRF', 'MOBI', 'RB', 'RTF', 'ZIP']
affiliate = False
class StoreBNStore(StoreBase):
name = 'Barnes and Noble'
@ -1161,6 +1163,7 @@ class StoreBNStore(StoreBase):
drm_free_only = False
headquarters = 'US'
formats = ['NOOK']
affiliate = True
class StoreBeamEBooksDEStore(StoreBase):
name = 'Beam EBooks DE'
@ -1181,6 +1184,7 @@ class StoreBeWriteStore(StoreBase):
drm_free_only = True
headquarters = 'US'
formats = ['EPUB', 'MOBI', 'PDF']
affiliate = False
class StoreDieselEbooksStore(StoreBase):
name = 'Diesel eBooks'
@ -1190,6 +1194,7 @@ class StoreDieselEbooksStore(StoreBase):
drm_free_only = False
headquarters = 'US'
formats = ['EPUB', 'PDF']
affiliate = True
class StoreEbookscomStore(StoreBase):
name = 'eBooks.com'
@ -1199,6 +1204,18 @@ class StoreEbookscomStore(StoreBase):
drm_free_only = False
headquarters = 'US'
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):
name = 'ebookShoppe UK'
@ -1211,16 +1228,6 @@ class StoreEBookShoppeUKStore(StoreBase):
formats = ['EPUB', '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']
class StoreEHarlequinStore(StoreBase):
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.'
@ -1229,6 +1236,7 @@ class StoreEHarlequinStore(StoreBase):
drm_free_only = False
headquarters = 'CA'
formats = ['EPUB', 'PDF']
affiliate = True
class StoreFeedbooksStore(StoreBase):
name = 'Feedbooks'
@ -1238,6 +1246,7 @@ class StoreFeedbooksStore(StoreBase):
drm_free_only = False
headquarters = 'FR'
formats = ['EPUB', 'MOBI', 'PDF']
affiliate = False
class StoreFoylesUKStore(StoreBase):
name = 'Foyles UK'
@ -1259,6 +1268,7 @@ class StoreGandalfStore(StoreBase):
drm_free_only = False
headquarters = 'PL'
formats = ['EPUB', 'PDF']
affiliate = False
class StoreGoogleBooksStore(StoreBase):
name = 'Google Books'
@ -1268,6 +1278,7 @@ class StoreGoogleBooksStore(StoreBase):
drm_free_only = False
headquarters = 'US'
formats = ['EPUB', 'PDF', 'TXT']
affiliate = False
class StoreGutenbergStore(StoreBase):
name = 'Project Gutenberg'
@ -1277,6 +1288,7 @@ class StoreGutenbergStore(StoreBase):
drm_free_only = True
headquarters = 'US'
formats = ['EPUB', 'HTML', 'MOBI', 'PDB', 'TXT']
affiliate = False
class StoreKoboStore(StoreBase):
name = 'Kobo'
@ -1286,6 +1298,7 @@ class StoreKoboStore(StoreBase):
drm_free_only = False
headquarters = 'CA'
formats = ['EPUB']
affiliate = True
class StoreLegimiStore(StoreBase):
name = 'Legimi'
@ -1296,6 +1309,7 @@ class StoreLegimiStore(StoreBase):
drm_free_only = False
headquarters = 'PL'
formats = ['EPUB']
affiliate = False
class StoreManyBooksStore(StoreBase):
name = 'ManyBooks'
@ -1305,6 +1319,7 @@ class StoreManyBooksStore(StoreBase):
drm_free_only = True
headquarters = 'US'
formats = ['EPUB', 'FB2', 'JAR', 'LIT', 'LRF', 'MOBI', 'PDB', 'PDF', 'RB', 'RTF', 'TCR', 'TXT', 'ZIP']
affiliate = False
class StoreMobileReadStore(StoreBase):
name = 'MobileRead'
@ -1314,6 +1329,7 @@ class StoreMobileReadStore(StoreBase):
drm_free_only = True
headquarters = 'CH'
formats = ['EPUB', 'IMP', 'LRF', 'LIT', 'MOBI', 'PDF']
affiliate = False
class StoreNextoStore(StoreBase):
name = 'Nexto'
@ -1324,6 +1340,7 @@ class StoreNextoStore(StoreBase):
drm_free_only = False
headquarters = 'PL'
formats = ['EPUB', 'PDF']
affiliate = True
class StoreOpenLibraryStore(StoreBase):
name = 'Open Library'
@ -1333,6 +1350,7 @@ class StoreOpenLibraryStore(StoreBase):
drm_free_only = True
headquarters = 'US'
formats = ['DAISY', 'DJVU', 'EPUB', 'MOBI', 'PDF', 'TXT']
affiliate = False
class StoreOReillyStore(StoreBase):
name = 'OReilly'
@ -1342,6 +1360,7 @@ class StoreOReillyStore(StoreBase):
drm_free_only = True
headquarters = 'US'
formats = ['APK', 'DAISY', 'EPUB', 'MOBI', 'PDF']
affiliate = False
class StorePragmaticBookshelfStore(StoreBase):
name = 'Pragmatic Bookshelf'
@ -1351,6 +1370,7 @@ class StorePragmaticBookshelfStore(StoreBase):
drm_free_only = True
headquarters = 'US'
formats = ['EPUB', 'MOBI', 'PDF']
affiliate = False
class StoreSmashwordsStore(StoreBase):
name = 'Smashwords'
@ -1360,6 +1380,7 @@ class StoreSmashwordsStore(StoreBase):
drm_free_only = True
headquarters = 'US'
formats = ['EPUB', 'HTML', 'LRF', 'MOBI', 'PDB', 'RTF', 'TXT']
affiliate = True
class StoreVirtualoStore(StoreBase):
name = 'Virtualo'
@ -1370,6 +1391,7 @@ class StoreVirtualoStore(StoreBase):
drm_free_only = False
headquarters = 'PL'
formats = ['EPUB', 'PDF']
affiliate = False
class StoreWaterstonesUKStore(StoreBase):
name = 'Waterstones UK'
@ -1380,6 +1402,7 @@ class StoreWaterstonesUKStore(StoreBase):
drm_free_only = False
headquarters = 'UK'
formats = ['EPUB', 'PDF']
affiliate = False
class StoreWeightlessBooksStore(StoreBase):
name = 'Weightless Books'
@ -1389,6 +1412,18 @@ class StoreWeightlessBooksStore(StoreBase):
drm_free_only = True
headquarters = 'US'
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):
name = 'Wizards Tower Books'
@ -1398,6 +1433,7 @@ class StoreWizardsTowerBooksStore(StoreBase):
drm_free_only = True
headquarters = 'UK'
formats = ['EPUB', 'MOBI']
affiliate = False
class StoreWoblinkStore(StoreBase):
name = 'Woblink'
@ -1408,6 +1444,7 @@ class StoreWoblinkStore(StoreBase):
drm_free_only = False
headquarters = 'PL'
formats = ['EPUB']
affiliate = False
plugins += [
StoreArchiveOrgStore,
@ -1440,6 +1477,7 @@ plugins += [
StoreVirtualoStore,
StoreWaterstonesUKStore,
StoreWeightlessBooksStore,
StoreWHSmithUKStore,
StoreWizardsTowerBooksStore,
StoreWoblinkStore
]

View File

@ -6,11 +6,17 @@ __license__ = 'GPL 3'
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
__docformat__ = 'restructuredtext en'
import urllib
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.amazon_plugin import AmazonKindleStore
from calibre.gui2.store.search_result import SearchResult
class AmazonUKKindleStore(AmazonKindleStore):
'''
@ -28,3 +34,81 @@ class AmazonUKKindleStore(AmazonKindleStore):
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
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.drm = SearchResult.DRM_UNLOCKED
s.detail_item = id
self.my_get_details(s, timeout)
self.get_author_and_formats(s, timeout)
if not s.author:
continue
yield s
def my_get_details(self, search_result, timeout):
def get_author_and_formats(self, search_result, timeout):
br = browser()
with closing(br.open(search_result.detail_item, timeout=timeout)) as nf:
idata = html.fromstring(nf.read())

View File

@ -9,8 +9,8 @@ __docformat__ = 'restructuredtext en'
import re
from random import shuffle
from PyQt4.Qt import (Qt, QDialog, QDialogButtonBox, QTimer, QCheckBox,
QVBoxLayout, QIcon, QWidget, QTabWidget)
from PyQt4.Qt import (Qt, QDialog, QDialogButtonBox, QTimer, QCheckBox, QLabel,
QVBoxLayout, QIcon, QWidget, QTabWidget, QGridLayout)
from calibre.gui2 import JSONConfig, info_dialog
from calibre.gui2.progress_indicator import ProgressIndicator
@ -80,7 +80,7 @@ class SearchDialog(QDialog, Ui_Dialog):
self.progress_checker.start(100)
self.restore_state()
def setup_store_checks(self):
# Add check boxes for each store so the user
# can disable searching specific stores on a
@ -88,18 +88,25 @@ class SearchDialog(QDialog, Ui_Dialog):
existing = {}
for n in self.store_checks:
existing[n] = self.store_checks[n].isChecked()
self.store_checks = {}
stores_check_widget = QWidget()
store_list_layout = QVBoxLayout()
store_list_layout = QGridLayout()
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.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
store_list_layout.addStretch()
store_list_layout.setRowStretch(store_list_layout.rowCount(), 10)
self.store_list.setWidget(stores_check_widget)
def build_adv_search(self):
@ -250,14 +257,14 @@ class SearchDialog(QDialog, Ui_Dialog):
button_box.accepted.connect(d.accept)
button_box.rejected.connect(d.reject)
d.setWindowTitle(_('Customize get books search'))
tab_widget = QTabWidget(d)
v.addWidget(tab_widget)
v.addWidget(button_box)
chooser_config_widget = StoreChooserWidget()
search_config_widget = StoreConfigWidget(self.config)
tab_widget.addTab(chooser_config_widget, _('Choose stores'))
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