diff --git a/src/calibre/devices/kobo/driver.py b/src/calibre/devices/kobo/driver.py index 9a86f61ef2..a49e6b16e0 100644 --- a/src/calibre/devices/kobo/driver.py +++ b/src/calibre/devices/kobo/driver.py @@ -31,6 +31,25 @@ from calibre.utils.config_base import prefs EPUB_EXT = '.epub' +# Implementation of QtQHash for strings. This doesn't seem to be in the Python implemention. +def qhash (inputstr): + instr = "" + if isinstance (inputstr, str): + instr = inputstr + elif isinstance (inputstr, unicode): + instr = inputstr.encode ("utf8") + else: + return -1 + + h = 0x00000000 + for i in range (0, len (instr)): + h = (h << 4) + ord(instr[i]) + h ^= (h & 0xf0000000) >> 23 + h &= 0x0fffffff + + return h + + class DummyCSSPreProcessor(object): def __call__(self, data, add_namespace=False): @@ -44,11 +63,11 @@ class KOBO(USBMS): gui_name = 'Kobo Reader' description = _('Communicate with the Kobo Reader') author = 'Timothy Legge and David Forrester' - version = (2, 1, 2) + version = (2, 1, 3) dbversion = 0 fwversion = 0 - supported_dbversion = 80 + supported_dbversion = 90 has_kepubs = False supported_platforms = ['windows', 'osx', 'linux'] @@ -1221,16 +1240,18 @@ class KOBOTOUCH(KOBO): description = 'Communicate with the Kobo Touch, Glo, Mini and Aura HD ereaders. Based on the existing Kobo driver by %s.' % (KOBO.author) # icon = I('devices/kobotouch.jpg') - supported_dbversion = 80 + supported_dbversion = 90 min_supported_dbversion = 53 min_dbversion_series = 65 min_dbversion_externalid = 65 min_dbversion_archive = 71 min_dbversion_images_on_sdcard = 77 min_dbversion_activiy = 77 + min_dbversion_keywords = 82 - max_supported_fwversion = (2,8,1) + max_supported_fwversion = (2,9,1) min_fwversion_images_on_sdcard = (2,4,1) + min_fwversion_images_tree = (2,9,0) # Cover images stored in tree under .kobo-images has_kepubs = True @@ -1331,15 +1352,16 @@ class KOBOTOUCH(KOBO): BCD = [0x0110, 0x0326] - # Image file name endings. Made up of: image size, min_dbversion, max_dbversion, + # Image file name endings. Made up of: image size, min_dbversion, max_dbversion, isFullSize, COVER_FILE_ENDINGS = { ' - N3_FULL.parsed':[(600,800),0, 99,True,], # Used for screensaver, home screen - ' - N3_LIBRARY_FULL.parsed':[(355,473),0, 99,False,], # Used for Details screen + ' - N3_LIBRARY_FULL.parsed':[(355,473),0, 81,False,], # Used for Details screen ' - N3_LIBRARY_GRID.parsed':[(149,198),0, 99,False,], # Used for library lists ' - N3_LIBRARY_LIST.parsed':[(60,90),0, 53,False,], + ' - AndroidBookLoadTablet_Aspect.parsed':[(355,473), 82, 99,False,], # Used for Details screen # ' - N3_LIBRARY_SHELF.parsed': [(40,60),0, 52,], } - GLO_COVER_FILE_ENDINGS = { + GLO_COVER_FILE_ENDINGS = { # Glo and Aura share resolution, so the image sizes should be the same. ' - N3_FULL.parsed':[(758,1024),0, 99,True,], # Used for screensaver, home screen ' - N3_LIBRARY_FULL.parsed':[(355,479),0, 99,False,], # Used for Details screen ' - N3_LIBRARY_GRID.parsed':[(149,201),0, 99,False,], # Used for library lists @@ -1404,6 +1426,7 @@ class KOBOTOUCH(KOBO): debug_print('Kobo device: %s' % self.gui_name) debug_print('Version of driver:', self.version, 'Has kepubs:', self.has_kepubs) debug_print('Version of firmware:', self.fwversion, 'Has kepubs:', self.has_kepubs) + debug_print('Firmware supports cover image tree:', self.fwversion >= self.min_fwversion_images_tree) self.booklist_class.rebuild_collections = self.rebuild_collections @@ -1526,7 +1549,7 @@ class KOBOTOUCH(KOBO): if (ContentType == '6' and MimeType != 'application/x-kobo-epub+zip'): if os.path.exists(self.normalize_path(os.path.join(prefix, lpath))): if self.update_metadata_item(bl[idx]): - print 'update_metadata_item returned true' +# print 'update_metadata_item returned true' changed = True else: debug_print(" Strange: The file: ", prefix, lpath, " does not exist!") @@ -1741,8 +1764,9 @@ class KOBOTOUCH(KOBO): bookshelves = get_bookshelvesforbook(connection, row[3]) prefix = self._card_a_prefix if oncard == 'carda' else self._main_prefix - changed = update_booklist(prefix, path, row[0], row[1], mime, row[2], row[3], row[5], row[ - 6], row[7], row[4], row[8], row[9], row[10], row[11], row[12], row[13], row[14], bookshelves) + changed = update_booklist(prefix, path, row[0], row[1], mime, row[2], row[3], row[5], + row[6], row[7], row[4], row[8], row[9], row[10], row[11], + row[12], row[13], row[14], bookshelves) if changed: need_sync = True @@ -1812,11 +1836,11 @@ class KOBOTOUCH(KOBO): def imagefilename_from_imageID(self, prefix, ImageID): show_debug = self.is_debugging_title(ImageID) - path = self.images_path(prefix) - path = self.normalize_path(path.replace('/', os.sep)) + path = self.images_path(prefix, ImageID) +# path = self.normalize_path(path.replace('/', os.sep)) for ending, cover_options in self.cover_file_endings().items(): - fpath = path + ImageID + ending + fpath = path + ending if os.path.exists(fpath): if show_debug: debug_print("KoboTouch:imagefilename_from_imageID - have cover image fpath=%s" % (fpath)) @@ -1917,8 +1941,6 @@ class KOBOTOUCH(KOBO): else: debug_print("KoboTouch:_modify_epub: received container") -# cssnames = [n for n in container.name_path_map if n.endswith('.css')] -# for cssname in cssnames: from calibre.ebooks.oeb.base import OEB_STYLES for cssname, mt in container.mime_map.iteritems(): if mt in OEB_STYLES: @@ -2009,16 +2031,22 @@ class KOBOTOUCH(KOBO): def delete_images(self, ImageID, book_path): debug_print("KoboTouch:delete_images - ImageID=", ImageID) if ImageID != None: - path = self.images_path(book_path) - path = path + ImageID + path = self.images_path(book_path, ImageID) + debug_print("KoboTouch:delete_images - path=%s" % path) for ending in self.cover_file_endings().keys(): fpath = path + ending fpath = self.normalize_path(fpath) + debug_print("KoboTouch:delete_images - fpath=%s" % fpath) if os.path.exists(fpath): - # print 'Image File Exists: ' + fpath + debug_print("KoboTouch:delete_images - Image File Exists") os.unlink(fpath) + + try: + os.removedirs(os.path.dirname(path)) + except: + pass def contentid_from_path(self, path, ContentType): show_debug = self.is_debugging_title(path) and True @@ -2297,14 +2325,22 @@ class KOBOTOUCH(KOBO): ImageID = ImageID.replace('.', '_') return ImageID - def images_path(self, path): + def images_path(self, path, imageId=None): if self._card_a_prefix and os.path.abspath(path).startswith(os.path.abspath(self._card_a_prefix)) and self.supports_covers_on_sdcard(): - path_prefix = 'koboExtStorage/images/' + path_prefix = 'koboExtStorage/images-cache/' if self.supports_images_tree() else 'koboExtStorage/images/' path = os.path.join(self._card_a_prefix, path_prefix) else: - path_prefix = '.kobo/images/' + path_prefix = '.kobo-images/' if self.supports_images_tree() else '.kobo/images/' path = os.path.join(self._main_prefix, path_prefix) + if self.supports_images_tree() and imageId: + hash1 = qhash(imageId) + dir1 = hash1 & (0xff * 1) + dir2 = (hash1 & (0xff00 * 1)) >> 8 + path = os.path.join(path, "%s" % dir1, "%s" % dir2) + + if imageId: + path = os.path.join(path, imageId) return path def _upload_cover(self, path, filename, metadata, filepath, uploadgrayscale, keep_cover_aspect=False): @@ -2326,8 +2362,7 @@ class KOBOTOUCH(KOBO): try: import sqlite3 as sqlite - with closing(sqlite.connect(self.normalize_path(self._main_prefix + - '.kobo/KoboReader.sqlite'))) as connection: + with closing(sqlite.connect(self.device_database_path())) as connection: # return bytestrings if the content cannot the decoded as unicode connection.text_factory = lambda x: unicode(x, "utf-8", "ignore") @@ -2347,7 +2382,7 @@ class KOBOTOUCH(KOBO): cursor.close() if ImageID != None: - path = os.path.join(self.images_path(path), ImageID) + path = self.images_path(path, ImageID) if show_debug: debug_print("KoboTouch:_upload_cover - About to loop over cover endings") @@ -2762,6 +2797,9 @@ class KOBOTOUCH(KOBO): def supports_covers_on_sdcard(self): return self.dbversion >= self.min_dbversion_images_on_sdcard and self.fwversion >= self.min_fwversion_images_on_sdcard + def supports_images_tree(self): + return self.fwversion >= self.min_fwversion_images_tree + def has_externalid(self): return self.dbversion >= self.min_dbversion_externalid