From db385fac26a541ac57f6d42c93d4d80fff47e183 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sat, 7 May 2011 15:50:07 +0100 Subject: [PATCH 01/16] First cut at beam ebooks. Also fix format names in foyles and waterstones to match other stores. --- src/calibre/customize/builtins.py | 12 ++- .../gui2/store/beam_ebooks_de_plugin.py | 89 +++++++++++++++++++ src/calibre/gui2/store/foyles_uk_plugin.py | 2 +- .../gui2/store/waterstones_uk_plugin.py | 2 +- 4 files changed, 100 insertions(+), 5 deletions(-) create mode 100644 src/calibre/gui2/store/beam_ebooks_de_plugin.py diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py index 36bcbdbfe2..ff9861c537 100644 --- a/src/calibre/customize/builtins.py +++ b/src/calibre/customize/builtins.py @@ -1171,13 +1171,19 @@ class StoreFoylesUKStore(StoreBase): description = _('Foyles of London, online') actual_plugin = 'calibre.gui2.store.foyles_uk_plugin:FoylesUKStore' -class AmazonDEKindleStore(StoreBase): +class StoreAmazonDEKindleStore(StoreBase): name = 'Amazon DE Kindle' description = _('Kindle eBooks') actual_plugin = 'calibre.gui2.store.amazon_de_plugin:AmazonDEKindleStore' -plugins += [StoreAmazonKindleStore, AmazonDEKindleStore, StoreAmazonUKKindleStore, - StoreBaenWebScriptionStore, StoreBNStore, +class StoreBeamEBooksDEStore(StoreBase): + name = 'Beam EBooks DE' + description = _('Kindle eBooks') + actual_plugin = 'calibre.gui2.store.beam_ebooks_de_plugin:BeamEBooksDEStore' + +plugins += [StoreAmazonKindleStore, StoreAmazonDEKindleStore, + StoreAmazonUKKindleStore, + StoreBaenWebScriptionStore, StoreBNStore, StoreBeamEBooksDEStore, StoreBeWriteStore, StoreDieselEbooksStore, StoreEbookscomStore, StoreEHarlequinStoretore, StoreFeedbooksStore, StoreFoylesUKStore, StoreGutenbergStore, StoreKoboStore, StoreManyBooksStore, diff --git a/src/calibre/gui2/store/beam_ebooks_de_plugin.py b/src/calibre/gui2/store/beam_ebooks_de_plugin.py new file mode 100644 index 0000000000..18cff7a411 --- /dev/null +++ b/src/calibre/gui2/store/beam_ebooks_de_plugin.py @@ -0,0 +1,89 @@ +# -*- coding: utf-8 -*- + +from __future__ import (unicode_literals, division, absolute_import, print_function) + +__license__ = 'GPL 3' +__copyright__ = '2011, John Schember ' +__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 BeamEBooksDEStore(BasicStoreConfig, StorePlugin): + + def open(self, parent=None, detail_item=None, external=False): + url = 'http://www.beam-ebooks.de' + url_details = 'http://www.beam-ebooks.de{0}' + + if external or self.config.get('open_external', False): + if detail_item: + url = url_details.format(detail_item) + open_url(QUrl(url)) + else: + detail_url = None + if detail_item: + detail_url = url_details.format(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.beam-ebooks.de/suchergebnis.php?Type=&sw=' + urllib2.quote(query) + print(url) + br = browser() + + counter = max_results + with closing(br.open(url, timeout=timeout)) as f: + doc = html.fromstring(f.read()) + print(doc) + for data in doc.xpath('//table[tr/td/div[@class="stil2"]]'): + print('here1') + if counter <= 0: + break + + id = ''.join(data.xpath('./tr/td/div[@class="stil2"]/a/@href')).strip() + print('here', id) + if not id: + continue + cover_url = ''.join(data.xpath('./tr/td[1]/a/img/@src')) + if cover_url: + cover_url = 'http://www.beam-ebooks.de' + cover_url + title = ''.join(data.xpath('./tr/td/div[@class="stil2"]/a/b/text()')) + author = ' '.join(data.xpath('./tr/td/div[@class="stil2"]/child::b/text()|./tr/td/div[@class="stil2"]/child::strong/text()')) + price = ''.join(data.xpath('./tr/td[3]/text()')) + print(data.xpath('./tr/td[3]/a/img/@alt')) + pdf = data.xpath('boolean(./tr/td[3]/a/img[contains(@alt, "PDF")]/@alt)') + epub = data.xpath('boolean(./tr/td[3]/a/img[contains(@alt, "ePub")]/@alt)') + mobi = data.xpath('boolean(./tr/td[3]/a/img[contains(@alt, "Mobipocket")]/@alt)') + print(id, cover_url, title, author, price, pdf, epub, mobi) + counter -= 1 + + s = SearchResult() + s.cover_url = cover_url + s.title = title.strip() + s.author = author.strip() + s.price = price + s.drm = SearchResult.DRM_UNLOCKED + s.detail_item = id + formats = [] + if epub: + formats.append('ePub') + if pdf: + formats.append('PDF') + if mobi: + formats.append('MOBI') + s.formats = ', '.join(formats) + + yield s diff --git a/src/calibre/gui2/store/foyles_uk_plugin.py b/src/calibre/gui2/store/foyles_uk_plugin.py index ca35fb6bb2..1a997cd671 100644 --- a/src/calibre/gui2/store/foyles_uk_plugin.py +++ b/src/calibre/gui2/store/foyles_uk_plugin.py @@ -73,6 +73,6 @@ class FoylesUKStore(BasicStoreConfig, StorePlugin): s.price = price s.detail_item = id s.drm = SearchResult.DRM_LOCKED - s.formats = 'EPUB' + s.formats = 'ePub' yield s diff --git a/src/calibre/gui2/store/waterstones_uk_plugin.py b/src/calibre/gui2/store/waterstones_uk_plugin.py index d422165c47..a5065128ba 100644 --- a/src/calibre/gui2/store/waterstones_uk_plugin.py +++ b/src/calibre/gui2/store/waterstones_uk_plugin.py @@ -76,7 +76,7 @@ class WaterstonesUKStore(BasicStoreConfig, StorePlugin): s.detail_item = id formats = [] if epub: - formats.append('EPUB') + formats.append('ePub') if pdf: formats.append('PDF') s.formats = ', '.join(formats) From e219f7214dca2b84b054077506867b2096298572 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sun, 8 May 2011 13:36:08 +0100 Subject: [PATCH 02/16] Add epubbuy. Continue a bit with beam. --- src/calibre/customize/builtins.py | 11 ++- .../gui2/store/beam_ebooks_de_plugin.py | 5 +- src/calibre/gui2/store/epubbuy_de_plugin.py | 73 +++++++++++++++++++ 3 files changed, 84 insertions(+), 5 deletions(-) create mode 100644 src/calibre/gui2/store/epubbuy_de_plugin.py diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py index 7f1705e8ff..5faa2de232 100644 --- a/src/calibre/customize/builtins.py +++ b/src/calibre/customize/builtins.py @@ -1126,7 +1126,7 @@ class StoreEbookscomStore(StoreBase): description = _('The digital bookstore.') actual_plugin = 'calibre.gui2.store.ebooks_com_plugin:EbookscomStore' -class StoreEHarlequinStoretore(StoreBase): +class StoreEHarlequinStore(StoreBase): name = 'eHarlequin' description = _('entertain, enrich, inspire.') actual_plugin = 'calibre.gui2.store.eharlequin_plugin:EHarlequinStore' @@ -1183,14 +1183,19 @@ class StoreAmazonDEKindleStore(StoreBase): class StoreBeamEBooksDEStore(StoreBase): name = 'Beam EBooks DE' - description = _('Kindle eBooks') + description = _('der eBook Shop') actual_plugin = 'calibre.gui2.store.beam_ebooks_de_plugin:BeamEBooksDEStore' +class StoreEPubBuyDEStore(StoreBase): + name = 'EPUBBuy DE' + description = _('EPUBReaders eBook Shop') + actual_plugin = 'calibre.gui2.store.epubbuy_de_plugin:EPubBuyDEStore' + plugins += [StoreAmazonKindleStore, StoreAmazonDEKindleStore, StoreAmazonUKKindleStore, StoreBaenWebScriptionStore, StoreBNStore, StoreBeamEBooksDEStore, StoreBeWriteStore, StoreDieselEbooksStore, StoreEbookscomStore, - StoreEHarlequinStoretore, StoreFeedbooksStore, + StoreEPubBuyDEStore, StoreEHarlequinStore, StoreFeedbooksStore, StoreFoylesUKStore, StoreGutenbergStore, StoreKoboStore, StoreManyBooksStore, StoreMobileReadStore, StoreOpenLibraryStore, StoreSmashwordsStore, StoreWaterstonesUKStore] diff --git a/src/calibre/gui2/store/beam_ebooks_de_plugin.py b/src/calibre/gui2/store/beam_ebooks_de_plugin.py index 18cff7a411..d5a9d8f12c 100644 --- a/src/calibre/gui2/store/beam_ebooks_de_plugin.py +++ b/src/calibre/gui2/store/beam_ebooks_de_plugin.py @@ -23,8 +23,8 @@ from calibre.gui2.store.web_store_dialog import WebStoreDialog class BeamEBooksDEStore(BasicStoreConfig, StorePlugin): def open(self, parent=None, detail_item=None, external=False): - url = 'http://www.beam-ebooks.de' - url_details = 'http://www.beam-ebooks.de{0}' + url = 'http://www.affiliwelt.net/klick.php?log=no&prid=908&pid=2&bannerid=10072&url=http://www.beam-ebooks.de' + url_details = 'http://www.affiliwelt.net/klick.php?log=no&prid=908&pid=2&bannerid=10072&url=http://www.beam-ebooks.de{0}' if external or self.config.get('open_external', False): if detail_item: @@ -34,6 +34,7 @@ class BeamEBooksDEStore(BasicStoreConfig, StorePlugin): detail_url = None if detail_item: detail_url = url_details.format(detail_item) + print(detail_url) d = WebStoreDialog(self.gui, url, parent, detail_url) d.setWindowTitle(self.name) d.set_tags(self.config.get('tags', '')) diff --git a/src/calibre/gui2/store/epubbuy_de_plugin.py b/src/calibre/gui2/store/epubbuy_de_plugin.py new file mode 100644 index 0000000000..6b92e101f8 --- /dev/null +++ b/src/calibre/gui2/store/epubbuy_de_plugin.py @@ -0,0 +1,73 @@ +# -*- coding: utf-8 -*- + +from __future__ import (unicode_literals, division, absolute_import, print_function) + +__license__ = 'GPL 3' +__copyright__ = '2011, John Schember ' +__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 EPubBuyDEStore(BasicStoreConfig, StorePlugin): + + def open(self, parent=None, detail_item=None, external=False): + url = 'http://www.epubbuy.com/' + url_details = '{0}' + + if external or self.config.get('open_external', False): + if detail_item: + url = url_details.format(detail_item) + open_url(QUrl(url)) + else: + detail_url = None + if detail_item: + detail_url = url_details.format(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.epubbuy.com/search.php?search_query=' + 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('//li[contains(@class, "ajax_block_product")]'): + if counter <= 0: + break + + id = ''.join(data.xpath('./div[@class="center_block"]/a[@class="product_img_link"]/@href')).strip() + if not id: + continue + cover_url = ''.join(data.xpath('./div[@class="center_block"]/a[@class="product_img_link"]/img/@src')) + if cover_url: + cover_url = 'http://www.epubbuy.com' + cover_url + title = ''.join(data.xpath('./div[@class="center_block"]/a[@class="product_img_link"]/@title')) + author = ''.join(data.xpath('./div[@class="center_block"]/a[2]/text()')) + price = ''.join(data.xpath('.//span[@class="price"]/text()')) + counter -= 1 + + s = SearchResult() + s.cover_url = cover_url + s.title = title.strip() + s.author = author.strip() + s.price = price + s.drm = SearchResult.DRM_UNLOCKED + s.detail_item = id + s.formats = 'ePub' + + yield s From a6261f3202d7caa46636f14f4d3437a28949b07b Mon Sep 17 00:00:00 2001 From: John Schember Date: Sun, 8 May 2011 14:00:52 -0400 Subject: [PATCH 03/16] Borders plugin. --- src/calibre/customize/builtins.py | 8 +- src/calibre/gui2/store/borders_plugin.py | 94 ++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 src/calibre/gui2/store/borders_plugin.py diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py index c1da8391e0..08049e48cf 100644 --- a/src/calibre/customize/builtins.py +++ b/src/calibre/customize/builtins.py @@ -1116,6 +1116,11 @@ class StoreBeWriteStore(StoreBase): description = _('Publishers of fine books.') actual_plugin = 'calibre.gui2.store.bewrite_plugin:BeWriteStore' +class StoreBordersStore(StoreBase): + name = 'Borders' + description = _('Buy Books, Used Books, Music, DVDs & Blu-ray Online.') + actual_plugin = 'calibre.gui2.store.borders_plugin:BordersStore' + class StoreDieselEbooksStore(StoreBase): name = 'Diesel eBooks' description = _('World Famous eBook Store.') @@ -1183,7 +1188,8 @@ class AmazonDEKindleStore(StoreBase): plugins += [StoreAmazonKindleStore, AmazonDEKindleStore, StoreAmazonUKKindleStore, StoreBaenWebScriptionStore, StoreBNStore, - StoreBeWriteStore, StoreDieselEbooksStore, StoreEbookscomStore, + StoreBeWriteStore, StoreBordersStore, + StoreDieselEbooksStore, StoreEbookscomStore, StoreEHarlequinStoretore, StoreFeedbooksStore, StoreFoylesUKStore, StoreGutenbergStore, StoreKoboStore, StoreManyBooksStore, StoreMobileReadStore, StoreOpenLibraryStore, StoreSmashwordsStore, diff --git a/src/calibre/gui2/store/borders_plugin.py b/src/calibre/gui2/store/borders_plugin.py new file mode 100644 index 0000000000..a610c2eaef --- /dev/null +++ b/src/calibre/gui2/store/borders_plugin.py @@ -0,0 +1,94 @@ +# -*- coding: utf-8 -*- + +from __future__ import (unicode_literals, division, absolute_import, print_function) + +__license__ = 'GPL 3' +__copyright__ = '2011, John Schember ' +__docformat__ = 'restructuredtext en' + +import random +import re +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 BordersStore(BasicStoreConfig, StorePlugin): + + def open(self, parent=None, detail_item=None, external=False): + #m_url = 'http://www.dpbolvw.net/' + #h_click = 'click-4879827-10762497' + #d_click = 'click-4879827-10772898' + # Use Kovid's affiliate id 30% of the time. + #if random.randint(1, 10) in (1, 2, 3): + # h_click = 'click-4913808-10762497' + # d_click = 'click-4913808-10772898' + + #url = m_url + h_click + url = 'http://www.borders.com/online/store/Landing?nav=5185+700152' + detail_url = None + if detail_item: + detail_url = 'http://www.borders.com/online/store/TitleDetail?sku=%s' % detail_item + #detail_url = m_url + d_click + detail_item + + if external or self.config.get('open_external', False): + open_url(QUrl(url_slash_cleaner(detail_url if detail_url else url))) + else: + 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.borders.com/online/store/SearchResults?type=44&simple=1&keyword=' + 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('//table[@class="browseResultsTable"]//tr'): + if counter <= 0: + break + + id = ''.join(data.xpath('.//td[1]/a[1]/@href')) + if not id: + continue + id = re.search('(?<=sku=)\d+', id) + if not id: + continue + id = id.group() + + cover_url = ''.join(data.xpath('.//img[@class="jtip prod-item"]/@src')) + + title = ''.join(data.xpath('.//td[@align="left"][1]//a[1]//text()')) + author = ''.join(data.xpath('.//td[@align="left"][1]//a[2]/text()')) + + price = ''.join(data.xpath('.//div[@class="sale_price"]//text()')) + price = re.search('\$[\d.]+', price) + if not price: + continue + price = price.group() + + 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.detail_item = '?url=http://www.kobobooks.com/' + id.strip() + s.drm = SearchResult.DRM_LOCKED + s.formats = 'EPUB' + + yield s From dd56bb7b6d9e32f811ee1a40eea630376f567ef8 Mon Sep 17 00:00:00 2001 From: John Schember Date: Mon, 9 May 2011 18:13:59 -0400 Subject: [PATCH 04/16] Remove Borders store as they do not want to be included. --- src/calibre/customize/builtins.py | 7 +- src/calibre/gui2/store/borders_plugin.py | 94 ------------------------ 2 files changed, 1 insertion(+), 100 deletions(-) delete mode 100644 src/calibre/gui2/store/borders_plugin.py diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py index 08049e48cf..1ca51c2f81 100644 --- a/src/calibre/customize/builtins.py +++ b/src/calibre/customize/builtins.py @@ -1116,11 +1116,6 @@ class StoreBeWriteStore(StoreBase): description = _('Publishers of fine books.') actual_plugin = 'calibre.gui2.store.bewrite_plugin:BeWriteStore' -class StoreBordersStore(StoreBase): - name = 'Borders' - description = _('Buy Books, Used Books, Music, DVDs & Blu-ray Online.') - actual_plugin = 'calibre.gui2.store.borders_plugin:BordersStore' - class StoreDieselEbooksStore(StoreBase): name = 'Diesel eBooks' description = _('World Famous eBook Store.') @@ -1188,7 +1183,7 @@ class AmazonDEKindleStore(StoreBase): plugins += [StoreAmazonKindleStore, AmazonDEKindleStore, StoreAmazonUKKindleStore, StoreBaenWebScriptionStore, StoreBNStore, - StoreBeWriteStore, StoreBordersStore, + StoreBeWriteStore, StoreDieselEbooksStore, StoreEbookscomStore, StoreEHarlequinStoretore, StoreFeedbooksStore, StoreFoylesUKStore, StoreGutenbergStore, StoreKoboStore, StoreManyBooksStore, diff --git a/src/calibre/gui2/store/borders_plugin.py b/src/calibre/gui2/store/borders_plugin.py deleted file mode 100644 index a610c2eaef..0000000000 --- a/src/calibre/gui2/store/borders_plugin.py +++ /dev/null @@ -1,94 +0,0 @@ -# -*- coding: utf-8 -*- - -from __future__ import (unicode_literals, division, absolute_import, print_function) - -__license__ = 'GPL 3' -__copyright__ = '2011, John Schember ' -__docformat__ = 'restructuredtext en' - -import random -import re -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 BordersStore(BasicStoreConfig, StorePlugin): - - def open(self, parent=None, detail_item=None, external=False): - #m_url = 'http://www.dpbolvw.net/' - #h_click = 'click-4879827-10762497' - #d_click = 'click-4879827-10772898' - # Use Kovid's affiliate id 30% of the time. - #if random.randint(1, 10) in (1, 2, 3): - # h_click = 'click-4913808-10762497' - # d_click = 'click-4913808-10772898' - - #url = m_url + h_click - url = 'http://www.borders.com/online/store/Landing?nav=5185+700152' - detail_url = None - if detail_item: - detail_url = 'http://www.borders.com/online/store/TitleDetail?sku=%s' % detail_item - #detail_url = m_url + d_click + detail_item - - if external or self.config.get('open_external', False): - open_url(QUrl(url_slash_cleaner(detail_url if detail_url else url))) - else: - 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.borders.com/online/store/SearchResults?type=44&simple=1&keyword=' + 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('//table[@class="browseResultsTable"]//tr'): - if counter <= 0: - break - - id = ''.join(data.xpath('.//td[1]/a[1]/@href')) - if not id: - continue - id = re.search('(?<=sku=)\d+', id) - if not id: - continue - id = id.group() - - cover_url = ''.join(data.xpath('.//img[@class="jtip prod-item"]/@src')) - - title = ''.join(data.xpath('.//td[@align="left"][1]//a[1]//text()')) - author = ''.join(data.xpath('.//td[@align="left"][1]//a[2]/text()')) - - price = ''.join(data.xpath('.//div[@class="sale_price"]//text()')) - price = re.search('\$[\d.]+', price) - if not price: - continue - price = price.group() - - 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.detail_item = '?url=http://www.kobobooks.com/' + id.strip() - s.drm = SearchResult.DRM_LOCKED - s.formats = 'EPUB' - - yield s From fb493ff8de2efb4cb0fbe8fbca0776074398e16e Mon Sep 17 00:00:00 2001 From: John Schember Date: Mon, 9 May 2011 18:44:00 -0400 Subject: [PATCH 05/16] Store: List of stores that do not want to be included. Weightless Books plugin. --- src/calibre/customize/builtins.py | 27 ++++--- src/calibre/gui2/store/declined.txt | 5 ++ .../gui2/store/weightless_books_plugin.py | 76 +++++++++++++++++++ 3 files changed, 97 insertions(+), 11 deletions(-) create mode 100644 src/calibre/gui2/store/declined.txt create mode 100644 src/calibre/gui2/store/weightless_books_plugin.py diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py index 1ca51c2f81..2c516f22c0 100644 --- a/src/calibre/customize/builtins.py +++ b/src/calibre/customize/builtins.py @@ -1096,6 +1096,11 @@ class StoreAmazonKindleStore(StoreBase): description = _('Kindle books from Amazon') actual_plugin = 'calibre.gui2.store.amazon_plugin:AmazonKindleStore' +class StoreAmazonDEKindleStore(StoreBase): + name = 'Amazon DE Kindle' + description = _('Kindle eBooks') + actual_plugin = 'calibre.gui2.store.amazon_de_plugin:AmazonDEKindleStore' + class StoreAmazonUKKindleStore(StoreBase): name = 'Amazon UK Kindle' description = _('Kindle books from Amazon.uk') @@ -1136,6 +1141,11 @@ class StoreFeedbooksStore(StoreBase): description = _('Read anywhere.') actual_plugin = 'calibre.gui2.store.feedbooks_plugin:FeedbooksStore' +class StoreFoylesUKStore(StoreBase): + name = 'Foyles UK' + description = _('Foyles of London, online') + actual_plugin = 'calibre.gui2.store.foyles_uk_plugin:FoylesUKStore' + class StoreGutenbergStore(StoreBase): name = 'Project Gutenberg' description = _('The first producer of free ebooks.') @@ -1171,23 +1181,18 @@ class StoreWaterstonesUKStore(StoreBase): description = _('Feel every word') actual_plugin = 'calibre.gui2.store.waterstones_uk_plugin:WaterstonesUKStore' -class StoreFoylesUKStore(StoreBase): - name = 'Foyles UK' - description = _('Foyles of London, online') - actual_plugin = 'calibre.gui2.store.foyles_uk_plugin:FoylesUKStore' +class StoreWeightlessBooksStore(StoreBase): + name = 'Weightless Books' + description = '(e)Books That Don\'t Weigh You Down' + actual_plugin = 'calibre.gui2.store.weightless_books_plugin:WeightlessBooksStore' -class AmazonDEKindleStore(StoreBase): - name = 'Amazon DE Kindle' - description = _('Kindle eBooks') - actual_plugin = 'calibre.gui2.store.amazon_de_plugin:AmazonDEKindleStore' - -plugins += [StoreAmazonKindleStore, AmazonDEKindleStore, StoreAmazonUKKindleStore, +plugins += [StoreAmazonKindleStore, StoreAmazonDEKindleStore, StoreAmazonUKKindleStore, StoreBaenWebScriptionStore, StoreBNStore, StoreBeWriteStore, StoreDieselEbooksStore, StoreEbookscomStore, StoreEHarlequinStoretore, StoreFeedbooksStore, StoreFoylesUKStore, StoreGutenbergStore, StoreKoboStore, StoreManyBooksStore, StoreMobileReadStore, StoreOpenLibraryStore, StoreSmashwordsStore, - StoreWaterstonesUKStore] + StoreWaterstonesUKStore, StoreWeightlessBooksStore] # }}} diff --git a/src/calibre/gui2/store/declined.txt b/src/calibre/gui2/store/declined.txt new file mode 100644 index 0000000000..2b0e5caed2 --- /dev/null +++ b/src/calibre/gui2/store/declined.txt @@ -0,0 +1,5 @@ +This is a list of stores that objected, declined +or asked not to be included in the store integration. + +* Borders (http://www.borders.com/) +* WH Smith (http://www.whsmith.co.uk/) diff --git a/src/calibre/gui2/store/weightless_books_plugin.py b/src/calibre/gui2/store/weightless_books_plugin.py new file mode 100644 index 0000000000..3fa1c76851 --- /dev/null +++ b/src/calibre/gui2/store/weightless_books_plugin.py @@ -0,0 +1,76 @@ +# -*- coding: utf-8 -*- + +from __future__ import (unicode_literals, division, absolute_import, print_function) + +__license__ = 'GPL 3' +__copyright__ = '2011, John Schember ' +__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 WeightlessBooksStore(BasicStoreConfig, StorePlugin): + + def open(self, parent=None, detail_item=None, external=False): + url = 'http://weightlessbooks.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://weightlessbooks.com/?s=' + 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('//li[@id="product"]'): + if counter <= 0: + break + + id = ''.join(data.xpath('.//div[@class="cover"]/a/@href')) + if not id: + continue + + cover_url = ''.join(data.xpath('.//div[@class="cover"]/a/img/@src')) + + price = ''.join(data.xpath('.//div[@class="buy_buttons"]/b[1]/text()')) + if not price: + continue + + formats = ', '.join(data.xpath('.//select[@class="eStore_variation"]//option//text()')) + formats = formats.upper() + + title = ''.join(data.xpath('.//h3/a/text()')) + author = ''.join(data.xpath('.//h3//text()')) + author = author.replace(title, '') + + 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 + s.formats = formats + + yield s From e1b0cfdacfdf772eaa9cf700191b431bc5d1f5df Mon Sep 17 00:00:00 2001 From: John Schember Date: Mon, 9 May 2011 18:49:13 -0400 Subject: [PATCH 06/16] Store: Make mention of declined.txt in comments. --- src/calibre/gui2/store/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/calibre/gui2/store/__init__.py b/src/calibre/gui2/store/__init__.py index 214ede3372..35fa440b28 100644 --- a/src/calibre/gui2/store/__init__.py +++ b/src/calibre/gui2/store/__init__.py @@ -43,6 +43,8 @@ class StorePlugin(object): # {{{ The easiest way to handle affiliate money payouts is to randomly select between the author's affiliate id and calibre's affiliate id so that 70% of the time the author's id is used. + + See declined.txt for a list of stores that do not want to be included. ''' def __init__(self, gui, name): From 4a20c220f13fe4922ed83a566cb4b03b27146406 Mon Sep 17 00:00:00 2001 From: John Schember Date: Mon, 9 May 2011 18:51:05 -0400 Subject: [PATCH 07/16] ... --- src/calibre/gui2/store/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/gui2/store/__init__.py b/src/calibre/gui2/store/__init__.py index 35fa440b28..d58ccbda84 100644 --- a/src/calibre/gui2/store/__init__.py +++ b/src/calibre/gui2/store/__init__.py @@ -9,7 +9,7 @@ __docformat__ = 'restructuredtext en' class StorePlugin(object): # {{{ ''' A plugin representing an online ebook repository (store). The store can - be a comercial store that sells ebooks or a source of free downloadable + be a commercial store that sells ebooks or a source of free downloadable ebooks. Note that this class is the base class for these plugins, however, to From f4878b5c7fa19fe85805263dce45a7f8d4b4a18e Mon Sep 17 00:00:00 2001 From: John Schember Date: Mon, 9 May 2011 19:05:01 -0400 Subject: [PATCH 08/16] Store: Always sort stores in case-insentive alphabetical order. --- src/calibre/gui2/actions/store.py | 2 +- src/calibre/gui2/store/search/search.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/calibre/gui2/actions/store.py b/src/calibre/gui2/actions/store.py index 90f2cac3cd..1989250bc8 100644 --- a/src/calibre/gui2/actions/store.py +++ b/src/calibre/gui2/actions/store.py @@ -27,7 +27,7 @@ class StoreAction(InterfaceAction): self.store_menu.clear() self.store_menu.addAction(_('Search'), self.search) self.store_menu.addSeparator() - for n, p in self.gui.istores.items(): + for n, p in sorted(self.gui.istores.items(), key=lambda x: x[0].lower()): self.store_menu.addAction(n, partial(self.open_store, p)) self.qaction.setMenu(self.store_menu) diff --git a/src/calibre/gui2/store/search/search.py b/src/calibre/gui2/store/search/search.py index 07d4afca54..62e4e97f11 100644 --- a/src/calibre/gui2/store/search/search.py +++ b/src/calibre/gui2/store/search/search.py @@ -47,7 +47,7 @@ class SearchDialog(QDialog, Ui_Dialog): # per search basis. stores_group_layout = QVBoxLayout() self.stores_group.setLayout(stores_group_layout) - for x in self.store_plugins: + for x in sorted(self.store_plugins.keys(), key=lambda x: x.lower()): cbox = QCheckBox(x) cbox.setChecked(True) stores_group_layout.addWidget(cbox) From 6ebdda7aa54af32316cb962f7b815a692eef7f75 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Tue, 10 May 2011 12:58:54 +0100 Subject: [PATCH 09/16] Beam store, with working links --- src/calibre/gui2/store/beam_ebooks_de_plugin.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/calibre/gui2/store/beam_ebooks_de_plugin.py b/src/calibre/gui2/store/beam_ebooks_de_plugin.py index d5a9d8f12c..385bbc5456 100644 --- a/src/calibre/gui2/store/beam_ebooks_de_plugin.py +++ b/src/calibre/gui2/store/beam_ebooks_de_plugin.py @@ -23,8 +23,8 @@ from calibre.gui2.store.web_store_dialog import WebStoreDialog class BeamEBooksDEStore(BasicStoreConfig, StorePlugin): def open(self, parent=None, detail_item=None, external=False): - url = 'http://www.affiliwelt.net/klick.php?log=no&prid=908&pid=2&bannerid=10072&url=http://www.beam-ebooks.de' - url_details = 'http://www.affiliwelt.net/klick.php?log=no&prid=908&pid=2&bannerid=10072&url=http://www.beam-ebooks.de{0}' + url = 'http://klick.affiliwelt.net/klick.php?bannerid=10072&pid=32307&prid=908' + url_details = 'http://klick.affiliwelt.net/klick.php?bannerid=10730&pid=32307&prid=908&prodid={0}' if external or self.config.get('open_external', False): if detail_item: @@ -34,7 +34,6 @@ class BeamEBooksDEStore(BasicStoreConfig, StorePlugin): detail_url = None if detail_item: detail_url = url_details.format(detail_item) - print(detail_url) d = WebStoreDialog(self.gui, url, parent, detail_url) d.setWindowTitle(self.name) d.set_tags(self.config.get('tags', '')) @@ -42,33 +41,28 @@ class BeamEBooksDEStore(BasicStoreConfig, StorePlugin): def search(self, query, max_results=10, timeout=60): url = 'http://www.beam-ebooks.de/suchergebnis.php?Type=&sw=' + urllib2.quote(query) - print(url) br = browser() counter = max_results with closing(br.open(url, timeout=timeout)) as f: doc = html.fromstring(f.read()) - print(doc) for data in doc.xpath('//table[tr/td/div[@class="stil2"]]'): - print('here1') if counter <= 0: break id = ''.join(data.xpath('./tr/td/div[@class="stil2"]/a/@href')).strip() - print('here', id) if not id: continue + id = id[7:] cover_url = ''.join(data.xpath('./tr/td[1]/a/img/@src')) if cover_url: cover_url = 'http://www.beam-ebooks.de' + cover_url title = ''.join(data.xpath('./tr/td/div[@class="stil2"]/a/b/text()')) author = ' '.join(data.xpath('./tr/td/div[@class="stil2"]/child::b/text()|./tr/td/div[@class="stil2"]/child::strong/text()')) price = ''.join(data.xpath('./tr/td[3]/text()')) - print(data.xpath('./tr/td[3]/a/img/@alt')) pdf = data.xpath('boolean(./tr/td[3]/a/img[contains(@alt, "PDF")]/@alt)') epub = data.xpath('boolean(./tr/td[3]/a/img[contains(@alt, "ePub")]/@alt)') mobi = data.xpath('boolean(./tr/td[3]/a/img[contains(@alt, "Mobipocket")]/@alt)') - print(id, cover_url, title, author, price, pdf, epub, mobi) counter -= 1 s = SearchResult() From c10349c5dd2b518cc7b6c8ab26dd102c28902a47 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Tue, 10 May 2011 15:18:16 +0100 Subject: [PATCH 10/16] Final versions of Beam and EPUBBuy. --- .../gui2/store/beam_ebooks_de_plugin.py | 19 ++++++++++++++----- src/calibre/gui2/store/epubbuy_de_plugin.py | 17 ++++++++++++----- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/src/calibre/gui2/store/beam_ebooks_de_plugin.py b/src/calibre/gui2/store/beam_ebooks_de_plugin.py index 385bbc5456..ee73607c57 100644 --- a/src/calibre/gui2/store/beam_ebooks_de_plugin.py +++ b/src/calibre/gui2/store/beam_ebooks_de_plugin.py @@ -24,7 +24,8 @@ class BeamEBooksDEStore(BasicStoreConfig, StorePlugin): def open(self, parent=None, detail_item=None, external=False): url = 'http://klick.affiliwelt.net/klick.php?bannerid=10072&pid=32307&prid=908' - url_details = 'http://klick.affiliwelt.net/klick.php?bannerid=10730&pid=32307&prid=908&prodid={0}' + url_details = ('http://klick.affiliwelt.net/klick.php?' + 'bannerid=10730&pid=32307&prid=908&prodid={0}') if external or self.config.get('open_external', False): if detail_item: @@ -54,15 +55,23 @@ class BeamEBooksDEStore(BasicStoreConfig, StorePlugin): if not id: continue id = id[7:] + print(id) cover_url = ''.join(data.xpath('./tr/td[1]/a/img/@src')) if cover_url: cover_url = 'http://www.beam-ebooks.de' + cover_url title = ''.join(data.xpath('./tr/td/div[@class="stil2"]/a/b/text()')) - author = ' '.join(data.xpath('./tr/td/div[@class="stil2"]/child::b/text()|./tr/td/div[@class="stil2"]/child::strong/text()')) + author = ' '.join(data.xpath('./tr/td/div[@class="stil2"]/' + 'child::b/text()' + '|' + './tr/td/div[@class="stil2"]/' + 'child::strong/text()')) price = ''.join(data.xpath('./tr/td[3]/text()')) - pdf = data.xpath('boolean(./tr/td[3]/a/img[contains(@alt, "PDF")]/@alt)') - epub = data.xpath('boolean(./tr/td[3]/a/img[contains(@alt, "ePub")]/@alt)') - mobi = data.xpath('boolean(./tr/td[3]/a/img[contains(@alt, "Mobipocket")]/@alt)') + pdf = data.xpath( + 'boolean(./tr/td[3]/a/img[contains(@alt, "PDF")]/@alt)') + epub = data.xpath( + 'boolean(./tr/td[3]/a/img[contains(@alt, "ePub")]/@alt)') + mobi = data.xpath( + 'boolean(./tr/td[3]/a/img[contains(@alt, "Mobipocket")]/@alt)') counter -= 1 s = SearchResult() diff --git a/src/calibre/gui2/store/epubbuy_de_plugin.py b/src/calibre/gui2/store/epubbuy_de_plugin.py index 6b92e101f8..242ef76793 100644 --- a/src/calibre/gui2/store/epubbuy_de_plugin.py +++ b/src/calibre/gui2/store/epubbuy_de_plugin.py @@ -23,8 +23,9 @@ from calibre.gui2.store.web_store_dialog import WebStoreDialog class EPubBuyDEStore(BasicStoreConfig, StorePlugin): def open(self, parent=None, detail_item=None, external=False): - url = 'http://www.epubbuy.com/' - url_details = '{0}' + url = 'http://klick.affiliwelt.net/klick.php?bannerid=47653&pid=32307&prid=2627' + url_details = ('http://klick.affiliwelt.net/klick.php?bannerid=47653' + '&pid=32307&prid=2627&prodid={0}') if external or self.config.get('open_external', False): if detail_item: @@ -50,13 +51,19 @@ class EPubBuyDEStore(BasicStoreConfig, StorePlugin): if counter <= 0: break - id = ''.join(data.xpath('./div[@class="center_block"]/a[@class="product_img_link"]/@href')).strip() + id = ''.join(data.xpath('./div[@class="center_block"]' + '/p[contains(text(), "artnr:")]/text()')).strip() if not id: continue - cover_url = ''.join(data.xpath('./div[@class="center_block"]/a[@class="product_img_link"]/img/@src')) + id = id[6:].strip() + if not id: + continue + cover_url = ''.join(data.xpath('./div[@class="center_block"]' + '/a[@class="product_img_link"]/img/@src')) if cover_url: cover_url = 'http://www.epubbuy.com' + cover_url - title = ''.join(data.xpath('./div[@class="center_block"]/a[@class="product_img_link"]/@title')) + title = ''.join(data.xpath('./div[@class="center_block"]' + '/a[@class="product_img_link"]/@title')) author = ''.join(data.xpath('./div[@class="center_block"]/a[2]/text()')) price = ''.join(data.xpath('.//span[@class="price"]/text()')) counter -= 1 From 2e94faa700e44cf8abb02fdbd40920cc3484599c Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 10 May 2011 08:37:27 -0600 Subject: [PATCH 11/16] Welt der Physik by schuster --- recipes/welt_der_physik.recipe | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 recipes/welt_der_physik.recipe diff --git a/recipes/welt_der_physik.recipe b/recipes/welt_der_physik.recipe new file mode 100644 index 0000000000..b3ce9cce1b --- /dev/null +++ b/recipes/welt_der_physik.recipe @@ -0,0 +1,20 @@ +from calibre.web.feeds.news import BasicNewsRecipe + +class AdvancedUserRecipe1303841067(BasicNewsRecipe): + + title = u'Welt der Physik' + __author__ = 'schuster' + remove_tags_befor = [dict(name='div', attrs={'class':'inhalt_bild_text_printonly'})] + remove_tags_after = [dict(name='span', attrs={'class':'clearinhalt_bild'})] + remove_tags = [dict(attrs={'class':['invisible', 'searchfld', 'searchbtn', 'topnavi', 'topsearch']}), + dict(id=['naservice', 'phservicemenu', '',]), + dict(name=['naservice'])] + oldest_article = 7 + max_articles_per_feed = 100 + no_stylesheets = True + use_embedded_content = False + language = 'de' + remove_javascript = True + + + feeds = [(u'Nachrichten und Neuigkeiten', u'http://www.weltderphysik.de/rss/alles.xml')] From 839c2e909a0f53cb43b60022c4a76a6f57b848c9 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 10 May 2011 08:46:57 -0600 Subject: [PATCH 12/16] Replica Vedetelor by Silviu Cotoara --- recipes/icons/replicavedetelor.png | Bin 0 -> 709 bytes recipes/replicavedetelor.recipe | 53 +++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 recipes/icons/replicavedetelor.png create mode 100644 recipes/replicavedetelor.recipe diff --git a/recipes/icons/replicavedetelor.png b/recipes/icons/replicavedetelor.png new file mode 100644 index 0000000000000000000000000000000000000000..57ba42c64ca752ecd07d720f292f7adf44e92997 GIT binary patch literal 709 zcmWlWQAiqb9LF!Z2MMD|wvFX5;u;0k!;n1;8P&|KFjwY|brWgsIHH+BNQ6iXxtHQ# zO|B7k*fN*HnS&uc#1T^tqi|+*i>RAWyU8Zb29qA*&)D_w`~CR7e7~O`-|0TngX*dq zRS*PKcR$pdrF-RaWR=oxZmk)Bpo+OZqoqrd>5L%eixz?uPQ_xW2)3i;yZ|nWz>fpB z2C>tj4c_5rfnClfd7aq>VjfK=9{t3D5hW-Hni&?Luw#k_UL82%U?RkA^YmI85OP%y z3EtrNIK`%T#765d3YUCrFulTB#+mp=T8>iufuNj-f*u$TaL8S&#ZH2M0JycGq@cgd zBGVaO$cZj5BPST!!i-p>ZFQ=B5Y%{?hr7+WRNYN@=7~x zw0p|uWyC}RzZJL!^mQe zr;>uv&jAbu>ogsV8=Xviof9Qglp=j0t--B zp!WPBA&FeKzQb~T?)mWjO8Ld;``M-5&(rVLb_dmw_Qk|8wK~`_`}Jz`hq2`vi-Pd` zPD`cmZ&i`GxizqtYS@pyXbqx?Km&>fUNfsZ4T+os`LVOvA6VKyqIX9US@iL*z{)`y zIi{aQ3x$=fY+h5>Xj3fMma(v3tQ*?QrkJ*CjV+~3WvH&OY!zgd1lq4qG`7vxtA8p! zs=mA(Tg)`IV2Ae6C#`1-;S(-OV literal 0 HcmV?d00001 diff --git a/recipes/replicavedetelor.recipe b/recipes/replicavedetelor.recipe new file mode 100644 index 0000000000..b6f1a51d67 --- /dev/null +++ b/recipes/replicavedetelor.recipe @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +#!/usr/bin/env python + +__license__ = 'GPL v3' +__copyright__ = u'2011, ' +''' +replicavedetelor.ro +''' + +from calibre.web.feeds.news import BasicNewsRecipe + +class ReplicaVedetelor(BasicNewsRecipe): + title = u'Replica Vedetelor' + __author__ = u'Silviu Cotoara' + description = u'Ofer\u0103 vedetelor dreptul la replic\u0103' + publisher = 'Replica Vedetelor' + oldest_article = 5 + language = 'ro' + max_articles_per_feed = 100 + no_stylesheets = True + use_embedded_content = False + category = 'Ziare,Reviste,Vedete' + encoding = 'utf-8' + cover_url = 'http://www.webart-software.eu/_pics/lucrari_referinta/medium/84/1-Replica-Vedetelor.jpg' + + conversion_options = { + 'comments' : description + ,'tags' : category + ,'language' : language + ,'publisher' : publisher + } + + keep_only_tags = [ + dict(name='div', attrs={'id':'zona-continut'}) + ] + + remove_tags = [ + dict(name='ul', attrs={'id':['lista-imagini']}) + , dict(name='form', attrs={'id':['f-trimite-unui-prieten']}) + + ] + + remove_tags_after = [ + dict(name='form', attrs={'id':['f-trimite-unui-prieten']}) + ] + + feeds = [ + (u'Feeds', u'http://www.replicavedetelor.ro/feed') + ] + + def preprocess_html(self, soup): + return self.adeify_images(soup) + From a325f9e8d6e1b0e6363823739b778fa611e393ba Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 10 May 2011 08:49:55 -0600 Subject: [PATCH 13/16] Fix #780459 (Kobo not recognized) --- src/calibre/devices/kobo/driver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/devices/kobo/driver.py b/src/calibre/devices/kobo/driver.py index 3a0254ef20..f5c9a74733 100644 --- a/src/calibre/devices/kobo/driver.py +++ b/src/calibre/devices/kobo/driver.py @@ -38,7 +38,7 @@ class KOBO(USBMS): VENDOR_ID = [0x2237] PRODUCT_ID = [0x4161] - BCD = [0x0110] + BCD = [0x0110, 0x0323] VENDOR_NAME = ['KOBO_INC', 'KOBO'] WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = ['.KOBOEREADER', 'EREADER'] From d9e0e361408aa64eecbcf683428d06c8ab10cac3 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Tue, 10 May 2011 16:18:13 +0100 Subject: [PATCH 14/16] Remove a print statement --- src/calibre/gui2/store/beam_ebooks_de_plugin.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/calibre/gui2/store/beam_ebooks_de_plugin.py b/src/calibre/gui2/store/beam_ebooks_de_plugin.py index ee73607c57..b589a8c310 100644 --- a/src/calibre/gui2/store/beam_ebooks_de_plugin.py +++ b/src/calibre/gui2/store/beam_ebooks_de_plugin.py @@ -55,7 +55,6 @@ class BeamEBooksDEStore(BasicStoreConfig, StorePlugin): if not id: continue id = id[7:] - print(id) cover_url = ''.join(data.xpath('./tr/td[1]/a/img/@src')) if cover_url: cover_url = 'http://www.beam-ebooks.de' + cover_url From 009090c99eee1fa83f028ac7dc8a21fbe8c62703 Mon Sep 17 00:00:00 2001 From: John Schember Date: Tue, 10 May 2011 18:23:18 -0400 Subject: [PATCH 15/16] Store: Add Wizards Tower Books plugin. --- src/calibre/customize/builtins.py | 7 +- .../gui2/store/wizards_tower_books_plugin.py | 88 +++++++++++++++++++ 2 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 src/calibre/gui2/store/wizards_tower_books_plugin.py diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py index 2c516f22c0..579403f6ba 100644 --- a/src/calibre/customize/builtins.py +++ b/src/calibre/customize/builtins.py @@ -1186,6 +1186,11 @@ class StoreWeightlessBooksStore(StoreBase): description = '(e)Books That Don\'t Weigh You Down' actual_plugin = 'calibre.gui2.store.weightless_books_plugin:WeightlessBooksStore' +class StoreWizardsTowerBooksStore(StoreBase): + name = 'Wizards Tower Books' + description = 'Wizard\'s Tower Press' + actual_plugin = 'calibre.gui2.store.wizards_tower_books_plugin:WizardsTowerBooksStore' + plugins += [StoreAmazonKindleStore, StoreAmazonDEKindleStore, StoreAmazonUKKindleStore, StoreBaenWebScriptionStore, StoreBNStore, StoreBeWriteStore, @@ -1193,6 +1198,6 @@ plugins += [StoreAmazonKindleStore, StoreAmazonDEKindleStore, StoreAmazonUKKindl StoreEHarlequinStoretore, StoreFeedbooksStore, StoreFoylesUKStore, StoreGutenbergStore, StoreKoboStore, StoreManyBooksStore, StoreMobileReadStore, StoreOpenLibraryStore, StoreSmashwordsStore, - StoreWaterstonesUKStore, StoreWeightlessBooksStore] + StoreWaterstonesUKStore, StoreWeightlessBooksStore, StoreWizardsTowerBooksStore] # }}} diff --git a/src/calibre/gui2/store/wizards_tower_books_plugin.py b/src/calibre/gui2/store/wizards_tower_books_plugin.py new file mode 100644 index 0000000000..56bb00ff7e --- /dev/null +++ b/src/calibre/gui2/store/wizards_tower_books_plugin.py @@ -0,0 +1,88 @@ +# -*- coding: utf-8 -*- + +from __future__ import (unicode_literals, division, absolute_import, print_function) + +__license__ = 'GPL 3' +__copyright__ = '2011, John Schember ' +__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 WizardsTowerBooksStore(BasicStoreConfig, StorePlugin): + + url = 'http://www.wizardstowerbooks.com/' + + def open(self, parent=None, detail_item=None, external=False): + if detail_item: + detail_item = self.url + detail_item + + 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, self.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.wizardstowerbooks.com/search.html?for=' + urllib.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('//table[@class="gridp"]//td'): + if counter <= 0: + break + + id = ''.join(data.xpath('.//span[@class="prti"]/a/@href')) + id = id.strip() + 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()) + + price = ''.join(data.xpath('.//font[@class="selling_price"]//text()')) + price = price.strip() + if not price: + continue + + title = ''.join(data.xpath('.//span[@class="prti"]/a/b/text()')) + author = ''.join(data.xpath('.//p[@class="last"]/text()')) + a, b, author = author.partition(' by ') + + 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 + + def get_details(self, search_result, timeout): + br = browser() + with closing(br.open(url_slash_cleaner(self.url + search_result.detail_item), timeout=timeout)) as nf: + idata = html.fromstring(nf.read()) + + formats = ', '.join(idata.xpath('//select[@id="N1_"]//option//text()')) + search_result.formats = formats.upper() + + return True From 1073c627d63e07b766fbc02f982782664a513908 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 10 May 2011 22:45:59 -0600 Subject: [PATCH 16/16] Fix #780849 (Spelling error in Welcome to calibre) --- src/calibre/gui2/wizard/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/gui2/wizard/__init__.py b/src/calibre/gui2/wizard/__init__.py index a32347dc72..6b1a793fc8 100644 --- a/src/calibre/gui2/wizard/__init__.py +++ b/src/calibre/gui2/wizard/__init__.py @@ -435,7 +435,7 @@ class DevicePage(QWizardPage, DeviceUI): self.registerField("device", self.device_view) def initializePage(self): - self.label.setText(_('Choose you e-book device. If your device is' + self.label.setText(_('Choose your e-book device. If your device is' ' not in the list, choose a "%s" device.')%Device.manufacturer) self.man_model = ManufacturerModel() self.manufacturer_view.setModel(self.man_model)