diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py index 75760ceada..2d45688535 100644 --- a/src/calibre/customize/builtins.py +++ b/src/calibre/customize/builtins.py @@ -1439,15 +1439,6 @@ class StoreEbooksGratuitsStore(StoreBase): # formats = ['EPUB', 'PDF'] # affiliate = True -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.' # noqa - actual_plugin = 'calibre.gui2.store.stores.eharlequin_plugin:EHarlequinStore' - - headquarters = 'CA' - formats = ['EPUB', 'PDF'] - affiliate = True - class StoreEKnigiStore(StoreBase): name = u'еКниги' author = 'Alex Stanev' @@ -1734,7 +1725,6 @@ plugins += [ StoreEbookpointStore, StoreEbookscomStore, StoreEbooksGratuitsStore, - StoreEHarlequinStore, StoreEKnigiStore, StoreEmpikStore, StoreFeedbooksStore, diff --git a/src/calibre/ebooks/rtf/rtfml.py b/src/calibre/ebooks/rtf/rtfml.py index 53d0c6ea07..aafcf52aa8 100644 --- a/src/calibre/ebooks/rtf/rtfml.py +++ b/src/calibre/ebooks/rtf/rtfml.py @@ -71,6 +71,7 @@ def txt2rtf(text): # Escape { and } in the text. text = text.replace('{', r'\'7b') text = text.replace('}', r'\'7d') + text = text.replace('\\', r'\'5c') if not isinstance(text, unicode): return text diff --git a/src/calibre/gui2/store/stores/amazon_ca_plugin.py b/src/calibre/gui2/store/stores/amazon_ca_plugin.py index e0ab82cf2e..17330c1c0c 100644 --- a/src/calibre/gui2/store/stores/amazon_ca_plugin.py +++ b/src/calibre/gui2/store/stores/amazon_ca_plugin.py @@ -55,7 +55,7 @@ class AmazonCAKindleStore(StorePlugin): './/ul[contains(@class, "rsltGridList")]' '//span[contains(@class, "lrg") and not(contains(@class, "bld"))]/text()') asin_xpath = '@name' - cover_xpath = './/img[@class="productImage"]/@src' + cover_xpath = './/img[contains(@class, "productImage")]/@src' title_xpath = './/h3[@class="newaps"]/a//text()' author_xpath = './/h3[@class="newaps"]//span[contains(@class, "reg")]//text()' price_xpath = ( @@ -80,7 +80,7 @@ class AmazonCAKindleStore(StorePlugin): './/ul[contains(@class, "rsltL")]' '//span[contains(@class, "lrg") and not(contains(@class, "bld"))]/text()') asin_xpath = '@name' - cover_xpath = './/img[@class="productImage"]/@src' + cover_xpath = './/img[contains(@class, "productImage")]/@src' title_xpath = './/h3[@class="newaps"]/a//text()' author_xpath = './/h3[@class="newaps"]//span[contains(@class, "reg")]//text()' price_xpath = ( diff --git a/src/calibre/gui2/store/stores/amazon_de_plugin.py b/src/calibre/gui2/store/stores/amazon_de_plugin.py index 7adfe58a81..5125b1cf51 100644 --- a/src/calibre/gui2/store/stores/amazon_de_plugin.py +++ b/src/calibre/gui2/store/stores/amazon_de_plugin.py @@ -94,7 +94,7 @@ class AmazonDEKindleStore(StorePlugin): './/ul[contains(@class, "rsltL")]' '//span[contains(@class, "lrg") and not(contains(@class, "bld"))]/text()') asin_xpath = '@name' - cover_xpath = './/img[@class="productImage"]/@src' + cover_xpath = './/img[contains(@class, "productImage")]/@src' title_xpath = './/h3[@class="newaps"]/a//text()' author_xpath = './/h3[@class="newaps"]//span[contains(@class, "reg")]//text()' price_xpath = ( diff --git a/src/calibre/gui2/store/stores/amazon_es_plugin.py b/src/calibre/gui2/store/stores/amazon_es_plugin.py index fac927a41c..b3464157e5 100644 --- a/src/calibre/gui2/store/stores/amazon_es_plugin.py +++ b/src/calibre/gui2/store/stores/amazon_es_plugin.py @@ -93,7 +93,7 @@ class AmazonESKindleStore(StorePlugin): './/ul[contains(@class, "rsltL")]' '//span[contains(@class, "lrg") and not(contains(@class, "bld"))]/text()') asin_xpath = '@name' - cover_xpath = './/img[@class="productImage"]/@src' + cover_xpath = './/img[contains(@class, "productImage")]/@src' title_xpath = './/h3[@class="newaps"]/a//text()' author_xpath = './/h3[@class="newaps"]//span[contains(@class, "reg")]//text()' price_xpath = ( diff --git a/src/calibre/gui2/store/stores/amazon_fr_plugin.py b/src/calibre/gui2/store/stores/amazon_fr_plugin.py index f9f7ff68d9..ab0050d881 100644 --- a/src/calibre/gui2/store/stores/amazon_fr_plugin.py +++ b/src/calibre/gui2/store/stores/amazon_fr_plugin.py @@ -90,7 +90,7 @@ class AmazonFRKindleStore(StorePlugin): './/ul[contains(@class, "rsltL")]' '//span[contains(@class, "lrg") and not(contains(@class, "bld"))]/text()') asin_xpath = '@name' - cover_xpath = './/img[@class="productImage"]/@src' + cover_xpath = './/img[contains(@class, "productImage")]/@src' title_xpath = './/h3[@class="newaps"]/a//text()' author_xpath = './/h3[@class="newaps"]//span[contains(@class, "reg")]//text()' price_xpath = ( diff --git a/src/calibre/gui2/store/stores/amazon_it_plugin.py b/src/calibre/gui2/store/stores/amazon_it_plugin.py index 9e9961f864..3217e90710 100644 --- a/src/calibre/gui2/store/stores/amazon_it_plugin.py +++ b/src/calibre/gui2/store/stores/amazon_it_plugin.py @@ -93,7 +93,7 @@ class AmazonITKindleStore(StorePlugin): './/ul[contains(@class, "rsltL")]' '//span[contains(@class, "lrg") and not(contains(@class, "bld"))]/text()') asin_xpath = '@name' - cover_xpath = './/img[@class="productImage"]/@src' + cover_xpath = './/img[contains(@class, "productImage")]/@src' title_xpath = './/h3[@class="newaps"]/a//text()' author_xpath = './/h3[@class="newaps"]//span[contains(@class, "reg")]//text()' price_xpath = ( diff --git a/src/calibre/gui2/store/stores/amazon_plugin.py b/src/calibre/gui2/store/stores/amazon_plugin.py index 82e83401e8..9d2c5e1368 100644 --- a/src/calibre/gui2/store/stores/amazon_plugin.py +++ b/src/calibre/gui2/store/stores/amazon_plugin.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from __future__ import (unicode_literals, division, absolute_import, print_function) -store_version = 4 # Needed for dynamic plugin loading +store_version = 5 # Needed for dynamic plugin loading __license__ = 'GPL 3' __copyright__ = '2011, John Schember ' @@ -134,7 +134,7 @@ class AmazonKindleStore(StorePlugin): './/ul[contains(@class, "rsltGridList")]' '//span[contains(@class, "lrg") and not(contains(@class, "bld"))]/text()') asin_xpath = '@name' - cover_xpath = './/img[@class="productImage"]/@src' + cover_xpath = './/img[contains(@class, "productImage")]/@src' title_xpath = './/h3[@class="newaps"]/a//text()' author_xpath = './/h3[@class="newaps"]//span[contains(@class, "reg")]//text()' price_xpath = ( @@ -159,7 +159,7 @@ class AmazonKindleStore(StorePlugin): './/ul[contains(@class, "rsltL")]' '//span[contains(@class, "lrg") and not(contains(@class, "bld"))]/text()') asin_xpath = '@name' - cover_xpath = './/img[@class="productImage"]/@src' + cover_xpath = './/img[contains(@class, "productImage")]/@src' title_xpath = './/h3[@class="newaps"]/a//text()' author_xpath = './/h3[@class="newaps"]//span[contains(@class, "reg")]//text()' price_xpath = ( diff --git a/src/calibre/gui2/store/stores/amazon_uk_plugin.py b/src/calibre/gui2/store/stores/amazon_uk_plugin.py index ef0650c9f3..4e24e86f3b 100644 --- a/src/calibre/gui2/store/stores/amazon_uk_plugin.py +++ b/src/calibre/gui2/store/stores/amazon_uk_plugin.py @@ -97,7 +97,7 @@ class AmazonUKKindleStore(StorePlugin): './/ul[contains(@class, "rsltL")]' '//span[contains(@class, "lrg") and not(contains(@class, "bld"))]/text()') asin_xpath = '@name' - cover_xpath = './/img[@class="productImage"]/@src' + cover_xpath = './/img[contains(@class, "productImage")]/@src' title_xpath = './/h3[@class="newaps"]/a//text()' author_xpath = './/h3[@class="newaps"]//span[contains(@class, "reg")]//text()' price_xpath = ( diff --git a/src/calibre/gui2/store/stores/eharlequin_plugin.py b/src/calibre/gui2/store/stores/eharlequin_plugin.py deleted file mode 100644 index 5c863af856..0000000000 --- a/src/calibre/gui2/store/stores/eharlequin_plugin.py +++ /dev/null @@ -1,91 +0,0 @@ -# -*- coding: utf-8 -*- - -from __future__ import (unicode_literals, division, absolute_import, print_function) -store_version = 1 # Needed for dynamic plugin loading - -__license__ = 'GPL 3' -__copyright__ = '2011, John Schember ' -__docformat__ = 'restructuredtext en' - -import re -import urllib2 -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 EHarlequinStore(BasicStoreConfig, StorePlugin): - - def open(self, parent=None, detail_item=None, external=False): - url = 'http://www.harlequin.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://ebooks.eharlequin.com/BANGSearch.dll?Type=FullText&FullTextField=All&FullTextCriteria=' + 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('//table[not(.//@class="sidelink")]/tr[.//ul[@id="details"]]'): - if counter <= 0: - break - - id = ''.join(data.xpath('.//ul[@id="details"]/li[@id="title-results"]/a/@href')) - if not id: - continue - - title = ''.join(data.xpath('.//ul[@id="details"]/li[@id="title-results"]/a/text()')) - author = ''.join(data.xpath('.//ul[@id="details"]/li[@id="author"][1]//a/text()')) - price = ''.join(data.xpath('.//div[@class="ourprice"]/font/text()')) - cover_url = ''.join(data.xpath('.//a[@href="%s"]/img/@src' % id)) - - counter -= 1 - - s = SearchResult() - s.cover_url = cover_url - s.title = title.strip() - s.author = author.strip() - s.price = price.strip() - s.detail_item = 'http://ebooks.eharlequin.com/' + id.strip() - s.formats = 'EPUB' - - yield s - - def get_details(self, search_result, timeout): - url = 'http://ebooks.eharlequin.com/en/ContentDetails.htm?ID=' - - mo = re.search(r'\?ID=(?P.+)', search_result.detail_item) - if mo: - id = mo.group('id') - if not id: - return - - - br = browser() - with closing(br.open(url + id, timeout=timeout)) as nf: - idata = html.fromstring(nf.read()) - drm = SearchResult.DRM_UNKNOWN - if idata.xpath('boolean(//div[@class="drm_head"])'): - if idata.xpath('boolean(//td[contains(., "Copy") and contains(., "not")])'): - drm = SearchResult.DRM_LOCKED - else: - drm = SearchResult.DRM_UNLOCKED - search_result.drm = drm - return True diff --git a/src/calibre/gui2/store/stores/kobo_plugin.py b/src/calibre/gui2/store/stores/kobo_plugin.py index 62652ca855..cfa5f514b4 100644 --- a/src/calibre/gui2/store/stores/kobo_plugin.py +++ b/src/calibre/gui2/store/stores/kobo_plugin.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from __future__ import (unicode_literals, division, absolute_import, print_function) -store_version = 2 # Needed for dynamic plugin loading +store_version = 3 # Needed for dynamic plugin loading __license__ = 'GPL 3' __copyright__ = '2011, John Schember ' @@ -9,7 +9,6 @@ __docformat__ = 'restructuredtext en' import random import urllib -import urllib2 from contextlib import closing from lxml import html @@ -49,44 +48,44 @@ class KoboStore(BasicStoreConfig, StorePlugin): d.exec_() def search(self, query, max_results=10, timeout=60): - url = 'http://www.kobobooks.com/search/search.html?q=' + urllib2.quote(query) + url = 'http://www.kobobooks.com/search/search.html?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('//ul[@class="SCShortCoverList"]/li'): + for data in doc.xpath('//ul[contains(@class, "flowview-items")]/li'): if counter <= 0: break - id = ''.join(data.xpath('.//div[@class="SearchImageContainer"]/a[1]/@href')) + id = ''.join(data.xpath('./a[contains(@class, "block-link")]/@href')) if not id: continue - try: - id = id.split('?', 1)[0] - except: - continue + id = id[1:] - price = ''.join(data.xpath('.//span[@class="KV2OurPrice"]/strong/text()')) - if not price: - price = '$0.00' + price = ''.join(data.xpath('.//a[contains(@class, "primary-button")]//text()')) - cover_url = ''.join(data.xpath('.//div[@class="SearchImageContainer"]//img[1]/@src')) + cover_url = ''.join(data.xpath('.//img[1]/@src')) + cover_url = 'http:%s' % cover_url - title = ''.join(data.xpath('.//div[@class="SCItemHeader"]//a[1]/text()')) - author = ', '.join(data.xpath('.//div[@class="SCItemSummary"]//span[contains(@class, "Author")]//a/text()')) - drm = data.xpath('boolean(.//span[@class="SCAvailibilityFormatsText" and not(contains(text(), "DRM-Free"))])') + title = ''.join(data.xpath('.//p[contains(@class, "flowview-item-title")]//text()')) counter -= 1 s = SearchResult() s.cover_url = cover_url s.title = title.strip() - s.author = author.strip() s.price = price.strip() - s.detail_item = 'http://www.kobobooks.com/' + id.strip() - s.drm = SearchResult.DRM_LOCKED if drm else SearchResult.DRM_UNLOCKED + s.detail_item = 'http://store.kobobooks.com/' + id.strip() s.formats = 'EPUB' + 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: + idata = html.fromstring(nf.read()) + search_result.author = ', '.join(idata.xpath('.//h2[contains(@class, "author")]//a/text()')) + return True diff --git a/src/calibre/gui2/store/stores/smashwords_plugin.py b/src/calibre/gui2/store/stores/smashwords_plugin.py index 580e3c2907..c3e5b5d0ec 100644 --- a/src/calibre/gui2/store/stores/smashwords_plugin.py +++ b/src/calibre/gui2/store/stores/smashwords_plugin.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from __future__ import (unicode_literals, division, absolute_import, print_function) -store_version = 1 # Needed for dynamic plugin loading +store_version = 2 # Needed for dynamic plugin loading __license__ = 'GPL 3' __copyright__ = '2011, John Schember ' @@ -54,36 +54,32 @@ class SmashwordsStore(BasicStoreConfig, StorePlugin): counter = max_results with closing(br.open(url, timeout=timeout)) as f: doc = html.fromstring(f.read()) - for data in doc.xpath('//div[@id="pageCenterContent"]//div[@class="bookCoverImg"]'): + for data in doc.xpath('//div[@id="pageCenterContent"]//div[@class="library-book"]'): if counter <= 0: break data = html.fromstring(html.tostring(data)) id = None - id_a = data.xpath('//a[@class="bookTitle"]') + id_a = ''.join(data.xpath('//a[contains(@class, "library-title")]/@href')) if id_a: - id = id_a[0].get('href', None) - if id: - id = id.split('/')[-1] + id = id_a.split('/')[-1] if not id: continue - cover_url = '' - c_url = data.get('style', None) - if c_url: - mo = re.search(r'http://[^\'"]+', c_url) - if mo: - cover_url = mo.group() + cover_url = ''.join(data.xpath('//img[contains(@class, "book-list-image")]/@src')) - title = ''.join(data.xpath('//a[@class="bookTitle"]/text()')) - subnote = ''.join(data.xpath('//span[@class="subnote"]/text()')) - author = ''.join(data.xpath('//span[@class="subnote"]//a[1]//text()')) - if '$' in subnote: - price = subnote.partition('$')[2] - price = price.split(u'\xa0')[0] - price = '$' + price - else: - price = '$0.00' + title = ''.join(data.xpath('.//a[contains(@class, "library-title")]/text()')) + author = ''.join(data.xpath('.//div[@class="subnote"]//a[1]//text()')) + + price = ''.join(data.xpath('.//div[@class="subnote"]//text()')) + if 'Price:' in price: + try: + price = price.partition('Price:')[2] + price = re.sub('\s', ' ', price).strip() + price = price.split(' ')[0] + price = price.strip() + except: + price = 'Unknown' counter -= 1 @@ -103,5 +99,5 @@ class SmashwordsStore(BasicStoreConfig, StorePlugin): br = browser() with closing(br.open(url + search_result.detail_item, timeout=timeout)) as nf: idata = html.fromstring(nf.read()) - search_result.formats = ', '.join(list(set(idata.xpath('//td//b//text()')))) + search_result.formats = ', '.join(list(set(idata.xpath('//p//abbr//text()')))) return True