mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-06-23 15:30:45 -04:00
Fix some annotation fetching issues for Kobo devices
There have been some long-term problems with missed annotations for kepubs. Plus a bug in the latest firmware causes problems with epub that don't have a ToC entry for every file.
This commit is contained in:
parent
2a7b9e30ac
commit
8266ccd8c5
@ -7,6 +7,9 @@ __docformat__ = 'restructuredtext en'
|
||||
|
||||
import os
|
||||
|
||||
from calibre.constants import ispy3
|
||||
from polyglot.builtins import unicode_type
|
||||
from calibre.devices.usbms.driver import debug_print
|
||||
|
||||
class Bookmark(): # {{{
|
||||
'''
|
||||
@ -14,7 +17,7 @@ class Bookmark(): # {{{
|
||||
kobo-specific
|
||||
'''
|
||||
|
||||
def __init__(self, db_connection, contentid, path, id, book_format, bookmark_extension):
|
||||
def __init__(self, db_connection, contentId, path, id, book_format, bookmark_extension):
|
||||
self.book_format = book_format
|
||||
self.bookmark_extension = bookmark_extension
|
||||
self.book_length = 0 # Not Used
|
||||
@ -25,8 +28,9 @@ class Bookmark(): # {{{
|
||||
self.timestamp = 0
|
||||
self.user_notes = None
|
||||
self.db_connection = db_connection
|
||||
self.contentid = contentid
|
||||
self.contentId = contentId
|
||||
self.percent_read = 0
|
||||
self.kepub = (self.contentId.endswith('.kepub.epub') or not os.path.splitext(self.contentId)[1])
|
||||
self.get_bookmark_data()
|
||||
self.get_book_length() # Not Used
|
||||
|
||||
@ -36,53 +40,87 @@ class Bookmark(): # {{{
|
||||
self.timestamp = os.path.getmtime(self.path)
|
||||
|
||||
cursor = self.db_connection.cursor()
|
||||
t = (self.contentid,)
|
||||
book_query_values = (self.contentId,)
|
||||
|
||||
kepub_chapter_query = (
|
||||
'SELECT Title, volumeIndex '
|
||||
'FROM content '
|
||||
'WHERE ContentID LIKE ? '
|
||||
'SELECT c.ContentID, c.BookTitle, c.Title, c.VolumeIndex, '
|
||||
'c.___NumPages, c.MimeType '
|
||||
'FROM content c '
|
||||
'WHERE ContentType = 899 '
|
||||
'AND c.BookID = ? '
|
||||
'ORDER BY c.VolumeIndex'
|
||||
)
|
||||
bookmark_query = ('SELECT bm.bookmarkid, bm.ContentID, bm.text, bm.annotation, '
|
||||
bookmark_query = (
|
||||
'SELECT bm.BookmarkID, bm.ContentID, bm.Text, bm.Annotation, '
|
||||
'bm.ChapterProgress, bm.StartContainerChildIndex, bm.StartOffset, '
|
||||
'c.BookTitle, c.TITLE, c.volumeIndex, c.MimeType '
|
||||
'c.BookTitle, c.Title, c.volumeIndex, c.MimeType '
|
||||
'FROM Bookmark bm LEFT OUTER JOIN Content c ON '
|
||||
'c.ContentID = bm.ContentID '
|
||||
'WHERE bm.Hidden = "false" '
|
||||
'AND bm.volumeid = ? '
|
||||
'ORDER BY bm.ContentID, bm.chapterprogress')
|
||||
cursor.execute(bookmark_query, t)
|
||||
'ORDER BY bm.ContentID, bm.chapterprogress'
|
||||
)
|
||||
|
||||
debug_print("Kobo::Bookmark::get_bookmark_data - getting kepub chapters: contentId={0}".format(self.contentId))
|
||||
cursor.execute(kepub_chapter_query, book_query_values)
|
||||
kepub_chapters = {}
|
||||
if self.kepub:
|
||||
try:
|
||||
for chapter_row in cursor:
|
||||
chapter_contentID = chapter_row['ContentID']
|
||||
chapter_contentID = chapter_contentID[:chapter_contentID.rfind('-')]
|
||||
kepub_chapters[chapter_contentID] = {
|
||||
'chapter_title': chapter_row['Title'],
|
||||
'chapter_index': chapter_row['VolumeIndex']
|
||||
}
|
||||
debug_print("Kobo::Bookmark::get_bookmark_data - getting kepub chapter: kepub chapters={0}".format(kepub_chapters))
|
||||
except:
|
||||
debug_print("Kobo::Bookmark::get_bookmark_data - No chapters found")
|
||||
|
||||
cursor.execute(bookmark_query, book_query_values)
|
||||
|
||||
previous_chapter = 0
|
||||
bm_count = 0
|
||||
for row in cursor:
|
||||
current_chapter = row[9]
|
||||
chapter_title = row[8]
|
||||
current_chapter = row['VolumeIndex'] if row['VolumeIndex'] is not None else 0
|
||||
chapter_title = row['Title']
|
||||
# For kepubs on newer firmware, the title needs to come from an 899 row.
|
||||
if not row[10] or row[10] == 'application/xhtml+xml' or row[10] == 'application/x-kobo-epub+zip':
|
||||
cursor2 = self.db_connection.cursor()
|
||||
kepub_chapter_data = ('{0}-%'.format(row[1]), )
|
||||
cursor2.execute(kepub_chapter_query, kepub_chapter_data)
|
||||
try:
|
||||
kepub_chapter = next(cursor2)
|
||||
chapter_title = kepub_chapter[0]
|
||||
current_chapter = kepub_chapter[1]
|
||||
except StopIteration:
|
||||
pass
|
||||
finally:
|
||||
cursor2.close
|
||||
if self.kepub:
|
||||
chapter_contentID = row['ContentID']
|
||||
debug_print("Kobo::Bookmark::get_bookmark_data - getting kepub: chapter chapter_contentID='{0}'".format(chapter_contentID))
|
||||
filename_index = chapter_contentID.find('!')
|
||||
book_contentID_part = chapter_contentID[:filename_index]
|
||||
debug_print("Kobo::Bookmark::get_bookmark_data - getting kepub: chapter book_contentID_part='{0}'".format(book_contentID_part))
|
||||
file_contentID_part = chapter_contentID[filename_index + 1:]
|
||||
filename_index = file_contentID_part.find('!')
|
||||
opf_reference = file_contentID_part[:filename_index]
|
||||
debug_print("Kobo::Bookmark::get_bookmark_data - getting kepub: chapter opf_reference='{0}'".format(opf_reference))
|
||||
file_contentID_part = file_contentID_part[filename_index + 1:]
|
||||
debug_print("Kobo::Bookmark::get_bookmark_data - getting kepub: chapter file_contentID_part='{0}'".format(file_contentID_part))
|
||||
# from urllib import quote
|
||||
# file_contentID_part = quote(file_contentID_part)
|
||||
chapter_contentID = book_contentID_part + "!" + opf_reference + "!" + file_contentID_part
|
||||
debug_print("Kobo::Bookmark::get_bookmark_data - getting kepub chapter chapter_contentID='{0}'".format(chapter_contentID))
|
||||
kepub_chapter = kepub_chapters.get(chapter_contentID, None)
|
||||
if kepub_chapter is not None:
|
||||
chapter_title = kepub_chapter['chapter_title']
|
||||
current_chapter = kepub_chapter['chapter_index']
|
||||
else:
|
||||
chapter_title = ''
|
||||
current_chapter = 0
|
||||
|
||||
if previous_chapter == current_chapter:
|
||||
bm_count = bm_count + 1
|
||||
else:
|
||||
bm_count = 0
|
||||
|
||||
text = row[2]
|
||||
annotation = row[3]
|
||||
text = row['Text']
|
||||
annotation = row['Annotation']
|
||||
|
||||
# A dog ear (bent upper right corner) is a bookmark
|
||||
if row[5] == row[6] == 0: # StartContainerChildIndex = StartOffset = 0
|
||||
if row['StartContainerChildIndex'] == row['StartOffset'] == 0: # StartContainerChildIndex = StartOffset = 0
|
||||
e_type = 'Bookmark'
|
||||
text = row[8]
|
||||
text = row['Title']
|
||||
# highlight is text with no annotation
|
||||
elif text is not None and (annotation is None or annotation == ""):
|
||||
e_type = 'Highlight'
|
||||
@ -94,7 +132,7 @@ class Bookmark(): # {{{
|
||||
note_id = current_chapter * 1000 + bm_count
|
||||
|
||||
# book_title = row[8]
|
||||
chapter_progress = min(round(float(100*row[4]),2),100)
|
||||
chapter_progress = min(round(float(100*row['ChapterProgress']),2),100)
|
||||
user_notes[note_id] = dict(id=self.id,
|
||||
displayed_location=note_id,
|
||||
type=e_type,
|
||||
@ -108,15 +146,16 @@ class Bookmark(): # {{{
|
||||
# 'annotation: ', annotation, 'chapter_title: ', chapter_title,
|
||||
# 'chapter_progress: ', chapter_progress, 'date: ')
|
||||
|
||||
cursor.execute('SELECT datelastread, ___PercentRead '
|
||||
cursor.execute('SELECT DateLastRead, ___PercentRead, ReadStatus '
|
||||
'FROM content '
|
||||
'WHERE bookid IS NULL '
|
||||
'AND ReadStatus > 0 '
|
||||
'AND contentid = ?',
|
||||
t)
|
||||
'AND ContentID = ? '
|
||||
'ORDER BY DateLastRead, ReadStatus',
|
||||
book_query_values)
|
||||
for row in cursor:
|
||||
self.last_read = row[0]
|
||||
self.percent_read = row[1]
|
||||
self.last_read = row['DateLastRead']
|
||||
self.percent_read = 100 if (row['ReadStatus'] == 2) else row['___PercentRead']
|
||||
# print row[1]
|
||||
cursor.close()
|
||||
|
||||
@ -128,4 +167,32 @@ class Bookmark(): # {{{
|
||||
# TL self.book_length = int(unpack('>I', record0[0x04:0x08])[0])
|
||||
pass
|
||||
|
||||
def __unicode__(self):
|
||||
'''
|
||||
A string representation of this object, suitable for printing to
|
||||
console
|
||||
'''
|
||||
ans = [u"Kobo bookmark:"]
|
||||
|
||||
def fmt(x, y):
|
||||
ans.append(u'%-20s: %s'%(unicode_type(x), unicode_type(y)))
|
||||
|
||||
if self.contentId:
|
||||
fmt('ContentID', self.contentId)
|
||||
if self.last_read:
|
||||
fmt('Last Read', self.last_read)
|
||||
if self.timestamp:
|
||||
fmt('Timestamp', self.timestamp)
|
||||
if self.percent_read:
|
||||
fmt('Percent Read', self.percent_read)
|
||||
if self.user_notes:
|
||||
fmt('User Notes', self.user_notes)
|
||||
|
||||
ans = u'\n'.join(ans) + u"\n"
|
||||
|
||||
return ans
|
||||
|
||||
if ispy3:
|
||||
__str__ = __unicode__
|
||||
|
||||
# }}}
|
||||
|
@ -1148,9 +1148,9 @@ class KOBO(USBMS):
|
||||
def resolve_bookmark_paths(storage, path_map):
|
||||
pop_list = []
|
||||
book_ext = {}
|
||||
for id in path_map:
|
||||
for book_id in path_map:
|
||||
file_fmts = set()
|
||||
for fmt in path_map[id]['fmts']:
|
||||
for fmt in path_map[book_id]['fmts']:
|
||||
file_fmts.add(fmt)
|
||||
bookmark_extension = None
|
||||
if file_fmts.intersection(epub_formats):
|
||||
@ -1159,37 +1159,37 @@ class KOBO(USBMS):
|
||||
|
||||
if bookmark_extension:
|
||||
for vol in storage:
|
||||
bkmk_path = path_map[id]['path']
|
||||
bkmk_path = path_map[book_id]['path']
|
||||
bkmk_path = bkmk_path
|
||||
if os.path.exists(bkmk_path):
|
||||
path_map[id] = bkmk_path
|
||||
book_ext[id] = book_extension
|
||||
path_map[book_id] = bkmk_path
|
||||
book_ext[book_id] = book_extension
|
||||
break
|
||||
else:
|
||||
pop_list.append(id)
|
||||
pop_list.append(book_id)
|
||||
else:
|
||||
pop_list.append(id)
|
||||
pop_list.append(book_id)
|
||||
|
||||
# Remove non-existent bookmark templates
|
||||
for id in pop_list:
|
||||
path_map.pop(id)
|
||||
for book_id in pop_list:
|
||||
path_map.pop(book_id)
|
||||
return path_map, book_ext
|
||||
|
||||
storage = get_storage()
|
||||
path_map, book_ext = resolve_bookmark_paths(storage, path_map)
|
||||
|
||||
bookmarked_books = {}
|
||||
with closing(self.device_database_connection()) as connection:
|
||||
for id in path_map:
|
||||
extension = os.path.splitext(path_map[id])[1]
|
||||
ContentType = self.get_content_type_from_extension(extension) if extension else self.get_content_type_from_path(path_map[id])
|
||||
ContentID = self.contentid_from_path(path_map[id], ContentType)
|
||||
with closing(self.device_database_connection(use_row_factory=True)) as connection:
|
||||
for book_id in path_map:
|
||||
extension = os.path.splitext(path_map[book_id])[1]
|
||||
ContentType = self.get_content_type_from_extension(extension) if extension else self.get_content_type_from_path(path_map[book_id])
|
||||
ContentID = self.contentid_from_path(path_map[book_id], ContentType)
|
||||
debug_print("get_annotations - ContentID: ", ContentID, "ContentType: ", ContentType)
|
||||
|
||||
bookmark_ext = extension
|
||||
|
||||
myBookmark = Bookmark(connection, ContentID, path_map[id], id, book_ext[id], bookmark_ext)
|
||||
bookmarked_books[id] = self.UserAnnotation(type='kobo_bookmark', value=myBookmark)
|
||||
myBookmark = Bookmark(connection, ContentID, path_map[book_id], book_id, book_ext[book_id], bookmark_ext)
|
||||
bookmarked_books[book_id] = self.UserAnnotation(type='kobo_bookmark', value=myBookmark)
|
||||
|
||||
# This returns as job.result in gui2.ui.annotations_fetched(self,job)
|
||||
return bookmarked_books
|
||||
@ -2431,7 +2431,7 @@ class KOBOTOUCH(KOBO):
|
||||
category_added = False
|
||||
|
||||
if book.contentID is None:
|
||||
debug_print(' Do not know ContentID - Title="%s, Authors=%s"'%(book.title, book.author))
|
||||
debug_print(' Do not know ContentID - Title="%s", Authors="%s", path="%s"'%(book.title, book.author, book.path))
|
||||
extension = os.path.splitext(book.path)[1]
|
||||
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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user