From 9c41f1173ad0962e5ecc529abd1107017d3aa26e Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sat, 12 Jan 2013 12:35:26 +0100 Subject: [PATCH 1/9] Fix amazon stores to account for website change. --- .../gui2/store/stores/amazon_plugin.py | 28 +++++++------------ .../gui2/store/stores/amazon_uk_plugin.py | 16 +++-------- 2 files changed, 14 insertions(+), 30 deletions(-) diff --git a/src/calibre/gui2/store/stores/amazon_plugin.py b/src/calibre/gui2/store/stores/amazon_plugin.py index 4bd1a42c9d..e26aa2a133 100644 --- a/src/calibre/gui2/store/stores/amazon_plugin.py +++ b/src/calibre/gui2/store/stores/amazon_plugin.py @@ -6,8 +6,6 @@ __license__ = 'GPL 3' __copyright__ = '2011, John Schember ' __docformat__ = 'restructuredtext en' -import random -import re from contextlib import closing from lxml import html @@ -130,16 +128,16 @@ class AmazonKindleStore(StorePlugin): data_xpath = '//div[contains(@class, "prod")]' format_xpath = './/ul[contains(@class, "rsltL")]//span[contains(@class, "lrg") and not(contains(@class, "bld"))]/text()' - asin_xpath = './/div[@class="image"]/a[1]' + asin_xpath = '@name' cover_xpath = './/img[@class="productImage"]/@src' title_xpath = './/h3[@class="newaps"]/a//text()' author_xpath = './/h3[@class="newaps"]//span[contains(@class, "reg")]/text()' price_xpath = './/ul[contains(@class, "rsltL")]//span[contains(@class, "lrg") and contains(@class, "bld")]/text()' - + for data in doc.xpath(data_xpath): if counter <= 0: break - + # Even though we are searching digital-text only Amazon will still # put in results for non Kindle books (author pages). Se we need # to explicitly check if the item is a Kindle book and ignore it @@ -147,21 +145,15 @@ class AmazonKindleStore(StorePlugin): format = ''.join(data.xpath(format_xpath)) if 'kindle' not in format.lower(): continue - + # We must have an asin otherwise we can't easily reference the # book later. - asin_href = None - asin_a = data.xpath(asin_xpath) - if asin_a: - asin_href = asin_a[0].get('href', '') - m = re.search(r'/dp/(?P.+?)(/|$)', asin_href) - if m: - asin = m.group('asin') - else: - continue + asin = data.xpath(asin_xpath) + if asin: + asin = asin[0] else: continue - + cover_url = ''.join(data.xpath(cover_xpath)) title = ''.join(data.xpath(title_xpath)) @@ -172,9 +164,9 @@ class AmazonKindleStore(StorePlugin): pass price = ''.join(data.xpath(price_xpath)) - + counter -= 1 - + s = SearchResult() s.cover_url = cover_url.strip() s.title = title.strip() diff --git a/src/calibre/gui2/store/stores/amazon_uk_plugin.py b/src/calibre/gui2/store/stores/amazon_uk_plugin.py index 0f9caf8f3e..486671c729 100644 --- a/src/calibre/gui2/store/stores/amazon_uk_plugin.py +++ b/src/calibre/gui2/store/stores/amazon_uk_plugin.py @@ -6,8 +6,6 @@ __license__ = 'GPL 3' __copyright__ = '2011, John Schember ' __docformat__ = 'restructuredtext en' -import re - from contextlib import closing from lxml import html @@ -53,7 +51,7 @@ class AmazonUKKindleStore(StorePlugin): data_xpath = '//div[contains(@class, "prod")]' format_xpath = './/ul[contains(@class, "rsltL")]//span[contains(@class, "lrg") and not(contains(@class, "bld"))]/text()' - asin_xpath = './/div[@class="image"]/a[1]' + asin_xpath = '@name' cover_xpath = './/img[@class="productImage"]/@src' title_xpath = './/h3[@class="newaps"]/a//text()' author_xpath = './/h3[@class="newaps"]//span[contains(@class, "reg")]/text()' @@ -73,15 +71,9 @@ class AmazonUKKindleStore(StorePlugin): # We must have an asin otherwise we can't easily reference the # book later. - asin_href = None - asin_a = data.xpath(asin_xpath) - if asin_a: - asin_href = asin_a[0].get('href', '') - m = re.search(r'/dp/(?P.+?)(/|$)', asin_href) - if m: - asin = m.group('asin') - else: - continue + asin = data.xpath(asin_xpath) + if asin: + asin = asin[0] else: continue From a84feb91b77f1252f78cfe5fe39bbfc60aca9111 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sun, 13 Jan 2013 11:23:43 +0100 Subject: [PATCH 2/9] Libre.de changed its name to ebook.de. --- src/calibre/customize/builtins.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py index a6dde30a94..20552d5d9c 100644 --- a/src/calibre/customize/builtins.py +++ b/src/calibre/customize/builtins.py @@ -1471,9 +1471,9 @@ class StoreLegimiStore(StoreBase): affiliate = True class StoreLibreDEStore(StoreBase): - name = 'Libri DE' + name = 'ebook.de' author = 'Charles Haley' - description = u'Sicher Bücher, Hörbücher und Downloads online bestellen.' + description = u'All Ihre Bücher immer dabei. Suchen, finden, kaufen: so einfach wie nie. ebook.de war libre.de' actual_plugin = 'calibre.gui2.store.stores.libri_de_plugin:LibreDEStore' headquarters = 'DE' From 41224d8ba0cd662c6d109deb22a7764cefd230e1 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Tue, 15 Jan 2013 10:58:07 +0100 Subject: [PATCH 3/9] 1) Make amazon EU plugins self sufficient 2) Fix broken waterstores UK store. --- .../gui2/store/stores/amazon_de_plugin.py | 95 ++++++++++++++++++- .../gui2/store/stores/amazon_es_plugin.py | 94 +++++++++++++++++- .../gui2/store/stores/amazon_fr_plugin.py | 95 ++++++++++++++++++- .../gui2/store/stores/amazon_it_plugin.py | 95 ++++++++++++++++++- .../gui2/store/stores/amazon_uk_plugin.py | 29 +++--- .../store/stores/waterstones_uk_plugin.py | 4 +- 6 files changed, 392 insertions(+), 20 deletions(-) diff --git a/src/calibre/gui2/store/stores/amazon_de_plugin.py b/src/calibre/gui2/store/stores/amazon_de_plugin.py index 58c67122e1..71ed8b0491 100644 --- a/src/calibre/gui2/store/stores/amazon_de_plugin.py +++ b/src/calibre/gui2/store/stores/amazon_de_plugin.py @@ -7,9 +7,100 @@ __license__ = 'GPL 3' __copyright__ = '2011, John Schember ' __docformat__ = 'restructuredtext en' -from calibre.gui2.store.stores.amazon_uk_plugin import AmazonUKKindleStore +from contextlib import closing +from lxml import html -class AmazonDEKindleStore(AmazonUKKindleStore): +from PyQt4.Qt import QUrl + +from calibre.gui2.store import StorePlugin +from calibre import browser +from calibre.gui2 import open_url +from calibre.gui2.store.search_result import SearchResult + + + +# This class is copy/pasted from amason_uk_plugin. Do not modify it in any +# other amazon EU plugin. Be sure to paste it into all other amazon EU plugins +# when modified. + +class AmazonEUBase(StorePlugin): + ''' + For comments on the implementation, please see amazon_plugin.py + ''' + + def open(self, parent=None, detail_item=None, external=False): + + store_link = self.store_link % self.aff_id + if detail_item: + self.aff_id['asin'] = detail_item + store_link = self.store_link_details % self.aff_id + open_url(QUrl(store_link)) + + def search(self, query, max_results=10, timeout=60): + url = self.search_url + query.encode('ascii', 'backslashreplace').replace('%', '%25').replace('\\x', '%').replace(' ', '+') + br = browser() + + counter = max_results + with closing(br.open(url, timeout=timeout)) as f: + doc = html.fromstring(f.read())#.decode('latin-1', 'replace')) + + data_xpath = '//div[contains(@class, "prod")]' + format_xpath = './/ul[contains(@class, "rsltL")]//span[contains(@class, "lrg") and not(contains(@class, "bld"))]/text()' + asin_xpath = '@name' + cover_xpath = './/img[@class="productImage"]/@src' + title_xpath = './/h3[@class="newaps"]/a//text()' + author_xpath = './/h3[@class="newaps"]//span[contains(@class, "reg")]/text()' + price_xpath = './/ul[contains(@class, "rsltL")]//span[contains(@class, "lrg") and contains(@class, "bld")]/text()' + + for data in doc.xpath(data_xpath): + if counter <= 0: + break + + # Even though we are searching digital-text only Amazon will still + # put in results for non Kindle books (author pages). Se we need + # to explicitly check if the item is a Kindle book and ignore it + # if it isn't. + format_ = ''.join(data.xpath(format_xpath)) + if 'kindle' not in format_.lower(): + continue + + # We must have an asin otherwise we can't easily reference the + # book later. + asin = data.xpath(asin_xpath) + if asin: + asin = asin[0] + else: + continue + + cover_url = ''.join(data.xpath(cover_xpath)) + + title = ''.join(data.xpath(title_xpath)) + author = ''.join(data.xpath(author_xpath)) + try: + if self.author_article: + author = author.split(self.author_article, 1)[1].split(" (")[0] + except: + pass + + price = ''.join(data.xpath(price_xpath)) + + counter -= 1 + + s = SearchResult() + s.cover_url = cover_url.strip() + s.title = title.strip() + s.author = author.strip() + s.price = price.strip() + s.detail_item = asin.strip() + s.drm = SearchResult.DRM_UNKNOWN + s.formats = 'Kindle' + + yield s + + def get_details(self, search_result, timeout): + pass + +class AmazonDEKindleStore(AmazonEUBase): ''' For comments on the implementation, please see amazon_plugin.py ''' diff --git a/src/calibre/gui2/store/stores/amazon_es_plugin.py b/src/calibre/gui2/store/stores/amazon_es_plugin.py index 427927a5a6..d613ced2a5 100644 --- a/src/calibre/gui2/store/stores/amazon_es_plugin.py +++ b/src/calibre/gui2/store/stores/amazon_es_plugin.py @@ -7,9 +7,99 @@ __license__ = 'GPL 3' __copyright__ = '2011, John Schember ' __docformat__ = 'restructuredtext en' -from calibre.gui2.store.stores.amazon_uk_plugin import AmazonUKKindleStore +from contextlib import closing +from lxml import html -class AmazonESKindleStore(AmazonUKKindleStore): +from PyQt4.Qt import QUrl + +from calibre.gui2.store import StorePlugin +from calibre import browser +from calibre.gui2 import open_url +from calibre.gui2.store.search_result import SearchResult + + +# This class is copy/pasted from amason_uk_plugin. Do not modify it in any +# other amazon EU plugin. Be sure to paste it into all other amazon EU plugins +# when modified. + +class AmazonEUBase(StorePlugin): + ''' + For comments on the implementation, please see amazon_plugin.py + ''' + + def open(self, parent=None, detail_item=None, external=False): + + store_link = self.store_link % self.aff_id + if detail_item: + self.aff_id['asin'] = detail_item + store_link = self.store_link_details % self.aff_id + open_url(QUrl(store_link)) + + def search(self, query, max_results=10, timeout=60): + url = self.search_url + query.encode('ascii', 'backslashreplace').replace('%', '%25').replace('\\x', '%').replace(' ', '+') + br = browser() + + counter = max_results + with closing(br.open(url, timeout=timeout)) as f: + doc = html.fromstring(f.read())#.decode('latin-1', 'replace')) + + data_xpath = '//div[contains(@class, "prod")]' + format_xpath = './/ul[contains(@class, "rsltL")]//span[contains(@class, "lrg") and not(contains(@class, "bld"))]/text()' + asin_xpath = '@name' + cover_xpath = './/img[@class="productImage"]/@src' + title_xpath = './/h3[@class="newaps"]/a//text()' + author_xpath = './/h3[@class="newaps"]//span[contains(@class, "reg")]/text()' + price_xpath = './/ul[contains(@class, "rsltL")]//span[contains(@class, "lrg") and contains(@class, "bld")]/text()' + + for data in doc.xpath(data_xpath): + if counter <= 0: + break + + # Even though we are searching digital-text only Amazon will still + # put in results for non Kindle books (author pages). Se we need + # to explicitly check if the item is a Kindle book and ignore it + # if it isn't. + format_ = ''.join(data.xpath(format_xpath)) + if 'kindle' not in format_.lower(): + continue + + # We must have an asin otherwise we can't easily reference the + # book later. + asin = data.xpath(asin_xpath) + if asin: + asin = asin[0] + else: + continue + + cover_url = ''.join(data.xpath(cover_xpath)) + + title = ''.join(data.xpath(title_xpath)) + author = ''.join(data.xpath(author_xpath)) + try: + if self.author_article: + author = author.split(self.author_article, 1)[1].split(" (")[0] + except: + pass + + price = ''.join(data.xpath(price_xpath)) + + counter -= 1 + + s = SearchResult() + s.cover_url = cover_url.strip() + s.title = title.strip() + s.author = author.strip() + s.price = price.strip() + s.detail_item = asin.strip() + s.drm = SearchResult.DRM_UNKNOWN + s.formats = 'Kindle' + + yield s + + def get_details(self, search_result, timeout): + pass + +class AmazonESKindleStore(AmazonEUBase): ''' For comments on the implementation, please see amazon_plugin.py ''' diff --git a/src/calibre/gui2/store/stores/amazon_fr_plugin.py b/src/calibre/gui2/store/stores/amazon_fr_plugin.py index e3eff50450..22e5d8ec8e 100644 --- a/src/calibre/gui2/store/stores/amazon_fr_plugin.py +++ b/src/calibre/gui2/store/stores/amazon_fr_plugin.py @@ -8,9 +8,100 @@ __copyright__ = '2011, John Schember ' __docformat__ = 'restructuredtext en' -from calibre.gui2.store.stores.amazon_uk_plugin import AmazonUKKindleStore +from contextlib import closing +from lxml import html -class AmazonFRKindleStore(AmazonUKKindleStore): +from PyQt4.Qt import QUrl + +from calibre.gui2.store import StorePlugin +from calibre import browser +from calibre.gui2 import open_url +from calibre.gui2.store.search_result import SearchResult + + + +# This class is copy/pasted from amason_uk_plugin. Do not modify it in any +# other amazon EU plugin. Be sure to paste it into all other amazon EU plugins +# when modified. + +class AmazonEUBase(StorePlugin): + ''' + For comments on the implementation, please see amazon_plugin.py + ''' + + def open(self, parent=None, detail_item=None, external=False): + + store_link = self.store_link % self.aff_id + if detail_item: + self.aff_id['asin'] = detail_item + store_link = self.store_link_details % self.aff_id + open_url(QUrl(store_link)) + + def search(self, query, max_results=10, timeout=60): + url = self.search_url + query.encode('ascii', 'backslashreplace').replace('%', '%25').replace('\\x', '%').replace(' ', '+') + br = browser() + + counter = max_results + with closing(br.open(url, timeout=timeout)) as f: + doc = html.fromstring(f.read())#.decode('latin-1', 'replace')) + + data_xpath = '//div[contains(@class, "prod")]' + format_xpath = './/ul[contains(@class, "rsltL")]//span[contains(@class, "lrg") and not(contains(@class, "bld"))]/text()' + asin_xpath = '@name' + cover_xpath = './/img[@class="productImage"]/@src' + title_xpath = './/h3[@class="newaps"]/a//text()' + author_xpath = './/h3[@class="newaps"]//span[contains(@class, "reg")]/text()' + price_xpath = './/ul[contains(@class, "rsltL")]//span[contains(@class, "lrg") and contains(@class, "bld")]/text()' + + for data in doc.xpath(data_xpath): + if counter <= 0: + break + + # Even though we are searching digital-text only Amazon will still + # put in results for non Kindle books (author pages). Se we need + # to explicitly check if the item is a Kindle book and ignore it + # if it isn't. + format_ = ''.join(data.xpath(format_xpath)) + if 'kindle' not in format_.lower(): + continue + + # We must have an asin otherwise we can't easily reference the + # book later. + asin = data.xpath(asin_xpath) + if asin: + asin = asin[0] + else: + continue + + cover_url = ''.join(data.xpath(cover_xpath)) + + title = ''.join(data.xpath(title_xpath)) + author = ''.join(data.xpath(author_xpath)) + try: + if self.author_article: + author = author.split(self.author_article, 1)[1].split(" (")[0] + except: + pass + + price = ''.join(data.xpath(price_xpath)) + + counter -= 1 + + s = SearchResult() + s.cover_url = cover_url.strip() + s.title = title.strip() + s.author = author.strip() + s.price = price.strip() + s.detail_item = asin.strip() + s.drm = SearchResult.DRM_UNKNOWN + s.formats = 'Kindle' + + yield s + + def get_details(self, search_result, timeout): + pass + +class AmazonFRKindleStore(AmazonEUBase): ''' For comments on the implementation, please see amazon_plugin.py ''' diff --git a/src/calibre/gui2/store/stores/amazon_it_plugin.py b/src/calibre/gui2/store/stores/amazon_it_plugin.py index 669831f89d..14c571e8e1 100644 --- a/src/calibre/gui2/store/stores/amazon_it_plugin.py +++ b/src/calibre/gui2/store/stores/amazon_it_plugin.py @@ -7,9 +7,100 @@ __license__ = 'GPL 3' __copyright__ = '2011, John Schember ' __docformat__ = 'restructuredtext en' -from calibre.gui2.store.stores.amazon_uk_plugin import AmazonUKKindleStore +from contextlib import closing +from lxml import html -class AmazonITKindleStore(AmazonUKKindleStore): +from PyQt4.Qt import QUrl + +from calibre.gui2.store import StorePlugin +from calibre import browser +from calibre.gui2 import open_url +from calibre.gui2.store.search_result import SearchResult + + +# This class is copy/pasted from amason_uk_plugin. Do not modify it in any +# other amazon EU plugin. Be sure to paste it into all other amazon EU plugins +# when modified. + +class AmazonEUBase(StorePlugin): + ''' + For comments on the implementation, please see amazon_plugin.py + ''' + + def open(self, parent=None, detail_item=None, external=False): + + store_link = self.store_link % self.aff_id + if detail_item: + self.aff_id['asin'] = detail_item + store_link = self.store_link_details % self.aff_id + open_url(QUrl(store_link)) + + def search(self, query, max_results=10, timeout=60): + url = self.search_url + query.encode('ascii', 'backslashreplace').replace('%', '%25').replace('\\x', '%').replace(' ', '+') + br = browser() + + counter = max_results + with closing(br.open(url, timeout=timeout)) as f: + doc = html.fromstring(f.read())#.decode('latin-1', 'replace')) + + data_xpath = '//div[contains(@class, "prod")]' + format_xpath = './/ul[contains(@class, "rsltL")]//span[contains(@class, "lrg") and not(contains(@class, "bld"))]/text()' + asin_xpath = '@name' + cover_xpath = './/img[@class="productImage"]/@src' + title_xpath = './/h3[@class="newaps"]/a//text()' + author_xpath = './/h3[@class="newaps"]//span[contains(@class, "reg")]/text()' + price_xpath = './/ul[contains(@class, "rsltL")]//span[contains(@class, "lrg") and contains(@class, "bld")]/text()' + + for data in doc.xpath(data_xpath): + if counter <= 0: + break + + # Even though we are searching digital-text only Amazon will still + # put in results for non Kindle books (author pages). Se we need + # to explicitly check if the item is a Kindle book and ignore it + # if it isn't. + format_ = ''.join(data.xpath(format_xpath)) + if 'kindle' not in format_.lower(): + continue + + # We must have an asin otherwise we can't easily reference the + # book later. + asin = data.xpath(asin_xpath) + if asin: + asin = asin[0] + else: + continue + + cover_url = ''.join(data.xpath(cover_xpath)) + + title = ''.join(data.xpath(title_xpath)) + author = ''.join(data.xpath(author_xpath)) + try: + if self.author_article: + author = author.split(self.author_article, 1)[1].split(" (")[0] + except: + pass + + price = ''.join(data.xpath(price_xpath)) + + counter -= 1 + + s = SearchResult() + s.cover_url = cover_url.strip() + s.title = title.strip() + s.author = author.strip() + s.price = price.strip() + s.detail_item = asin.strip() + s.drm = SearchResult.DRM_UNKNOWN + s.formats = 'Kindle' + + yield s + + def get_details(self, search_result, timeout): + pass + + +class AmazonITKindleStore(AmazonEUBase): ''' For comments on the implementation, please see amazon_plugin.py ''' diff --git a/src/calibre/gui2/store/stores/amazon_uk_plugin.py b/src/calibre/gui2/store/stores/amazon_uk_plugin.py index ab67fc477c..0abc19f92e 100644 --- a/src/calibre/gui2/store/stores/amazon_uk_plugin.py +++ b/src/calibre/gui2/store/stores/amazon_uk_plugin.py @@ -17,19 +17,12 @@ from calibre.gui2 import open_url from calibre.gui2.store import StorePlugin from calibre.gui2.store.search_result import SearchResult -class AmazonUKKindleStore(StorePlugin): - aff_id = {'tag': 'calcharles-21'} - store_link = ('http://www.amazon.co.uk/gp/redirect.html?ie=UTF8&' - 'location=http://www.amazon.co.uk/Kindle-eBooks/b?' - 'ie=UTF8&node=341689031&ref_=sa_menu_kbo2&tag=%(tag)s&' - 'linkCode=ur2&camp=1634&creative=19450') - store_link_details = ('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') - search_url = 'http://www.amazon.co.uk/s/?url=search-alias%3Ddigital-text&field-keywords=' - author_article = 'by ' +# This class is copy/pasted from amason_uk_plugin. Do not modify it in any +# other amazon EU plugin. Be sure to paste it into all other amazon EU plugins +# when modified. +class AmazonEUBase(StorePlugin): ''' For comments on the implementation, please see amazon_plugin.py ''' @@ -105,3 +98,17 @@ class AmazonUKKindleStore(StorePlugin): def get_details(self, search_result, timeout): pass + +class AmazonUKKindleStore(AmazonEUBase): + aff_id = {'tag': 'calcharles-21'} + store_link = ('http://www.amazon.co.uk/gp/redirect.html?ie=UTF8&' + 'location=http://www.amazon.co.uk/Kindle-eBooks/b?' + 'ie=UTF8&node=341689031&ref_=sa_menu_kbo2&tag=%(tag)s&' + 'linkCode=ur2&camp=1634&creative=19450') + store_link_details = ('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') + search_url = 'http://www.amazon.co.uk/s/?url=search-alias%3Ddigital-text&field-keywords=' + + author_article = 'by ' + diff --git a/src/calibre/gui2/store/stores/waterstones_uk_plugin.py b/src/calibre/gui2/store/stores/waterstones_uk_plugin.py index 397b8ee53f..1bcbb865bf 100644 --- a/src/calibre/gui2/store/stores/waterstones_uk_plugin.py +++ b/src/calibre/gui2/store/stores/waterstones_uk_plugin.py @@ -41,7 +41,7 @@ class WaterstonesUKStore(BasicStoreConfig, StorePlugin): d.exec_() def search(self, query, max_results=10, timeout=60): - url = 'http://www.waterstones.com/waterstonesweb/advancedSearch.do?buttonClicked=1&format=3757&bookkeywords=' + urllib2.quote(query) + url = 'http://www.waterstones.com/waterstonesweb/simpleSearch.do?simpleSearchString=ebook+' + urllib2.quote(query) br = browser() @@ -56,6 +56,8 @@ class WaterstonesUKStore(BasicStoreConfig, StorePlugin): if not id: continue cover_url = ''.join(data.xpath('.//div[@class="image"]/a/img/@src')) + if not cover_url.startswith("http"): + cover_url = 'http://www.waterstones.com' + cover_url title = ''.join(data.xpath('./div/div/h2/a/text()')) author = ', '.join(data.xpath('.//p[@class="byAuthor"]/a/text()')) price = ''.join(data.xpath('.//p[@class="price"]/span[@class="priceRed2"]/text()')) From 190af68ca6b1db22b24b1248fafb909b48c13a37 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 15 Jan 2013 21:33:40 +0530 Subject: [PATCH 4/9] Fix #1099835 (description on plugin install dialog resizes dialogbox out of screen) --- src/calibre/gui2/dialogs/plugin_updater.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/calibre/gui2/dialogs/plugin_updater.py b/src/calibre/gui2/dialogs/plugin_updater.py index 4e56db9016..3820169876 100644 --- a/src/calibre/gui2/dialogs/plugin_updater.py +++ b/src/calibre/gui2/dialogs/plugin_updater.py @@ -519,6 +519,7 @@ class PluginUpdaterDialog(SizePersistedDialog): self.description.setFrameStyle(QFrame.Panel | QFrame.Sunken) self.description.setAlignment(Qt.AlignTop | Qt.AlignLeft) self.description.setMinimumHeight(40) + self.description.setWordWrap(True) layout.addWidget(self.description) self.button_box = QDialogButtonBox(QDialogButtonBox.Close) From f5094c211ec3327e3c6996d6b237d31533f510bb Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 16 Jan 2013 10:51:55 +0530 Subject: [PATCH 5/9] Content server: Respect the restriction when picking a random book --- src/calibre/library/server/browse.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/calibre/library/server/browse.py b/src/calibre/library/server/browse.py index e315bb6f3e..0f4878b06c 100644 --- a/src/calibre/library/server/browse.py +++ b/src/calibre/library/server/browse.py @@ -894,7 +894,8 @@ class BrowseServer(object): @Endpoint() def browse_random(self, *args, **kwargs): import random - book_id = random.choice(tuple(self.db.all_ids())) + book_id = random.choice(self.db.search_getting_ids( + '', self.search_restriction)) ans = self.browse_render_details(book_id) return self.browse_template('').format( title='', script='book();', main=ans) From 39aa2072cb791e2f5ce5591e6d2b51aece6354e6 Mon Sep 17 00:00:00 2001 From: davidfor Date: Wed, 16 Jan 2013 17:12:47 +1100 Subject: [PATCH 6/9] Bug #1099190: Updating metadata on startup can be very slow The series_index is stored as a string in the Kobo database. Convert this to a float for comparison with the value in calibre. This will reduce the number of updates done to the database and reduce the time taken to sync. --- src/calibre/devices/kobo/driver.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/calibre/devices/kobo/driver.py b/src/calibre/devices/kobo/driver.py index 1baeb2842d..4fb260f9cf 100644 --- a/src/calibre/devices/kobo/driver.py +++ b/src/calibre/devices/kobo/driver.py @@ -33,7 +33,7 @@ class KOBO(USBMS): gui_name = 'Kobo Reader' description = _('Communicate with the Kobo Reader') author = 'Timothy Legge and David Forrester' - version = (2, 0, 4) + version = (2, 0, 5) dbversion = 0 fwversion = 0 @@ -2353,10 +2353,17 @@ class KOBOTOUCH(KOBO): debug_print('KoboTouch:set_series book.series="%s"'%book.series) debug_print('KoboTouch:set_series book.series_index=', book.series_index) - if book.series == book.kobo_series and book.series_index == book.kobo_series_number: - if show_debug: - debug_print('KoboTouch:set_series - series info the same - not changing') - return + if book.series == book.kobo_series: + kobo_series_number = None + if book.kobo_series_number is not None: + try: + kobo_series_number = float(book.kobo_series_number) + except: + kobo_series_number = None + if kobo_series_number == book.series_index: + if show_debug: + debug_print('KoboTouch:set_series - series info the same - not changing') + return update_query = 'UPDATE content SET Series=?, SeriesNumber==? where BookID is Null and ContentID = ?' if book.series is None: From 732545871c7879c19107b5ad302dfe6c57ce35e1 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 16 Jan 2013 12:11:19 +0530 Subject: [PATCH 7/9] ... --- recipes/nytimes_sub.recipe | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/recipes/nytimes_sub.recipe b/recipes/nytimes_sub.recipe index 35f7b46517..cf25865b9c 100644 --- a/recipes/nytimes_sub.recipe +++ b/recipes/nytimes_sub.recipe @@ -124,19 +124,19 @@ class NYTimes(BasicNewsRecipe): if headlinesOnly: title='New York Times Headlines' description = 'Headlines from the New York Times' - needs_subscription = True + needs_subscription = 'optional' elif webEdition: title='New York Times (Web)' description = 'New York Times on the Web' - needs_subscription = True + needs_subscription = 'optional' elif replaceKindleVersion: title='The New York Times' description = 'Today\'s New York Times' - needs_subscription = True + needs_subscription = 'optional' else: title='New York Times' description = 'Today\'s New York Times' - needs_subscription = True + needs_subscription = 'optional' def decode_url_date(self,url): urlitems = url.split('/') From 992ceaa8566fc3e8b044a7b253b720e558bb3c0b Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 16 Jan 2013 15:55:11 +0530 Subject: [PATCH 8/9] Asco de Vida by Krittika Goyal --- recipes/asco_de_vida.recipe | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 recipes/asco_de_vida.recipe diff --git a/recipes/asco_de_vida.recipe b/recipes/asco_de_vida.recipe new file mode 100644 index 0000000000..fa1944f95d --- /dev/null +++ b/recipes/asco_de_vida.recipe @@ -0,0 +1,20 @@ +from calibre.web.feeds.news import BasicNewsRecipe + +class HindustanTimes(BasicNewsRecipe): + title = u'Asco de vida' + language = 'es' + __author__ = 'Krittika Goyal' + oldest_article = 1 #days + max_articles_per_feed = 25 + #encoding = 'cp1252' + use_embedded_content = False + + no_stylesheets = True + keep_only_tags = dict(name='div', attrs={'class':'box story'}) + + + feeds = [ +('News', + 'http://feeds2.feedburner.com/AscoDeVida'), +] + From 7208c73480ded02511ae4578921b43a362832627 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 16 Jan 2013 16:12:43 +0530 Subject: [PATCH 9/9] E-book-viewer: Fix TOC links without anchors not scrolling to the top of the current flow --- src/calibre/gui2/viewer/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/gui2/viewer/main.py b/src/calibre/gui2/viewer/main.py index cb1d978c0e..903f41eeb5 100644 --- a/src/calibre/gui2/viewer/main.py +++ b/src/calibre/gui2/viewer/main.py @@ -758,7 +758,7 @@ class EbookViewer(MainWindow, Ui_EbookViewer): self.view.scroll_to(frag) else: # Scroll to top - self.view.scroll_to('#') + self.view.scroll_to(0) if self.view.document.ypos == oldpos: # If we are coming from goto_next_section() call this will # cause another goto next section call with the next toc