diff --git a/src/calibre/devices/kobo/books.py b/src/calibre/devices/kobo/books.py index e01e457122..777c8e7a98 100644 --- a/src/calibre/devices/kobo/books.py +++ b/src/calibre/devices/kobo/books.py @@ -80,6 +80,10 @@ class ImageWrapper(object): class KTCollectionsBookList(CollectionsBookList): + def __init__(self, oncard, prefix, settings): + super(KTCollectionsBookList, self).__init__(oncard, prefix, settings) + self.set_device_managed_collections([]) + def get_collections(self, collection_attributes): debug_print("KTCollectionsBookList:get_collections - start - collection_attributes=", collection_attributes) @@ -126,6 +130,16 @@ class KTCollectionsBookList(CollectionsBookList): # For existing books, modify the collections only if the user # specified 'on_connect' attrs = collection_attributes + for cat_name in self.device_managed_collections: + if cat_name in book.device_collections: + if cat_name not in collections: + collections[cat_name] = {} + if show_debug: + debug_print("KTCollectionsBookList:get_collections - Device Managed Collection:", cat_name) + if lpath not in collections[cat_name]: + collections[cat_name][lpath] = (book, tsval, tsval) + if show_debug: + debug_print("KTCollectionsBookList:get_collections - Device Managed Collection -added book to cat_name", cat_name) book.device_collections = [] if show_debug: debug_print("KTCollectionsBookList:get_collections - attrs=", attrs) @@ -255,9 +269,13 @@ class KTCollectionsBookList(CollectionsBookList): books = lpaths.values() books.sort(cmp=none_cmp) result[category] = [x[0] for x in books] + # debug_print("KTCollectionsBookList:get_collections - result=", result.keys()) debug_print("KTCollectionsBookList:get_collections - end") return result + def set_device_managed_collections(self, collection_names): + self.device_managed_collections = collection_names + def set_debugging_title(self, title): self.debugging_title = title diff --git a/src/calibre/devices/kobo/driver.py b/src/calibre/devices/kobo/driver.py index 1fe97bbc57..fee41f3f98 100644 --- a/src/calibre/devices/kobo/driver.py +++ b/src/calibre/devices/kobo/driver.py @@ -63,11 +63,11 @@ class KOBO(USBMS): gui_name = 'Kobo Reader' description = _('Communicate with the Kobo Reader') author = 'Timothy Legge and David Forrester' - version = (2, 3, 0) + version = (2, 3, 1) dbversion = 0 fwversion = (0,0,0) - supported_dbversion = 125 + supported_dbversion = 128 has_kepubs = False supported_platforms = ['windows', 'osx', 'linux'] @@ -1305,11 +1305,11 @@ class KOBOTOUCH(KOBO): name = 'KoboTouch' gui_name = 'Kobo Touch/Glo/Mini/Aura HD/Aura H2O/Glo HD/Touch 2' author = 'David Forrester' - description = 'Communicate with the Kobo Touch, Glo, Mini, Aura HD, Aura H2O, Glo HD and Touch 2ereaders. Based on the existing Kobo driver by %s.' % ( + description = 'Communicate with the Kobo Touch, Glo, Mini, Aura HD, Aura H2O, Glo HD and Touch 2 ereaders. Based on the existing Kobo driver by %s.' % ( KOBO.author) # icon = I('devices/kobotouch.jpg') - supported_dbversion = 125 + supported_dbversion = 128 min_supported_dbversion = 53 min_dbversion_series = 65 min_dbversion_externalid = 65 @@ -1327,6 +1327,7 @@ class KOBOTOUCH(KOBO): min_aurah2o_fwversion = (3, 7, 0) min_reviews_fwversion = (3, 12, 0) min_glohd_fwversion = (3, 14, 0) + min_auraone_fwversion = (3, 20, 7280) has_kepubs = True @@ -1346,8 +1347,10 @@ class KOBOTOUCH(KOBO): TIMESTAMP_STRING = "%Y-%m-%dT%H:%M:%SZ" AURA_PRODUCT_ID = [0x4203] + AURA_EDITION2_PRODUCT_ID = [0x4226] AURA_HD_PRODUCT_ID = [0x4193] AURA_H2O_PRODUCT_ID = [0x4213] + AURA_ONE_PRODUCT_ID = [0x4225] GLO_PRODUCT_ID = [0x4173] GLO_HD_PRODUCT_ID = [0x4223] MINI_PRODUCT_ID = [0x4183] @@ -1355,7 +1358,8 @@ class KOBOTOUCH(KOBO): TOUCH2_PRODUCT_ID = [0x4224] PRODUCT_ID = AURA_PRODUCT_ID + AURA_HD_PRODUCT_ID + AURA_H2O_PRODUCT_ID + \ GLO_PRODUCT_ID + GLO_HD_PRODUCT_ID + \ - MINI_PRODUCT_ID + TOUCH_PRODUCT_ID +TOUCH2_PRODUCT_ID + MINI_PRODUCT_ID + TOUCH_PRODUCT_ID + TOUCH2_PRODUCT_ID + \ + AURA_ONE_PRODUCT_ID + AURA_EDITION2_PRODUCT_ID BCD = [0x0110, 0x0326] @@ -1363,27 +1367,45 @@ class KOBOTOUCH(KOBO): # Note: "200" has been used just as a much larger number than the current versions. It is just a lazy # way of making it open ended. COVER_FILE_ENDINGS = { + # Used for screensaver, home screen ' - N3_FULL.parsed':[(600,800),0, 200,True,], # Used for screensaver, home screen # Used for Details screen before FW2.8.1, then for current book tile on home screen ' - N3_LIBRARY_FULL.parsed':[(355,473),0, 200,False,], + # Used for library lists ' - N3_LIBRARY_GRID.parsed':[(149,198),0, 200,False,], # Used for library lists + # Used for library lists ' - N3_LIBRARY_LIST.parsed':[(60,90),0, 53,False,], - ' - AndroidBookLoadTablet_Aspect.parsed':[(355,473), 82, 100,False,], # Used for Details screen from FW2.8.1 -# ' - N3_LIBRARY_SHELF.parsed': [(40,60),0, 52,], + # Used for Details screen from FW2.8.1 + ' - AndroidBookLoadTablet_Aspect.parsed':[(355,473), 82, 100,False,], } - GLO_COVER_FILE_ENDINGS = { # Glo and Aura share resolution, so the image sizes should be the same. - ' - N3_FULL.parsed':[(758,1024),0, 200,True,], # Used for screensaver, home screen + # Glo and Aura share resolution, so the image sizes should be the same. + GLO_COVER_FILE_ENDINGS = { + # Used for screensaver, home screen + ' - N3_FULL.parsed':[(758,1024),0, 200,True,], # Used for Details screen before FW2.8.1, then for current book tile on home screen ' - N3_LIBRARY_FULL.parsed':[(355,479),0, 200,False,], - ' - N3_LIBRARY_GRID.parsed':[(149,201),0, 200,False,], # Used for library lists - ' - AndroidBookLoadTablet_Aspect.parsed':[(355,479), 88, 100,False,], # Used for Details screen from FW2.8.1 + # Used for library lists + ' - N3_LIBRARY_GRID.parsed':[(149,201),0, 200,False,], + # Used for Details screen from FW2.8.1 + ' - AndroidBookLoadTablet_Aspect.parsed':[(355,479), 88, 100,False,], } AURA_HD_COVER_FILE_ENDINGS = { + # Used for screensaver, home screen ' - N3_FULL.parsed': [(1080,1440), 0, 200,True,], # Used for screensaver, home screen # Used for Details screen before FW2.8.1, then for current book tile on home screen ' - N3_LIBRARY_FULL.parsed':[(355, 471), 0, 200,False,], + # Used for library lists + ' - N3_LIBRARY_GRID.parsed':[(149, 198), 0, 200,False,], # Used for library lists + # Used for Details screen from FW2.8.1 + ' - AndroidBookLoadTablet_Aspect.parsed':[(355, 471), 88, 100,False,], + } + AURA_ONE_COVER_FILE_ENDINGS = { + # Used for screensaver, home screen + ' - N3_FULL.parsed': [(1872,1404), 0, 200,True,], # Used for screensaver, home screen + # Used for Details screen before FW2.8.1, then for current book tile on home screen + ' - N3_LIBRARY_FULL.parsed':[(355, 473), 0, 200,False,], + # Used for library lists ' - N3_LIBRARY_GRID.parsed':[(149, 198), 0, 200,False,], # Used for library lists - ' - AndroidBookLoadTablet_Aspect.parsed':[(355, 471), 88, 100,False,], # Used for Details screen from FW2.8.1 } # Following are the sizes used with pre2.1.4 firmware # COVER_FILE_ENDINGS = { @@ -2187,12 +2209,13 @@ class KOBOTOUCH(KOBO): } # debug_print('KoboTouch:update_device_database_collections - collections_attributes=', collections_attributes) - create_bookshelves = self.create_bookshelves - delete_empty_shelves = self.delete_empty_shelves + create_collections = self.create_collections + delete_empty_collections = self.delete_empty_collections update_series_details = self.update_series_details debugging_title = self.get_debugging_title() debug_print("KoboTouch:update_device_database_collections - set_debugging_title to '%s'" % debugging_title) booklists.set_debugging_title(debugging_title) + booklists.set_device_managed_collections(self.ignore_collections_names) bookshelf_attribute = len(collections_attributes) > 0 @@ -2223,7 +2246,7 @@ class KOBOTOUCH(KOBO): # Process any collections that exist for category, books in collections.items(): debug_print("KoboTouch:update_device_database_collections - category='%s' books=%d"%(category, len(books))) - if create_bookshelves and not (category in supportedcategories or category in readstatuslist or category in accessibilitylist): + if create_collections and not (category in supportedcategories or category in readstatuslist or category in accessibilitylist): self.check_for_bookshelf(connection, category) # if category in self.bookshelvelist: # debug_print("Category: ", category, " id = ", readstatuslist.get(category)) @@ -2248,7 +2271,10 @@ class KOBOTOUCH(KOBO): ContentType = self.get_content_type_from_extension(extension) if extension != '' else self.get_content_type_from_path(book.path) book.contentID = self.contentid_from_path(book.path, ContentType) - if category in self.bookshelvelist and self.supports_bookshelves: + if category in self.ignore_collections_names: + debug_print(' Ignoring collection=%s' % category) + category_added = True + elif category in self.bookshelvelist and self.supports_bookshelves: if show_debug: debug_print(' length book.device_collections=%d'%len(book.device_collections)) if category not in book.device_collections: @@ -2311,7 +2337,7 @@ class KOBOTOUCH(KOBO): debug_print("KoboTouch:update_device_database_collections - about to remove a book from shelves book.title=%s" % book.title) self.remove_book_from_device_bookshelves(connection, book) book.device_collections.extend(book.kobo_collections) - if not prefs['manage_device_metadata'] == 'manual' and delete_empty_shelves: + if not prefs['manage_device_metadata'] == 'manual' and delete_empty_collections: debug_print("KoboTouch:update_device_database_collections - about to clear empty bookshelves") self.delete_empty_bookshelves(connection) debug_print("KoboTouch:update_device_database_collections - Number of series set=%d Number of books=%d" % (self.series_set, books_in_library)) @@ -2467,6 +2493,7 @@ class KOBOTOUCH(KOBO): show_debug = self.is_debugging_title(book.title) # or True remove_shelf_list = set(book.current_shelves) - set(book.device_collections) + remove_shelf_list = remove_shelf_list - set(self.ignore_collections_names) if show_debug: debug_print('KoboTouch:remove_book_from_device_bookshelves - book.application_id="%s"'%book.application_id) @@ -2648,13 +2675,14 @@ class KOBOTOUCH(KOBO): "true", "false", ) + shelf_type = "UserTag" # if self.supports_reading_list else None if self.dbversion < 64: addquery += ' ("CreationDate","InternalName","LastModified","Name","_IsDeleted","_IsVisible","_IsSynced")'\ ' VALUES (?, ?, ?, ?, ?, ?, ?)' else: - addquery += ' ("CreationDate", "InternalName","LastModified","Name","_IsDeleted","_IsVisible","_IsSynced", "Id")'\ - ' VALUES (?, ?, ?, ?, ?, ?, ?, ?)' - add_values = add_values +(bookshelf_name,) + addquery += ' ("CreationDate", "InternalName","LastModified","Name","_IsDeleted","_IsVisible","_IsSynced", "Id", "Type")'\ + ' VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)' + add_values = add_values +(bookshelf_name, shelf_type) if show_debug: debug_print('KoboTouch:check_for_bookshelf addquery=', addquery) @@ -2755,15 +2783,15 @@ class KOBOTOUCH(KOBO): debug_print("KoboTouch:set_series - end") @classmethod - def config_widget(self): + def config_widget(cls): # TODO: Cleanup the following - self.current_friendly_name = self.gui_name + cls.current_friendly_name = cls.gui_name from calibre.devices.kobo.kobotouch_config import KOBOTOUCHConfig - return KOBOTOUCHConfig(self.settings(), self.FORMATS, - self.SUPPORTS_SUB_DIRS, self.MUST_READ_METADATA, - self.SUPPORTS_USE_AUTHOR_SORT, self.EXTRA_CUSTOMIZATION_MESSAGE, - self, extra_customization_choices=self.EXTRA_CUSTOMIZATION_CHOICES + return KOBOTOUCHConfig(cls.settings(), cls.FORMATS, + cls.SUPPORTS_SUB_DIRS, cls.MUST_READ_METADATA, + cls.SUPPORTS_USE_AUTHOR_SORT, cls.EXTRA_CUSTOMIZATION_MESSAGE, + cls, extra_customization_choices=cls.EXTRA_CUSTOMIZATION_CHOICES ) @classmethod @@ -2782,6 +2810,7 @@ class KOBOTOUCH(KOBO): @classmethod def save_settings(cls, config_widget): + cls.opts = None config_widget.commit() @classmethod @@ -2796,6 +2825,7 @@ class KOBOTOUCH(KOBO): c.add_opt('collections_columns', default='') c.add_opt('create_collections', default=False) c.add_opt('delete_empty_collections', default=False) + c.add_opt('ignore_collections_names', default='') c.add_opt('upload_covers', default=False) c.add_opt('keep_cover_aspect', default=False) @@ -2827,10 +2857,14 @@ class KOBOTOUCH(KOBO): def isAura(self): return self.detected_device.idProduct in self.AURA_PRODUCT_ID + def isAuraEdition2(self): + return self.detected_device.idProduct in self.AURA_EDITION2_PRODUCT_ID def isAuraHD(self): return self.detected_device.idProduct in self.AURA_HD_PRODUCT_ID def isAuraH2O(self): return self.detected_device.idProduct in self.AURA_H2O_PRODUCT_ID + def isAuraOne(self): + return self.detected_device.idProduct in self.AURA_ONE_PRODUCT_ID def isGlo(self): return self.detected_device.idProduct in self.GLO_PRODUCT_ID def isGloHD(self): @@ -2843,8 +2877,9 @@ class KOBOTOUCH(KOBO): return self.detected_device.idProduct in self.TOUCH2_PRODUCT_ID def cover_file_endings(self): - return self.GLO_COVER_FILE_ENDINGS if self.isGlo() or self.isAura() \ + return self.GLO_COVER_FILE_ENDINGS if self.isGlo() or self.isAura() or self.isAuraEdition2 \ else self.AURA_HD_COVER_FILE_ENDINGS if self.isAuraHD() or self.isAuraH2O() or self.isGloHD() \ + else self.AURA_ONE_COVER_FILE_ENDINGS if self.isAuraOne() \ else self.COVER_FILE_ENDINGS def set_device_name(self): @@ -2870,16 +2905,27 @@ class KOBOTOUCH(KOBO): @property def manage_collections(self): - return self.get_pref('manage_collections') + return self.get_pref('manage_collections') and self.supports_bookshelves @property def create_collections(self): - return self.get_pref('create_collections') + return self.manage_collections and self.get_pref('create_collections') and len(self.collections_columns) > 0 @property def collections_columns(self): return self.get_pref('collections_columns') @property def delete_empty_collections(self): - return self.get_pref('delete_empty_collections') + return self.manage_collections and self.get_pref('delete_empty_collections') + @property + def ignore_collections_names(self): + return [x.lower().strip() for x in self.get_pref('ignore_collections_names').split(',')] + @property + def create_bookshelves(self): + # Only for backwards compatabilty + return self.manage_collections + @property + def delete_empty_shelves(self): + # Only for backwards compatabilty + return self.delete_empty_collections @property def upload_covers(self): @@ -2898,12 +2944,6 @@ class KOBOTOUCH(KOBO): return self.get_pref('modify_css') @property - def create_bookshelves(self): - return self.get_pref('create_collections') and self.supports_bookshelves - @property - def delete_empty_shelves(self): - return self.get_pref('delete_empty_collections') and self.supports_bookshelves - @property def update_device_metadata(self): return self.get_pref('update_device_metadata') @property @@ -2920,7 +2960,6 @@ class KOBOTOUCH(KOBO): @property def supports_bookshelves(self): return self.dbversion >= self.min_supported_dbversion - @property def show_archived_books(self): return self.get_pref('show_archived_books') @@ -2956,8 +2995,7 @@ class KOBOTOUCH(KOBO): def modify_database_check(self, function): # Checks to see whether the database version is supported # and whether the user has chosen to support the firmware version - # debug_print("KoboTouch:modify_database_check - self.fwversion > self.max_supported_fwversion=", self.fwversion > self.max_supported_fwversion) - if self.dbversion > self.supported_dbversion or self.fwversion > self.max_supported_fwversion: + if self.dbversion > self.supported_dbversion or self.is_supported_fwversion: # Unsupported database if not self.get_pref('support_newer_firmware'): debug_print('The database has been upgraded past supported version') @@ -2989,6 +3027,13 @@ class KOBOTOUCH(KOBO): # Supported database version return True + @property + def is_supported_fwversion(self): + # Starting with firmware version 3.19.x, the last number appears to be is a + # build number. It can be safely ignored when testing the firmware version. + debug_print("KoboTouch::is_supported_fwversion - self.fwversion[:2]", self.fwversion[:2]) + return self.fwversion[:2] > self.max_supported_fwversion + @classmethod def migrate_old_settings(cls, settings): debug_print("KoboTouch::migrate_old_settings - start") diff --git a/src/calibre/devices/kobo/kobotouch_config.py b/src/calibre/devices/kobo/kobotouch_config.py index f87c6c16a4..47f6ce358e 100644 --- a/src/calibre/devices/kobo/kobotouch_config.py +++ b/src/calibre/devices/kobo/kobotouch_config.py @@ -98,6 +98,7 @@ class KOBOTOUCHConfig(TabbedDeviceConfig): p['manage_collections'] = self.manage_collections p['create_collections'] = self.create_collections p['collections_columns'] = self.collections_columns + p['ignore_collections_names'] = self.ignore_collections_names p['delete_empty_collections'] = self.delete_empty_collections p['upload_covers'] = self.upload_covers @@ -221,10 +222,19 @@ class CollectionsGroupBox(DeviceOptionsGroupBox): device.get_pref('delete_empty_collections') ) + self.ignore_collections_names_label = QLabel(_('Ignore Collections')) + self.ignore_collections_names_edit = QLineEdit(self) + self.ignore_collections_names_edit.setToolTip(_('List the names of collections to be ignored by ' + + 'the collection management. The collections listed ' + + 'will not be changed. Names are separated by commas.')) + self.ignore_collections_names_edit.setText(device.get_pref('ignore_collections_names')) + self.options_layout.addWidget(self.collections_columns_label, 1, 0, 1, 1) self.options_layout.addWidget(self.collections_columns_edit, 1, 1, 1, 1) self.options_layout.addWidget(self.create_collections_checkbox, 2, 0, 1, 2) self.options_layout.addWidget(self.delete_empty_collections_checkbox, 3, 0, 1, 2) + self.options_layout.addWidget(self.ignore_collections_names_label, 4, 0, 1, 1) + self.options_layout.addWidget(self.ignore_collections_names_edit, 4, 1, 1, 1) self.options_layout.setRowStretch(4, 1) @property @@ -243,6 +253,10 @@ class CollectionsGroupBox(DeviceOptionsGroupBox): def delete_empty_collections(self): return self.delete_empty_collections_checkbox.isChecked() + @property + def ignore_collections_names(self): + return self.ignore_collections_names_edit.text().strip() + class CoversGroupBox(DeviceOptionsGroupBox):