diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py index ce8c7f71cb..d3fa6e2e83 100644 --- a/src/calibre/customize/builtins.py +++ b/src/calibre/customize/builtins.py @@ -1,4 +1,5 @@ -import os.path +# -*- coding: utf-8 -*- + __license__ = 'GPL v3' __copyright__ = '2008, Kovid Goyal ' @@ -1094,19 +1095,25 @@ plugins += [LookAndFeel, Behavior, Columns, Toolbar, Search, InputOptions, # Store plugins {{{ class StoreAmazonKindleStore(StoreBase): name = 'Amazon Kindle' - description = _('Kindle books from Amazon') + description = _('Kindle books from Amazon.') actual_plugin = 'calibre.gui2.store.amazon_plugin:AmazonKindleStore' class StoreAmazonDEKindleStore(StoreBase): name = 'Amazon DE Kindle' - description = _('Kindle eBooks') + description = _('Kindle books from Amazon.de.') actual_plugin = 'calibre.gui2.store.amazon_de_plugin:AmazonDEKindleStore' class StoreAmazonUKKindleStore(StoreBase): name = 'Amazon UK Kindle' - description = _('Kindle books from Amazon.uk') + description = _('Kindle books from Amazon.uk.') actual_plugin = 'calibre.gui2.store.amazon_uk_plugin:AmazonUKKindleStore' +class StoreArchiveOrgStore(StoreBase): + name = 'Archive.org' + description = _('Free Books : Download & Streaming : Ebook and Texts Archive : Internet Archive.') + actual_plugin = 'calibre.gui2.store.archive_org_plugin:ArchiveOrgStore' + + class StoreBaenWebScriptionStore(StoreBase): name = 'Baen WebScription' description = _('Ebooks for readers.') @@ -1119,7 +1126,7 @@ class StoreBNStore(StoreBase): class StoreBeamEBooksDEStore(StoreBase): name = 'Beam EBooks DE' - description = _('der eBook Shop') + description = _('Der eBook Shop.') actual_plugin = 'calibre.gui2.store.beam_ebooks_de_plugin:BeamEBooksDEStore' class StoreBeWriteStore(StoreBase): @@ -1139,12 +1146,12 @@ class StoreEbookscomStore(StoreBase): class StoreEPubBuyDEStore(StoreBase): name = 'EPUBBuy DE' - description = _('EPUBReaders eBook Shop') + description = _('EPUBReaders eBook Shop.') actual_plugin = 'calibre.gui2.store.epubbuy_de_plugin:EPubBuyDEStore' class StoreEHarlequinStore(StoreBase): name = 'eHarlequin' - description = _('entertain, enrich, inspire.') + description = _('Entertain, enrich, inspire.') actual_plugin = 'calibre.gui2.store.eharlequin_plugin:EHarlequinStore' class StoreFeedbooksStore(StoreBase): @@ -1154,7 +1161,7 @@ class StoreFeedbooksStore(StoreBase): class StoreFoylesUKStore(StoreBase): name = 'Foyles UK' - description = _('Foyles of London, online') + description = _('Foyles of London, online.') actual_plugin = 'calibre.gui2.store.foyles_uk_plugin:FoylesUKStore' class StoreGutenbergStore(StoreBase): @@ -1174,9 +1181,14 @@ class StoreManyBooksStore(StoreBase): class StoreMobileReadStore(StoreBase): name = 'MobileRead' - description = _('Ebooks handcrafted with the utmost care') + description = _('Ebooks handcrafted with the utmost care.') actual_plugin = 'calibre.gui2.store.mobileread.mobileread_plugin:MobileReadStore' +class StoreNextoStore(StoreBase): + name = 'Nexto' + description = _('Audiobooki mp3, ebooki, prasa - księgarnia internetowa.') + actual_plugin = 'calibre.gui2.store.nexto_plugin:NextoStore' + class StoreOpenLibraryStore(StoreBase): name = 'Open Library' description = _('One web page for every book.') @@ -1189,26 +1201,26 @@ class StoreSmashwordsStore(StoreBase): class StoreWaterstonesUKStore(StoreBase): name = 'Waterstones UK' - description = _('Feel every word') + description = _('Feel every word.') actual_plugin = 'calibre.gui2.store.waterstones_uk_plugin:WaterstonesUKStore' class StoreWeightlessBooksStore(StoreBase): name = 'Weightless Books' - description = '(e)Books That Don\'t Weigh You Down' + 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' + description = 'Wizard\'s Tower Press.' actual_plugin = 'calibre.gui2.store.wizards_tower_books_plugin:WizardsTowerBooksStore' -plugins += [StoreAmazonKindleStore, StoreAmazonDEKindleStore, StoreAmazonUKKindleStore, - StoreBaenWebScriptionStore, StoreBNStore, +plugins += [StoreArchiveOrgStore, StoreAmazonKindleStore, StoreAmazonDEKindleStore, + StoreAmazonUKKindleStore, StoreBaenWebScriptionStore, StoreBNStore, StoreBeamEBooksDEStore, StoreBeWriteStore, StoreDieselEbooksStore, StoreEbookscomStore, StoreEPubBuyDEStore, StoreEHarlequinStore, StoreFeedbooksStore, StoreFoylesUKStore, StoreGutenbergStore, StoreKoboStore, StoreManyBooksStore, - StoreMobileReadStore, StoreOpenLibraryStore, StoreSmashwordsStore, + StoreMobileReadStore, StoreNextoStore, StoreOpenLibraryStore, StoreSmashwordsStore, StoreWaterstonesUKStore, StoreWeightlessBooksStore, StoreWizardsTowerBooksStore] # }}} diff --git a/src/calibre/gui2/store/archive_org_plugin.py b/src/calibre/gui2/store/archive_org_plugin.py new file mode 100644 index 0000000000..e8e96b3839 --- /dev/null +++ b/src/calibre/gui2/store/archive_org_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 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 ArchiveOrgStore(BasicStoreConfig, StorePlugin): + + def open(self, parent=None, detail_item=None, external=False): + url = 'http://www.archive.org/details/texts' + + if detail_item: + detail_item = url_slash_cleaner('http://www.archive.org' + 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, 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): + query = query + ' AND mediatype:texts' + url = 'http://www.archive.org/search.php?query=' + 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('//td[@class="hitCell"]'): + if counter <= 0: + break + + id = ''.join(data.xpath('.//a[@class="titleLink"]/@href')) + if not id: + continue + + title = ''.join(data.xpath('.//a[@class="titleLink"]//text()')) + authors = data.xpath('.//text()') + if not authors: + continue + author = None + for a in authors: + if '-' in a: + author = a.replace('-', ' ').strip() + if author: + break + if not author: + continue + + counter -= 1 + + s = SearchResult() + s.title = title.strip() + s.author = author.strip() + s.price = '$0.00' + s.detail_item = id.strip() + s.drm = SearchResult.DRM_UNLOCKED + + yield s + + def get_details(self, search_result, timeout): + url = url_slash_cleaner('http://www.archive.org' + search_result.detail_item) + + br = browser() + with closing(br.open(url, timeout=timeout)) as nf: + idata = html.fromstring(nf.read()) + formats = ', '.join(idata.xpath('//p[@id="dl" and @class="content"]//a/text()')) + search_result.formats = formats.upper() + + return True diff --git a/src/calibre/gui2/store/nexto_plugin.py b/src/calibre/gui2/store/nexto_plugin.py new file mode 100644 index 0000000000..154274d12a --- /dev/null +++ b/src/calibre/gui2/store/nexto_plugin.py @@ -0,0 +1,86 @@ +# -*- coding: utf-8 -*- + +from __future__ import (unicode_literals, division, absolute_import, print_function) + +__license__ = 'GPL 3' +__copyright__ = '2011, Tomasz Długosz ' +__docformat__ = 'restructuredtext en' + +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 NextoStore(BasicStoreConfig, StorePlugin): + + def open(self, parent=None, detail_item=None, external=False): + pid = '155711' + + url = 'http://www.nexto.pl/ebooki_c1015.xml?pid=' + pid + detail_url = None + + if detail_item: + book_id = re.search(r'p[0-9]*\.xml\Z', detail_item) + book_id = book_id.group(0).replace('.xml','').replace('p','') + if book_id: + detail_url = 'http://www.nexto.pl/rf/pr?p=' + book_id + '&pid=' + pid + + 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.nexto.pl/szukaj.xml?search-clause=' + urllib.quote_plus(query.encode('utf-8')) + '&scid=1015' + + 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="productslist"]/li'): + if counter <= 0: + break + + id = ''.join(data.xpath('.//div[@class="cover_container"]/a[1]/@href')) + if not id: + continue + + price = ''.join(data.xpath('.//strong[@class="nprice"]/text()')) + + cover_url = ''.join(data.xpath('.//img[@class="cover"]/@src')) + title = ''.join(data.xpath('.//a[@class="title"]/text()')) + formats = ', '.join(data.xpath('.//ul[@class="formats_available"]/li//b/text()')) + DrmFree = re.search(r'bez.DRM', formats) + formats = re.sub(r'\(.+\)', '', formats) + + author = '' + with closing(br.open('http://www.nexto.pl/' + id.strip(), timeout=timeout/4)) as nf: + idata = html.fromstring(nf.read()) + author = ''.join(idata.xpath('//div[@class="basic_data"]/p[1]/b/a/text()')) + + counter -= 1 + + s = SearchResult() + s.cover_url = cover_url + s.title = title.strip() + s.author = author.strip() + s.price = price + s.detail_item = id.strip() + s.drm = SearchResult.DRM_UNLOCKED if DrmFree else SearchResult.DRM_LOCKED + s.formats = formats.upper().strip() + + yield s diff --git a/src/calibre/gui2/store/search/search.py b/src/calibre/gui2/store/search/search.py index e9c9c149a7..29bd6822a9 100644 --- a/src/calibre/gui2/store/search/search.py +++ b/src/calibre/gui2/store/search/search.py @@ -9,7 +9,7 @@ __docformat__ = 'restructuredtext en' import re from random import shuffle -from PyQt4.Qt import (Qt, QDialog, QTimer, QCheckBox, QVBoxLayout, QIcon) +from PyQt4.Qt import (Qt, QDialog, QTimer, QCheckBox, QVBoxLayout, QIcon, QWidget) from calibre.gui2 import JSONConfig, info_dialog from calibre.gui2.progress_indicator import ProgressIndicator @@ -47,14 +47,16 @@ class SearchDialog(QDialog, Ui_Dialog): # Add check boxes for each store so the user # can disable searching specific stores on a # per search basis. + stores_check_widget = QWidget() stores_group_layout = QVBoxLayout() - self.stores_group.setLayout(stores_group_layout) + stores_check_widget.setLayout(stores_group_layout) for x in sorted(self.store_plugins.keys(), key=lambda x: x.lower()): cbox = QCheckBox(x) cbox.setChecked(True) stores_group_layout.addWidget(cbox) setattr(self, 'store_check_' + x, cbox) stores_group_layout.addStretch() + self.stores_group.setWidget(stores_check_widget) # Set the search query self.search_edit.setText(query)