diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py index 95f6bdbf76..75c1d5a21e 100644 --- a/src/calibre/customize/builtins.py +++ b/src/calibre/customize/builtins.py @@ -1476,6 +1476,14 @@ class StoreWoblinkStore(StoreBase): headquarters = 'PL' formats = ['EPUB'] +class XinXiiStore(StoreBase): + name = 'XinXii' + description = '' + actual_plugin = 'calibre.gui2.store.stores.xinxii_plugin:XinXiiStore' + + headquarters = 'DE' + formats = ['EPUB', 'PDF'] + class StoreZixoStore(StoreBase): name = 'Zixo' author = u'Tomasz Długosz' @@ -1524,6 +1532,7 @@ plugins += [ StoreWHSmithUKStore, StoreWizardsTowerBooksStore, StoreWoblinkStore, + XinXiiStore, StoreZixoStore ] diff --git a/src/calibre/gui2/store/stores/xinxii_plugin.py b/src/calibre/gui2/store/stores/xinxii_plugin.py new file mode 100644 index 0000000000..e8721a79b8 --- /dev/null +++ b/src/calibre/gui2/store/stores/xinxii_plugin.py @@ -0,0 +1,81 @@ +# -*- 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 etree + +from calibre import browser +from calibre.gui2.store.basic_config import BasicStoreConfig +from calibre.gui2.store.opensearch_store import OpenSearchOPDSStore +from calibre.gui2.store.search_result import SearchResult + +class XinXiiStore(BasicStoreConfig, OpenSearchOPDSStore): + + open_search_url = 'http://www.xinxii.com/catalog-search/' + web_url = 'http://xinxii.com/' + + # http://www.xinxii.com/catalog/ + + def search(self, query, max_results=10, timeout=60): + ''' + XinXii's open search url is: + http://www.xinxii.com/catalog-search/query/?keywords={searchTerms}&pw={startPage?}&doc_lang={docLang}&ff={docFormat},{docFormat},{docFormat} + + This url requires the docLang and docFormat. However, the search itself + sent to XinXii does not require them. They can be ignored. We cannot + push this into the stanard OpenSearchOPDSStore search because of the + required attributes. + + XinXii doesn't return all info supported by OpenSearchOPDSStore search + function so this one is modified to remove parts that are used. + ''' + + url = 'http://www.xinxii.com/catalog-search/query/?keywords=' + urllib.quote_plus(query) + + counter = max_results + br = browser() + with closing(br.open(url, timeout=timeout)) as f: + doc = etree.fromstring(f.read()) + for data in doc.xpath('//*[local-name() = "entry"]'): + if counter <= 0: + break + + counter -= 1 + + s = SearchResult() + + s.detail_item = ''.join(data.xpath('./*[local-name() = "id"]/text()')).strip() + + for link in data.xpath('./*[local-name() = "link"]'): + rel = link.get('rel') + href = link.get('href') + type = link.get('type') + + if rel and href and type: + if rel in ('http://opds-spec.org/thumbnail', 'http://opds-spec.org/image/thumbnail'): + s.cover_url = href + if rel == 'alternate': + s.detail_item = href + + s.formats = 'EPUB, PDF' + + s.title = ' '.join(data.xpath('./*[local-name() = "title"]//text()')).strip() + s.author = ', '.join(data.xpath('./*[local-name() = "author"]//*[local-name() = "name"]//text()')).strip() + + price_e = data.xpath('.//*[local-name() = "price"][1]') + if price_e: + price_e = price_e[0] + currency_code = price_e.get('currencycode', '') + price = ''.join(price_e.xpath('.//text()')).strip() + s.price = currency_code + ' ' + price + s.price = s.price.strip() + + + yield s