From 3228374bf935e1b971e197053257762fa5c6488f Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Tue, 22 Jun 2010 22:32:19 +0100 Subject: [PATCH] Fix yet another problem with sony collections. This one was provoked by doing a sync booklist just after loading the cache. The problem was that the collections had not been computed, so they were all removed. The bug could happen if only one book was out of sync. --- src/calibre/devices/prs505/sony_cache.py | 109 ++++++++++------------- src/calibre/gui2/device.py | 5 ++ src/calibre/gui2/library/models.py | 2 +- 3 files changed, 53 insertions(+), 63 deletions(-) diff --git a/src/calibre/devices/prs505/sony_cache.py b/src/calibre/devices/prs505/sony_cache.py index e7d0e4686c..a704824a3f 100644 --- a/src/calibre/devices/prs505/sony_cache.py +++ b/src/calibre/devices/prs505/sony_cache.py @@ -9,7 +9,6 @@ import os, time from pprint import pprint from base64 import b64decode from uuid import uuid4 - from lxml import etree from calibre import prints, guess_type @@ -151,15 +150,14 @@ class XMLCache(object): else: seen.add(title) - def get_playlist_map(self): - debug_print('Start get_playlist_map') + def build_playlist_id_map(self): + debug_print('Start build_playlist_id_map') ans = {} self.ensure_unique_playlist_titles() debug_print('after ensure_unique_playlist_titles') self.prune_empty_playlists() - debug_print('get_playlist_map loop') for i, root in self.record_roots.items(): - debug_print('get_playlist_map loop', i) + debug_print('build_playlist_id_map loop', i) id_map = self.build_id_map(root) ans[i] = [] for playlist in root.xpath('//*[local-name()="playlist"]'): @@ -170,9 +168,23 @@ class XMLCache(object): if record is not None: items.append(record) ans[i].append((playlist.get('title'), items)) - debug_print('end get_playlist_map') + debug_print('end build_playlist_id_map') return ans + def build_id_playlist_map(self, bl_index): + debug_print('Start build_id_playlist_map') + pmap = self.build_playlist_id_map()[bl_index] + playlist_map = {} + for title, records in pmap: + for record in records: + path = record.get('path', None) + if path: + if path not in playlist_map: + playlist_map[path] = set() + playlist_map[path].add(title) + debug_print('Finish build_id_playlist_map. Found', len(playlist_map)) + return playlist_map + def get_or_create_playlist(self, bl_idx, title): root = self.record_roots[bl_idx] for playlist in root.xpath('//*[local-name()="playlist"]'): @@ -192,8 +204,7 @@ class XMLCache(object): # }}} def fix_ids(self): # {{{ - if DEBUG: - debug_print('Running fix_ids()') + debug_print('Running fix_ids()') def ensure_numeric_ids(root): idmap = {} @@ -276,38 +287,19 @@ class XMLCache(object): def update_booklist(self, bl, bl_index): if bl_index not in self.record_roots: return - if DEBUG: - debug_print('Updating JSON cache:', bl_index) + debug_print('Updating JSON cache:', bl_index) + playlist_map = self.build_id_playlist_map(bl_index) root = self.record_roots[bl_index] - pmap = self.get_playlist_map()[bl_index] - playlist_map = {} - for title, records in pmap: - for record in records: - path = record.get('path', None) - if path: - if path not in playlist_map: - playlist_map[path] = [] - playlist_map[path].append(title) - lpath_map = self.build_lpath_map(root) for book in bl: record = lpath_map.get(book.lpath, None) if record is not None: title = record.get('title', None) if title is not None and title != book.title: - if DEBUG: - debug_print('Renaming title', book.title, 'to', title) + debug_print('Renaming title', book.title, 'to', title) book.title = title -# We shouldn't do this for Sonys, because the reader strips -# all but the first author. -# authors = record.get('author', None) -# if authors is not None: -# authors = string_to_authors(authors) -# if authors != book.authors: -# if DEBUG: -# prints('Renaming authors', book.authors, 'to', -# authors) -# book.authors = authors + # Don't set the author, because the reader strips all but + # the first author. for thumbnail in record.xpath( 'descendant::*[local-name()="thumbnail"]'): for img in thumbnail.xpath( @@ -318,45 +310,45 @@ class XMLCache(object): book.thumbnail = raw break break - if book.lpath in playlist_map: - tags = playlist_map[book.lpath] - book.device_collections = tags + book.device_collections = list(playlist_map.get(book.lpath, set())) debug_print('Finished updating JSON cache:', bl_index) # }}} # Update XML from JSON {{{ def update(self, booklists, collections_attributes): - debug_print('Starting update XML from JSON') - playlist_map = self.get_playlist_map() - + debug_print('In update. Starting update XML from JSON') for i, booklist in booklists.items(): - if DEBUG: - debug_print('Updating XML Cache:', i) + playlist_map = self.build_id_playlist_map(i) + debug_print('Updating XML Cache:', i) root = self.record_roots[i] lpath_map = self.build_lpath_map(root) for book in booklist: path = os.path.join(self.prefixes[i], *(book.lpath.split('/'))) -# record = self.book_by_lpath(book.lpath, root) record = lpath_map.get(book.lpath, None) if record is None: record = self.create_text_record(root, i, book.lpath) self.update_text_record(record, book, path, i) - - bl_pmap = playlist_map[i] - self.update_playlists(i, root, booklist, bl_pmap, - collections_attributes) - - self.fix_ids() - - # This is needed to update device_collections + # Ensure the collections in the XML database are recorded for + # this book + if book.device_collections is None: + book.device_collections = [] + book.device_collections = list(set(book.device_collections) | + playlist_map.get(book.lpath, set())) + self.update_playlists(i, root, booklist, collections_attributes) + # Update the device collections because update playlist could have added + # some new ones. + debug_print('In update/ Starting refresh of device_collections') for i, booklist in booklists.items(): - self.update_booklist(booklist, i) + playlist_map = self.build_id_playlist_map(i) + for book in booklist: + book.device_collections = list(set(book.device_collections) | + playlist_map.get(book.lpath, set())) + self.fix_ids() debug_print('Finished update XML from JSON') - def update_playlists(self, bl_index, root, booklist, playlist_map, - collections_attributes): - debug_print('Starting update_playlists') + def update_playlists(self, bl_index, root, booklist, collections_attributes): + debug_print('Starting update_playlists', collections_attributes) collections = booklist.get_collections(collections_attributes) lpath_map = self.build_lpath_map(root) for category, books in collections.items(): @@ -372,10 +364,8 @@ class XMLCache(object): rec.set('id', str(self.max_id(root)+1)) ids = [x.get('id', None) for x in records] if None in ids: - if DEBUG: - debug_print('WARNING: Some elements do not have ids') - ids = [x for x in ids if x is not None] - + debug_print('WARNING: Some elements do not have ids') + ids = [x for x in ids if x is not None] playlist = self.get_or_create_playlist(bl_index, category) playlist_ids = [] for item in playlist: @@ -544,10 +534,5 @@ class XMLCache(object): break self.namespaces[i] = ns -# if DEBUG: -# debug_print('Found nsmaps:') -# pprint(self.nsmaps) -# debug_print('Found namespaces:') -# pprint(self.namespaces) # }}} diff --git a/src/calibre/gui2/device.py b/src/calibre/gui2/device.py index 850396bc5d..836105aba9 100644 --- a/src/calibre/gui2/device.py +++ b/src/calibre/gui2/device.py @@ -1228,6 +1228,11 @@ class DeviceMixin(object): # {{{ return cp, fs = job.result self.location_view.model().update_devices(cp, fs) + # reset the views so that up-to-date info is shown. These need to be + # here because the sony driver updates collections in sync_booklists + self.memory_view.reset() + self.card_a_view.reset() + self.card_b_view.reset() def upload_books(self, files, names, metadata, on_card=None, memory=None): ''' diff --git a/src/calibre/gui2/library/models.py b/src/calibre/gui2/library/models.py index 435b5c4c07..a85462e22d 100644 --- a/src/calibre/gui2/library/models.py +++ b/src/calibre/gui2/library/models.py @@ -1118,7 +1118,7 @@ class DeviceBooksModel(BooksModel): # {{{ elif cname == 'collections': tags = self.db[self.map[row]].device_collections if tags: - return QVariant(', '.join(tags)) + return QVariant(', '.join(sorted(tags, key=str.lower))) elif role == Qt.ToolTipRole and index.isValid(): if self.map[row] in self.indices_to_be_deleted(): return QVariant(_('Marked for deletion'))