Add support for new cover handling in Kobo Aura

The new Kobo Aura has firmware version 2.9.0. The main change needed for
the driver is that cover images are stored in a different place.

Version number is set to 2.9.1 to handle the later release for other
Kobo devices.
This commit is contained in:
David Forrester 2013-09-18 23:08:56 +10:00 committed by Kovid Goyal
parent 07547628cc
commit c893f0de7e

View File

@ -31,6 +31,25 @@ from calibre.utils.config_base import prefs
EPUB_EXT = '.epub' 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): class DummyCSSPreProcessor(object):
def __call__(self, data, add_namespace=False): def __call__(self, data, add_namespace=False):
@ -44,11 +63,11 @@ class KOBO(USBMS):
gui_name = 'Kobo Reader' gui_name = 'Kobo Reader'
description = _('Communicate with the Kobo Reader') description = _('Communicate with the Kobo Reader')
author = 'Timothy Legge and David Forrester' author = 'Timothy Legge and David Forrester'
version = (2, 1, 2) version = (2, 1, 3)
dbversion = 0 dbversion = 0
fwversion = 0 fwversion = 0
supported_dbversion = 80 supported_dbversion = 90
has_kepubs = False has_kepubs = False
supported_platforms = ['windows', 'osx', 'linux'] 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) 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') # icon = I('devices/kobotouch.jpg')
supported_dbversion = 80 supported_dbversion = 90
min_supported_dbversion = 53 min_supported_dbversion = 53
min_dbversion_series = 65 min_dbversion_series = 65
min_dbversion_externalid = 65 min_dbversion_externalid = 65
min_dbversion_archive = 71 min_dbversion_archive = 71
min_dbversion_images_on_sdcard = 77 min_dbversion_images_on_sdcard = 77
min_dbversion_activiy = 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_on_sdcard = (2,4,1)
min_fwversion_images_tree = (2,9,0) # Cover images stored in tree under .kobo-images
has_kepubs = True has_kepubs = True
@ -1331,15 +1352,16 @@ class KOBOTOUCH(KOBO):
BCD = [0x0110, 0x0326] 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 = { COVER_FILE_ENDINGS = {
' - N3_FULL.parsed':[(600,800),0, 99,True,], # Used for screensaver, home screen ' - 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_GRID.parsed':[(149,198),0, 99,False,], # Used for library lists
' - N3_LIBRARY_LIST.parsed':[(60,90),0, 53,False,], ' - 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,], # ' - 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_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_FULL.parsed':[(355,479),0, 99,False,], # Used for Details screen
' - N3_LIBRARY_GRID.parsed':[(149,201),0, 99,False,], # Used for library lists ' - 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('Kobo device: %s' % self.gui_name)
debug_print('Version of driver:', self.version, 'Has kepubs:', self.has_kepubs) 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('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 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 (ContentType == '6' and MimeType != 'application/x-kobo-epub+zip'):
if os.path.exists(self.normalize_path(os.path.join(prefix, lpath))): if os.path.exists(self.normalize_path(os.path.join(prefix, lpath))):
if self.update_metadata_item(bl[idx]): if self.update_metadata_item(bl[idx]):
print 'update_metadata_item returned true' # print 'update_metadata_item returned true'
changed = True changed = True
else: else:
debug_print(" Strange: The file: ", prefix, lpath, " does not exist!") debug_print(" Strange: The file: ", prefix, lpath, " does not exist!")
@ -1741,8 +1764,9 @@ class KOBOTOUCH(KOBO):
bookshelves = get_bookshelvesforbook(connection, row[3]) bookshelves = get_bookshelvesforbook(connection, row[3])
prefix = self._card_a_prefix if oncard == 'carda' else self._main_prefix 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[ changed = update_booklist(prefix, path, row[0], row[1], mime, row[2], row[3], row[5],
6], row[7], row[4], row[8], row[9], row[10], row[11], row[12], row[13], row[14], bookshelves) row[6], row[7], row[4], row[8], row[9], row[10], row[11],
row[12], row[13], row[14], bookshelves)
if changed: if changed:
need_sync = True need_sync = True
@ -1812,11 +1836,11 @@ class KOBOTOUCH(KOBO):
def imagefilename_from_imageID(self, prefix, ImageID): def imagefilename_from_imageID(self, prefix, ImageID):
show_debug = self.is_debugging_title(ImageID) show_debug = self.is_debugging_title(ImageID)
path = self.images_path(prefix) path = self.images_path(prefix, ImageID)
path = self.normalize_path(path.replace('/', os.sep)) # path = self.normalize_path(path.replace('/', os.sep))
for ending, cover_options in self.cover_file_endings().items(): for ending, cover_options in self.cover_file_endings().items():
fpath = path + ImageID + ending fpath = path + ending
if os.path.exists(fpath): if os.path.exists(fpath):
if show_debug: if show_debug:
debug_print("KoboTouch:imagefilename_from_imageID - have cover image fpath=%s" % (fpath)) debug_print("KoboTouch:imagefilename_from_imageID - have cover image fpath=%s" % (fpath))
@ -1917,8 +1941,6 @@ class KOBOTOUCH(KOBO):
else: else:
debug_print("KoboTouch:_modify_epub: received container") 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 from calibre.ebooks.oeb.base import OEB_STYLES
for cssname, mt in container.mime_map.iteritems(): for cssname, mt in container.mime_map.iteritems():
if mt in OEB_STYLES: if mt in OEB_STYLES:
@ -2009,17 +2031,23 @@ class KOBOTOUCH(KOBO):
def delete_images(self, ImageID, book_path): def delete_images(self, ImageID, book_path):
debug_print("KoboTouch:delete_images - ImageID=", ImageID) debug_print("KoboTouch:delete_images - ImageID=", ImageID)
if ImageID != None: if ImageID != None:
path = self.images_path(book_path) path = self.images_path(book_path, ImageID)
path = path + ImageID debug_print("KoboTouch:delete_images - path=%s" % path)
for ending in self.cover_file_endings().keys(): for ending in self.cover_file_endings().keys():
fpath = path + ending fpath = path + ending
fpath = self.normalize_path(fpath) fpath = self.normalize_path(fpath)
debug_print("KoboTouch:delete_images - fpath=%s" % fpath)
if os.path.exists(fpath): if os.path.exists(fpath):
# print 'Image File Exists: ' + fpath debug_print("KoboTouch:delete_images - Image File Exists")
os.unlink(fpath) os.unlink(fpath)
try:
os.removedirs(os.path.dirname(path))
except:
pass
def contentid_from_path(self, path, ContentType): def contentid_from_path(self, path, ContentType):
show_debug = self.is_debugging_title(path) and True show_debug = self.is_debugging_title(path) and True
if show_debug: if show_debug:
@ -2297,14 +2325,22 @@ class KOBOTOUCH(KOBO):
ImageID = ImageID.replace('.', '_') ImageID = ImageID.replace('.', '_')
return ImageID 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(): 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) path = os.path.join(self._card_a_prefix, path_prefix)
else: 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) 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 return path
def _upload_cover(self, path, filename, metadata, filepath, uploadgrayscale, keep_cover_aspect=False): def _upload_cover(self, path, filename, metadata, filepath, uploadgrayscale, keep_cover_aspect=False):
@ -2326,8 +2362,7 @@ class KOBOTOUCH(KOBO):
try: try:
import sqlite3 as sqlite import sqlite3 as sqlite
with closing(sqlite.connect(self.normalize_path(self._main_prefix + with closing(sqlite.connect(self.device_database_path())) as connection:
'.kobo/KoboReader.sqlite'))) as connection:
# return bytestrings if the content cannot the decoded as unicode # return bytestrings if the content cannot the decoded as unicode
connection.text_factory = lambda x: unicode(x, "utf-8", "ignore") connection.text_factory = lambda x: unicode(x, "utf-8", "ignore")
@ -2347,7 +2382,7 @@ class KOBOTOUCH(KOBO):
cursor.close() cursor.close()
if ImageID != None: if ImageID != None:
path = os.path.join(self.images_path(path), ImageID) path = self.images_path(path, ImageID)
if show_debug: if show_debug:
debug_print("KoboTouch:_upload_cover - About to loop over cover endings") debug_print("KoboTouch:_upload_cover - About to loop over cover endings")
@ -2762,6 +2797,9 @@ class KOBOTOUCH(KOBO):
def supports_covers_on_sdcard(self): def supports_covers_on_sdcard(self):
return self.dbversion >= self.min_dbversion_images_on_sdcard and self.fwversion >= self.min_fwversion_images_on_sdcard 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): def has_externalid(self):
return self.dbversion >= self.min_dbversion_externalid return self.dbversion >= self.min_dbversion_externalid