Example #1
0
 def update_booklist(self, bl, bl_index):
     if bl_index not in self.record_roots:
         return
     debug_print('Updating JSON cache:', bl_index)
     playlist_map = self.build_id_playlist_map(bl_index)
     root = self.record_roots[bl_index]
     lpath_map = self.build_lpath_map(root)
     for book in bl:
         record = lpath_map.get(book.lpath, None)
         if record is not None:
             for thumbnail in record.xpath(
                     'descendant::*[local-name()="thumbnail"]'):
                 for img in thumbnail.xpath(
                         'descendant::*[local-name()="jpeg"]|'
                         'descendant::*[local-name()="png"]'):
                     if img.text:
                         try:
                             raw = b64decode(img.text.strip())
                         except:
                             continue
                         book.thumbnail = raw
                         break
                 break
             book.device_collections = playlist_map.get(book.lpath, [])
     debug_print('Finished updating JSON cache:', bl_index)
Example #2
0
 def books(self, oncard=None, end_session=True):
     debug_print('PRS505: starting fetching books for card', oncard)
     bl = USBMS.books(self, oncard=oncard, end_session=end_session)
     c = self.initialize_XML_cache()
     c.update_booklist(bl, {'carda': 1, 'cardb': 2}.get(oncard, 0))
     debug_print('PRS505: finished fetching books for card', oncard)
     return bl
Example #3
0
    def commit(self):
        debug_print("KOBOTOUCHConfig::commit: start")
        p = super(KOBOTOUCHConfig, self).commit()

        p['manage_collections'] = self.manage_collections
        p['create_collections'] = self.create_collections
        p['collections_columns'] = self.collections_columns
        p['ignore_collections_names'] = self.ignore_collections_names
        p['delete_empty_collections'] = self.delete_empty_collections

        p['upload_covers'] = self.upload_covers
        p['keep_cover_aspect'] = self.keep_cover_aspect
        p['upload_grayscale'] = self.upload_grayscale

        p['show_recommendations'] = self.show_recommendations
        p['show_previews'] = self.show_previews
        p['show_archived_books'] = self.show_archived_books

        p['update_series'] = self.update_series
        p['modify_css'] = self.modify_css

        p['support_newer_firmware'] = self.support_newer_firmware
        p['debugging_title'] = self.debugging_title
        p['driver_version'] = '.'.join(
            [unicode(i) for i in self.device.version])

        return p
Example #4
0
    def remove_orphaned_records(self, connection, dbpath):
        try:
            cursor = connection.cursor()

            debug_print("Removing Orphaned Collection Records")

            # Purge any collections references that point into the abyss
            query = 'DELETE FROM booktags WHERE book_id NOT IN (SELECT _id FROM books)'
            cursor.execute(query)
            query = 'DELETE FROM booktags WHERE tag_id NOT IN (SELECT _id FROM tags)'
            cursor.execute(query)

            debug_print("Removing Orphaned Book Records")

            cursor.close()
        except Exception:
            import traceback
            tb = traceback.format_exc()
            raise DeviceError((
                ('The Paladin database is corrupted. '
                 ' Delete the file %s on your reader and then disconnect '
                 ' reconnect it. If you are using an SD card, you '
                 ' should delete the file on the card as well. Note that '
                 ' deleting this file will cause your reader to forget '
                 ' any notes/highlights, etc.') % dbpath) +
                              ' Underlying error:'
                              '\n' + tb)
Example #5
0
    def _log_location(self, *args):
        '''
        Upon first call, switch to appropriate method
        '''
        from calibre_plugins.syncman.config import prefs
        if not prefs.get('debug_plugin', False):
            # Neuter the method
            self._log = self.__null
            self._log_location = self.__null
        else:
            # Log the message from here so stack trace is valid
            arg1 = arg2 = ''

            if len(args) > 0:
                arg1 = str(args[0])
            if len(args) > 1:
                arg2 = str(args[1])

            debug_print(self.LOCATION_TEMPLATE.format(cls=self.__class__.__name__,
                        func=sys._getframe(1).f_code.co_name,
                        arg1=arg1, arg2=arg2))

            # Switch to real method
            self._log = self.__log
            self._log_location = self.__log_location
Example #6
0
 def get_or_create_playlist(self, bl_idx, title):
     # maintain a private map of playlists to their ids. Don't check if it
     # exists, because reset_existing_playlist_map must be called before it
     # is used to ensure that deleted playlists are taken into account
     root = self.record_roots[bl_idx]
     if bl_idx not in self._playlist_to_playlist_id_map:
         self._playlist_to_playlist_id_map[bl_idx] = {}
         for playlist in root.xpath('//*[local-name()="playlist"]'):
             pl_title = playlist.get('title', None)
             if pl_title is not None:
                 self._playlist_to_playlist_id_map[bl_idx][
                     pl_title] = playlist
     if title in self._playlist_to_playlist_id_map[bl_idx]:
         return self._playlist_to_playlist_id_map[bl_idx][title]
     debug_print('Creating playlist:', title)
     ans = root.makeelement('{%s}playlist' % self.namespaces[bl_idx],
                            nsmap=root.nsmap,
                            attrib={
                                'uuid': uuid(),
                                'title': title,
                                'id': unicode_type(self.max_id(root) + 1),
                                'sourceid': '1'
                            })
     root.append(ans)
     self._playlist_to_playlist_id_map[bl_idx][title] = ans
     return ans
Example #7
0
 def books(self, oncard=None, end_session=True):
     debug_print('PRS505: starting fetching books for card', oncard)
     bl = USBMS.books(self, oncard=oncard, end_session=end_session)
     c = self.initialize_XML_cache()
     c.update_booklist(bl, {'carda':1, 'cardb':2}.get(oncard, 0))
     debug_print('PRS505: finished fetching books for card', oncard)
     return bl
Example #8
0
 def update_booklist(self, bl, bl_index):
     if bl_index not in self.record_roots:
         return
     debug_print('Updating JSON cache:', bl_index)
     playlist_map = self.build_id_playlist_map(bl_index)
     root = self.record_roots[bl_index]
     lpath_map = self.build_lpath_map(root)
     for book in bl:
         record = lpath_map.get(book.lpath, None)
         if record is not None:
             for thumbnail in record.xpath(
                     'descendant::*[local-name()="thumbnail"]'):
                 for img in thumbnail.xpath(
                         'descendant::*[local-name()="jpeg"]|'
                         'descendant::*[local-name()="png"]'):
                     if img.text:
                         try:
                             raw = from_base64_bytes(img.text.strip())
                         except Exception:
                             continue
                         book.thumbnail = raw
                         break
                 break
             book.device_collections = playlist_map.get(book.lpath, [])
     debug_print('Finished updating JSON cache:', bl_index)
Example #9
0
    def remove_orphaned_records(self, connection, dbpath):
        try:
            cursor = connection.cursor()

            debug_print("Removing Orphaned Collection Records")

            # Purge any collections references that point into the abyss
            query = 'DELETE FROM booktags WHERE book_id NOT IN (SELECT _id FROM books)'
            cursor.execute(query)
            query = 'DELETE FROM booktags WHERE tag_id NOT IN (SELECT _id FROM tags)'
            cursor.execute(query)

            debug_print("Removing Orphaned Book Records")

            cursor.close()
        except Exception:
            import traceback
            tb = traceback.format_exc()
            raise DeviceError((('The Paladin database is corrupted. '
                    ' Delete the file %s on your reader and then disconnect '
                    ' reconnect it. If you are using an SD card, you '
                    ' should delete the file on the card as well. Note that '
                    ' deleting this file will cause your reader to forget '
                    ' any notes/highlights, etc.')%dbpath)+' Underlying error:'
                    '\n'+tb)
Example #10
0
    def _upload_cover(self, path, filename, metadata, filepath):
        if metadata.thumbnail and metadata.thumbnail[-1]:
            path = path.replace('/', os.sep)
            is_main = path.startswith(self._main_prefix)
            thumbnail_dir = MEDIA_THUMBNAIL if is_main else CACHE_THUMBNAIL
            prefix = None
            if is_main:
                prefix = self._main_prefix
            else:
                if self._card_a_prefix and \
                    path.startswith(self._card_a_prefix):
                    prefix = self._card_a_prefix
                elif self._card_b_prefix and \
                        path.startswith(self._card_b_prefix):
                    prefix = self._card_b_prefix
            if prefix is None:
                prints('WARNING: Failed to find prefix for:', filepath)
                return
            thumbnail_dir = os.path.join(prefix, *thumbnail_dir.split('/'))

            relpath = os.path.relpath(filepath, prefix)
            if relpath.startswith('..\\'):
                relpath = relpath[3:]
            thumbnail_dir = os.path.join(thumbnail_dir, relpath)
            if not os.path.exists(thumbnail_dir):
                os.makedirs(thumbnail_dir)
            cpath = os.path.join(thumbnail_dir, 'main_thumbnail.jpg')
            with lopen(cpath, 'wb') as f:
                f.write(metadata.thumbnail[-1])
            debug_print('Cover uploaded to: %r'%cpath)
Example #11
0
    def commit(self):
        debug_print("KOBOTOUCHConfig::commit: start")
        p = super(KOBOTOUCHConfig, self).commit()

        p['manage_collections'] = self.manage_collections
        p['create_collections'] = self.create_collections
        p['collections_columns'] = self.collections_columns
        p['ignore_collections_names'] = self.ignore_collections_names
        p['delete_empty_collections'] = self.delete_empty_collections

        p['upload_covers'] = self.upload_covers
        p['keep_cover_aspect'] = self.keep_cover_aspect
        p['upload_grayscale'] = self.upload_grayscale
        p['dithered_covers'] = self.dithered_covers
        p['letterbox_fs_covers'] = self.letterbox_fs_covers
        p['png_covers'] = self.png_covers

        p['show_recommendations'] = self.show_recommendations
        p['show_previews'] = self.show_previews
        p['show_archived_books'] = self.show_archived_books

        p['update_series'] = self.update_series
        p['update_core_metadata'] = self.update_core_metadata
        p['update_purchased_kepubs'] = self.update_purchased_kepubs
        p['subtitle_template'] = self.subtitle_template
        p['update_subtitle'] = self.update_subtitle

        p['modify_css'] = self.modify_css
        p['override_kobo_replace_existing'] = self.override_kobo_replace_existing

        p['support_newer_firmware'] = self.support_newer_firmware
        p['debugging_title'] = self.debugging_title
        p['driver_version'] = '.'.join([unicode(i) for i in self.device.version])

        return p
    def open(self, parent=None, detail_item=None, external=False):
        '''
        Open the specified item in the external, or Calibre's browser
        '''

        debug_print('Libgen Fiction::__init__.py:LibgenStore:open:locals() =',
                    locals())

        detail_url = (
            self.libgen.get_detail_url(detail_item)
            if detail_item
            else self.libgen.base_url
        )

        debug_print('Libgen Fiction::__init__.py:LibgenStore:open:detail_url =',
                    detail_url)

        if external or self.config.get('open_external', False):
            open_url(QUrl(detail_url))
        else:
            d = WebStoreDialog(
                self.gui, self.libgen.base_url, parent, detail_url)
            d.setWindowTitle(self.name)
            d.set_tags(self.config.get('tags', ''))
            d.exec_()
    def genesis(self):
        '''
        Initialize the Libgen Client
        '''
        debug_print('Libgen Fiction::__init__.py:LibgenStore:genesis')

        self.libgen = LibgenFictionClient()
Example #14
0
    def _upload_cover(self, path, filename, metadata, filepath):
        if metadata.thumbnail and metadata.thumbnail[-1]:
            path = path.replace('/', os.sep)
            is_main = path.startswith(self._main_prefix)
            thumbnail_dir = MEDIA_THUMBNAIL if is_main else CACHE_THUMBNAIL
            prefix = None
            if is_main:
                prefix = self._main_prefix
            else:
                if self._card_a_prefix and \
                    path.startswith(self._card_a_prefix):
                    prefix = self._card_a_prefix
                elif self._card_b_prefix and \
                        path.startswith(self._card_b_prefix):
                    prefix = self._card_b_prefix
            if prefix is None:
                prints('WARNING: Failed to find prefix for:', filepath)
                return
            thumbnail_dir = os.path.join(prefix, *thumbnail_dir.split('/'))

            relpath = os.path.relpath(filepath, prefix)
            if relpath.startswith('..\\'):
                relpath = relpath[3:]
            thumbnail_dir = os.path.join(thumbnail_dir, relpath)
            if not os.path.exists(thumbnail_dir):
                os.makedirs(thumbnail_dir)
            cpath = os.path.join(thumbnail_dir, 'main_thumbnail.jpg')
            with lopen(cpath, 'wb') as f:
                f.write(metadata.thumbnail[-1])
            debug_print('Cover uploaded to: %r' % cpath)
Example #15
0
    def __init__(self, parent, object_name):
        self.parent = parent
        self.prefs = parent.prefs
        self.elements = self.prefs.get('appearance_css', None)
        debug_print("AnnotationElementsTable::__init__ - self.elements",
                    self.elements)
        if not self.elements:
            self.elements = default_elements
            debug_print("AnnotationElementsTable::__init__ - self.elements",
                        self.elements)

        QTableWidget.__init__(self)
        self.setObjectName(object_name)
        self.layout = parent.elements_hl.layout()

        # Add ourselves to the layout
        sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum)
        sizePolicy.setHorizontalStretch(0)
        #sizePolicy.setVerticalStretch(0)
        #sizePolicy.setHeightForWidth(self.sizePolicy().hasHeightForWidth())
        self.setSizePolicy(sizePolicy)
        #self.setMaximumSize(QSize(16777215, self.MAXIMUM_TABLE_HEIGHT))

        self.setColumnCount(0)
        self.setRowCount(0)
        self.layout.addWidget(self)
Example #16
0
    def commit(self):
        debug_print("KOBOTOUCHConfig::commit: start")
        p = super(KOBOTOUCHConfig, self).commit()

        p['manage_collections'] = self.manage_collections
        p['create_collections'] = self.create_collections
        p['collections_columns'] = self.collections_columns
        p['ignore_collections_names'] = self.ignore_collections_names
        p['delete_empty_collections'] = self.delete_empty_collections

        p['upload_covers'] = self.upload_covers
        p['keep_cover_aspect'] = self.keep_cover_aspect
        p['upload_grayscale'] = self.upload_grayscale

        p['show_recommendations'] = self.show_recommendations
        p['show_previews'] = self.show_previews
        p['show_archived_books'] = self.show_archived_books

        p['update_series'] = self.update_series
        p['modify_css'] = self.modify_css

        p['support_newer_firmware'] = self.support_newer_firmware
        p['debugging_title'] = self.debugging_title
        p['driver_version'] = '.'.join([unicode(i) for i in self.device.version])

        return p
Example #17
0
    def upload_book_cover(self, connection, book, source_id):
        debug_print('PRST1: Uploading/Refreshing Cover for ' + book.title)
        if (not book.thumbnail or isinstance(book.thumbnail, ImageWrapper) or
                not book.thumbnail[-1]):
            # If the thumbnail is an ImageWrapper instance, it refers to a book
            # not in the calibre library
            return
        cursor = connection.cursor()

        thumbnail_path = THUMBPATH%book.bookId

        prefix = self._main_prefix if source_id == 0 else self._card_a_prefix
        thumbnail_file_path = os.path.join(prefix, *thumbnail_path.split('/'))
        thumbnail_dir_path = os.path.dirname(thumbnail_file_path)
        if not os.path.exists(thumbnail_dir_path):
            os.makedirs(thumbnail_dir_path)

        with lopen(thumbnail_file_path, 'wb') as f:
            f.write(book.thumbnail[-1])
            fsync(f)

        query = 'UPDATE books SET thumbnail = ? WHERE _id = ?'
        t = (thumbnail_path, book.bookId,)
        cursor.execute(query, t)

        connection.commit()
        cursor.close()
Example #18
0
    def upload_book_cover(self, connection, book, source_id):
        debug_print('PRST1: Uploading/Refreshing Cover for ' + book.title)
        if (not book.thumbnail or isinstance(book.thumbnail, ImageWrapper) or
                not book.thumbnail[-1]):
            # If the thumbnail is an ImageWrapper instance, it refers to a book
            # not in the calibre library
            return
        cursor = connection.cursor()

        thumbnail_path = THUMBPATH%book.bookId

        prefix = self._main_prefix if source_id is 0 else self._card_a_prefix
        thumbnail_file_path = os.path.join(prefix, *thumbnail_path.split('/'))
        thumbnail_dir_path = os.path.dirname(thumbnail_file_path)
        if not os.path.exists(thumbnail_dir_path):
            os.makedirs(thumbnail_dir_path)

        with open(thumbnail_file_path, 'wb') as f:
            f.write(book.thumbnail[-1])

        query = 'UPDATE books SET thumbnail = ? WHERE _id = ?'
        t = (thumbnail_path, book.bookId,)
        cursor.execute(query, t)

        connection.commit()
        cursor.close()
Example #19
0
 def __log(self, msg=None):
     '''
     The real method
     '''
     if msg:
         debug_print(" {0}".format(str(msg)))
     else:
         debug_print()
Example #20
0
 def prune_empty_playlists(self):
     for i, root in self.record_roots.items():
         self.purge_broken_playlist_items(root)
         for playlist in root.xpath('//*[local-name()="playlist"]'):
             if len(playlist) == 0 or not playlist.get("title", None):
                 if DEBUG:
                     debug_print("Removing playlist id:", playlist.get("id", None), playlist.get("title", None))
                 playlist.getparent().remove(playlist)
Example #21
0
 def remap_playlist_references(root, idmap):
     for playlist in root.xpath('//*[local-name()="playlist"]'):
         for item in playlist.xpath('descendant::*[@id and local-name()="item"]'):
             id_ = item.get("id")
             if id_ in idmap:
                 item.set("id", idmap[id_])
                 if DEBUG:
                     debug_print("Remapping id %s to %s" % (id_, idmap[id_]))
Example #22
0
    def update(self, booklists, collections_attributes, plugboard):
        debug_print('Starting update', collections_attributes)
        use_tz_var = False
        for i, booklist in booklists.items():
            playlist_map = self.build_id_playlist_map(i)
            debug_print('Updating XML Cache:', i)
            root = self.record_roots[i]
            lpath_map = self.build_lpath_map(root)
            ext_root = self.ext_roots[i] if i in self.ext_roots else None
            ext_lpath_map = None
            if ext_root is not None:
                ext_lpath_map = self.build_lpath_map(ext_root)
            gtz_count = ltz_count = 0
            use_tz_var = False
            for book in booklist:
                path = os.path.join(self.prefixes[i], *(book.lpath.split('/')))
                record = lpath_map.get(book.lpath, None)
                created = False
                if record is None:
                    created = True
                    record = self.create_text_record(root, i, book.lpath)
                if plugboard is not None:
                    newmi = book.deepcopy_metadata()
                    newmi.template_to_attribute(book, plugboard)
                    newmi.set('_new_book', getattr(book, '_new_book', False))
                    book.set('_pb_title_sort',
                             newmi.get('title_sort', newmi.get('title', None)))
                    book.set('_pb_author_sort', newmi.get('author_sort', ''))
                else:
                    newmi = book
                (gtz_count, ltz_count, use_tz_var) = \
                    self.update_text_record(record, newmi, path, i,
                                            gtz_count, ltz_count, use_tz_var)
                # Ensure the collections in the XML database are recorded for
                # this book
                if book.device_collections is None:
                    book.device_collections = []
                book.device_collections = playlist_map.get(book.lpath, [])

                if created and ext_root is not None and \
                        ext_lpath_map.get(book.lpath, None) is None:
                    ext_record = self.create_ext_text_record(ext_root, i,
                            book.lpath, book.thumbnail)
                    self.periodicalize_book(book, ext_record)

            debug_print('Timezone votes: %d GMT, %d LTZ, use_tz_var=%s'%
                                        (gtz_count, ltz_count, use_tz_var))
            self.update_playlists(i, root, booklist, collections_attributes)
        # Update the device collections because update playlist could have added
        # some new ones.
        debug_print('In update/ Starting refresh of device_collections')
        for i, booklist in booklists.items():
            playlist_map = self.build_id_playlist_map(i)
            for book in booklist:
                book.device_collections = playlist_map.get(book.lpath, [])
        self.fix_ids()
        debug_print('Finished update')
Example #23
0
 def remap_playlist_references(root, idmap):
     for playlist in root.xpath('//*[local-name()="playlist"]'):
         for item in playlist.xpath(
                 'descendant::*[@id and local-name()="item"]'):
             id_ = item.get('id')
             if id_ in idmap:
                 item.set('id', idmap[id_])
                 if DEBUG:
                     debug_print('Remapping id %s to %s'%(id_, idmap[id_]))
Example #24
0
 def prune_empty_playlists(self):
     for i, root in self.record_roots.items():
         self.purge_broken_playlist_items(root)
         for playlist in root.xpath('//*[local-name()="playlist"]'):
             if len(playlist) == 0 or not playlist.get('title', None):
                 if DEBUG:
                     debug_print('Removing playlist id:', playlist.get('id', None),
                             playlist.get('title', None))
                 playlist.getparent().remove(playlist)
Example #25
0
    def update(self, booklists, collections_attributes, plugboard):
        debug_print('Starting update', collections_attributes)
        use_tz_var = False
        for i, booklist in booklists.items():
            playlist_map = self.build_id_playlist_map(i)
            debug_print('Updating XML Cache:', i)
            root = self.record_roots[i]
            lpath_map = self.build_lpath_map(root)
            ext_root = self.ext_roots[i] if i in self.ext_roots else None
            ext_lpath_map = None
            if ext_root is not None:
                ext_lpath_map = self.build_lpath_map(ext_root)
            gtz_count = ltz_count = 0
            use_tz_var = False
            for book in booklist:
                path = os.path.join(self.prefixes[i], *(book.lpath.split('/')))
                record = lpath_map.get(book.lpath, None)
                created = False
                if record is None:
                    created = True
                    record = self.create_text_record(root, i, book.lpath)
                if plugboard is not None:
                    newmi = book.deepcopy_metadata()
                    newmi.template_to_attribute(book, plugboard)
                    newmi.set('_new_book', getattr(book, '_new_book', False))
                    book.set('_pb_title_sort',
                             newmi.get('title_sort', newmi.get('title', None)))
                    book.set('_pb_author_sort', newmi.get('author_sort', ''))
                else:
                    newmi = book
                (gtz_count, ltz_count, use_tz_var) = \
                    self.update_text_record(record, newmi, path, i,
                                            gtz_count, ltz_count, use_tz_var)
                # Ensure the collections in the XML database are recorded for
                # this book
                if book.device_collections is None:
                    book.device_collections = []
                book.device_collections = playlist_map.get(book.lpath, [])

                if created and ext_root is not None and \
                        ext_lpath_map.get(book.lpath, None) is None:
                    ext_record = self.create_ext_text_record(ext_root, i,
                            book.lpath, book.thumbnail)
                    self.periodicalize_book(book, ext_record)

            debug_print('Timezone votes: %d GMT, %d LTZ, use_tz_var=%s'%
                                        (gtz_count, ltz_count, use_tz_var))
            self.update_playlists(i, root, booklist, collections_attributes)
        # Update the device collections because update playlist could have added
        # some new ones.
        debug_print('In update/ Starting refresh of device_collections')
        for i, booklist in booklists.items():
            playlist_map = self.build_id_playlist_map(i)
            for book in booklist:
                book.device_collections = playlist_map.get(book.lpath, [])
        self.fix_ids()
        debug_print('Finished update')
Example #26
0
 def rebuild_collections(self, booklist, oncard):
     debug_print('PRS505: started rebuild_collections on card', oncard)
     c = self.initialize_XML_cache()
     c.rebuild_collections(booklist, {
         'carda': 1,
         'cardb': 2
     }.get(oncard, 0))
     c.write()
     debug_print('PRS505: finished rebuild_collections')
Example #27
0
    def remove_orphaned_records(self, connection, dbpath):
        from sqlite3 import DatabaseError

        try:
            cursor = connection.cursor()

            debug_print("Removing Orphaned Collection Records")

            # Purge any collections references that point into the abyss
            query = "DELETE FROM collections WHERE content_id NOT IN (SELECT _id FROM books)"
            cursor.execute(query)
            query = "DELETE FROM collections WHERE collection_id NOT IN (SELECT _id FROM collection)"
            cursor.execute(query)

            debug_print("Removing Orphaned Book Records")

            # Purge any references to books not in this database
            # Idea is to prevent any spill-over where these wind up applying to some other book
            query = "DELETE FROM %s WHERE content_id NOT IN (SELECT _id FROM books)"
            cursor.execute(query % "annotation")
            cursor.execute(query % "bookmark")
            cursor.execute(query % "current_position")
            cursor.execute(query % "freehand")
            cursor.execute(query % "history")
            cursor.execute(query % "layout_cache")
            cursor.execute(query % "preference")

            cursor.close()
        except DatabaseError:
            import traceback

            tb = traceback.format_exc()
            raise DeviceError(
                (
                    (
                        "The SONY database is corrupted. "
                        " Delete the file %s on your reader and then disconnect "
                        " reconnect it. If you are using an SD card, you "
                        " should delete the file on the card as well. Note that "
                        " deleting this file will cause your reader to forget "
                        " any notes/highlights, etc."
                    )
                    % dbpath
                )
                + " Underlying error:"
                "\n" + tb
            )

        def get_lastrowid(self, cursor):
            # SQLite3 + Python has a fun issue on 32-bit systems with integer overflows.
            # Issue a SQL query instead, getting the value as a string, and then converting to a long python int manually.
            query = "SELECT last_insert_rowid()"
            cursor.execute(query)
            row = cursor.fetchone()

            return long(row[0])
Example #28
0
    def save_settings(cls, config_widget):
        try:
            config_widget = config_widget.widget()
            debug_print(
                "KoboTouchExtended:save_settings: Have old style configuration.")
        except:
            debug_print(
                "KoboTouchExtended:save_settings: Have new style configuration.")

        super(KOBOTOUCHEXTENDED, cls).save_settings(config_widget)
Example #29
0
 def settings(cls):
     opts = super(KOBOTOUCHEXTENDED, cls).settings()
     debug_print("KoboTouchExtended:settings: settings=", opts)
     # Make sure that each option is actually the right type
     for idx in range(0, len(cls.EXTRA_CUSTOMIZATION_DEFAULT)):
         if not isinstance(opts.extra_customization[idx],
                           type(cls.EXTRA_CUSTOMIZATION_DEFAULT[idx])):
             opts.extra_customization[
                 idx] = cls.EXTRA_CUSTOMIZATION_DEFAULT[idx]
     return opts
Example #30
0
 def settings(cls):
     opts = super(KOBOTOUCHEXTENDED, cls).settings()
     debug_print("KoboTouchExtended:settings: settings=", opts)
     # Make sure that each option is actually the right type
     for idx in range(0, len(cls.EXTRA_CUSTOMIZATION_DEFAULT)):
         if not isinstance(opts.extra_customization[idx],
                           type(cls.EXTRA_CUSTOMIZATION_DEFAULT[idx])):
             opts.extra_customization[
                 idx] = cls.EXTRA_CUSTOMIZATION_DEFAULT[idx]
     return opts
Example #31
0
    def commit(self):
        debug_print("TabbedDeviceConfig::commit: start")
        p = self.device._configProxy()

        p['format_map'] = self.formats.format_map
        p['use_subdirs'] = self.use_subdirs()
        p['read_metadata'] = self.read_metadata()
        p['save_template'] = self.template.template
        p['extra_customization'] = self.extra_tab.extra_customization()

        return p
Example #32
0
    def commit(self):
        debug_print("TabbedDeviceConfig::commit: start")
        p = self.device._configProxy()

        p['format_map'] = self.formats.format_map
        p['use_subdirs'] = self.use_subdirs()
        p['read_metadata'] = self.read_metadata()
        p['save_template'] = self.template.template
        p['extra_customization'] = self.extra_tab.extra_customization()

        return p
    def _log(self, msg=None):
        '''
        Print msg to console
        '''
        if not self.verbose:
            return

        if msg:
            debug_print(" %s" % msg)
        else:
            debug_print()
    def _log(self, msg=None):
        '''
        Print msg to console
        '''
        if not self.verbose:
            return

        if msg:
            debug_print(" %s" % msg)
        else:
            debug_print()
Example #35
0
    def filename_callback(self, path, mi):
        opts = self.settings()
        if opts.extra_customization[self.OPT_EXTRA_FEATURES]:
            debug_print("KoboTouchExtended:filename_callback:Path - {0}".format(path))
            if path.endswith(KEPUB_EXT):
                path += EPUB_EXT
            elif path.endswith(EPUB_EXT) and mi.uuid not in self.skip_renaming_files:
                path = path[:-len(EPUB_EXT)] + KEPUB_EXT + EPUB_EXT

            debug_print("KoboTouchExtended:filename_callback:New path - {0}".format(path))
        return path
Example #36
0
    def _log(self, msg=None):
        '''
        Print msg to console
        '''
        if not plugin_prefs.get('cfg_plugin_debug_log_checkbox', False):
            return

        if msg:
            debug_print(" %s" % str(msg))
        else:
            debug_print()
    def _log(self, msg=None):
        '''
        Print msg to console
        '''
        if not plugin_prefs.get('cfg_plugin_debug_log_checkbox', False):
            return

        if msg:
            debug_print(" %s" % str(msg))
        else:
            debug_print()
    def _log(self, msg=None):
        '''
        Print msg to console
        '''
        from calibre_plugins.annotations.config import plugin_prefs
        if not plugin_prefs.get('cfg_plugin_debug_log_checkbox', False):
            return

        if msg:
            debug_print(" %s" % str(msg))
        else:
            debug_print()
Example #39
0
    def _log(self, msg=None):
        '''
        Print msg to console
        '''
        from calibre_plugins.marvin_manager.config import plugin_prefs
        if not plugin_prefs.get('debug_plugin', False):
            return

        if msg:
            debug_print(" %s" % str(msg))
        else:
            debug_print()
Example #40
0
    def save_settings(cls, config_widget):
        try:
            config_widget = config_widget.widget()
            debug_print(
                "KoboTouchExtended:save_settings: Have old style configuration."
            )
        except:
            debug_print(
                "KoboTouchExtended:save_settings: Have new style configuration."
            )

        super(KOBOTOUCHEXTENDED, cls).save_settings(config_widget)
Example #41
0
    def filename_callback(self, path, mi):
        opts = self.settings()
        if opts.extra_customization[self.OPT_EXTRA_FEATURES]:
            debug_print("KoboTouchExtended:filename_callback:Path - {0}".format(path))

            idx = path.rfind('.')
            ext = path[idx:]
            if ext == KEPUB_EXT or (ext == EPUB_EXT and mi.uuid not in self.skip_renaming_files):
                path = "{0}.kepub{1}".format(path[:idx], EPUB_EXT)
                debug_print("KoboTouchExtended:filename_callback:New path - {0}".format(path))

        return path
Example #42
0
def _log(msg=None):
    '''
    Print msg to console
    '''
    from calibre_plugins.annotations.config import plugin_prefs
    if not plugin_prefs.get('cfg_plugin_debug_log_checkbox', False):
        return

    if msg:
        debug_print(" %s" % str(msg))
    else:
        debug_print()
Example #43
0
	def upload_books(self, files, names, on_card = None, end_session = True, metadata = None):
		opts = self.settings()
		if opts.extra_customization[self.OPT_EXTRA_FEATURES]:
			debug_print("KoboTouchExtended:upload_books:Enabling extra ePub features for Kobo devices")
			for file in files:
				ext = file[file.rfind('.'):]
				if ext == EPUB_EXT:
					self._modify_epub(file)

		result = super(KOBOTOUCHEXTENDED, self).upload_books(files, names, on_card, end_session, metadata)

		return result
Example #44
0
	def filename_callback(self, path, mi):
		opts = self.settings()
		if opts.extra_customization[self.OPT_EXTRA_FEATURES]:
			debug_print("KoboTouchExtended:filename_callback:Path - {0}".format(path))

			idx = path.rfind('.')
			ext = path[idx:]
			if ext == EPUB_EXT:
				path = "{0}.kepub{1}".format(path[:idx], EPUB_EXT)
				debug_print("KoboTouchExtended:filename_callback:New path - {0}".format(path))

		return path
    def _log(self, msg=None):
        """
        Print msg to console
        """
        from calibre_plugins.marvin_manager.config import plugin_prefs

        if not plugin_prefs.get("debug_plugin", False):
            return

        if msg:
            debug_print(" %s" % str(msg))
        else:
            debug_print()
Example #46
0
    def __log_location(self, *args):
        '''
        The real method
        '''
        arg1 = arg2 = ''

        if len(args) > 0:
            arg1 = str(args[0])
        if len(args) > 1:
            arg2 = str(args[1])

        debug_print(self.LOCATION_TEMPLATE.format(cls=self.__class__.__name__,
                    func=sys._getframe(1).f_code.co_name,
                    arg1=arg1, arg2=arg2))
Example #47
0
        def ensure_numeric_ids(root):
            idmap = {}
            for x in root.xpath('child::*[@id]'):
                id_ = x.get('id')
                try:
                    id_ = int(id_)
                except:
                    x.set('id', '-1')
                    idmap[id_] = '-1'

            if DEBUG and idmap:
                debug_print('Found non numeric ids:')
                debug_print(list(idmap.keys()))
            return idmap
Example #48
0
    def upload_cover(self, path, filename, metadata, filepath):
        import sqlite3 as sqlite

        debug_print('PRS-T1: uploading cover')

        if filepath.startswith(self._main_prefix):
            prefix = self._main_prefix
            source_id = 0
        else:
            prefix = self._card_a_prefix
            source_id = 1

        metadata.lpath = filepath.partition(prefix)[2]
        metadata.lpath = metadata.lpath.replace('\\', '/')
        dbpath = self.normalize_path(prefix + DBPATH)
        debug_print("SQLite DB Path: " + dbpath)

        with closing(sqlite.connect(dbpath)) as connection:
            cursor = connection.cursor()

            query = 'SELECT _id FROM books WHERE file_path = ?'
            t = (metadata.lpath,)
            cursor.execute(query, t)

            for i, row in enumerate(cursor):
                metadata.bookId = row[0]

            cursor.close()

            if getattr(metadata, 'bookId', None) is not None:
                debug_print('PRS-T1: refreshing cover for book being sent')
                self.upload_book_cover(connection, metadata, source_id)

        debug_print('PRS-T1: done uploading cover')
Example #49
0
    def update_device_database(self, booklist, collections_attributes, oncard):
        import sqlite3 as sqlite

        debug_print('PRST1: starting update_device_database')

        plugboard = None
        if self.plugboard_func:
            plugboard = self.plugboard_func(self.__class__.__name__,
                    'device_db', self.plugboards)
            debug_print("PRST1: Using Plugboard", plugboard)

        prefix = self._card_a_prefix if oncard == 'carda' else self._main_prefix
        if prefix is None:
            # Reader has no sd card inserted
            return
        source_id = 1 if oncard == 'carda' else 0

        dbpath = self.normalize_path(prefix + DBPATH)
        debug_print("SQLite DB Path: " + dbpath)

        collections = booklist.get_collections(collections_attributes)

        with closing(sqlite.connect(dbpath)) as connection:
            self.remove_orphaned_records(connection, dbpath)
            self.update_device_books(connection, booklist, source_id,
                    plugboard, dbpath)
            self.update_device_collections(connection, booklist, collections, source_id, dbpath)

        debug_print('PRST1: finished update_device_database')
Example #50
0
        def ensure_numeric_ids(root):
            idmap = {}
            for x in root.xpath("child::*[@id]"):
                id_ = x.get("id")
                try:
                    id_ = int(id_)
                except:
                    x.set("id", "-1")
                    idmap[id_] = "-1"

            if DEBUG and idmap:
                debug_print("Found non numeric ids:")
                debug_print(list(idmap.keys()))
            return idmap
Example #51
0
def convert_kobo_date(kobo_date):
    """
    KoBo stores dates as a timestamp string. The exact format has changed with firmware
    and what part of the firmware writes it. The following is overkill, but it handles all 
    the formats I have seen.
    """
    from calibre.utils.date import utc_tz, local_tz
    from calibre.devices.usbms.driver import debug_print
    #     debug_print("convert_kobo_date - start - kobo_date={0}'".format(kobo_date))

    try:
        converted_date = datetime.datetime.strptime(kobo_date,
                                                    "%Y-%m-%dT%H:%M:%S+00:00")
#         debug_print("convert_kobo_date - '%Y-%m-%dT%H:%M:%S+00:00' - kobo_date=%s' - kobo_date={0}'".format(kobo_date))
    except Exception as e:
        #         debug_print("convert_kobo_date - exception={0}'".format(e))
        try:
            converted_date = datetime.datetime.strptime(
                kobo_date, "%Y-%m-%dT%H:%M:%SZ")
#             debug_print("convert_kobo_date - '%Y-%m-%dT%H:%M:%SZ' - kobo_date={0}'".format(kobo_date))
        except:
            try:
                converted_date = datetime.datetime.strptime(
                    kobo_date[0:19], "%Y-%m-%dT%H:%M:%S")
#                 debug_print("convert_kobo_date - '%Y-%m-%dT%H:%M:%S' - kobo_date={0}'".format(kobo_date))
            except:
                try:
                    converted_date = datetime.datetime.strptime(
                        kobo_date.split('+')[0], "%Y-%m-%dT%H:%M:%S")
#                     debug_print("convert_kobo_date - '%Y-%m-%dT%H:%M:%S' - kobo_date={0}'".format(kobo_date))
                except:
                    try:
                        converted_date = datetime.datetime.strptime(
                            kobo_date.split('+')[0], "%Y-%m-%d")
    #                     converted_date = converted_date.replace(tzinfo=utc_tz)
#                         debug_print("convert_kobo_date - '%Y-%m-%d' - kobo_date={0}'".format(kobo_date))
                    except:
                        try:
                            from calibre.utils.date import parse_date
                            converted_date = parse_date(
                                kobo_date)  #, assume_utc=True)
#                             debug_print("convert_kobo_date - parse_date - kobo_date={0}'".format(kobo_date))
                        except:
                            converted_date = time.gmtime()
                            debug_print(
                                "convert_kobo_date - could not convert, using current time - kobo_date={0}'"
                                .format(kobo_date))

    converted_date = converted_date.replace(tzinfo=utc_tz).astimezone(local_tz)
    return converted_date
Example #52
0
	def upload_books(self, files, names, on_card = None, end_session = True, metadata = None):
		opts = self.settings()
		skip_failed = opts.extra_customization[self.OPT_SKIP_FAILED]
		new_files = []
		new_names = []
		new_metadata = []
		errors = []
		if opts.extra_customization[self.OPT_EXTRA_FEATURES]:
			debug_print("KoboTouchExtended:upload_books:Enabling extra ePub features for Kobo devices")
			i = 0
			for file, n, mi in zip(files, names, metadata):
				self.report_progress(i / float(len(files)), "Processing book: {0} by {1}".format(mi.title, " and ".join(mi.authors)))
				ext = file[file.rfind('.'):]
				if ext == EPUB_EXT:
					try:
						self._modify_epub(file, mi)
					except Exception as e:
						(exc_type, exc_obj, exc_tb) = sys.exc_info()
						while exc_tb.tb_next and 'kobotouch_extended' in exc_tb.tb_next.tb_frame.f_code.co_filename:
							exc_tb = exc_tb.tb_next
						fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
						if not skip_failed:
							raise InvalidEPub(mi.title, " and ".join(mi.authors), e.message, fname = fname, lineno = exc_tb.tb_lineno)
						else:
							errors.append("Failed to upload {0} with error: {1}".format("'{0}' by '{1}'".format(mi.title, " and ".join(mi.authors)), e.message))
							if mi.uuid not in self.skip_renaming_files:
								self.skip_renaming_files.append(mi.uuid)
							debug_print("Failed to process {0} by {1} with error: {2} (file: {3}, lineno: {4})".format(mi.title, " and ".join(mi.authors), e.message, fname, exc_tb.tb_lineno))
					else:
						new_files.append(file)
						new_names.append(n)
						new_metadata.append(mi)
				else:
					new_files.append(file)
					new_names.append(n)
					new_metadata.append(mi)
				i += 1
		else:
			new_files = files
			new_names = names
			new_metadata = metadata

		if metadata and new_metadata and len(metadata) != len(new_metadata) and len(new_metadata) > 0:
			print("The following books could not be processed and will not be uploaded to your device:")
			print("\n".join(errors))

		self.report_progress(0, 'Working...')
		result = super(KOBOTOUCHEXTENDED, self).upload_books(new_files, new_names, on_card, end_session, new_metadata)

		return result
Example #53
0
    def filename_callback(self, path, mi):
        if self.extra_features:
            debug_print(
                "KoboTouchExtended:filename_callback:Path - {0}".format(path))
            if path.endswith(KEPUB_EXT):
                path += EPUB_EXT
            elif path.endswith(
                    EPUB_EXT) and mi.uuid not in self.skip_renaming_files:
                path = path[:-len(EPUB_EXT)] + KEPUB_EXT + EPUB_EXT

            debug_print(
                "KoboTouchExtended:filename_callback:New path - {0}".format(
                    path))
        return path
Example #54
0
        def ensure_numeric_ids(root):
            idmap = {}
            for x in root.xpath('child::*[@id]'):
                id_ = x.get('id')
                try:
                    id_ = int(id_)
                except:
                    x.set('id', '-1')
                    idmap[id_] = '-1'

            if DEBUG and idmap:
                debug_print('Found non numeric ids:')
                debug_print(list(idmap.keys()))
            return idmap
Example #55
0
    def filename_callback(self, path, mi):
        if self.extra_features:
            debug_print(
                "KoboTouchExtended:filename_callback:Path - {0}".format(path))
            if path.endswith(KEPUB_EXT):
                path += EPUB_EXT
            elif path.endswith(
                    EPUB_EXT) and mi.uuid not in self.skip_renaming_files:
                path = path[:-len(EPUB_EXT)] + KEPUB_EXT + EPUB_EXT

            debug_print(
                "KoboTouchExtended:filename_callback:New path - {0}".format(
                    path))
        return path
Example #56
0
    def __init__(self, prefix, lpath, title=None, authors=None, mime=None, date=None, ContentType=None,
                 thumbnail_name=None, size=None, other=None):
        from calibre.utils.date import parse_date
#         debug_print('Book::__init__ - title=', title)
        show_debug = title is not None and title.lower().find("xxxxx") >= 0
        if other is not None:
            other.title = title
            other.published_date = date
        if show_debug:
            debug_print("Book::__init__ - title=", title, 'authors=', authors)
            debug_print("Book::__init__ - other=", other)
        super(Book, self).__init__(prefix, lpath, size, other)

        if title is not None and len(title) > 0:
            self.title = title

        if authors is not None and len(authors) > 0:
            self.authors_from_string(authors)
            if self.author_sort is None or self.author_sort == "Unknown":
                self.author_sort = author_to_author_sort(authors)

        self.mime = mime

        self.size = size  # will be set later if None

        if ContentType == '6' and date is not None:
            try:
                self.datetime = time.strptime(date, "%Y-%m-%dT%H:%M:%S.%f")
            except:
                try:
                    self.datetime = time.strptime(date.split('+')[0], "%Y-%m-%dT%H:%M:%S")
                except:
                    try:
                        self.datetime = time.strptime(date.split('+')[0], "%Y-%m-%d")
                    except:
                        try:
                            self.datetime = parse_date(date,
                                    assume_utc=True).timetuple()
                        except:
                            try:
                                self.datetime = time.gmtime(os.path.getctime(self.path))
                            except:
                                self.datetime = time.gmtime()

        self.kobo_metadata = Metadata(title, self.authors)
        self.contentID          = None
        self.current_shelves    = []
        self.kobo_collections   = []
        self.can_put_on_shelves = True
        self.kobo_series        = None
        self.kobo_series_number = None  # Kobo stores the series number as string. And it can have a leading "#".
        self.kobo_series_id     = None
        self.kobo_subtitle      = None

        if thumbnail_name is not None:
            self.thumbnail = ImageWrapper(thumbnail_name)

        if show_debug:
            debug_print("Book::__init__ end - self=", self)
            debug_print("Book::__init__ end - title=", title, 'authors=', authors)
Example #57
0
    def update_device_database(self, booklist, collections_attributes, oncard):
        import sqlite3 as sqlite

        debug_print('PRST1: starting update_device_database')

        plugboard = None
        if self.plugboard_func:
            plugboard = self.plugboard_func(self.__class__.__name__,
                    'device_db', self.plugboards)
            debug_print("PRST1: Using Plugboard", plugboard)

        prefix = self._card_a_prefix if oncard == 'carda' else self._main_prefix
        if prefix is None:
            # Reader has no sd card inserted
            return
        source_id = 1 if oncard == 'carda' else 0

        dbpath = self.normalize_path(prefix + DBPATH)
        debug_print("SQLite DB Path: " + dbpath)

        collections = booklist.get_collections(collections_attributes)

        with closing(sqlite.connect(dbpath)) as connection:
            self.remove_orphaned_records(connection, dbpath)
            self.update_device_books(connection, booklist, source_id,
                    plugboard, dbpath)
            self.update_device_collections(connection, booklist, collections, source_id, dbpath)

        debug_print('PRST1: finished update_device_database')
Example #58
0
    def upload_cover(self, path, filename, metadata, filepath):
        import sqlite3 as sqlite

        debug_print('PRS-T1: uploading cover')

        if filepath.startswith(self._main_prefix):
            prefix = self._main_prefix
            source_id = 0
        else:
            prefix = self._card_a_prefix
            source_id = 1

        metadata.lpath = filepath.partition(prefix)[2]
        metadata.lpath = metadata.lpath.replace('\\', '/')
        dbpath = self.normalize_path(prefix + DBPATH)
        debug_print("SQLite DB Path: " + dbpath)

        with closing(sqlite.connect(dbpath)) as connection:
            cursor = connection.cursor()

            query = 'SELECT _id FROM books WHERE file_path = ?'
            t = (metadata.lpath,)
            cursor.execute(query, t)

            for i, row in enumerate(cursor):
                metadata.bookId = row[0]

            cursor.close()

            if getattr(metadata, 'bookId', None) is not None:
                debug_print('PRS-T1: refreshing cover for book being sent')
                self.upload_book_cover(connection, metadata, source_id)

        debug_print('PRS-T1: done uploading cover')
Example #59
0
    def remove_orphaned_records(self, connection, dbpath):
        from sqlite3 import DatabaseError

        try:
            cursor = connection.cursor()

            debug_print("Removing Orphaned Collection Records")

            # Purge any collections references that point into the abyss
            query = 'DELETE FROM collections WHERE content_id NOT IN (SELECT _id FROM books)'
            cursor.execute(query)
            query = 'DELETE FROM collections WHERE collection_id NOT IN (SELECT _id FROM collection)'
            cursor.execute(query)

            debug_print("Removing Orphaned Book Records")

            # Purge any references to books not in this database
            # Idea is to prevent any spill-over where these wind up applying to some other book
            query = 'DELETE FROM %s WHERE content_id NOT IN (SELECT _id FROM books)'
            cursor.execute(query % 'annotation')
            cursor.execute(query % 'bookmark')
            cursor.execute(query % 'current_position')
            cursor.execute(query % 'freehand')
            cursor.execute(query % 'history')
            cursor.execute(query % 'layout_cache')
            cursor.execute(query % 'preference')

            cursor.close()
        except DatabaseError:
            import traceback
            tb = traceback.format_exc()
            raise DeviceError((
                ('The SONY database is corrupted. '
                 ' Delete the file %s on your reader and then disconnect '
                 ' reconnect it. If you are using an SD card, you '
                 ' should delete the file on the card as well. Note that '
                 ' deleting this file will cause your reader to forget '
                 ' any notes/highlights, etc.') % dbpath) +
                              ' Underlying error:'
                              '\n' + tb)

        def get_lastrowid(self, cursor):
            # SQLite3 + Python has a fun issue on 32-bit systems with integer overflows.
            # Issue a SQL query instead, getting the value as a string, and then converting to a long python int manually.
            query = 'SELECT last_insert_rowid()'
            cursor.execute(query)
            row = cursor.fetchone()

            return long(row[0])
Example #60
0
    def upload_books(self,
                     files,
                     names,
                     on_card=None,
                     end_session=True,
                     metadata=None):
        opts = self.settings()
        if opts.extra_customization[self.OPT_MODIFY_CSS]:
            debug_print(
                "KoboTouchExtended:upload_books:Searching for device-specific CSS file"
            )
            device_css_file_name = self.KOBO_EXTRA_CSSFILE
            if self.isAuraHD():
                device_css_file_name = 'kobo_extra_AURAHD.css'
            elif self.isAura():
                device_css_file_name = 'kobo_extra_AURA.css'
            elif self.isGlo():
                device_css_file_name = 'kobo_extra_GLO.css'
            elif self.isMini():
                device_css_file_name = 'kobo_extra_MINI.css'
            elif self.isTouch():
                device_css_file_name = 'kobo_extra_TOUCH.css'
            device_css_file_name = os.path.join(self.configdir,
                                                device_css_file_name)
            if os.path.isfile(device_css_file_name):
                debug_print(
                    "KoboTouchExtended:upload_books:Found device-specific file {0}"
                    .format(device_css_file_name))
                shutil.copy(
                    device_css_file_name,
                    os.path.join(self._main_prefix, self.KOBO_EXTRA_CSSFILE))
            else:
                debug_print(
                    "KoboTouchExtended:upload_books:No device-specific CSS file found (expecting {0})"
                    .format(device_css_file_name))

        kobo_config_file = os.path.join(self._main_prefix, '.kobo', 'Kobo',
                                        'Kobo eReader.conf')
        if os.path.isfile(kobo_config_file):
            cfg = SafeConfigParser(allow_no_value=True)
            cfg.optionxform = str
            cfg.read(kobo_config_file)

            if not cfg.has_section("FeatureSettings"):
                cfg.add_section("FeatureSettings")
            debug_print(
                "KoboTouchExtended:upload_books:Setting FeatureSettings.FullBookPageNumbers to {0}"
                .format("true" if opts.extra_customization[
                    self.OPT_FULL_PAGE_NUMBERS] else "false"))
            cfg.set(
                "FeatureSettings", "FullBookPageNumbers",
                "true" if opts.extra_customization[self.OPT_FULL_PAGE_NUMBERS]
                else "false")
            with open(kobo_config_file, 'wb') as cfgfile:
                cfg.write(cfgfile)

        return super(KOBOTOUCHEXTENDED,
                     self).upload_books(files, names, on_card, end_session,
                                        metadata)