mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Kobo driver: Update for new firmware from Kobo
Also fix incorrect cover size being uploaded to the Glo HD Merge branch 'master' of https://github.com/davidfor/calibre
This commit is contained in:
commit
1d0d413f8f
@ -65,7 +65,7 @@ class KOBO(USBMS):
|
||||
gui_name = 'Kobo Reader'
|
||||
description = _('Communicate with the Kobo Reader')
|
||||
author = 'Timothy Legge and David Forrester'
|
||||
version = (2, 3, 2)
|
||||
version = (2, 3, 3)
|
||||
|
||||
dbversion = 0
|
||||
fwversion = (0,0,0)
|
||||
@ -1325,7 +1325,7 @@ class KOBOTOUCH(KOBO):
|
||||
# Starting with firmware version 3.19.x, the last number appears to be is a
|
||||
# build number. A number will be recorded here but it can be safely ignored
|
||||
# when testing the firmware version.
|
||||
max_supported_fwversion = (4, 2, 8094)
|
||||
max_supported_fwversion = (4, 2, 8283)
|
||||
# The following document firwmare versions where new function or devices were added.
|
||||
# Not all are used, but this feels a good place to record it.
|
||||
min_fwversion_shelves = (2, 0, 0)
|
||||
@ -1397,19 +1397,29 @@ class KOBOTOUCH(KOBO):
|
||||
# Used for Details screen from FW2.8.1
|
||||
' - AndroidBookLoadTablet_Aspect.parsed':[(355,479), 88, 100,False,],
|
||||
}
|
||||
GLO_HD_COVER_FILE_ENDINGS = {
|
||||
# Used for screensaver, home screen
|
||||
' - N3_FULL.parsed': [(1072,1448), 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,],
|
||||
# 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, 471), 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
|
||||
' - N3_FULL.parsed': [(1080,1440), 0, 200,True,],
|
||||
# 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
|
||||
' - N3_LIBRARY_GRID.parsed':[(149, 198), 0, 200,False,],
|
||||
# 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': [(1404,1872), 0, 200,True,], # Used for screensaver, home screen
|
||||
' - N3_FULL.parsed': [(1404,1872), 0, 200,True,],
|
||||
# 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
|
||||
@ -2390,7 +2400,7 @@ class KOBOTOUCH(KOBO):
|
||||
|
||||
if not self.upload_covers:
|
||||
# Building thumbnails disabled
|
||||
# debug_print('KoboTouch: not uploading cover')
|
||||
# debug_print('KoboTouch: not uploading cover')
|
||||
return
|
||||
|
||||
# Only upload covers to SD card if that is supported
|
||||
@ -2428,87 +2438,115 @@ class KOBOTOUCH(KOBO):
|
||||
path = os.path.join(path, imageId)
|
||||
return path
|
||||
|
||||
def _upload_cover(self, path, filename, metadata, filepath, uploadgrayscale, keep_cover_aspect=False):
|
||||
from calibre.utils.img import save_cover_data_to
|
||||
from calibre.utils.imghdr import identify
|
||||
debug_print("KoboTouch:_upload_cover - filename='%s' uploadgrayscale='%s' "%(filename, uploadgrayscale))
|
||||
|
||||
if metadata.cover:
|
||||
show_debug = self.is_debugging_title(filename)
|
||||
if show_debug:
|
||||
debug_print("KoboTouch:_upload_cover - path='%s'"%path, "filename='%s'"%filename)
|
||||
debug_print(" filepath='%s'"%filepath)
|
||||
cover = self.normalize_path(metadata.cover.replace('/', os.sep))
|
||||
|
||||
if os.path.exists(cover):
|
||||
# Get ContentID for Selected Book
|
||||
extension = os.path.splitext(filepath)[1]
|
||||
ContentType = self.get_content_type_from_extension(extension) if extension != '' else self.get_content_type_from_path(filepath)
|
||||
ContentID = self.contentid_from_path(filepath, ContentType)
|
||||
|
||||
try:
|
||||
with closing(self.device_database_connection()) as connection:
|
||||
|
||||
cursor = connection.cursor()
|
||||
t = (ContentID,)
|
||||
cursor.execute('select ImageId from Content where BookID is Null and ContentID = ?', t)
|
||||
try:
|
||||
result = cursor.next()
|
||||
ImageID = result[0]
|
||||
except StopIteration:
|
||||
ImageID = self.imageid_from_contentid(ContentID)
|
||||
debug_print("KoboTouch:_upload_cover - No rows exist in the database - generated ImageID='%s'" % ImageID)
|
||||
|
||||
cursor.close()
|
||||
|
||||
if ImageID is not None:
|
||||
path = self.images_path(path, ImageID)
|
||||
|
||||
if show_debug:
|
||||
debug_print("KoboTouch:_upload_cover - About to loop over cover endings")
|
||||
|
||||
image_dir = os.path.dirname(os.path.abspath(path))
|
||||
if not os.path.exists(image_dir):
|
||||
debug_print("KoboTouch:_upload_cover - Image directory does not exust. Creating path='%s'" % (image_dir))
|
||||
os.makedirs(image_dir)
|
||||
|
||||
for ending, cover_options in self.cover_file_endings().items():
|
||||
resize, min_dbversion, max_dbversion, isFullsize = cover_options
|
||||
if show_debug:
|
||||
debug_print("KoboTouch:_upload_cover - resize=%s min_dbversion=%d max_dbversion=%d" % (resize, min_dbversion, max_dbversion))
|
||||
if self.dbversion >= min_dbversion and self.dbversion <= max_dbversion:
|
||||
if show_debug:
|
||||
debug_print("KoboTouch:_upload_cover - creating cover for ending='%s'"%ending) # , "resize'%s'"%resize)
|
||||
fpath = path + ending
|
||||
fpath = self.normalize_path(fpath.replace('/', os.sep))
|
||||
|
||||
with lopen(cover, 'rb') as f:
|
||||
data = f.read()
|
||||
|
||||
if keep_cover_aspect:
|
||||
if isFullsize:
|
||||
resize = None
|
||||
else:
|
||||
fmt, width, height = identify(data)
|
||||
cover_aspect = width / height
|
||||
if cover_aspect > 1:
|
||||
resize = (resize[0], int(resize[0] / cover_aspect))
|
||||
elif cover_aspect < 1:
|
||||
resize = (int(cover_aspect * resize[1]), resize[1])
|
||||
|
||||
# Return the data resized and in Grayscale if
|
||||
# required
|
||||
data = save_cover_data_to(data, grayscale=uploadgrayscale, resize_to=resize)
|
||||
|
||||
with lopen(fpath, 'wb') as f:
|
||||
f.write(data)
|
||||
fsync(f)
|
||||
except Exception as e:
|
||||
err = str(e)
|
||||
debug_print("KoboTouch:_upload_cover - Exception string: %s"%err)
|
||||
raise
|
||||
def _calculate_kobo_cover_size(self, library_size, kobo_size, keep_cover_aspect, is_full_size):
|
||||
if keep_cover_aspect:
|
||||
library_aspect = library_size[0] / library_size[1]
|
||||
kobo_aspect = kobo_size[0] / kobo_size[1]
|
||||
if library_aspect > kobo_aspect:
|
||||
kobo_size = (kobo_size[0], int(kobo_size[0] / library_aspect))
|
||||
else:
|
||||
debug_print("KoboTouch:_upload_cover - ImageID could not be retrieved from the database")
|
||||
kobo_size = (int(library_aspect * kobo_size[1]), kobo_size[1])
|
||||
return kobo_size
|
||||
|
||||
def _create_cover_data(self, cover_data, resize_to, kobo_size, upload_grayscale=False, keep_cover_aspect=False, is_full_size=False):
|
||||
'''
|
||||
This will generate the new cover image from the cover in the library. It is a wrapper
|
||||
for save_cover_data_to to allow it to be overriden in a subclass. For this reason,
|
||||
options are passed in that are not used by this implementation.
|
||||
|
||||
:param cover_data: original cover data
|
||||
:param resize_to: Size to resize the cover to (width, height). None means do not resize.
|
||||
:param kobo_size: Size of the cover image on the device.
|
||||
:param upload_grayscale: boolean True if driver configured to send grayscale thumbnails
|
||||
Passed to allow ability to decide to quantize to 16-col grayscale
|
||||
at calibre end
|
||||
:param keep_cover_aspect: bookean - True if the aspect ratio of the cover in the library is to be kept.
|
||||
:param is_full_size: True if this is the kobo_size is for the full size cover image
|
||||
Passed to allow ability to process screensaver differently
|
||||
to smaller thumbnails
|
||||
'''
|
||||
|
||||
from calibre.utils.img import save_cover_data_to
|
||||
data = save_cover_data_to(cover_data, grayscale=upload_grayscale, resize_to=resize_to)
|
||||
return data
|
||||
|
||||
def _upload_cover(self, path, filename, metadata, filepath, upload_grayscale, keep_cover_aspect=False):
|
||||
from calibre.utils.imghdr import identify
|
||||
debug_print("KoboTouch:_upload_cover - filename='%s' upload_grayscale='%s' "%(filename, upload_grayscale))
|
||||
|
||||
if not metadata.cover:
|
||||
return
|
||||
|
||||
show_debug = self.is_debugging_title(filename)
|
||||
if show_debug:
|
||||
debug_print("KoboTouch:_upload_cover - path='%s'"%path, "filename='%s'"%filename)
|
||||
debug_print(" filepath='%s'"%filepath)
|
||||
cover = self.normalize_path(metadata.cover.replace('/', os.sep))
|
||||
|
||||
if not os.path.exists(cover):
|
||||
debug_print("KoboTouch:_upload_cover - Cover file does not exist in library")
|
||||
return
|
||||
|
||||
# Get ContentID for Selected Book
|
||||
extension = os.path.splitext(filepath)[1]
|
||||
ContentType = self.get_content_type_from_extension(extension) if extension != '' else self.get_content_type_from_path(filepath)
|
||||
ContentID = self.contentid_from_path(filepath, ContentType)
|
||||
|
||||
try:
|
||||
with closing(self.device_database_connection()) as connection:
|
||||
|
||||
cursor = connection.cursor()
|
||||
t = (ContentID,)
|
||||
cursor.execute('select ImageId from Content where BookID is Null and ContentID = ?', t)
|
||||
try:
|
||||
result = cursor.next()
|
||||
ImageID = result[0]
|
||||
except StopIteration:
|
||||
ImageID = self.imageid_from_contentid(ContentID)
|
||||
debug_print("KoboTouch:_upload_cover - No rows exist in the database - generated ImageID='%s'" % ImageID)
|
||||
|
||||
cursor.close()
|
||||
|
||||
if ImageID is not None:
|
||||
path = self.images_path(path, ImageID)
|
||||
|
||||
if show_debug:
|
||||
debug_print("KoboTouch:_upload_cover - About to loop over cover endings")
|
||||
|
||||
image_dir = os.path.dirname(os.path.abspath(path))
|
||||
if not os.path.exists(image_dir):
|
||||
debug_print("KoboTouch:_upload_cover - Image directory does not exust. Creating path='%s'" % (image_dir))
|
||||
os.makedirs(image_dir)
|
||||
|
||||
with lopen(cover, 'rb') as f:
|
||||
cover_data = f.read()
|
||||
|
||||
fmt, width, height = identify(cover_data)
|
||||
library_cover_size = (width, height)
|
||||
|
||||
for ending, cover_options in self.cover_file_endings().items():
|
||||
kobo_size, min_dbversion, max_dbversion, is_full_size = cover_options
|
||||
if show_debug:
|
||||
debug_print("KoboTouch:_upload_cover - library_cover_size=%s min_dbversion=%d max_dbversion=%d" % (
|
||||
library_cover_size, min_dbversion, max_dbversion))
|
||||
if self.dbversion >= min_dbversion and self.dbversion <= max_dbversion:
|
||||
if show_debug:
|
||||
debug_print("KoboTouch:_upload_cover - creating cover for ending='%s'"%ending) # , "library_cover_size'%s'"%library_cover_size)
|
||||
fpath = path + ending
|
||||
fpath = self.normalize_path(fpath.replace('/', os.sep))
|
||||
|
||||
resize_to = self._calculate_kobo_cover_size(library_cover_size, kobo_size, keep_cover_aspect, is_full_size)
|
||||
|
||||
# Return the data resized and in Grayscale if required
|
||||
data = self._create_cover_data(cover_data, resize_to, kobo_size, upload_grayscale, keep_cover_aspect, is_full_size)
|
||||
|
||||
with lopen(fpath, 'wb') as f:
|
||||
f.write(data)
|
||||
fsync(f)
|
||||
except Exception as e:
|
||||
err = str(e)
|
||||
debug_print("KoboTouch:_upload_cover - Exception string: %s"%err)
|
||||
raise
|
||||
|
||||
def remove_book_from_device_bookshelves(self, connection, book):
|
||||
show_debug = self.is_debugging_title(book.title) # or True
|
||||
@ -2914,10 +2952,30 @@ 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() 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
|
||||
if self.isAura():
|
||||
_cover_file_endings = self.GLO_COVER_FILE_ENDINGS
|
||||
elif self.isAuraEdition2():
|
||||
_cover_file_endings = self.GLO_COVER_FILE_ENDINGS
|
||||
elif self.isAuraHD():
|
||||
_cover_file_endings = self.AURA_HD_COVER_FILE_ENDINGS
|
||||
elif self.isAuraH2O():
|
||||
_cover_file_endings = self.AURA_HD_COVER_FILE_ENDINGS
|
||||
elif self.isAuraOne():
|
||||
_cover_file_endings = self.AURA_ONE_COVER_FILE_ENDINGS
|
||||
elif self.isGlo():
|
||||
_cover_file_endings = self.GLO_COVER_FILE_ENDINGS
|
||||
elif self.isGloHD():
|
||||
_cover_file_endings = self.GLO_HD_COVER_FILE_ENDINGS
|
||||
elif self.isMini():
|
||||
_cover_file_endings = self.COVER_FILE_ENDINGS
|
||||
elif self.isTouch():
|
||||
_cover_file_endings = self.COVER_FILE_ENDINGS
|
||||
elif self.isTouch2():
|
||||
_cover_file_endings = self.COVER_FILE_ENDINGS
|
||||
else:
|
||||
_cover_file_endings = self.COVER_FILE_ENDINGS
|
||||
|
||||
return _cover_file_endings
|
||||
|
||||
def set_device_name(self):
|
||||
device_name = self.gui_name
|
||||
|
Loading…
x
Reference in New Issue
Block a user