From d3b728d5326a98efe5f80ff365b49fbe4d10ee86 Mon Sep 17 00:00:00 2001 From: GRiker Date: Sun, 27 Nov 2011 06:22:52 -0700 Subject: [PATCH 01/31] Added format check before calling _update_epub_metadata --- src/calibre/devices/apple/driver.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/calibre/devices/apple/driver.py b/src/calibre/devices/apple/driver.py index d7fc6e791a..d6dd5ccf73 100644 --- a/src/calibre/devices/apple/driver.py +++ b/src/calibre/devices/apple/driver.py @@ -1306,7 +1306,8 @@ class ITUNES(DriverBase): if DEBUG: self.log.info(" ITUNES._add_new_copy()") - self._update_epub_metadata(fpath, metadata) + if fpath.rpartition('.')[2].lower() == 'epub': + self._update_epub_metadata(fpath, metadata) db_added = None lb_added = None From 686b061b85873e20f39637ab2154e8932c9ee3bb Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 27 Nov 2011 19:38:37 +0530 Subject: [PATCH 02/31] Fix #896864 (installer crash in resources area) --- src/calibre/web/feeds/recipes/collection.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/calibre/web/feeds/recipes/collection.py b/src/calibre/web/feeds/recipes/collection.py index 6b9c3a2129..46d6c2db81 100644 --- a/src/calibre/web/feeds/recipes/collection.py +++ b/src/calibre/web/feeds/recipes/collection.py @@ -13,7 +13,7 @@ from datetime import timedelta from lxml import etree from lxml.builder import ElementMaker -from calibre import browser +from calibre import browser, force_unicode from calibre.utils.date import parse_date, now as nowf, utcnow, tzlocal, \ isoformat, fromordinal @@ -66,8 +66,9 @@ def serialize_collection(mapping_of_recipe_classes): x.title.decode('ascii') ''' for urn in sorted(mapping_of_recipe_classes.keys(), - key=lambda key: getattr(mapping_of_recipe_classes[key], 'title', - 'zzz')): + key=lambda key: force_unicode( + getattr(mapping_of_recipe_classes[key], 'title', 'zzz'), + 'utf-8')): recipe = serialize_recipe(urn, mapping_of_recipe_classes[urn]) collection.append(recipe) collection.set('count', str(len(collection))) From 97ddd245a7deded1de3b19ddc0941b1fc234e12a Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 27 Nov 2011 20:48:12 +0530 Subject: [PATCH 03/31] TechDirt by Krittika Goyal --- recipes/techdirt.recipe | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 recipes/techdirt.recipe diff --git a/recipes/techdirt.recipe b/recipes/techdirt.recipe new file mode 100644 index 0000000000..a936e1a216 --- /dev/null +++ b/recipes/techdirt.recipe @@ -0,0 +1,20 @@ +from calibre.web.feeds.news import BasicNewsRecipe + +class TechDirt(BasicNewsRecipe): + title = u'Tech Dirt' + language = 'en' + __author__ = 'Krittika Goyal' + oldest_article = 7 #days + max_articles_per_feed = 25 + use_embedded_content = False + + no_stylesheets = True + auto_cleanup = True + encoding = 'latin1' + + + feeds = [ +('News', + 'http://feeds.feedburner.com/techdirt/feed'), +] + From 735de1d229e786a55e81baefbd4bfce409e2cf71 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 28 Nov 2011 08:25:03 +0530 Subject: [PATCH 04/31] Fix update title sort in bulk metadata edit not using language information --- src/calibre/gui2/dialogs/metadata_bulk.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/calibre/gui2/dialogs/metadata_bulk.py b/src/calibre/gui2/dialogs/metadata_bulk.py index 84bf7f6f57..928be3843c 100644 --- a/src/calibre/gui2/dialogs/metadata_bulk.py +++ b/src/calibre/gui2/dialogs/metadata_bulk.py @@ -164,7 +164,14 @@ class MyBlockingBusy(QDialog): # {{{ self.db.set_title(id, titlecase(title), notify=False) if do_title_sort: title = self.db.title(id, index_is_id=True) - self.db.set_title_sort(id, title_sort(title), notify=False) + if languages: + lang = languages[0] + else: + lang = self.db.languages(id, index_is_id=True) + if lang: + lang = lang.partition(',')[0] + self.db.set_title_sort(id, title_sort(title, lang=lang), + notify=False) if au: self.db.set_authors(id, string_to_authors(au), notify=False) if cover_action == 'remove': From 3fd851a355f8fd1e5507abfd14ba5edf454b2b39 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 28 Nov 2011 09:29:07 +0530 Subject: [PATCH 05/31] Preserve capitalization of Scottish author names when downloading metadata --- src/calibre/ebooks/metadata/sources/base.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/calibre/ebooks/metadata/sources/base.py b/src/calibre/ebooks/metadata/sources/base.py index 532c97b624..045f9e44be 100644 --- a/src/calibre/ebooks/metadata/sources/base.py +++ b/src/calibre/ebooks/metadata/sources/base.py @@ -121,7 +121,15 @@ def cap_author_token(token): # Normalize tokens of the form J.K. to J. K. parts = token.split('.') return '. '.join(map(capitalize, parts)).strip() + scots_name = None + for x in ('mc', 'mac'): + if (token.lower().startswith(x) and len(token) > len(x) and + token[len(x)] == upper(token[len(x)])): + scots_name = len(x) + break ans = capitalize(token) + if scots_name is not None: + ans = ans[:scots_name] + upper(ans[scots_name]) + ans[scots_name+1:] for x in ('-', "'"): idx = ans.find(x) if idx > -1 and len(ans) > idx+2: From f130425da8e284aa8d8805527d544384579280e3 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 28 Nov 2011 09:33:16 +0530 Subject: [PATCH 06/31] ... --- src/calibre/ebooks/metadata/sources/base.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/calibre/ebooks/metadata/sources/base.py b/src/calibre/ebooks/metadata/sources/base.py index 045f9e44be..f04291eae9 100644 --- a/src/calibre/ebooks/metadata/sources/base.py +++ b/src/calibre/ebooks/metadata/sources/base.py @@ -124,7 +124,10 @@ def cap_author_token(token): scots_name = None for x in ('mc', 'mac'): if (token.lower().startswith(x) and len(token) > len(x) and - token[len(x)] == upper(token[len(x)])): + ( + token[len(x)] == upper(token[len(x)]) or + lt == token + )): scots_name = len(x) break ans = capitalize(token) From 8f16a5e02a07e7fc0f35f3c95b8fb59e40a70395 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 28 Nov 2011 20:17:45 +0530 Subject: [PATCH 07/31] Fix #855060 (calibre, version 0.8.19 ERROR: Unhandled exception: AttributeError:'MobileReadStore' object has no attribute 'lock' Traceback (most recent call last): File "site-packages/calibre/gui2/actions/store.py", line 130, in open_store File "site-packages/calibre/gui2/store/stores/mobileread/mobileread_plugin.py", line 40, in open File "site-packages/calibre/gui2/store/stores/mobileread/mobileread_plugin.py", line 57, in update_cache AttributeError: 'MobileReadStore' object has no attribute 'lock') --- .../stores/mobileread/mobileread_plugin.py | 24 ++++++++++--------- src/calibre/gui2/ui.py | 3 ++- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/calibre/gui2/store/stores/mobileread/mobileread_plugin.py b/src/calibre/gui2/store/stores/mobileread/mobileread_plugin.py index 1aabe86c50..9e41aa45a1 100644 --- a/src/calibre/gui2/store/stores/mobileread/mobileread_plugin.py +++ b/src/calibre/gui2/store/stores/mobileread/mobileread_plugin.py @@ -21,13 +21,14 @@ from calibre.gui2.store.stores.mobileread.cache_update_thread import CacheUpdate from calibre.gui2.store.stores.mobileread.store_dialog import MobileReadStoreDialog class MobileReadStore(BasicStoreConfig, StorePlugin): - - def genesis(self): + + def __init__(self, *args, **kwargs): + StorePlugin.__init__(self, *args, **kwargs) self.lock = Lock() - + def open(self, parent=None, detail_item=None, external=False): url = 'http://www.mobileread.com/' - + if external or self.config.get('open_external', False): open_url(QUrl(detail_item if detail_item else url)) else: @@ -44,7 +45,7 @@ class MobileReadStore(BasicStoreConfig, StorePlugin): def search(self, query, max_results=10, timeout=60): books = self.get_book_list() - + if not books: return @@ -56,24 +57,25 @@ class MobileReadStore(BasicStoreConfig, StorePlugin): book.drm = SearchResult.DRM_UNLOCKED yield book - def update_cache(self, parent=None, timeout=10, force=False, suppress_progress=False): + def update_cache(self, parent=None, timeout=10, force=False, + suppress_progress=False): if self.lock.acquire(False): try: update_thread = CacheUpdateThread(self.config, self.seralize_books, timeout) if not suppress_progress: progress = CacheProgressDialog(parent) progress.set_message(_('Updating MobileRead book cache...')) - + update_thread.total_changed.connect(progress.set_total) update_thread.update_progress.connect(progress.set_progress) update_thread.update_details.connect(progress.set_details) progress.rejected.connect(update_thread.abort) - + progress.open() update_thread.start() while update_thread.is_alive() and not progress.canceled: QCoreApplication.processEvents() - + if progress.isVisible(): progress.accept() return not progress.canceled @@ -84,7 +86,7 @@ class MobileReadStore(BasicStoreConfig, StorePlugin): def get_book_list(self): return self.deseralize_books(self.config.get('book_list', [])) - + def seralize_books(self, books): sbooks = [] for b in books: @@ -95,7 +97,7 @@ class MobileReadStore(BasicStoreConfig, StorePlugin): data['formats'] = b.formats sbooks.append(data) return sbooks - + def deseralize_books(self, sbooks): books = [] for s in sbooks: diff --git a/src/calibre/gui2/ui.py b/src/calibre/gui2/ui.py index c12a15829a..63db7a561c 100644 --- a/src/calibre/gui2/ui.py +++ b/src/calibre/gui2/ui.py @@ -195,7 +195,8 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{ for ac in self.iactions.values(): ac.do_genesis() - self.donate_action = QAction(QIcon(I('donate.png')), _('&Donate to support calibre'), self) + self.donate_action = QAction(QIcon(I('donate.png')), + _('&Donate to support calibre'), self) for st in self.istores.values(): st.do_genesis() MainWindowMixin.__init__(self, db) From c48ac22bf07f9e3bc5619bb208243212599ebd26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20D=C5=82ugosz?= Date: Mon, 28 Nov 2011 21:04:44 +0100 Subject: [PATCH 08/31] Woblink often uses its custom DRM, and sells PDFs --- src/calibre/customize/builtins.py | 2 +- src/calibre/gui2/store/stores/woblink_plugin.py | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py index 6dfa360384..bff4fe4060 100644 --- a/src/calibre/customize/builtins.py +++ b/src/calibre/customize/builtins.py @@ -1531,7 +1531,7 @@ class StoreWoblinkStore(StoreBase): actual_plugin = 'calibre.gui2.store.stores.woblink_plugin:WoblinkStore' headquarters = 'PL' - formats = ['EPUB'] + formats = ['EPUB', 'PDF', 'WOBLINK'] class XinXiiStore(StoreBase): name = 'XinXii' diff --git a/src/calibre/gui2/store/stores/woblink_plugin.py b/src/calibre/gui2/store/stores/woblink_plugin.py index fe1cb4db69..9992c80eb9 100644 --- a/src/calibre/gui2/store/stores/woblink_plugin.py +++ b/src/calibre/gui2/store/stores/woblink_plugin.py @@ -61,6 +61,12 @@ class WoblinkStore(BasicStoreConfig, StorePlugin): price = ''.join(data.xpath('.//div[@class="prices"]/p[1]/span/text()')) price = re.sub('PLN', ' zł', price) price = re.sub('\.', ',', price) + formats = ', '.join(data.xpath('.//p[3]/img/@src')) + formats = formats[8:-4].upper() + if formats == 'EPUB': + formats = 'WOBLINK' + if 'E Ink' in data.xpath('.//div[@class="prices"]/img/@title'): + formats += ', EPUB' counter -= 1 @@ -71,6 +77,6 @@ class WoblinkStore(BasicStoreConfig, StorePlugin): s.price = price s.detail_item = id.strip() s.drm = SearchResult.DRM_LOCKED - s.formats = 'EPUB' + s.formats = formats yield s From d6ab402774bc16dac08b8c8685041af456dc28f0 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 29 Nov 2011 07:13:00 +0530 Subject: [PATCH 09/31] Daily Writing Tips by NotTaken --- recipes/daily_writing_tips.recipe | 18 ++++++++++++++++++ src/calibre/manual/faq.rst | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 recipes/daily_writing_tips.recipe diff --git a/recipes/daily_writing_tips.recipe b/recipes/daily_writing_tips.recipe new file mode 100644 index 0000000000..836f8ec4d1 --- /dev/null +++ b/recipes/daily_writing_tips.recipe @@ -0,0 +1,18 @@ +from calibre.web.feeds.news import BasicNewsRecipe + +class DailyWritingTips(BasicNewsRecipe): + title = u'Daily Writing Tips' + language = 'en_GB' + __author__ = 'NotTaken' + oldest_article = 7 #days + max_articles_per_feed = 40 + use_embedded_content = True + no_stylesheets = True + auto_cleanup = False + encoding = 'utf-8' + + + feeds = [ +('Latest tips', + 'http://feeds2.feedburner.com/DailyWritingTips'), +] diff --git a/src/calibre/manual/faq.rst b/src/calibre/manual/faq.rst index 3700451b2d..b58b87cc5e 100644 --- a/src/calibre/manual/faq.rst +++ b/src/calibre/manual/faq.rst @@ -265,7 +265,7 @@ How do I use |app| with my Android phone/tablet? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ There are two ways that you can connect your Android device to calibre. Using a USB cable-- or wirelessly, over the air. -The USB cable method only works if your Android device can act as a USB disk, which some Android tablets cannot. +**The USB cable method only works if your Android device can act as a USB disk, that means in windows it must have a drive letter, like K:**. Using a USB cable ^^^^^^^^^^^^^^^^^^^^ From 6b731fe3d5b016e26ab77eeee9266991fe22044e Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 29 Nov 2011 07:18:10 +0530 Subject: [PATCH 10/31] Fix #897330 (driver needed for android device (Motorola Electrify)) --- src/calibre/devices/android/driver.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/calibre/devices/android/driver.py b/src/calibre/devices/android/driver.py index 0ad36a460a..f3aa465476 100644 --- a/src/calibre/devices/android/driver.py +++ b/src/calibre/devices/android/driver.py @@ -167,12 +167,12 @@ class ANDROID(USBMS): 'MB525', 'ANDROID2.3', 'SGH-I997', 'GT-I5800_CARD', 'MB612', 'GT-S5830_CARD', 'GT-S5570_CARD', 'MB870', 'MID7015A', 'ALPANDIGITAL', 'ANDROID_MID', 'VTAB1008', 'EMX51_BBG_ANDROI', - 'UMS', '.K080', 'P990', 'LTE'] + 'UMS', '.K080', 'P990', 'LTE', 'MB853'] WINDOWS_CARD_A_MEM = ['ANDROID_PHONE', 'GT-I9000_CARD', 'SGH-I897', 'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID', 'GT-P1000_CARD', 'A70S', 'A101IT', '7', 'INCREDIBLE', 'A7EB', 'SGH-T849_CARD', '__UMS_COMPOSITE', 'SGH-I997_CARD', 'MB870', 'ALPANDIGITAL', - 'ANDROID_MID', 'P990_SD_CARD', '.K080', 'LTE_CARD'] + 'ANDROID_MID', 'P990_SD_CARD', '.K080', 'LTE_CARD', 'MB853'] OSX_MAIN_MEM = 'Android Device Main Memory' From ee6554871d4d914f595787abb356285b834724e2 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 29 Nov 2011 08:25:25 +0530 Subject: [PATCH 11/31] Skylife by thomass --- recipes/skylife.recipe | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 recipes/skylife.recipe diff --git a/recipes/skylife.recipe b/recipes/skylife.recipe new file mode 100644 index 0000000000..7abd2f1f42 --- /dev/null +++ b/recipes/skylife.recipe @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- + +from calibre.web.feeds.news import BasicNewsRecipe + +class THY (BasicNewsRecipe): + + title = u'Skylife' + __author__ = u'thomass' + description = ' Türk Hava Yollarının yayınladığı aylık kültür dergisi (Fotoğrafları da içermesini isterseniz keep_only_tag''da belirttiğim kodu da ekleyin) ' + oldest_article =32 + max_articles_per_feed =100 + no_stylesheets = True + #delay = 1 + #use_embedded_content = False + encoding = 'utf-8' + publisher = 'thomass' + category = 'genel kültür, gezi,Türkçe' + language = 'tr' + publication_type = 'magazine' + + conversion_options = { + 'comment' : description + , 'tags' : category + , 'publisher' : publisher + , 'language' : language + } + keep_only_tags = [dict(name='h3', attrs={'id':['hpbaslik']}),dict(name='p', attrs={'id':['pyayin','hspot','picerik']})] #Fotoğrafları da eklemek için: dict(name='div', attrs={'id':['divResimler']}) + masthead_url = 'http://www.turkishairlines.com/static/img/skylife/logo.png' + remove_empty_feeds= True + remove_attributes = ['width','height'] + + feeds = [( u'SKYLIFE', u'http://feed43.com/7783278414103376.xml')] From b2c7c601c9dd6e68cb2452b7f96f4c964a2a1fb6 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 29 Nov 2011 08:26:39 +0530 Subject: [PATCH 12/31] ... --- recipes/icons/skylife.png | Bin 0 -> 3330 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 recipes/icons/skylife.png diff --git a/recipes/icons/skylife.png b/recipes/icons/skylife.png new file mode 100644 index 0000000000000000000000000000000000000000..6cfcf5c79747b148e62874f5c2d86a222abd25ba GIT binary patch literal 3330 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 literal 0 HcmV?d00001 From 29041f74a77e73d0006edc82a9bedea4f1a4e8e0 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 29 Nov 2011 09:59:35 +0530 Subject: [PATCH 13/31] Fix #897523 (Cybook Odyssee does not show transferred books) --- src/calibre/devices/hanvon/driver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/devices/hanvon/driver.py b/src/calibre/devices/hanvon/driver.py index 2d6b825c37..fbee2565a0 100644 --- a/src/calibre/devices/hanvon/driver.py +++ b/src/calibre/devices/hanvon/driver.py @@ -175,7 +175,7 @@ class ODYSSEY(N516): FORMATS = ['epub', 'fb2', 'html', 'pdf', 'txt'] - EBOOK_DIR_MAIN = 'calibre' + EBOOK_DIR_MAIN = 'Digital Editions' def get_main_ebook_dir(self, for_upload=False): if for_upload: From 9fe008069283b9a193631196061cf2e4eb93dfa3 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 29 Nov 2011 10:24:02 +0530 Subject: [PATCH 14/31] Content server: When sending ebook files, respect the If-Modified-Since header and send the file as a stream, to reduce memory consumption when sending very large files. Fixes #897343 (Bad memory leak when accessing library over network) --- src/calibre/library/server/content.py | 32 +++++++++++++++++++-------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/src/calibre/library/server/content.py b/src/calibre/library/server/content.py index 983dae8aae..374ea6e345 100644 --- a/src/calibre/library/server/content.py +++ b/src/calibre/library/server/content.py @@ -8,6 +8,7 @@ __docformat__ = 'restructuredtext en' import re, os, posixpath import cherrypy +from cherrypy.lib import cptools from calibre import fit_image, guess_type from calibre.utils.date import fromtimestamp @@ -195,13 +196,26 @@ class ContentServer(object): return data - def get_format(self, id, format): format = format.upper() + fm = self.db.format_metadata(id, format, allow_cache=False) + if not fm: + raise cherrypy.HTTPError(404, 'book: %d does not have format: %s'%(id, format)) + cherrypy.response.headers['Last-Modified'] = \ + self.last_modified(fm['mtime']) + # Check the If-Modified-Since request header. Returns appropriate HTTP + # response + cptools.validate_since() + fmt = self.db.format(id, format, index_is_id=True, as_file=True, mode='rb') if fmt is None: raise cherrypy.HTTPError(404, 'book: %d does not have format: %s'%(id, format)) + mt = guess_type('dummy.'+format.lower())[0] + if mt is None: + mt = 'application/octet-stream' + cherrypy.response.headers['Content-Type'] = mt + mi = newmi = self.db.get_metadata(id, index_is_id=True) if format == 'EPUB': # Get the original metadata @@ -221,19 +235,19 @@ class ContentServer(object): set_metadata(fmt, newmi, format.lower()) fmt.seek(0) - mt = guess_type('dummy.'+format.lower())[0] - if mt is None: - mt = 'application/octet-stream' - au = authors_to_string(mi.authors if mi.authors else [_('Unknown')]) - title = mi.title if mi.title else _('Unknown') + fmt.seek(0, 2) + cherrypy.response.headers['Content-Length'] = fmt.tell() + fmt.seek(0) + + au = authors_to_string(newmi.authors if newmi.authors else + [_('Unknown')]) + title = newmi.title if newmi.title else _('Unknown') fname = u'%s - %s_%s.%s'%(title[:30], au[:30], id, format.lower()) fname = ascii_filename(fname).replace('"', '_') - cherrypy.response.headers['Content-Type'] = mt cherrypy.response.headers['Content-Disposition'] = \ b'attachment; filename="%s"'%fname + cherrypy.response.body = fmt cherrypy.response.timeout = 3600 - cherrypy.response.headers['Last-Modified'] = \ - self.last_modified(self.db.format_last_modified(id, format)) return fmt # }}} From 4cb1907a134edf505002a98ef53dc71e6cd49b8a Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 29 Nov 2011 10:35:45 +0530 Subject: [PATCH 15/31] Content server: On second thoughts dont honor If-Modified-Since as it will lead to hard to replicate conditions --- src/calibre/library/server/content.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/calibre/library/server/content.py b/src/calibre/library/server/content.py index 374ea6e345..dd3a964c27 100644 --- a/src/calibre/library/server/content.py +++ b/src/calibre/library/server/content.py @@ -8,7 +8,6 @@ __docformat__ = 'restructuredtext en' import re, os, posixpath import cherrypy -from cherrypy.lib import cptools from calibre import fit_image, guess_type from calibre.utils.date import fromtimestamp @@ -201,11 +200,10 @@ class ContentServer(object): fm = self.db.format_metadata(id, format, allow_cache=False) if not fm: raise cherrypy.HTTPError(404, 'book: %d does not have format: %s'%(id, format)) + mi = newmi = self.db.get_metadata(id, index_is_id=True) + cherrypy.response.headers['Last-Modified'] = \ - self.last_modified(fm['mtime']) - # Check the If-Modified-Since request header. Returns appropriate HTTP - # response - cptools.validate_since() + self.last_modified(max(fm['mtime'], mi.last_modified)) fmt = self.db.format(id, format, index_is_id=True, as_file=True, mode='rb') @@ -216,7 +214,6 @@ class ContentServer(object): mt = 'application/octet-stream' cherrypy.response.headers['Content-Type'] = mt - mi = newmi = self.db.get_metadata(id, index_is_id=True) if format == 'EPUB': # Get the original metadata From 2e5f38a5bc9bbf14500ae3c11fd2ddda5b01c149 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 29 Nov 2011 11:25:11 +0530 Subject: [PATCH 16/31] Fix #897531 (epub output do not add xml:lang from lang attribute) --- src/calibre/ebooks/epub/output.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/calibre/ebooks/epub/output.py b/src/calibre/ebooks/epub/output.py index bb515f95a4..52bbfb4a08 100644 --- a/src/calibre/ebooks/epub/output.py +++ b/src/calibre/ebooks/epub/output.py @@ -132,9 +132,11 @@ class EPUBOutput(OutputFormatPlugin): def upshift_markup(self): # {{{ 'Upgrade markup to comply with XHTML 1.1 where possible' - from calibre.ebooks.oeb.base import XPath + from calibre.ebooks.oeb.base import XPath, XML for x in self.oeb.spine: root = x.data + if (not root.get(XML('lang'))) and (root.get('lang')): + root.set(XML('lang'), root.get('lang')) body = XPath('//h:body')(root) if body: body = body[0] From 24fb85ea06f662bfdc48beaac39c8da1398dd2d2 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 29 Nov 2011 15:20:32 +0530 Subject: [PATCH 17/31] Fix Rolling Stones Mag --- recipes/rstones.recipe | 39 +++------------------------------------ 1 file changed, 3 insertions(+), 36 deletions(-) diff --git a/recipes/rstones.recipe b/recipes/rstones.recipe index fa09701e15..46b817876c 100644 --- a/recipes/rstones.recipe +++ b/recipes/rstones.recipe @@ -29,22 +29,7 @@ class RollingStones(BasicNewsRecipe): max_articles_per_feed = 25 use_embedded_content = False no_stylesheets = True - - remove_javascript = True - ##################################################################################### - # cleanup section # - ##################################################################################### - keep_only_tags = [ - dict(name='div', attrs={'class':['c65l']}), - dict(name='div', attrs={'id':['col1']}), - - - ] - remove_tags = [ - dict(name='div', attrs={'class': ['storyActions upper','storyActions lowerArticleNav']}), - dict(name='div', attrs={'id': ['comments','related']}), - ] - + auto_cleanup = True feeds = [ (u'News', u'http://www.rollingstone.com/siteServices/rss/allNews'), @@ -58,25 +43,7 @@ class RollingStones(BasicNewsRecipe): - def get_article_url(self, article): - return article.get('guid', None) - - - def append_page(self, soup, appendtag, position): - ''' - Some are the articles are multipage so the below function - will get the articles that have - ''' - pager = soup.find('li',attrs={'class':'next'}) - if pager: - nexturl = pager.a['href'] - soup2 = self.index_to_soup(nexturl) - texttag = soup2.find('div', attrs={'id':'storyTextContainer'}) - for it in texttag.findAll(style=True): - del it['style'] - newpos = len(texttag.contents) - self.append_page(soup2,texttag,newpos) - texttag.extract() - appendtag.insert(position,texttag) + def print_version(self, url): + return url +'?print=true' From c349fe1e330dcc2a941b42ddae4e9fc72a9ccae9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Szkutnik?= Date: Tue, 29 Nov 2011 22:50:30 +0100 Subject: [PATCH 18/31] Add delay between consecutive feed downloads according to recipe's delay value --- src/calibre/web/feeds/news.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/calibre/web/feeds/news.py b/src/calibre/web/feeds/news.py index 14021cb142..13b79fa947 100644 --- a/src/calibre/web/feeds/news.py +++ b/src/calibre/web/feeds/news.py @@ -1398,6 +1398,8 @@ class BasicNewsRecipe(Recipe): oldest_article=self.oldest_article, max_articles_per_feed=self.max_articles_per_feed, get_article_url=self.get_article_url)) + if (self.delay > 0): + time.sleep(self.delay) except Exception as err: feed = Feed() msg = 'Failed feed: %s'%(title if title else url) From 23caca5f47fc09438d358879c7758572eb258edb Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 30 Nov 2011 08:20:32 +0530 Subject: [PATCH 19/31] Vanity Fair by Barty --- recipes/vanityfair.recipe | 98 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 recipes/vanityfair.recipe diff --git a/recipes/vanityfair.recipe b/recipes/vanityfair.recipe new file mode 100644 index 0000000000..61ec76e003 --- /dev/null +++ b/recipes/vanityfair.recipe @@ -0,0 +1,98 @@ +from datetime import date +import re +from calibre.web.feeds.news import BasicNewsRecipe + +class VanityFair(BasicNewsRecipe): + title = u"Vanity Fair" + description = 'Vanity Fair Magazine (U.S.)' + language = 'en' + __author__ = 'Barty' + max_articles_per_feed = 100 + no_stylesheets = False + auto_cleanup = False + timefmt = ' [%B %Y]' + oldest_article = 365 + + masthead_url = 'http://www.vanityfair.com/etc/designs/vanityfair/images/shell/print-logo.png' + + INDEX = 'http://www.vanityfair.com' + CATEGORIES = [ + # comment out categories you don't want + # (user friendly name, url suffix, max number of articles to load) + ('Hollywood','hollywood',10), + ('Culture','culture',10), + ('Business','business',10), + ('Politics','politics',10), + ('Society','society',10), + ('Style','style',10), + ('VF Daily','online/daily',10), + ("James Wolcott's Blog",'online/wolcott',10), + ("The Oscars",'online/oscars',10), + ] + # set this to False if you don't want to put the first article + # that appears in each section to a "Featured" section + FEATURED_CAT = True + + + remove_tags = [ + {'name':['nav']}, + {'class':re.compile(r'_(header|rubric|share|subnav|leaderboard)|comments-count|ecom_placement')} + ] + remove_tags_after = [{'class':'cn_blogpost'},{'id':'wrapper'}] + + def parse_index(self): + self.cover_url = 'http://www.vanityfair.com/magazine/toc/contents-%s/_jcr_content/par/cn_contentwell/par-main/cn_pagination_contai/cn_image.size.cover_vanityfair_300.jpg' % (date.today().strftime('%Y%m')) + feeds = [] + seen_urls = set([]) + features = [] + + for category in self.CATEGORIES: + + (cat_name, tag, max_articles) = category + self.log('Reading category:', cat_name) + articles = [] + + page = "%s/%s" % (self.INDEX, tag) + soup = self.index_to_soup(page) + headers = soup.findAll(attrs={'class':'headline '}) + add_featured = self.FEATURED_CAT + + for header in headers: + self.log(self.tag_to_string(header)) + atags = header.findAll('a') + # if there's more than one a tag, it's some kind of list, skip + if not atags or len(atags)>1: + continue + atag = atags[0] + url = atag['href'] + if url.startswith('/'): + url = self.INDEX + url + if url in seen_urls: + continue + seen_urls.add(url) + title = self.tag_to_string(atag) + self.log('\tFound article:', title) + self.log('\t', url) + par = header.findParent('article') if tag.startswith('online/') else header.findParent('section') + if par is not None: + desc = par.find(attrs={'class':'body '}) + desc = self.tag_to_string(desc) if desc else '' + #self.log('\t', desc) + if add_featured: + features.append({'title':title,'url':url,'description':desc}) + add_featured = False + else: + articles.append({'title':title,'url':url,'description':desc}) + if len(articles) >= max_articles: + break + + if articles: + feeds.append((cat_name, articles)) + + if features: + feeds.insert(0,('Featured', features)) + + return feeds + + def print_version(self, url): + return url.replace('.html', '.print') From e082bf17f250bfa8e64fa64d560a23c08fcb33fa Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 30 Nov 2011 09:34:23 +0530 Subject: [PATCH 20/31] Device subsystem: Make the USB ids of the device being opened available to the driver while open() is being called. --- src/calibre/devices/__init__.py | 8 +-- src/calibre/devices/apple/driver.py | 4 +- src/calibre/devices/bambook/driver.py | 8 +-- src/calibre/devices/folder_device/driver.py | 2 +- src/calibre/devices/interface.py | 24 ++++++-- src/calibre/devices/prs500/cli/main.py | 6 +- src/calibre/devices/prs500/driver.py | 2 +- src/calibre/devices/usbms/device.py | 62 +++++++++++---------- src/calibre/gui2/device.py | 2 +- 9 files changed, 69 insertions(+), 49 deletions(-) diff --git a/src/calibre/devices/__init__.py b/src/calibre/devices/__init__.py index e47cd82b50..dce04034c8 100644 --- a/src/calibre/devices/__init__.py +++ b/src/calibre/devices/__init__.py @@ -39,15 +39,15 @@ def get_connected_device(): if ok: dev = d dev.reset(log_packets=False, detected_device=det) - connected_devices.append(dev) + connected_devices.append((det, dev)) if dev is None: print >>sys.stderr, 'Unable to find a connected ebook reader.' return - for d in connected_devices: + for det, d in connected_devices: try: - d.open(None) + d.open(det, None) except: continue else: @@ -121,7 +121,7 @@ def debug(ioreg_to_tmp=False, buf=None): out('Trying to open', dev.name, '...', end=' ') try: dev.reset(detected_device=det) - dev.open(None) + dev.open(det, None) out('OK') except: import traceback diff --git a/src/calibre/devices/apple/driver.py b/src/calibre/devices/apple/driver.py index d6dd5ccf73..d8584600ae 100644 --- a/src/calibre/devices/apple/driver.py +++ b/src/calibre/devices/apple/driver.py @@ -808,7 +808,7 @@ class ITUNES(DriverBase): self.log.info("ITUNES.get_file(): exporting '%s'" % path) outfile.write(open(self.cached_books[path]['lib_book'].location().path).read()) - def open(self, library_uuid): + def open(self, connected_device, library_uuid): ''' Perform any device specific initialization. Called after the device is detected but before any other functions that communicate with the device. @@ -3224,7 +3224,7 @@ class ITUNES_ASYNC(ITUNES): only_presence=False): return self.connected, self - def open(self, library_uuid): + def open(self, connected_device, library_uuid): ''' Perform any device specific initialization. Called after the device is detected but before any other functions that communicate with the device. diff --git a/src/calibre/devices/bambook/driver.py b/src/calibre/devices/bambook/driver.py index 5068a9c4d8..fecdd53ac4 100644 --- a/src/calibre/devices/bambook/driver.py +++ b/src/calibre/devices/bambook/driver.py @@ -59,9 +59,9 @@ class BAMBOOK(DeviceConfig, DevicePlugin): def reset(self, key='-1', log_packets=False, report_progress=None, detected_device=None) : - self.open(None) + self.open(None, None) - def open(self, library_uuid): + def open(self, connected_device, library_uuid): # Make sure the Bambook library is ready if not is_bambook_lib_ready(): raise OpenFeedback(_("Unable to connect to Bambook, you need to install Bambook library first.")) @@ -309,8 +309,8 @@ class BAMBOOK(DeviceConfig, DevicePlugin): with TemporaryFile('.snb') as snbfile: if self.bambook.PackageSNB(snbfile, tdir) and self.bambook.VerifySNB(snbfile): guid = self.bambook.SendFile(snbfile, self.get_guid(metadata[i].uuid)) - - elif f[-3:].upper() == 'SNB': + + elif f[-3:].upper() == 'SNB': if self.bambook.VerifySNB(f): guid = self.bambook.SendFile(f, self.get_guid(metadata[i].uuid)) else: diff --git a/src/calibre/devices/folder_device/driver.py b/src/calibre/devices/folder_device/driver.py index f9a3a1508c..09df8cd6d8 100644 --- a/src/calibre/devices/folder_device/driver.py +++ b/src/calibre/devices/folder_device/driver.py @@ -79,7 +79,7 @@ class FOLDER_DEVICE(USBMS): only_presence=False): return self.is_connected, self - def open(self, library_uuid): + def open(self, connected_device, library_uuid): self.current_library_uuid = library_uuid if not self._main_prefix: return False diff --git a/src/calibre/devices/interface.py b/src/calibre/devices/interface.py index 15c9e9c55f..cc26c15192 100644 --- a/src/calibre/devices/interface.py +++ b/src/calibre/devices/interface.py @@ -133,8 +133,14 @@ class DevicePlugin(Plugin): if debug: self.print_usb_device_info(device_id) if only_presence or self.can_handle_windows(device_id, debug=debug): - return True - return False + try: + bcd = int(device_id.rpartition( + 'rev_')[-1].replace(':', 'a'), 16) + except: + bcd = None + return True, (vendor_id, product_id, bcd, None, + None, None) + return False, None def test_bcd(self, bcdDevice, bcd): if bcd is None or len(bcd) == 0: @@ -154,7 +160,7 @@ class DevicePlugin(Plugin): ''' if iswindows: return self.is_usb_connected_windows(devices_on_system, - debug=debug, only_presence=only_presence), None + debug=debug, only_presence=only_presence) vendors_on_system = set([x[0] for x in devices_on_system]) vendors = self.VENDOR_ID if hasattr(self.VENDOR_ID, '__len__') else [self.VENDOR_ID] @@ -224,7 +230,7 @@ class DevicePlugin(Plugin): return True - def open(self, library_uuid): + def open(self, connected_device, library_uuid): ''' Perform any device specific initialization. Called after the device is detected but before any other functions that communicate with the device. @@ -238,6 +244,16 @@ class DevicePlugin(Plugin): This method can raise an OpenFeedback exception to display a message to the user. + + :param connected_device: The device that we are trying to open. It is + a tuple of (vendor id, product id, bcd, manufacturer name, product + name, device serial number). However, some device have no serial number + and on windows only the first three fields are present, the rest are + None. + + :param library_uuid: The UUID of the current calibre library. Can be + None if there is no library (for example when used from the command + line. ''' raise NotImplementedError() diff --git a/src/calibre/devices/prs500/cli/main.py b/src/calibre/devices/prs500/cli/main.py index f122f6332e..e32d5e362e 100755 --- a/src/calibre/devices/prs500/cli/main.py +++ b/src/calibre/devices/prs500/cli/main.py @@ -205,15 +205,15 @@ def main(): if ok: dev = d dev.reset(log_packets=options.log_packets, detected_device=det) - connected_devices.append(dev) + connected_devices.append((det, dev)) if dev is None: print >>sys.stderr, 'Unable to find a connected ebook reader.' return 1 - for d in connected_devices: + for det, d in connected_devices: try: - d.open(None) + d.open(det, None) except: continue else: diff --git a/src/calibre/devices/prs500/driver.py b/src/calibre/devices/prs500/driver.py index aaba094fb3..28545b2d86 100644 --- a/src/calibre/devices/prs500/driver.py +++ b/src/calibre/devices/prs500/driver.py @@ -240,7 +240,7 @@ class PRS500(DeviceConfig, DevicePlugin): def set_progress_reporter(self, report_progress): self.report_progress = report_progress - def open(self, library_uuid) : + def open(self, connected_device, library_uuid) : """ Claim an interface on the device for communication. Requires write privileges to the device file. diff --git a/src/calibre/devices/usbms/device.py b/src/calibre/devices/usbms/device.py index 3b4075f601..f62dc44149 100644 --- a/src/calibre/devices/usbms/device.py +++ b/src/calibre/devices/usbms/device.py @@ -847,38 +847,42 @@ class Device(DeviceConfig, DevicePlugin): self._card_b_prefix = None # ------------------------------------------------------ - def open(self, library_uuid): + def open(self, connected_device, library_uuid): time.sleep(5) self._main_prefix = self._card_a_prefix = self._card_b_prefix = None - if islinux: - try: - self.open_linux() - except DeviceError: - time.sleep(7) - self.open_linux() - if isfreebsd: - self._main_dev = self._card_a_dev = self._card_b_dev = None - try: - self.open_freebsd() - except DeviceError: - subprocess.Popen(["camcontrol", "rescan", "all"]) - time.sleep(2) - self.open_freebsd() - if iswindows: - try: - self.open_windows() - except DeviceError: - time.sleep(7) - self.open_windows() - if isosx: - try: - self.open_osx() - except DeviceError: - time.sleep(7) - self.open_osx() + self.device_being_opened = connected_device + try: + if islinux: + try: + self.open_linux() + except DeviceError: + time.sleep(7) + self.open_linux() + if isfreebsd: + self._main_dev = self._card_a_dev = self._card_b_dev = None + try: + self.open_freebsd() + except DeviceError: + subprocess.Popen(["camcontrol", "rescan", "all"]) + time.sleep(2) + self.open_freebsd() + if iswindows: + try: + self.open_windows() + except DeviceError: + time.sleep(7) + self.open_windows() + if isosx: + try: + self.open_osx() + except DeviceError: + time.sleep(7) + self.open_osx() - self.current_library_uuid = library_uuid - self.post_open_callback() + self.current_library_uuid = library_uuid + self.post_open_callback() + finally: + self.device_being_opened = None def post_open_callback(self): pass diff --git a/src/calibre/gui2/device.py b/src/calibre/gui2/device.py index 7be64e002d..51e4a1a8c1 100644 --- a/src/calibre/gui2/device.py +++ b/src/calibre/gui2/device.py @@ -162,7 +162,7 @@ class DeviceManager(Thread): # {{{ try: dev.reset(detected_device=detected_device, report_progress=self.report_progress) - dev.open(self.current_library_uuid) + dev.open(detected_device, self.current_library_uuid) except OpenFeedback as e: if dev not in self.ejected_devices: self.open_feedback_msg(dev.get_gui_name(), e) From ef375dd42f0d734969fbadb352c265570af3821a Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 30 Nov 2011 09:43:29 +0530 Subject: [PATCH 21/31] Fix #896881 (archos E70B internal/carda memory swapped) --- src/calibre/devices/android/driver.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/calibre/devices/android/driver.py b/src/calibre/devices/android/driver.py index f3aa465476..73e6bd0dad 100644 --- a/src/calibre/devices/android/driver.py +++ b/src/calibre/devices/android/driver.py @@ -199,6 +199,15 @@ class ANDROID(USBMS): dirs = list(map(aldiko_tweak, dirs)) return dirs + def windows_sort_drives(self, drives): + vid, pid, bcd = self.device_being_opened[:3] + if (vid, pid, bcd) == (0x0e79, 0x1408, 0x0222): + letter_a = drives.get('carda', None) + if letter_a is not None: + drives['carda'] = drives['main'] + drives['main'] = letter_a + return drives + class S60(USBMS): name = 'S60 driver' From 2c333bdf841e421762790a28a1982c0a8a824f49 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 30 Nov 2011 09:57:48 +0530 Subject: [PATCH 22/31] ... --- src/calibre/devices/android/driver.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/calibre/devices/android/driver.py b/src/calibre/devices/android/driver.py index 73e6bd0dad..d478f47cea 100644 --- a/src/calibre/devices/android/driver.py +++ b/src/calibre/devices/android/driver.py @@ -200,7 +200,10 @@ class ANDROID(USBMS): return dirs def windows_sort_drives(self, drives): - vid, pid, bcd = self.device_being_opened[:3] + try: + vid, pid, bcd = self.device_being_opened[:3] + except: + vid, pid, bcd = -1, -1, -1 if (vid, pid, bcd) == (0x0e79, 0x1408, 0x0222): letter_a = drives.get('carda', None) if letter_a is not None: From 15343d7af3d60809eccc86973f37af6be78a588a Mon Sep 17 00:00:00 2001 From: GRiker Date: Wed, 30 Nov 2011 04:30:06 -0700 Subject: [PATCH 23/31] Added diagnostics reporting connected_device in open() --- src/calibre/devices/apple/driver.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/calibre/devices/apple/driver.py b/src/calibre/devices/apple/driver.py index d8584600ae..592208fc99 100644 --- a/src/calibre/devices/apple/driver.py +++ b/src/calibre/devices/apple/driver.py @@ -824,7 +824,7 @@ class ITUNES(DriverBase): ''' if DEBUG: - self.log.info("ITUNES.open()") + self.log.info("ITUNES.open(connected_device: %s)" % repr(connected_device)) # Display a dialog recommending using 'Connect to iTunes' if user hasn't # previously disabled the dialog @@ -3239,7 +3239,7 @@ class ITUNES_ASYNC(ITUNES): we need to talk to iTunes to discover if there's a connected iPod ''' if DEBUG: - self.log.info("ITUNES_ASYNC.open()") + self.log.info("ITUNES_ASYNC.open(connected_device: iTunes)") # Confirm/create thumbs archive if not os.path.exists(self.cache_dir): From ace9429430c257915da7c1c27c156feb0bedc70a Mon Sep 17 00:00:00 2001 From: GRiker Date: Wed, 30 Nov 2011 04:39:56 -0700 Subject: [PATCH 24/31] Revised diagnostics reporting connected_device in open() --- src/calibre/devices/apple/driver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/devices/apple/driver.py b/src/calibre/devices/apple/driver.py index 592208fc99..5ed4ef214e 100644 --- a/src/calibre/devices/apple/driver.py +++ b/src/calibre/devices/apple/driver.py @@ -3239,7 +3239,7 @@ class ITUNES_ASYNC(ITUNES): we need to talk to iTunes to discover if there's a connected iPod ''' if DEBUG: - self.log.info("ITUNES_ASYNC.open(connected_device: iTunes)") + self.log.info("ITUNES_ASYNC.open(connected_device: %s)" % repr(connected_device)) # Confirm/create thumbs archive if not os.path.exists(self.cache_dir): From 5943156eaa6f13199192867cf3f80ca486d1edf6 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 30 Nov 2011 18:04:28 +0530 Subject: [PATCH 25/31] Fix #898123 (Support for new Android device Samsung Galaxy GIO S5660) --- src/calibre/devices/android/driver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/devices/android/driver.py b/src/calibre/devices/android/driver.py index d478f47cea..8f5395b514 100644 --- a/src/calibre/devices/android/driver.py +++ b/src/calibre/devices/android/driver.py @@ -167,7 +167,7 @@ class ANDROID(USBMS): 'MB525', 'ANDROID2.3', 'SGH-I997', 'GT-I5800_CARD', 'MB612', 'GT-S5830_CARD', 'GT-S5570_CARD', 'MB870', 'MID7015A', 'ALPANDIGITAL', 'ANDROID_MID', 'VTAB1008', 'EMX51_BBG_ANDROI', - 'UMS', '.K080', 'P990', 'LTE', 'MB853'] + 'UMS', '.K080', 'P990', 'LTE', 'MB853', 'GT-S5660_CARD'] WINDOWS_CARD_A_MEM = ['ANDROID_PHONE', 'GT-I9000_CARD', 'SGH-I897', 'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID', 'GT-P1000_CARD', 'A70S', 'A101IT', '7', 'INCREDIBLE', 'A7EB', 'SGH-T849_CARD', From b2bea092fc5641f057c0964b7a47a55f58ab9c90 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 30 Nov 2011 21:33:00 +0530 Subject: [PATCH 26/31] Conversion pipeline: Do not error out on books that set font size to zero. Fixes #898194 (Private bug) --- src/calibre/ebooks/oeb/stylizer.py | 3 ++- src/calibre/ebooks/oeb/transforms/flatcss.py | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/calibre/ebooks/oeb/stylizer.py b/src/calibre/ebooks/oeb/stylizer.py index adef0f1bfb..20be750925 100644 --- a/src/calibre/ebooks/oeb/stylizer.py +++ b/src/calibre/ebooks/oeb/stylizer.py @@ -539,7 +539,8 @@ class Style(object): 'Return value in pts' if base is None: base = self.width - font = font or self.fontSize + if not font and font != 0: + font = self.fontSize return unit_convert(value, base, font, self._profile.dpi) def pt_to_px(self, value): diff --git a/src/calibre/ebooks/oeb/transforms/flatcss.py b/src/calibre/ebooks/oeb/transforms/flatcss.py index ba90a31632..489498d90c 100644 --- a/src/calibre/ebooks/oeb/transforms/flatcss.py +++ b/src/calibre/ebooks/oeb/transforms/flatcss.py @@ -283,7 +283,10 @@ class CSSFlattener(object): psize = fsize elif 'font-size' in cssdict or tag == 'body': fsize = self.fmap[font_size] - cssdict['font-size'] = "%0.5fem" % (fsize / psize) + try: + cssdict['font-size'] = "%0.5fem" % (fsize / psize) + except ZeroDivisionError: + cssdict['font-size'] = '%.1fpt'%fsize psize = fsize try: From 37b410705228ecac6e1362371560327963a7d051 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 1 Dec 2011 07:59:03 +0530 Subject: [PATCH 27/31] gs24.pl by Michal Szkutnik --- recipes/gs24_pl.recipe | 43 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 recipes/gs24_pl.recipe diff --git a/recipes/gs24_pl.recipe b/recipes/gs24_pl.recipe new file mode 100644 index 0000000000..db7125b116 --- /dev/null +++ b/recipes/gs24_pl.recipe @@ -0,0 +1,43 @@ +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai + +import re +import string +from calibre.web.feeds.news import BasicNewsRecipe + +class AdvancedUserRecipe1322322819(BasicNewsRecipe): + title = u'GS24.pl (Głos Szczeciński)' + description = u'Internetowy serwis Głosu Szczecińskiego' + __author__ = u'Michał Szkutnik' + __license__ = u'GPL v3' + language = 'pl' + publisher = 'Media Regionalne sp. z o.o.' + category = 'news, szczecin' + oldest_article = 2 + max_articles_per_feed = 100 + auto_cleanup = True + cover_url = "http://www.gs24.pl/images/top_logo.png" + + feeds = [ + # (u'Wszystko', u'http://www.gs24.pl/rss.xml'), + (u'Szczecin', u'http://www.gs24.pl/szczecin.xml'), + (u'Stargard', u'http://www.gs24.pl/stargard.xml'), + (u'Świnoujście', u'http://www.gs24.pl/swinoujscie.xml'), + (u'Goleniów', u'http://www.gs24.pl/goleniow.xml'), + (u'Gryfice', u'http://www.gs24.pl/gryfice.xml'), + (u'Kamień Pomorski', u'http://www.gs24.pl/kamienpomorski.xml'), + (u'Police', u'http://www.gs24.pl/police.xml'), + (u'Region', u'http://www.gs24.pl/region.xml'), + (u'Sport', u'http://www.gs24.pl/sport.xml'), + ] + + def get_article_url(self, article): + s = re.search("""/0L0S(gs24.*)/story01.htm""", article.link) + s = s.group(1) + replacements = { "0B" : ".", "0C" : "/", "0H" : ",", "0I" : "_", "0D" : "?", "0F" : "="} + for (a, b) in replacements.iteritems(): + s = string.replace(s, a, b) + s = string.replace(s, "0A", "0") + return "http://"+s + + def print_version(self, url): + return url + "&Template=printpicart" From 61e828b2c2c6647d92e21973be2eba9ff9f0e8bb Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 1 Dec 2011 08:33:47 +0530 Subject: [PATCH 28/31] Metadata download, do not strip # from titles. Fixes #898310 (Special characters stripped rather than encoded when getting metadata) --- src/calibre/ebooks/metadata/sources/amazon.py | 8 ++++++++ src/calibre/ebooks/metadata/sources/base.py | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/calibre/ebooks/metadata/sources/amazon.py b/src/calibre/ebooks/metadata/sources/amazon.py index 52dd109b47..8f663bcf86 100644 --- a/src/calibre/ebooks/metadata/sources/amazon.py +++ b/src/calibre/ebooks/metadata/sources/amazon.py @@ -741,6 +741,14 @@ if __name__ == '__main__': # tests {{{ isbn_test, title_test, authors_test) com_tests = [ # {{{ + ( # # in title + {'title':'Expert C# 2008 Business Objects', + 'authors':['Lhotka']}, + [title_test('Expert C# 2008 Business Objects', exact=True), + authors_test(['Rockford Lhotka']) + ] + ), + ( # Description has links {'identifiers':{'isbn': '9780671578275'}}, [title_test('A Civil Campaign: A Comedy of Biology and Manners', diff --git a/src/calibre/ebooks/metadata/sources/base.py b/src/calibre/ebooks/metadata/sources/base.py index f04291eae9..9ae8902671 100644 --- a/src/calibre/ebooks/metadata/sources/base.py +++ b/src/calibre/ebooks/metadata/sources/base.py @@ -344,7 +344,7 @@ class Source(Plugin): # Remove single quotes not followed by 's' (r"'(?!s)", ''), # Replace other special chars with a space - (r'''[:,;+!@#$%^&*(){}.`~"\s\[\]/]''', ' ') + (r'''[:,;+!@$%^&*(){}.`~"\s\[\]/]''', ' '), ]] for pat, repl in title_patterns: From 3e711576078592f3e5e1019a11bb4ee3a9e583a1 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 1 Dec 2011 08:48:05 +0530 Subject: [PATCH 29/31] Driver for Blackberry Playbook --- src/calibre/customize/builtins.py | 4 ++-- src/calibre/devices/blackberry/driver.py | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py index bff4fe4060..b29d67065d 100644 --- a/src/calibre/customize/builtins.py +++ b/src/calibre/customize/builtins.py @@ -545,7 +545,7 @@ from calibre.customize.profiles import input_profiles, output_profiles from calibre.devices.apple.driver import ITUNES from calibre.devices.hanlin.driver import HANLINV3, HANLINV5, BOOX, SPECTRA -from calibre.devices.blackberry.driver import BLACKBERRY +from calibre.devices.blackberry.driver import BLACKBERRY, PLAYBOOK from calibre.devices.cybook.driver import CYBOOK, ORIZON from calibre.devices.eb600.driver import (EB600, COOL_ER, SHINEBOOK, POCKETBOOK360, GER2, ITALICA, ECLICTO, DBOOK, INVESBOOK, @@ -646,7 +646,7 @@ plugins += [ plugins += [ HANLINV3, HANLINV5, - BLACKBERRY, + BLACKBERRY, PLAYBOOK, CYBOOK, ORIZON, ILIAD, diff --git a/src/calibre/devices/blackberry/driver.py b/src/calibre/devices/blackberry/driver.py index 1ae6a6c49f..4a85fa695a 100644 --- a/src/calibre/devices/blackberry/driver.py +++ b/src/calibre/devices/blackberry/driver.py @@ -28,3 +28,26 @@ class BLACKBERRY(USBMS): EBOOK_DIR_MAIN = 'eBooks' SUPPORTS_SUB_DIRS = True + +class PLAYBOOK(USBMS): + + name = 'Blackberry Playbook Interface' + gui_name = 'Playbook' + description = _('Communicate with the Blackberry playbook.') + author = _('Kovid Goyal') + supported_platforms = ['windows', 'linux', 'osx'] + + # Ordered list of supported formats + FORMATS = ['epub'] + + VENDOR_ID = [0x0fca] + PRODUCT_ID = [0x8010] + BCD = [0x1] + + VENDOR_NAME = 'GENERIC-' + WINDOWS_MAIN_MEM = 'MULTI-CARD' + + MAIN_MEMORY_VOLUME_LABEL = 'Blackberry' + + EBOOK_DIR_MAIN = 'media/books' + SUPPORTS_SUB_DIRS = True From 184ffa4d5cc44a2c62ec536429a8090f53ce3c35 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 1 Dec 2011 10:08:35 +0530 Subject: [PATCH 30/31] ... --- src/calibre/manual/faq.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/calibre/manual/faq.rst b/src/calibre/manual/faq.rst index b58b87cc5e..4fc13ef80d 100644 --- a/src/calibre/manual/faq.rst +++ b/src/calibre/manual/faq.rst @@ -374,6 +374,8 @@ any |app| developers will ever feel motivated enough to support it. There is how that allows you to create collections on your Kindle from the |app| metadata. It is available `from here `_. +.. note:: Amazon have removed the ability to manipulate collections completely in their newer models, like the Kindle Touch and Kindle Fire, making even the above plugin useless. If you really want the ability to manage collections on your Kindle via a USB connection, we encourage you to complain to Amazon about it, or get a reader where this is supported, like the SONY Readers. + Library Management ------------------ From a4a9b15fa688ac023763feaf15d5dc2b2fcb0486 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 1 Dec 2011 10:09:31 +0530 Subject: [PATCH 31/31] Metadata search and replace, make the regular expressions unicode aware --- src/calibre/gui2/dialogs/metadata_bulk.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/calibre/gui2/dialogs/metadata_bulk.py b/src/calibre/gui2/dialogs/metadata_bulk.py index 928be3843c..c956036fda 100644 --- a/src/calibre/gui2/dialogs/metadata_bulk.py +++ b/src/calibre/gui2/dialogs/metadata_bulk.py @@ -743,6 +743,8 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog): else: flags = re.I + flags |= re.UNICODE + try: if self.search_mode.currentIndex() == 0: self.s_r_obj = re.compile(re.escape(unicode(self.search_for.text())), flags)