From 477767f2ce3d68528f0aee600af7f18a5a14faac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gae=CC=88tan=20Lehmann?= Date: Thu, 11 Jul 2013 15:53:27 +0200 Subject: [PATCH 01/14] add acrimed.org recipe --- recipes/acrimed.recipe | 24 ++++++++++++++++++++++++ recipes/icons/acrimed.png | Bin 0 -> 709 bytes 2 files changed, 24 insertions(+) create mode 100644 recipes/acrimed.recipe create mode 100644 recipes/icons/acrimed.png diff --git a/recipes/acrimed.recipe b/recipes/acrimed.recipe new file mode 100644 index 0000000000..66e3e732bb --- /dev/null +++ b/recipes/acrimed.recipe @@ -0,0 +1,24 @@ +__license__ = 'GPL v3' +__copyright__ = '2012' +''' +acrimed.org +''' + +class Acrimed(BasicNewsRecipe): + title = u'Acrimed' + __author__ = 'Gaƫtan Lehmann' + oldest_article = 30 + max_articles_per_feed = 100 + auto_cleanup = True + auto_cleanup_keep = '//div[@class="crayon article-chapo-4112 chapo"]' + language = 'fr' + masthead_url = 'http://www.acrimed.org/IMG/siteon0.gif' + feeds = [(u'Acrimed', u'http://www.acrimed.org/spip.php?page=backend')] + + preprocess_regexps = [ + (re.compile(r'(.*) - Acrimed \| Action Critique M.*dias'), lambda m: '' + m.group(1) + ''), + (re.compile(r'

(.*) - Acrimed \| Action Critique M.*dias

'), lambda m: '

' + m.group(1) + '

')] + + extra_css = """ + .chapo{font-style:italic; margin: 1em 0 0.5em} + """ diff --git a/recipes/icons/acrimed.png b/recipes/icons/acrimed.png new file mode 100644 index 0000000000000000000000000000000000000000..b88f368d3ca856f4b1efc14a62323bb1a82264b0 GIT binary patch literal 709 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>nyLa#5=H?a<6x7qxGd4Cpefo5Gc=&?{4}f0L)zwW* zOtiDJQ&Li@tE-zmd-lhVA0;Ft;^PyFi;E{toOtBOk<`@G#KffX^75ZQe+C5jZ```*1afYAx;TbNT=qTpGVG8859@=M0tr!G%}lObH-gwwg)bPT zin4}>ggp3rzjZ_9p8b>Ft2~c6W+K;EpLj7M<_+6j519i#MR|(tS8i5T@y}c{clQbF z*8S5LwYj~#bo2-3o=wlg|C9tB>kBv=6MKDup0w|+AQs){%kDA;nrx1J5yV*bdS=KI zz4o#T!HSOc3tZS4t`?ZgyCdu)KV<<^|GS)jU;a1=?AiCw+Cg#Sj|q9EOgwhI>s34d z{eApme+gq4fA30;uvz-kYuKl3)oJH3e|v$8??TYLB(uX4e&sVxeJRdam;1~O=t*k Wacl6}cIZ7&1B0ilpUXO@geCyLCONwR literal 0 HcmV?d00001 From 998a79075ce9c68f5c0e7860af663dcc5ed2bde2 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 12 Jul 2013 13:39:14 +0530 Subject: [PATCH 02/14] all_formats() --- src/calibre/db/cache.py | 3 +++ src/calibre/db/legacy.py | 1 + src/calibre/db/tests/legacy.py | 1 + 3 files changed, 5 insertions(+) diff --git a/src/calibre/db/cache.py b/src/calibre/db/cache.py index a322c9b6d7..ce921537f2 100644 --- a/src/calibre/db/cache.py +++ b/src/calibre/db/cache.py @@ -363,6 +363,9 @@ class Cache(object): @read_api def all_field_names(self, field): ''' Frozen set of all fields names (should only be used for many-one and many-many fields) ''' + if field == 'formats': + return frozenset(self.fields[field].table.col_book_map) + try: return frozenset(self.fields[field].table.id_map.itervalues()) except AttributeError: diff --git a/src/calibre/db/legacy.py b/src/calibre/db/legacy.py index 4c825247e9..398d1b1a86 100644 --- a/src/calibre/db/legacy.py +++ b/src/calibre/db/legacy.py @@ -68,6 +68,7 @@ class LibraryDatabase(object): for field in ('authors', 'tags', 'publisher', 'series'): name = field[:-1] if field in {'authors', 'tags'} else field setattr(self, 'all_%s_names' % name, partial(self.new_api.all_field_names, field)) + self.all_formats = partial(self.new_api.all_field_names, 'formats') for func, field in {'all_authors':'authors', 'all_titles':'title', 'all_tags2':'tags', 'all_series':'series', 'all_publishers':'publisher'}.iteritems(): setattr(self, func, partial(self.field_id_map, field)) diff --git a/src/calibre/db/tests/legacy.py b/src/calibre/db/tests/legacy.py index 6413dc0d8f..b816341b5a 100644 --- a/src/calibre/db/tests/legacy.py +++ b/src/calibre/db/tests/legacy.py @@ -158,6 +158,7 @@ class LegacyTest(BaseTest): 'custom_field_keys':[(True,), (False,)], 'get_usage_count_by_id':[('authors',), ('tags',), ('series',), ('publisher',), ('#tags',), ('languages',)], 'get_field':[(1, 'title'), (2, 'tags'), (0, 'rating'), (1, 'authors'), (2, 'series'), (1, '#tags')], + 'all_formats':[()], }.iteritems(): for a in args: fmt = lambda x: x From 9249fd8a3e79ebe8a9dc7049e6fad8878d1f8c2d Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 12 Jul 2013 14:25:39 +0530 Subject: [PATCH 03/14] Refactor the author data api for multiple authors --- src/calibre/db/cache.py | 17 +++++++++-------- src/calibre/db/tests/legacy.py | 2 ++ src/calibre/db/view.py | 6 ++---- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/calibre/db/cache.py b/src/calibre/db/cache.py index ce921537f2..2c74a31438 100644 --- a/src/calibre/db/cache.py +++ b/src/calibre/db/cache.py @@ -161,7 +161,8 @@ class Cache(object): def _get_metadata(self, book_id, get_user_categories=True): # {{{ mi = Metadata(None, template_cache=self.formatter_template_cache) author_ids = self._field_ids_for('authors', book_id) - aut_list = [self._author_data(i) for i in author_ids] + adata = self._author_data(author_ids) + aut_list = [adata[i] for i in author_ids] aum = [] aus = {} aul = {} @@ -388,17 +389,17 @@ class Cache(object): raise ValueError('%s is not a many-one or many-many field' % field) @read_api - def author_data(self, author_id): + def author_data(self, author_ids): ''' Return author data as a dictionary with keys: name, sort, link - If no author with the specified id is found an empty dictionary is - returned. + If no authors with the specified ids are found an empty dictionary is + returned. If author_ids is None, data for all authors is returned. ''' - try: - return self.fields['authors'].author_data(author_id) - except (KeyError, IndexError): - return {} + af = self.fields['authors'] + if author_ids is None: + author_ids = tuple(af.table.id_map) + return {aid:af.author_data(aid) for aid in author_ids if aid in af.table.id_map} @read_api def format_metadata(self, book_id, fmt, allow_cache=True): diff --git a/src/calibre/db/tests/legacy.py b/src/calibre/db/tests/legacy.py index b816341b5a..b28a5cb93e 100644 --- a/src/calibre/db/tests/legacy.py +++ b/src/calibre/db/tests/legacy.py @@ -243,6 +243,8 @@ class LegacyTest(BaseTest): '_set_title', '_set_custom', '_update_author_in_cache', # Feeds are now stored in the config folder 'get_feeds', 'get_feed', 'update_feed', 'remove_feeds', 'add_feed', 'set_feeds', + # Obsolete/broken methods + 'author_id', # replaced by get_author_id } SKIP_ARGSPEC = { '__init__', 'get_next_series_num_for', 'has_book', 'author_sort_from_authors', 'all_tags', diff --git a/src/calibre/db/view.py b/src/calibre/db/view.py index 3f135860d9..ecd5182232 100644 --- a/src/calibre/db/view.py +++ b/src/calibre/db/view.py @@ -189,10 +189,8 @@ class View(object): id_ = idx if index_is_id else self.index_to_id(idx) with self.cache.read_lock: ids = self.cache._field_ids_for('authors', id_) - ans = [] - for id_ in ids: - data = self.cache._author_data(id_) - ans.append(':::'.join((data['name'], data['sort'], data['link']))) + adata = self.cache._author_data(ids) + ans = [':::'.join((adata[aid]['name'], adata[aid]['sort'], adata[aid]['link'])) for aid in ids if aid in adata] return ':#:'.join(ans) if ans else default_value def multisort(self, fields=[], subsort=False, only_ids=None): From ca8f29db0bd79392a51a59ef7e57598b54b161c3 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 12 Jul 2013 14:43:08 +0530 Subject: [PATCH 04/14] get_*_with_ids() API --- src/calibre/db/cache.py | 2 +- src/calibre/db/legacy.py | 6 ++++++ src/calibre/db/tests/legacy.py | 29 ++++++++++++++++++----------- 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/src/calibre/db/cache.py b/src/calibre/db/cache.py index 2c74a31438..eca5a2a395 100644 --- a/src/calibre/db/cache.py +++ b/src/calibre/db/cache.py @@ -389,7 +389,7 @@ class Cache(object): raise ValueError('%s is not a many-one or many-many field' % field) @read_api - def author_data(self, author_ids): + def author_data(self, author_ids=None): ''' Return author data as a dictionary with keys: name, sort, link diff --git a/src/calibre/db/legacy.py b/src/calibre/db/legacy.py index 398d1b1a86..7f375756e7 100644 --- a/src/calibre/db/legacy.py +++ b/src/calibre/db/legacy.py @@ -73,6 +73,12 @@ class LibraryDatabase(object): for func, field in {'all_authors':'authors', 'all_titles':'title', 'all_tags2':'tags', 'all_series':'series', 'all_publishers':'publisher'}.iteritems(): setattr(self, func, partial(self.field_id_map, field)) self.all_tags = lambda : list(self.all_tag_names()) + self.get_authors_with_ids = lambda : [[aid, adata['name'], adata['sort'], adata['link']] for aid, adata in self.new_api.author_data().iteritems()] + self.get_tags_with_ids = lambda : [[tid, tag] for tid, tag in self.new_api.get_id_map('tags').iteritems()] + self.get_series_with_ids = lambda : [[tid, tag] for tid, tag in self.new_api.get_id_map('series').iteritems()] + self.get_publishers_with_ids = lambda : [[tid, tag] for tid, tag in self.new_api.get_id_map('publisher').iteritems()] + self.get_ratings_with_ids = lambda : [[tid, tag] for tid, tag in self.new_api.get_id_map('rating').iteritems()] + self.get_languages_with_ids = lambda : [[tid, tag] for tid, tag in self.new_api.get_id_map('languages').iteritems()] for func in ( 'standard_field_keys', 'custom_field_keys', 'all_field_keys', diff --git a/src/calibre/db/tests/legacy.py b/src/calibre/db/tests/legacy.py index b28a5cb93e..b731dcc819 100644 --- a/src/calibre/db/tests/legacy.py +++ b/src/calibre/db/tests/legacy.py @@ -143,12 +143,12 @@ class LegacyTest(BaseTest): 'all_tag_names':[()], 'all_series_names':[()], 'all_publisher_names':[()], - 'all_authors':[()], - 'all_tags2':[()], - 'all_tags':[()], - 'all_publishers':[()], - 'all_titles':[()], - 'all_series':[()], + '!all_authors':[()], + '!all_tags2':[()], + '@all_tags':[()], + '!all_publishers':[()], + '!all_titles':[()], + '!all_series':[()], 'standard_field_keys':[()], 'all_field_keys':[()], 'searchable_fields':[()], @@ -156,16 +156,23 @@ class LegacyTest(BaseTest): 'metadata_for_field':[('title',), ('tags',)], 'sortable_field_keys':[()], 'custom_field_keys':[(True,), (False,)], - 'get_usage_count_by_id':[('authors',), ('tags',), ('series',), ('publisher',), ('#tags',), ('languages',)], + '!get_usage_count_by_id':[('authors',), ('tags',), ('series',), ('publisher',), ('#tags',), ('languages',)], 'get_field':[(1, 'title'), (2, 'tags'), (0, 'rating'), (1, 'authors'), (2, 'series'), (1, '#tags')], 'all_formats':[()], + 'get_authors_with_ids':[()], + '!get_tags_with_ids':[()], + '!get_series_with_ids':[()], + '!get_publishers_with_ids':[()], + '!get_ratings_with_ids':[()], + '!get_languages_with_ids':[()], }.iteritems(): for a in args: fmt = lambda x: x - if meth in {'get_usage_count_by_id', 'all_series', 'all_authors', 'all_tags2', 'all_publishers', 'all_titles'}: - fmt = dict - elif meth in {'all_tags'}: - fmt = frozenset + if meth[0] in {'!', '@'}: + fmt = {'!':dict, '@':frozenset}[meth[0]] + meth = meth[1:] + elif meth == 'get_authors_with_ids': + fmt = lambda val:{x[0]:tuple(x[1:]) for x in val} self.assertEqual(fmt(getattr(db, meth)(*a)), fmt(getattr(ndb, meth)(*a)), 'The method: %s() returned different results for argument %s' % (meth, a)) db.close() From d80f36ef28d93795c5c2fe1d76b086a1e22a131c Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 12 Jul 2013 15:16:16 +0530 Subject: [PATCH 05/14] Use MethodType for legacy interface to ensure method signatures match --- src/calibre/db/legacy.py | 32 ++++++++++++++++++++++---------- src/calibre/db/tests/legacy.py | 2 +- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/calibre/db/legacy.py b/src/calibre/db/legacy.py index 7f375756e7..2c84542d4f 100644 --- a/src/calibre/db/legacy.py +++ b/src/calibre/db/legacy.py @@ -6,7 +6,7 @@ from __future__ import (unicode_literals, division, absolute_import, __license__ = 'GPL v3' __copyright__ = '2013, Kovid Goyal ' -import os, traceback +import os, traceback, types from functools import partial from future_builtins import zip @@ -62,24 +62,36 @@ class LibraryDatabase(object): setattr(self, prop, partial(self.get_property, loc=self.FIELD_MAP[fm])) + MT = lambda func: types.MethodType(func, self, LibraryDatabase) + for meth in ('get_next_series_num_for', 'has_book', 'author_sort_from_authors'): setattr(self, meth, getattr(self.new_api, meth)) + # Legacy API to get information about many-(one, many) fields for field in ('authors', 'tags', 'publisher', 'series'): + def getter(field): + def func(self): + return self.new_api.all_field_names(field) + return func name = field[:-1] if field in {'authors', 'tags'} else field - setattr(self, 'all_%s_names' % name, partial(self.new_api.all_field_names, field)) - self.all_formats = partial(self.new_api.all_field_names, 'formats') + setattr(self, 'all_%s_names' % name, MT(getter(field))) + self.all_formats = MT(lambda self:self.new_api.all_field_names('formats')) for func, field in {'all_authors':'authors', 'all_titles':'title', 'all_tags2':'tags', 'all_series':'series', 'all_publishers':'publisher'}.iteritems(): setattr(self, func, partial(self.field_id_map, field)) - self.all_tags = lambda : list(self.all_tag_names()) - self.get_authors_with_ids = lambda : [[aid, adata['name'], adata['sort'], adata['link']] for aid, adata in self.new_api.author_data().iteritems()] - self.get_tags_with_ids = lambda : [[tid, tag] for tid, tag in self.new_api.get_id_map('tags').iteritems()] - self.get_series_with_ids = lambda : [[tid, tag] for tid, tag in self.new_api.get_id_map('series').iteritems()] - self.get_publishers_with_ids = lambda : [[tid, tag] for tid, tag in self.new_api.get_id_map('publisher').iteritems()] - self.get_ratings_with_ids = lambda : [[tid, tag] for tid, tag in self.new_api.get_id_map('rating').iteritems()] - self.get_languages_with_ids = lambda : [[tid, tag] for tid, tag in self.new_api.get_id_map('languages').iteritems()] + self.all_tags = MT(lambda self: list(self.all_tag_names())) + self.get_authors_with_ids = MT( + lambda self: [[aid, adata['name'], adata['sort'], adata['link']] for aid, adata in self.new_api.author_data().iteritems()]) + for field in ('tags', 'series', 'publishers', 'ratings', 'languages'): + def getter(field): + fname = field[:-1] if field in {'publishers', 'ratings'} else field + def func(self): + return [[tid, tag] for tid, tag in self.new_api.get_id_map(fname).iteritems()] + return func + setattr(self, 'get_%s_with_ids' % field, + MT(getter(field))) + # Legacy field API for func in ( 'standard_field_keys', 'custom_field_keys', 'all_field_keys', 'searchable_fields', 'sortable_field_keys', diff --git a/src/calibre/db/tests/legacy.py b/src/calibre/db/tests/legacy.py index b731dcc819..55fac0120d 100644 --- a/src/calibre/db/tests/legacy.py +++ b/src/calibre/db/tests/legacy.py @@ -254,7 +254,7 @@ class LegacyTest(BaseTest): 'author_id', # replaced by get_author_id } SKIP_ARGSPEC = { - '__init__', 'get_next_series_num_for', 'has_book', 'author_sort_from_authors', 'all_tags', + '__init__', 'get_next_series_num_for', 'has_book', 'author_sort_from_authors', } missing = [] From 71e27433ec30d3f8995d5bade3af0eb3341aac41 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 12 Jul 2013 15:37:07 +0530 Subject: [PATCH 06/14] API to get item name from item id --- src/calibre/db/cache.py | 4 ++++ src/calibre/db/legacy.py | 7 +++++++ src/calibre/db/tests/legacy.py | 7 ++++++- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/calibre/db/cache.py b/src/calibre/db/cache.py index eca5a2a395..bc8f1024f5 100644 --- a/src/calibre/db/cache.py +++ b/src/calibre/db/cache.py @@ -388,6 +388,10 @@ class Cache(object): return self.fields[field].table.book_col_map.copy() raise ValueError('%s is not a many-one or many-many field' % field) + @read_api + def get_item_name(self, field, item_id): + return self.fields[field].table.id_map[item_id] + @read_api def author_data(self, author_ids=None): ''' diff --git a/src/calibre/db/legacy.py b/src/calibre/db/legacy.py index 2c84542d4f..f8faed4290 100644 --- a/src/calibre/db/legacy.py +++ b/src/calibre/db/legacy.py @@ -90,6 +90,13 @@ class LibraryDatabase(object): return func setattr(self, 'get_%s_with_ids' % field, MT(getter(field))) + for field in ('author', 'tag', 'series'): + def getter(field): + field = field if field == 'series' else (field+'s') + def func(self, item_id): + return self.new_api.get_item_name(field, item_id) + return func + setattr(self, '%s_name' % field, MT(getter(field))) # Legacy field API for func in ( diff --git a/src/calibre/db/tests/legacy.py b/src/calibre/db/tests/legacy.py index 55fac0120d..96fc98d5be 100644 --- a/src/calibre/db/tests/legacy.py +++ b/src/calibre/db/tests/legacy.py @@ -32,7 +32,9 @@ class ET(object): return newres def compare_argspecs(old, new, attr): - ok = len(old.args) == len(new.args) and len(old.defaults or ()) == len(new.defaults or ()) and old.args[-len(old.defaults or ()):] == new.args[-len(new.defaults or ()):] # noqa + num = len(old.defaults or ()) + + ok = len(old.args) == len(new.args) and old.defaults == new.defaults and (num == 0 or old.args[-num:] == new.args[-num:]) if not ok: raise AssertionError('The argspec for %s does not match. %r != %r' % (attr, old, new)) @@ -165,6 +167,9 @@ class LegacyTest(BaseTest): '!get_publishers_with_ids':[()], '!get_ratings_with_ids':[()], '!get_languages_with_ids':[()], + 'tag_name':[(3,)], + 'author_name':[(3,)], + 'series_name':[(3,)], }.iteritems(): for a in args: fmt = lambda x: x From 14441ff0524064a05ba2d05c80f82b6ba12edf7b Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 12 Jul 2013 16:01:16 +0530 Subject: [PATCH 07/14] ... --- src/calibre/db/tests/legacy.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/calibre/db/tests/legacy.py b/src/calibre/db/tests/legacy.py index 96fc98d5be..5df09ec065 100644 --- a/src/calibre/db/tests/legacy.py +++ b/src/calibre/db/tests/legacy.py @@ -32,6 +32,8 @@ class ET(object): return newres def compare_argspecs(old, new, attr): + # We dont compare the names of the non-keyword arguments as they are often + # different and they dont affect the usage of the API. num = len(old.defaults or ()) ok = len(old.args) == len(new.args) and old.defaults == new.defaults and (num == 0 or old.args[-num:] == new.args[-num:]) From 598a604853a8b0b8a2b435aa5b4fc1c3bf0adb54 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 12 Jul 2013 17:11:13 +0530 Subject: [PATCH 08/14] MTP driver: Ignore the zinio folder by default --- src/calibre/devices/mtp/driver.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/calibre/devices/mtp/driver.py b/src/calibre/devices/mtp/driver.py index f0e532639a..40fa9d900b 100644 --- a/src/calibre/devices/mtp/driver.py +++ b/src/calibre/devices/mtp/driver.py @@ -83,7 +83,7 @@ class MTP_DEVICE(BASE): return name in { 'alarms', 'android', 'dcim', 'movies', 'music', 'notifications', 'pictures', 'ringtones', 'samsung', 'sony', 'htc', 'bluetooth', - 'games', 'lost.dir', 'video', 'whatsapp', 'image'} + 'games', 'lost.dir', 'video', 'whatsapp', 'image', 'com.zinio.mobile.android.reader'} def configure_for_kindle_app(self): proxy = self.prefs @@ -159,16 +159,17 @@ class MTP_DEVICE(BASE): def get_driveinfo(self): if not self.driveinfo: self.driveinfo = {} - for sid, location_code in ( (self._main_id, 'main'), (self._carda_id, + for sid, location_code in ((self._main_id, 'main'), (self._carda_id, 'A'), (self._cardb_id, 'B')): - if sid is None: continue + if sid is None: + continue self._update_drive_info(self.filesystem_cache.storage(sid), location_code) return self.driveinfo def get_device_information(self, end_session=True): self.report_progress(1.0, _('Get device information...')) dinfo = self.get_basic_device_information() - return tuple( list(dinfo) + [self.driveinfo] ) + return tuple(list(dinfo) + [self.driveinfo]) def card_prefix(self, end_session=True): return (self._carda_id, self._cardb_id) @@ -190,7 +191,7 @@ class MTP_DEVICE(BASE): from calibre.devices.mtp.books import JSONCodec from calibre.devices.mtp.books import BookList, Book self.report_progress(0, _('Listing files, this can take a while')) - self.get_driveinfo() # Ensure driveinfo is loaded + self.get_driveinfo() # Ensure driveinfo is loaded sid = {'carda':self._carda_id, 'cardb':self._cardb_id}.get(oncard, self._main_id) if sid is None: @@ -230,7 +231,7 @@ class MTP_DEVICE(BASE): cached_metadata.path = mtp_file.mtp_id_path debug('Using cached metadata for', '/'.join(mtp_file.full_path)) - continue # No need to update metadata + continue # No need to update metadata book = cached_metadata else: book = Book(sid, '/'.join(relpath)) @@ -352,8 +353,8 @@ class MTP_DEVICE(BASE): def prefix_for_location(self, on_card): if self.location_paths is None: self.location_paths = {} - for sid, loc in ( (self._main_id, None), (self._carda_id, 'carda'), - (self._cardb_id, 'cardb') ): + for sid, loc in ((self._main_id, None), (self._carda_id, 'carda'), + (self._cardb_id, 'cardb')): if sid is not None: storage = self.filesystem_cache.storage(sid) prefixes = self.get_pref('send_to') @@ -470,7 +471,8 @@ class MTP_DEVICE(BASE): def remove_books_from_metadata(self, paths, booklists): self.report_progress(0, _('Removing books from metadata')) - class NextPath(Exception): pass + class NextPath(Exception): + pass for i, path in enumerate(paths): try: @@ -549,3 +551,4 @@ if __name__ == '__main__': dev.shutdown() + From a8033fc610b940a493734624485579bb5c8cc65e Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 12 Jul 2013 17:27:13 +0530 Subject: [PATCH 09/14] Author data legacy API --- src/calibre/db/legacy.py | 19 +++++++++++++++++++ src/calibre/db/tests/legacy.py | 3 +++ 2 files changed, 22 insertions(+) diff --git a/src/calibre/db/legacy.py b/src/calibre/db/legacy.py index f8faed4290..0ab29d2e3d 100644 --- a/src/calibre/db/legacy.py +++ b/src/calibre/db/legacy.py @@ -312,6 +312,25 @@ class LibraryDatabase(object): mi = self.new_api.get_metadata(book_id, get_cover=key == 'cover') return mi.get(key, default) + def authors_sort_strings(self, index, index_is_id=False): + book_id = index if index_is_id else self.data.index_to_id(index) + with self.new_api.read_lock: + af = self.new_api.fields['authors'].table + authors = af.book_col_map.get(book_id, ()) + adata = self.new_api._author_data(authors) + return [adata[aid]['sort'] for aid in authors] + + def author_sort_from_book(self, index, index_is_id=False): + return ' & '.join(self.authors_sort_strings(index, index_is_id=index_is_id)) + + def authors_with_sort_strings(self, index, index_is_id=False): + book_id = index if index_is_id else self.data.index_to_id(index) + with self.new_api.read_lock: + af = self.new_api.fields['authors'].table + authors = af.book_col_map.get(book_id, ()) + adata = self.new_api._author_data(authors) + return [(aid, adata[aid]['name'], adata[aid]['sort'], adata[aid]['link']) for aid in authors] + # Private interface {{{ def __iter__(self): diff --git a/src/calibre/db/tests/legacy.py b/src/calibre/db/tests/legacy.py index 5df09ec065..fa76439387 100644 --- a/src/calibre/db/tests/legacy.py +++ b/src/calibre/db/tests/legacy.py @@ -172,6 +172,9 @@ class LegacyTest(BaseTest): 'tag_name':[(3,)], 'author_name':[(3,)], 'series_name':[(3,)], + 'authors_sort_strings':[(0,), (1,), (2,)], + 'author_sort_from_book':[(0,), (1,), (2,)], + 'authors_with_sort_strings':[(0,), (1,), (2,)], }.iteritems(): for a in args: fmt = lambda x: x From 7c1421d15a6137a4a5fa90ec060d1ebec6e1433f Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 12 Jul 2013 17:38:16 +0530 Subject: [PATCH 10/14] ... --- src/calibre/db/legacy.py | 6 ++---- src/calibre/db/tests/legacy.py | 3 +++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/calibre/db/legacy.py b/src/calibre/db/legacy.py index 0ab29d2e3d..ff0c742e9d 100644 --- a/src/calibre/db/legacy.py +++ b/src/calibre/db/legacy.py @@ -315,8 +315,7 @@ class LibraryDatabase(object): def authors_sort_strings(self, index, index_is_id=False): book_id = index if index_is_id else self.data.index_to_id(index) with self.new_api.read_lock: - af = self.new_api.fields['authors'].table - authors = af.book_col_map.get(book_id, ()) + authors = self.new_api._field_ids_for('authors', book_id) adata = self.new_api._author_data(authors) return [adata[aid]['sort'] for aid in authors] @@ -326,8 +325,7 @@ class LibraryDatabase(object): def authors_with_sort_strings(self, index, index_is_id=False): book_id = index if index_is_id else self.data.index_to_id(index) with self.new_api.read_lock: - af = self.new_api.fields['authors'].table - authors = af.book_col_map.get(book_id, ()) + authors = self.new_api._field_ids_for('authors', book_id) adata = self.new_api._author_data(authors) return [(aid, adata[aid]['name'], adata[aid]['sort'], adata[aid]['link']) for aid in authors] diff --git a/src/calibre/db/tests/legacy.py b/src/calibre/db/tests/legacy.py index fa76439387..daa53b7ddd 100644 --- a/src/calibre/db/tests/legacy.py +++ b/src/calibre/db/tests/legacy.py @@ -262,6 +262,8 @@ class LegacyTest(BaseTest): 'get_feeds', 'get_feed', 'update_feed', 'remove_feeds', 'add_feed', 'set_feeds', # Obsolete/broken methods 'author_id', # replaced by get_author_id + 'books_for_author', # broken + 'books_in_old_database', # unused } SKIP_ARGSPEC = { '__init__', 'get_next_series_num_for', 'has_book', 'author_sort_from_authors', @@ -329,3 +331,4 @@ class LegacyTest(BaseTest): T(('n', object())) old.close() # }}} + From 3719dfb4d8a607566b2ac4f069671ec878b4a44a Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 12 Jul 2013 17:42:16 +0530 Subject: [PATCH 11/14] book_on_device API --- src/calibre/db/legacy.py | 23 +++++++++++++++++++++++ src/calibre/db/tests/legacy.py | 1 + 2 files changed, 24 insertions(+) diff --git a/src/calibre/db/legacy.py b/src/calibre/db/legacy.py index ff0c742e9d..97be03fb19 100644 --- a/src/calibre/db/legacy.py +++ b/src/calibre/db/legacy.py @@ -108,6 +108,7 @@ class LibraryDatabase(object): self.metadata_for_field = self.field_metadata.get self.last_update_check = self.last_modified() + self.book_on_device_func = None def close(self): self.backend.close() @@ -329,6 +330,28 @@ class LibraryDatabase(object): adata = self.new_api._author_data(authors) return [(aid, adata[aid]['name'], adata[aid]['sort'], adata[aid]['link']) for aid in authors] + def book_on_device(self, book_id): + if callable(self.book_on_device_func): + return self.book_on_device_func(book_id) + return None + + def book_on_device_string(self, book_id): + loc = [] + count = 0 + on = self.book_on_device(book_id) + if on is not None: + m, a, b, count = on[:4] + if m is not None: + loc.append(_('Main')) + if a is not None: + loc.append(_('Card A')) + if b is not None: + loc.append(_('Card B')) + return ', '.join(loc) + ((_(' (%s books)')%count) if count > 1 else '') + + def set_book_on_device_func(self, func): + self.book_on_device_func = func + # Private interface {{{ def __iter__(self): diff --git a/src/calibre/db/tests/legacy.py b/src/calibre/db/tests/legacy.py index daa53b7ddd..e5a8a8e7b5 100644 --- a/src/calibre/db/tests/legacy.py +++ b/src/calibre/db/tests/legacy.py @@ -175,6 +175,7 @@ class LegacyTest(BaseTest): 'authors_sort_strings':[(0,), (1,), (2,)], 'author_sort_from_book':[(0,), (1,), (2,)], 'authors_with_sort_strings':[(0,), (1,), (2,)], + 'book_on_device_string':[(1,), (2,), (3,)], }.iteritems(): for a in args: fmt = lambda x: x From ecbea8b0e22b507d88db5cfab997d38031a46196 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 12 Jul 2013 18:04:27 +0530 Subject: [PATCH 12/14] More API --- src/calibre/db/legacy.py | 16 ++++++++++++++++ src/calibre/db/tests/legacy.py | 3 +++ 2 files changed, 19 insertions(+) diff --git a/src/calibre/db/legacy.py b/src/calibre/db/legacy.py index 97be03fb19..a4dd30a88c 100644 --- a/src/calibre/db/legacy.py +++ b/src/calibre/db/legacy.py @@ -109,6 +109,9 @@ class LibraryDatabase(object): self.last_update_check = self.last_modified() self.book_on_device_func = None + # Cleaning is not required anymore + self.clean = self.clean_custom = MT(lambda self:None) + self.clean_standard_field = MT(lambda self, field, commit=False:None) def close(self): self.backend.close() @@ -352,6 +355,19 @@ class LibraryDatabase(object): def set_book_on_device_func(self, func): self.book_on_device_func = func + def books_in_series(self, series_id): + with self.new_api.read_lock: + book_ids = self.new_api._books_for_field('series', series_id) + ff = self.new_api._field_for + return sorted(book_ids, key=lambda x:ff('series_index', x)) + + def books_in_series_of(self, index, index_is_id=False): + book_id = index if index_is_id else self.data.index_to_id(index) + series_ids = self.new_api.field_ids_for('series', book_id) + if not series_ids: + return [] + return self.books_in_series(series_ids[0]) + # Private interface {{{ def __iter__(self): diff --git a/src/calibre/db/tests/legacy.py b/src/calibre/db/tests/legacy.py index e5a8a8e7b5..5c26e50ae5 100644 --- a/src/calibre/db/tests/legacy.py +++ b/src/calibre/db/tests/legacy.py @@ -176,6 +176,7 @@ class LegacyTest(BaseTest): 'author_sort_from_book':[(0,), (1,), (2,)], 'authors_with_sort_strings':[(0,), (1,), (2,)], 'book_on_device_string':[(1,), (2,), (3,)], + 'books_in_series_of':[(0,), (1,), (2,)], }.iteritems(): for a in args: fmt = lambda x: x @@ -265,6 +266,8 @@ class LegacyTest(BaseTest): 'author_id', # replaced by get_author_id 'books_for_author', # broken 'books_in_old_database', # unused + 'clean_user_categories', # internal API + 'cleanup_tags', # internal API } SKIP_ARGSPEC = { '__init__', 'get_next_series_num_for', 'has_book', 'author_sort_from_authors', From 1e4d11d78fb59a7cb548106ac9b699a158bac559 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 12 Jul 2013 18:21:37 +0530 Subject: [PATCH 13/14] books_with_same_title() --- src/calibre/db/legacy.py | 16 +++++++++++++++- src/calibre/db/tests/legacy.py | 6 ++++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/calibre/db/legacy.py b/src/calibre/db/legacy.py index a4dd30a88c..8c5fa5bd31 100644 --- a/src/calibre/db/legacy.py +++ b/src/calibre/db/legacy.py @@ -10,6 +10,7 @@ import os, traceback, types from functools import partial from future_builtins import zip +from calibre import force_unicode from calibre.db import _get_next_series_num_for_list, _get_series_values from calibre.db.adding import ( find_books_in_directory, import_book_directory_multiple, @@ -112,6 +113,8 @@ class LibraryDatabase(object): # Cleaning is not required anymore self.clean = self.clean_custom = MT(lambda self:None) self.clean_standard_field = MT(lambda self, field, commit=False:None) + # apsw operates in autocommit mode + self.commit = MT(lambda self:None) def close(self): self.backend.close() @@ -368,8 +371,19 @@ class LibraryDatabase(object): return [] return self.books_in_series(series_ids[0]) - # Private interface {{{ + def books_with_same_title(self, mi, all_matches=True): + title = mi.title + ans = set() + if title: + title = icu_lower(force_unicode(title)) + for book_id, x in self.new_api.get_id_map('title').iteritems(): + if icu_lower(x) == title: + ans.add(book_id) + if not all_matches: + break + return ans + # Private interface {{{ def __iter__(self): for row in self.data.iterall(): yield row diff --git a/src/calibre/db/tests/legacy.py b/src/calibre/db/tests/legacy.py index 5c26e50ae5..2f5c879532 100644 --- a/src/calibre/db/tests/legacy.py +++ b/src/calibre/db/tests/legacy.py @@ -177,6 +177,7 @@ class LegacyTest(BaseTest): 'authors_with_sort_strings':[(0,), (1,), (2,)], 'book_on_device_string':[(1,), (2,), (3,)], 'books_in_series_of':[(0,), (1,), (2,)], + 'books_with_same_title':[(Metadata(db.title(0)),), (Metadata(db.title(1)),), (Metadata('1234'),)], }.iteritems(): for a in args: fmt = lambda x: x @@ -266,8 +267,9 @@ class LegacyTest(BaseTest): 'author_id', # replaced by get_author_id 'books_for_author', # broken 'books_in_old_database', # unused - 'clean_user_categories', # internal API - 'cleanup_tags', # internal API + + # Internal API + 'clean_user_categories', 'cleanup_tags', 'books_list_filter', } SKIP_ARGSPEC = { '__init__', 'get_next_series_num_for', 'has_book', 'author_sort_from_authors', From 72c0346874fa2bb96236b76c0a1c7a49752a3400 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 12 Jul 2013 18:23:34 +0530 Subject: [PATCH 14/14] Update lamebook --- recipes/lamebook.recipe | 2 ++ 1 file changed, 2 insertions(+) diff --git a/recipes/lamebook.recipe b/recipes/lamebook.recipe index e449285d84..0f4fbe9dc0 100644 --- a/recipes/lamebook.recipe +++ b/recipes/lamebook.recipe @@ -13,6 +13,8 @@ class LamebookRecipe(BasicNewsRecipe): language = 'en' use_embedded_content = False publication_type = 'blog' + reverse_article_order = True + encoding = 'utf-8' keep_only_tags = [ dict(name='div', attrs={'class':'entry'})