diff --git a/src/calibre/devices/kobo/bookmark.py b/src/calibre/devices/kobo/bookmark.py index beda79a72e..ee27246ed6 100644 --- a/src/calibre/devices/kobo/bookmark.py +++ b/src/calibre/devices/kobo/bookmark.py @@ -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 ? ' - ) - bookmark_query = ('SELECT bm.bookmarkid, bm.ContentID, bm.text, bm.annotation, ' + '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, ' '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__ + # }}} diff --git a/src/calibre/devices/kobo/driver.py b/src/calibre/devices/kobo/driver.py index 834019b0b5..e8ce401836 100644 --- a/src/calibre/devices/kobo/driver.py +++ b/src/calibre/devices/kobo/driver.py @@ -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)