mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-06-23 15:30:45 -04:00
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:
parent
07547628cc
commit
c893f0de7e
@ -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,16 +2031,22 @@ 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
|
||||||
@ -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
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user