View Single Post
Old 01-13-2023, 06:37 PM   #2610
ownedbycats
Custom User Title
ownedbycats ought to be getting tired of karma fortunes by now.ownedbycats ought to be getting tired of karma fortunes by now.ownedbycats ought to be getting tired of karma fortunes by now.ownedbycats ought to be getting tired of karma fortunes by now.ownedbycats ought to be getting tired of karma fortunes by now.ownedbycats ought to be getting tired of karma fortunes by now.ownedbycats ought to be getting tired of karma fortunes by now.ownedbycats ought to be getting tired of karma fortunes by now.ownedbycats ought to be getting tired of karma fortunes by now.ownedbycats ought to be getting tired of karma fortunes by now.ownedbycats ought to be getting tired of karma fortunes by now.
 
ownedbycats's Avatar
 
Posts: 11,023
Karma: 75555555
Join Date: Oct 2018
Location: Canada
Device: Kobo Libra H2O, formerly Aura HD
While I suspect that davidfor would be the only one to know what happened here, can anybody understand the code enough to figure out why this error would occur upon running "update table of contents"? It also resulted in the book glitching out on Kobo and skipping directly to the end.

Details about the book:
- Originally AZW3 downloaded with Kindle for PC, DeDRM'd and converted to ePub.
- Table of contents is nested (1 sublevel)
- Using KoboTouchExtended.

Error:

Code:
calibre, version 6.11.0
ERROR: Unhandled exception: <b>ConstraintError</b>:ConstraintError: UNIQUE constraint failed: content.ContentID

calibre 6.11*  embedded-python: True
Windows-10-10.0.19045-SP0 Windows ('64bit', 'WindowsPE')
('Windows', '10', '10.0.19045')
Python 3.10.1
Windows: ('10', '10.0.19045', 'SP0', 'Multiprocessor Free')
Interface language: None
Successfully initialized third party plugins: DeACSM (0, 0, 16) && DeDRM (10, 0, 2) && Action Chains (1, 18, 3) && Baen (1, 1, 0) && Barnes & Noble (1, 4, 0) && Comments Cleaner (1, 8, 0) && Count Pages (1, 12, 0) && Embed Comic Metadata (1, 6, 4) && EpubCheck (0, 2, 4) && EpubMerge (2, 15, 0) && EpubSplit (3, 5, 0) && Extract ISBN (1, 6, 0) && FanFicFare (4, 19, 0) && Fantastic Fiction (1, 6, 0) && Favourites Menu (1, 3, 0) && Find Duplicates (1, 10, 7) && Generate Cover (2, 3, 2) && GetFileName (0, 2, 0) && Goodreads (1, 7, 2) && Import List (1, 9, 0) && Job Spy (1, 0, 197) && Kindle hi-res covers (0, 5, 0) && KindleUnpack - The Plugin (0, 83, 1) && Kobo Books (1, 9, 2) && Kobo Utilities (2, 16, 7) && KoboTouchExtended (3, 6, 3) && Last Modified (0, 8, 4) && Manage Series (1, 5, 0) && Modify ePub (1, 8, 1) && MultiColumnSearch (1, 0, 95) && Obok DeDRM (7, 2, 1) && Open With (1, 8, 1) && Overdrive Link (2, 53, 0) && Quality Check (1, 13, 3) && Reading List (1, 14, 0) && Search The Internet (1, 10, 0) && SmartEject (2, 5, 0) && Standard Ebooks (1, 0, 0) && Sum Column (0, 3, 3) && View Manager (1, 10, 2) && Walk Search History (1, 5, 3) && Overdrive Link Metadata Source (2, 53, 0)
Traceback (most recent call last):
  File "calibre_plugins.koboutilities.action", line 5390, in update_book_toc_on_device
  File "calibre_plugins.koboutilities.action", line 5818, in update_device_toc_for_books
  File "calibre_plugins.koboutilities.action", line 5852, in update_device_toc_for_book
  File "calibre_plugins.koboutilities.action", line 6003, in addManifestEntryToDatabase
apsw.ConstraintError: ConstraintError: UNIQUE constraint failed: content.ContentID
Here are the lines in the traceback, all from actions.py:

Spoiler:


5381-5390:
Code:
        debug_print("update_book_toc_on_device - update_books=%d" % len(update_books))
        # only if there's some good ones.
        update_books = list(filter(lambda x : not x['good'], update_books))
        debug_print("update_book_toc_on_device - filtered update_books=%d" % len(update_books))
        if len(update_books) > 0:
            self.options = {}
            self.options['version'] = self.version
            debug_print("version=%s" % self.version)
            
            self.update_device_toc_for_books( update_books )

5803-5820:

Code:
    def update_device_toc_for_books(self, books):
        self.gui.status_bar.show_message(_('Updating ToC in device database for {0} books.').format(len(books)), 3000)
        debug_print("update_device_toc_for_books - books=", books)
        self.progressbar(_("Updating ToC in device database"), on_top=False)
        self.set_progressbar_label(_("Number of books to update {0}").format(len(books)))
        self.show_progressbar(len(books))
        with closing(self.device_database_connection()) as connection:
            for book in books:
                debug_print("update_device_toc_for_books - book=", book)
                debug_print("update_device_toc_for_books - ContentID=", book['ContentID'])
                self.increment_progressbar()
    
                if len(book['kobo_chapters']) > 0:
                    self.remove_all_toc_entries(connection, book['ContentID'])
    
                    self.update_device_toc_for_book(connection, book, book['ContentID'], book['title'], book['kobo_format'])

        self.hide_progressbar()
5823-6017

Code:
    def update_device_toc_for_book(self, connection, book, bookID, bookTitle, book_format='EPUB'):
        debug_print("update_device_toc_for_book - bookTitle=%s, len(book['library_chapters'])=%d" % (bookTitle, len(book['library_chapters'])))
        num_chapters = len(book['kobo_chapters'])
        for i, chapter in enumerate(book['kobo_chapters']):
            debug_print("update_device_toc_for_book - chapter=", (chapter))
            if book_format == 'KEPUB':
                chapterContentId = "{0}!{1}!{2}".format(book['ContentID'], book['kobo_opf_dir'], chapter['path'])
            else:
                chapterContentId = book['ContentID'] + '#({0})'.format(i) + chapter['path']
            debug_print("update_device_toc_for_book - chapterContentId=", chapterContentId)
            databaseChapterId = self.getDatabaseChapterId(book['ContentID'], chapter['path'], connection)
            has_chapter = not databaseChapterId is None
            debug_print("update_device_toc_for_book - has_chapter=", has_chapter)
            if has_chapter and chapter['path'].endswith('finish.xhtml') \
                and not chapterContentId == databaseChapterId:
                debug_print("update_device_toc_for_book - removing SOL finish chapter")
                self.removeChapterFromDatabase(databaseChapterId, bookID, connection)
                has_chapter = False
            if not has_chapter:
                self.addChapterToDatabase(chapterContentId, chapter, bookID, bookTitle, i, connection, book_format)
                chapter['added'] = True

        if book_format == 'KEPUB':
            num_chapters = len(book['kobo_manifest'])
            file_offset = 0
            total_file_size = sum([manifest_entry['file_size'] for manifest_entry in book['kobo_manifest']])
            for i, manifest_entry in enumerate(book['kobo_manifest']):
                file_size = manifest_entry['file_size'] * 100 / total_file_size
                manifest_entry_ContentId = "{0}!{1}!{2}".format(book['ContentID'][len('file://'):], book['kobo_opf_dir'], manifest_entry['path'])
                self.addManifestEntryToDatabase(manifest_entry_ContentId, bookID, bookTitle, manifest_entry['path'], i, connection, book_format, file_size=int(file_size), file_offset=int(file_offset))
                file_offset += file_size

        self.update_database_content_entry(connection, book['ContentID'], num_chapters)
        return 0

    def getDatabaseChapterId(self, bookId, toc_file, connection):
        cursor = connection.cursor()
        t = ("{0}%{1}%".format(bookId,toc_file),)
        cursor.execute('select ContentID from Content where ContentID like ?', t)
        try:
            result = next(cursor)
            chapterContentId = result['ContentID'] 
        except StopIteration:
            chapterContentId = None

        debug_print('getDatabaseChapterId - chapterContentId=%s' % chapterContentId)
        cursor.close()
        return chapterContentId

    def removeChapterFromDatabase(self, chapterContentId, bookID, connection):
        cursor = connection.cursor()
        t = (chapterContentId,)
        cursor.execute('delete from Content where ContentID = ?', t)
        t = (bookID, chapterContentId,)
        cursor.execute('delete from volume_shortcovers where volumeId = ? and shortcoverId = ?', t)

        cursor.close()
        return

    def update_database_content_entry(self, connection, contentId, num_chapters):
        cursor = connection.cursor()
        t = (contentId, num_chapters)
        cursor.execute('UPDATE content SET NumShortcovers = ? where ContentID = ?', t)

        cursor.close()
        return

    def remove_all_toc_entries(self, connection, contentId):
        debug_print("remove_all_toc_entries - contentId=", contentId)

        cursor = connection.cursor()
        t = (contentId,)
        
        cursor.execute('DELETE FROM Content WHERE BookID = ?', t)
        cursor.execute('DELETE FROM volume_shortcovers WHERE volumeId = ?', t)

        cursor.close()

        return

    def addChapterToDatabase(self, chapterContentId, chapter, bookID, bookTitle, volumeIndex, connection, book_format='EPUB'):
        cursorContent = connection.cursor()
        insertContentQuery = 'INSERT INTO content '\
            '(ContentID, ContentType, MimeType, BookID, BookTitle, Title, Attribution, adobe_location'\
            ', IsEncrypted, FirstTimeReading, ParagraphBookmarked, BookmarkWordOffset, VolumeIndex, ___NumPages'\
            ', ReadStatus, ___UserID, ___FileOffset, ___FileSize, ___PercentRead'\
            ', Depth, ChapterIDBookmarked'\
            ') VALUES ('\
            "?, ?, ?, ?, ?, ?, null, ?"\
            ", 'false', 'true', 0, 0, ?, -1"\
            ", 0, ?, 0, 0, 0"\
            ", ?, ?" \
            ')'

        if book_format == 'KEPUB':
            mime_type = 'application/x-kobo-epub+zip'
            content_type = 899
            content_userid = ''
            adobe_location = None
            matches = re.match(r'(?:file://)?((.*?)(?:\#.*)?(?:-\d+))$', chapterContentId)
            debug_print("addChapterToDatabase - regex matches=", matches.groups())
            chapterContentId = chapterContentId[len('file://'):]
            chapterContentId = matches.group(1)
            fragment_start = chapterContentId.rfind("#")
            chapter_id_bookmarked = chapterContentId if fragment_start < 0 else chapterContentId[:fragment_start]
            chapter_id_bookmarked = matches.group(2)
#             chapterContentId = "{0}-{1}".format(chapterContentId, chapter['toc_depth']) 
        else:
            mime_type = 'application/epub+zip'
            content_type = 9
            content_userid = 'adobe_user'
            chapter_id_bookmarked = None
            if 'chapter_location' in chapter:
                adobe_location = chapter['chapter_location']
            else:
                adobe_location = chapter['path']

        insertContentData = (
                    chapterContentId,
                    content_type,
                    mime_type,
                    bookID,
                    bookTitle,
                    chapter['title'],
                    adobe_location,
                    volumeIndex,
                    content_userid,
                    chapter['toc_depth'],
                    chapter_id_bookmarked
                    )

        debug_print("addChapterToDatabase - insertContentData=", insertContentData)
        cursorContent.execute(insertContentQuery, insertContentData)
        cursorContent.close()
        
        if book_format == 'EPUB':
            cursorShortCover = connection.cursor()
            insertShortCoverQuery = 'INSERT INTO volume_shortcovers (volumeId, shortcoverId, VolumeIndex) VALUES (?,?,?)'
            insertShortCoverData = (bookID, chapterContentId, volumeIndex, )
            debug_print("addChapterToDatabase - insertShortCoverData=", insertShortCoverData)
            cursorShortCover.execute(insertShortCoverQuery, insertShortCoverData)
        
            cursorShortCover.close()

    def addManifestEntryToDatabase(self, manifest_entry, bookID, bookTitle, title, volumeIndex, connection, 
                                   book_format='EPUB', file_size=None, file_offset=None):
        cursorContent = connection.cursor()
        insertContentQuery = 'INSERT INTO content '\
            '(ContentID, ContentType, MimeType, BookID, BookTitle, Title, Attribution, adobe_location'\
            ', IsEncrypted, FirstTimeReading, ParagraphBookmarked, BookmarkWordOffset, VolumeIndex, ___NumPages'\
            ', ReadStatus, ___UserID, ___FileOffset, ___FileSize, ___PercentRead'\
            ', Depth, ChapterIDBookmarked'\
            ') VALUES ('\
            "?, ?, ?, ?, ?, ?, null, ?"\
            ", 'false', 'true', 0, 0, ?, -1"\
            ", 0, ?, ?, ?, 0"\
            ", ?, ?" \
            ')'

        mime_type = 'application/xhtml+xml'
        content_type = 9
        content_userid = ''
        adobe_location = None

        insertContentData = (
                    manifest_entry,
                    content_type,
                    mime_type,
                    bookID,
                    bookTitle,
                    title,
                    adobe_location,
                    volumeIndex,
                    content_userid,
                    file_offset,
                    file_size,
                    0,
                    None
                    )
        debug_print("addManifestEntryToDatabase - insertContentData=", insertContentData)
        cursorContent.execute(insertContentQuery, insertContentData)
        
        cursorShortCover = connection.cursor()
        insertShortCoverQuery = 'INSERT INTO volume_shortcovers (volumeId, shortcoverId, VolumeIndex) VALUES (?,?,?)'
        insertShortCoverData = (bookID, manifest_entry, volumeIndex, )
        debug_print("addManifestEntryToDatabase - insertShortCoverData=", insertShortCoverData)
        cursorShortCover.execute(insertShortCoverQuery, insertShortCoverData)
        
        cursorContent.close()
        cursorShortCover.close()


    '''
    End ToC Updating
    '''
ownedbycats is online now