Add Google Books to Get Books stores. Turned off by default.

This commit is contained in:
Kovid Goyal 2011-05-18 09:02:21 -06:00
commit 1eecc5270c
7 changed files with 167 additions and 37 deletions

View File

@ -1164,6 +1164,11 @@ class StoreFoylesUKStore(StoreBase):
description = _('Foyles of London, online.') description = _('Foyles of London, online.')
actual_plugin = 'calibre.gui2.store.foyles_uk_plugin:FoylesUKStore' actual_plugin = 'calibre.gui2.store.foyles_uk_plugin:FoylesUKStore'
class StoreGoogleBooksStore(StoreBase):
name = 'Google Books'
description = _('Google Books')
actual_plugin = 'calibre.gui2.store.google_books_plugin:GoogleBooksStore'
class StoreGutenbergStore(StoreBase): class StoreGutenbergStore(StoreBase):
name = 'Project Gutenberg' name = 'Project Gutenberg'
description = _('The first producer of free ebooks.') description = _('The first producer of free ebooks.')
@ -1219,7 +1224,8 @@ plugins += [StoreArchiveOrgStore, StoreAmazonKindleStore, StoreAmazonDEKindleSto
StoreBeamEBooksDEStore, StoreBeWriteStore, StoreBeamEBooksDEStore, StoreBeWriteStore,
StoreDieselEbooksStore, StoreEbookscomStore, StoreEPubBuyDEStore, StoreDieselEbooksStore, StoreEbookscomStore, StoreEPubBuyDEStore,
StoreEHarlequinStore, StoreFeedbooksStore, StoreEHarlequinStore, StoreFeedbooksStore,
StoreFoylesUKStore, StoreGutenbergStore, StoreKoboStore, StoreManyBooksStore, StoreFoylesUKStore, StoreGoogleBooksStore, StoreGutenbergStore,
StoreKoboStore, StoreManyBooksStore,
StoreMobileReadStore, StoreNextoStore, StoreOpenLibraryStore, StoreSmashwordsStore, StoreMobileReadStore, StoreNextoStore, StoreOpenLibraryStore, StoreSmashwordsStore,
StoreWaterstonesUKStore, StoreWeightlessBooksStore, StoreWizardsTowerBooksStore] StoreWaterstonesUKStore, StoreWeightlessBooksStore, StoreWizardsTowerBooksStore]

View File

@ -631,10 +631,11 @@ class Application(QApplication):
if (islinux or isfreebsd) and st in ('windows', 'motif', 'cde'): if (islinux or isfreebsd) and st in ('windows', 'motif', 'cde'):
from PyQt4.Qt import QStyleFactory from PyQt4.Qt import QStyleFactory
styles = set(map(unicode, QStyleFactory.keys())) styles = set(map(unicode, QStyleFactory.keys()))
if 'Cleanlooks' in styles: if 'Plastique' in styles and os.environ.get('KDE_FULL_SESSION',
self.setStyle('Cleanlooks') False):
else:
self.setStyle('Plastique') self.setStyle('Plastique')
elif 'Cleanlooks' in styles:
self.setStyle('Cleanlooks')
def _send_file_open_events(self): def _send_file_open_events(self):
with self._file_open_lock: with self._file_open_lock:

View File

@ -68,7 +68,7 @@ class StoreAction(InterfaceAction):
a = ' '.join(a) a = ' '.join(a)
corrected_authors.append(a) corrected_authors.append(a)
return ' & '.join(corrected_authors) return ' & '.join(corrected_authors).strip()
def search_author(self): def search_author(self):
row = self._get_selected_row() row = self._get_selected_row()
@ -87,7 +87,7 @@ class StoreAction(InterfaceAction):
mi = self.gui.current_view().model().get_book_display_info(row) mi = self.gui.current_view().model().get_book_display_info(row)
title = mi.title title = mi.title
return title return title.strip()
def search_title(self): def search_title(self):
row = self._get_selected_row() row = self._get_selected_row()

View File

@ -0,0 +1,93 @@
# -*- 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 urllib
from contextlib import closing
from lxml import html
from PyQt4.Qt import QUrl
from calibre import browser, url_slash_cleaner
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 GoogleBooksStore(BasicStoreConfig, StorePlugin):
def open(self, parent=None, detail_item=None, external=False):
url = 'http://books.google.com/'
if external or self.config.get('open_external', False):
open_url(QUrl(url_slash_cleaner(detail_item if detail_item else url)))
else:
d = WebStoreDialog(self.gui, url, parent, detail_item)
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.google.com/search?tbm=bks&q=' + urllib.quote_plus(query)
br = browser()
counter = max_results
with closing(br.open(url, timeout=timeout)) as f:
doc = html.fromstring(f.read())
for data in doc.xpath('//ol[@id="rso"]/li'):
if counter <= 0:
break
id = ''.join(data.xpath('.//h3/a/@href'))
if not id:
continue
title = ''.join(data.xpath('.//h3/a//text()'))
authors = data.xpath('.//span[@class="gl"]//a//text()')
if authors[-1].strip().lower() == 'preview':
authors = authors[:-1]
else:
continue
author = ', '.join(authors)
counter -= 1
s = SearchResult()
s.title = title.strip()
s.author = author.strip()
s.detail_item = id.strip()
s.drm = SearchResult.DRM_UNKNOWN
yield s
def get_details(self, search_result, timeout):
br = browser()
with closing(br.open(search_result.detail_item, timeout=timeout)) as nf:
doc = html.fromstring(nf.read())
search_result.cover_url = ''.join(doc.xpath('//div[@class="sidebarcover"]//img/@src'))
# Try to get the set price.
price = ''.join(doc.xpath('//div[@class="buy-price-container"]/span[contains(@class, "buy-price")]/text()'))
# Try to get the price inside of a buy button.
if not price.strip():
price = ''.join(doc.xpath('//div[@class="buy-container"]/a/text()'))
price = price.split('-')[-1]
# No price set for this book.
if not price.strip():
price = '$0.00'
search_result.price = price.strip()
search_result.formats = ', '.join(doc.xpath('//div[contains(@class, "download-panel-div")]//a/text()')).upper()
if not search_result.formats:
search_result.formats = _('Unknown')
return True

View File

@ -26,7 +26,7 @@ class NextoStore(BasicStoreConfig, StorePlugin):
def open(self, parent=None, detail_item=None, external=False): def open(self, parent=None, detail_item=None, external=False):
pid = '155711' pid = '155711'
url = 'http://www.nexto.pl/ebooki_c1015.xml?pid=' + pid url = 'http://www.nexto.pl/ebooki_c1015.xml'
detail_url = None detail_url = None
if detail_item: if detail_item:

View File

@ -52,7 +52,7 @@ class SearchDialog(QDialog, Ui_Dialog):
stores_check_widget.setLayout(stores_group_layout) stores_check_widget.setLayout(stores_group_layout)
for x in sorted(self.store_plugins.keys(), key=lambda x: x.lower()): for x in sorted(self.store_plugins.keys(), key=lambda x: x.lower()):
cbox = QCheckBox(x) cbox = QCheckBox(x)
cbox.setChecked(True) cbox.setChecked(False)
stores_group_layout.addWidget(cbox) stores_group_layout.addWidget(cbox)
setattr(self, 'store_check_' + x, cbox) setattr(self, 'store_check_' + x, cbox)
stores_group_layout.addStretch() stores_group_layout.addStretch()

View File

@ -44,40 +44,70 @@ class WizardsTowerBooksStore(BasicStoreConfig, StorePlugin):
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[@class="gridp"]//td'): if 'search.html' in f.geturl():
if counter <= 0: for data in doc.xpath('//table[@class="gridp"]//td'):
break if counter <= 0:
break
id = ''.join(data.xpath('.//span[@class="prti"]/a/@href'))
id = id.strip() id = ''.join(data.xpath('.//span[@class="prti"]/a/@href'))
if not id: id = id.strip()
continue if not id:
continue
cover_url = ''.join(data.xpath('.//div[@class="prim"]/a/img/@src'))
cover_url = url_slash_cleaner(self.url + cover_url.strip()) cover_url = ''.join(data.xpath('.//div[@class="prim"]/a/img/@src'))
cover_url = url_slash_cleaner(self.url + cover_url.strip())
price = ''.join(data.xpath('.//font[@class="selling_price"]//text()'))
price = price.strip() price = ''.join(data.xpath('.//font[@class="selling_price"]//text()'))
if not price: price = price.strip()
continue if not price:
continue
title = ''.join(data.xpath('.//span[@class="prti"]/a/b/text()'))
author = ''.join(data.xpath('.//p[@class="last"]/text()')) title = ''.join(data.xpath('.//span[@class="prti"]/a/b/text()'))
a, b, author = author.partition(' by ') author = ''.join(data.xpath('.//p[@class="last"]/text()'))
a, b, author = author.partition(' by ')
counter -= 1
counter -= 1
s = SearchResult()
s.cover_url = cover_url
s.title = title.strip()
s.author = author.strip()
s.price = price.strip()
s.detail_item = id.strip()
s.drm = SearchResult.DRM_UNLOCKED
yield s
# Exact match brought us to the books detail page.
else:
s = SearchResult() s = SearchResult()
s.cover_url = cover_url
s.title = title.strip() cover_url = ''.join(doc.xpath('//div[@id="image"]/a/img[@title="Zoom"]/@src')).strip()
s.author = author.strip() s.cover_url = url_slash_cleaner(self.url + cover_url.strip())
s.price = price.strip()
s.detail_item = id.strip() s.title = ''.join(doc.xpath('//form[@name="details"]/h1/text()')).strip()
authors = doc.xpath('//p[contains(., "Author:")]//text()')
author_index = None
for i, a in enumerate(authors):
if 'author' in a.lower():
author_index = i + 1
break
if author_index is not None and len(authors) > author_index:
a = authors[author_index]
a = a.replace(u'\xa0', '')
s.author = a.strip()
s.price = ''.join(doc.xpath('//span[@id="price_selling"]//text()')).strip()
s.detail_item = f.geturl().replace(self.url, '').strip()
s.formats = ', '.join(doc.xpath('//select[@id="N1_"]//option//text()'))
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):
if search_result.formats:
return False
br = browser() br = browser()
with closing(br.open(url_slash_cleaner(self.url + search_result.detail_item), timeout=timeout)) as nf: with closing(br.open(url_slash_cleaner(self.url + search_result.detail_item), timeout=timeout)) as nf:
idata = html.fromstring(nf.read()) idata = html.fromstring(nf.read())