home *** CD-ROM | disk | FTP | other *** search
Wrap
# Source Generated with Decompyle++ # File: in.pyc (Python 2.6) __license__ = 'GPL v3' __copyright__ = '2010, Timothy Legge <timlegge at gmail.com> and Kovid Goyal <kovid@kovidgoyal.net>' __docformat__ = 'restructuredtext en' import os import time import sqlite3 as sqlite from calibre.devices.usbms.books import BookList from calibre.devices.kobo.books import Book from calibre.devices.kobo.books import ImageWrapper from calibre.devices.mime import mime_type_ext from calibre.devices.usbms.driver import USBMS, debug_print from calibre import prints from calibre.devices.usbms.books import CollectionsBookList class KOBO(USBMS): name = 'Kobo Reader Device Interface' gui_name = 'Kobo Reader' description = _('Communicate with the Kobo Reader') author = 'Timothy Legge and Kovid Goyal' version = (1, 0, 7) dbversion = 0 supported_platforms = [ 'windows', 'osx', 'linux'] booklist_class = CollectionsBookList FORMATS = [ 'epub', 'pdf'] CAN_SET_METADATA = [ 'collections'] VENDOR_ID = [ 8759] PRODUCT_ID = [ 16737] BCD = [ 272] VENDOR_NAME = [ 'KOBO_INC', 'KOBO'] WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = [ '.KOBOEREADER', 'EREADER'] EBOOK_DIR_MAIN = '' SUPPORTS_SUB_DIRS = True VIRTUAL_BOOK_EXTENSIONS = frozenset([ 'kobo']) EXTRA_CUSTOMIZATION_MESSAGE = _('The Kobo supports only one collection currently: the "Im_Reading" list. Create a tag called "Im_Reading" ') + 'for automatic management' EXTRA_CUSTOMIZATION_DEFAULT = ', '.join([ 'tags']) def initialize(self): USBMS.initialize(self) self.book_class = Book def books(self, oncard = None, end_session = True): path_to_ext = path_to_ext import calibre.ebooks.metadata.meta dummy_bl = BookList(None, None, None) if oncard == 'carda' and not (self._card_a_prefix): self.report_progress(1, _('Getting list of books on device...')) return dummy_bl if oncard == 'cardb' and not (self._card_b_prefix): self.report_progress(1, _('Getting list of books on device...')) return dummy_bl if oncard and oncard != 'carda' and oncard != 'cardb': self.report_progress(1, _('Getting list of books on device...')) return dummy_bl if oncard == 'carda': pass elif oncard == 'cardb': pass prefix = self._main_prefix self.booklist_class.rebuild_collections = self.rebuild_collections bl = self.booklist_class(oncard, prefix, self.settings) need_sync = self.parse_metadata_cache(bl, prefix, self.METADATA_CACHE) bl_cache = { } for idx, b in enumerate(bl): bl_cache[b.lpath] = idx def update_booklist(prefix, path, title, authors, mime, date, ContentType, ImageID, readstatus): changed = False try: lpath = path.partition(self.normalize_path(prefix))[2] if lpath.startswith(os.sep): lpath = lpath[len(os.sep):] lpath = lpath.replace('\\', '/') playlist_map = { } if readstatus == 1: playlist_map[lpath] = 'Im_Reading' elif readstatus == 2: playlist_map[lpath] = 'Read' path = self.normalize_path(path) idx = bl_cache.get(lpath, None) if idx is not None: bl_cache[lpath] = None if ImageID is not None: imagename = self.normalize_path(self._main_prefix + '.kobo/images/' + ImageID + ' - NickelBookCover.parsed') if imagename is not None: bl[idx].thumbnail = ImageWrapper(imagename) if ContentType != '6' or self.dbversion < 8 or self.dbversion >= 8: if self.update_metadata_item(bl[idx]): changed = True if lpath in playlist_map and playlist_map[lpath] not in bl[idx].device_collections: bl[idx].device_collections.append(playlist_map[lpath]) elif ContentType == '6' and self.dbversion < 8: book = Book(prefix, lpath, title, authors, mime, date, ContentType, ImageID, size = 1048576) else: try: book = self.book_from_path(prefix, lpath, title, authors, mime, date, ContentType, ImageID) except: debug_print('prefix: ', prefix, 'lpath: ', lpath, 'title: ', title, 'authors: ', authors, 'mime: ', mime, 'date: ', date, 'ContentType: ', ContentType, 'ImageID: ', ImageID) raise book.device_collections = None if lpath in playlist_map else [] if bl.add_book(book, replace_metadata = False): changed = True except: import traceback traceback.print_exc() return changed connection = sqlite.connect(self._main_prefix + '.kobo/KoboReader.sqlite') cursor = connection.cursor() cursor.execute('select version from dbversion') result = cursor.fetchone() self.dbversion = result[0] query = 'select Title, Attribution, DateCreated, ContentID, MimeType, ContentType, ImageID, ReadStatus from content where BookID is Null' cursor.execute(query) changed = False for i, row in enumerate(cursor): path = self.path_from_contentid(row[3], row[5], oncard) mime = (not (self._card_a_prefix), not (self._card_b_prefix), oncard != 'cardb') if path.find('kepub') == -1 else 'application/epub+zip' if oncard != 'carda' and oncard != 'cardb' and not row[3].startswith('file:///mnt/sd/'): changed = update_booklist(self._main_prefix, path, row[0], row[1], mime, row[2], row[5], row[6], row[7]) elif oncard == 'carda' and row[3].startswith('file:///mnt/sd/'): changed = update_booklist(self._card_a_prefix, path, row[0], row[1], mime, row[2], row[5], row[6], row[7]) if changed: need_sync = True continue cursor.close() connection.close() for idx in sorted(bl_cache.itervalues(), reverse = True): if idx is not None: need_sync = True del bl[idx] continue if need_sync: if oncard == 'cardb': self.sync_booklists((None, None, bl)) elif oncard == 'carda': self.sync_booklists((None, bl, None)) else: self.sync_booklists((bl, None, None)) self.report_progress(1, _('Getting list of books on device...')) return bl def delete_via_sql(self, ContentID, ContentType): connection = sqlite.connect(self._main_prefix + '.kobo/KoboReader.sqlite') cursor = connection.cursor() t = (ContentID,) cursor.execute('select ImageID from content where ContentID = ?', t) ImageID = None for row in cursor: ImageID = row[0] cursor.close() cursor = connection.cursor() if ContentType == 6 and self.dbversion < 8: cursor.execute('delete from shortcover_page where shortcoverid in (select ContentID from content where BookID = ?)', t) cursor.execute('delete from volume_shortcovers where volumeid = ?', t) t = (ContentID, ContentID) cursor.execute('delete from content where BookID = ? or ContentID = ?', t) connection.commit() cursor.close() if ImageID == None: print 'Error condition ImageID was not found' print 'You likely tried to delete a book that the kobo has not yet added to the database' connection.close() return ImageID def delete_images(self, ImageID): if ImageID != None: path_prefix = '.kobo/images/' path = self._main_prefix + path_prefix + ImageID file_endings = (' - iPhoneThumbnail.parsed', ' - bbMediumGridList.parsed', ' - NickelBookCover.parsed') for ending in file_endings: fpath = path + ending fpath = self.normalize_path(fpath) if os.path.exists(fpath): os.unlink(fpath) continue def delete_books(self, paths, end_session = True): for i, path in enumerate(paths): self.report_progress((i + 1) / float(len(paths)), _('Removing books from device...')) path = self.normalize_path(path) extension = os.path.splitext(path)[1] ContentType = None if extension != '' else self.get_content_type_from_path(path) ContentID = self.contentid_from_path(path, ContentType) ImageID = self.delete_via_sql(ContentID, ContentType) self.delete_images(ImageID) if os.path.exists(path): os.unlink(path) filepath = os.path.splitext(path)[0] for ext in self.DELETE_EXTS: if os.path.exists(filepath + ext): os.unlink(filepath + ext) if os.path.exists(path + ext): os.unlink(path + ext) continue if self.SUPPORTS_SUB_DIRS: try: os.removedirs(os.path.dirname(path)) self.SUPPORTS_SUB_DIRS self.report_progress(1, _('Removing books from device...')) def remove_books_from_metadata(self, paths, booklists): for i, path in enumerate(paths): self.report_progress((i + 1) / float(len(paths)), _('Removing books from device metadata listing...')) for bl in booklists: for book in bl: if path.endswith(book.path): bl.remove_book(book) continue self.report_progress(1, _('Removing books from device metadata listing...')) def add_books_to_metadata(self, locations, metadata, booklists): metadata = iter(metadata) for i, location in enumerate(locations): self.report_progress((i + 1) / float(len(locations)), _('Adding books to device metadata listing...')) info = metadata.next() if location[1] == 'cardb': pass elif location[1] == 'carda': pass blist = 0 path = self.normalize_path(location[0]) if self._main_prefix: prefix = None if path.startswith(self.normalize_path(self._main_prefix)) else None if not prefix and self._card_a_prefix: prefix = None if path.startswith(self.normalize_path(self._card_a_prefix)) else None if not prefix and self._card_b_prefix: prefix = None if path.startswith(self.normalize_path(self._card_b_prefix)) else None if prefix is None: prints('in add_books_to_metadata. Prefix is None!', path, self._main_prefix) continue lpath = path.partition(prefix)[2] if lpath.startswith('/') or lpath.startswith('\\'): lpath = lpath[1:] book = Book(prefix, lpath, '', '', '', '', '', '', other = info) if book.size is None: book.size = os.stat(self.normalize_path(path)).st_size b = booklists[blist].add_book(book, replace_metadata = True) if b: b._new_book = True continue self.report_progress(1, _('Adding books to device metadata listing...')) def contentid_from_path(self, path, ContentType): if ContentType == 6: if self.dbversion < 8: ContentID = os.path.splitext(path)[0] ContentID = ContentID.replace(self._main_prefix, '') else: ContentID = path ContentID = ContentID.replace(self._main_prefix + '.kobo/kepub/', '') if self._card_a_prefix is not None: ContentID = ContentID.replace(self._card_a_prefix, '') elif ContentType == 999: ContentID = path ContentID = ContentID.replace(self._main_prefix, '/mnt/onboard/') if self._card_a_prefix is not None: ContentID = ContentID.replace(self._card_a_prefix, '/mnt/sd/') else: ContentID = path ContentID = ContentID.replace(self._main_prefix, 'file:///mnt/onboard/') if self._card_a_prefix is not None: ContentID = ContentID.replace(self._card_a_prefix, 'file:///mnt/sd/') ContentID = ContentID.replace('\\', '/') return ContentID def get_content_type_from_path(self, path): if path.find('kepub') >= 0: ContentType = 6 return ContentType def get_content_type_from_extension(self, extension): if extension == '.kobo': ContentType = 6 elif extension == '.pdf' or extension == '.epub': ContentType = 16 else: ContentType = 999 return ContentType def path_from_contentid(self, ContentID, ContentType, oncard): path = ContentID if oncard == 'cardb': print 'path from_contentid cardb' elif oncard == 'carda': path = path.replace('file:///mnt/sd/', self._card_a_prefix) elif ContentType == '6' and self.dbversion < 8: path = self._main_prefix + path + '.kobo' elif (ContentType == '6' or ContentType == '10') and self.dbversion >= 8: path = self._main_prefix + '.kobo/kepub/' + path else: path = path.replace('file:///mnt/onboard/', self._main_prefix) path = path.replace('/mnt/onboard/', self._main_prefix) return path def get_file(self, path, *args, **kwargs): tpath = self.munge_path(path) extension = os.path.splitext(tpath)[1] if extension == '.kobo': UserFeedback = UserFeedback import calibre.devices.errors raise UserFeedback(_('Not Implemented'), _('".kobo" files do not exist on the device as books instead, they are rows in the sqlite database. Currently they cannot be exported or viewed.'), UserFeedback.WARN) extension == '.kobo' return USBMS.get_file(self, path, *args, **kwargs) def book_from_path(cls, prefix, lpath, title, authors, mime, date, ContentType, ImageID): MetaInformation = MetaInformation import calibre.ebooks.metadata if cls.settings().read_metadata or cls.MUST_READ_METADATA: mi = cls.metadata_from_path(cls.normalize_path(os.path.join(prefix, lpath))) else: metadata_from_filename = metadata_from_filename import calibre.ebooks.metadata.meta mi = metadata_from_filename(cls.normalize_path(os.path.basename(lpath)), cls.build_template_regexp()) if mi is None: mi = MetaInformation(os.path.splitext(os.path.basename(lpath))[0], [ _('Unknown')]) size = os.stat(cls.normalize_path(os.path.join(prefix, lpath))).st_size book = Book(prefix, lpath, title, authors, mime, date, ContentType, ImageID, size = size, other = mi) return book book_from_path = classmethod(book_from_path) def get_device_paths(self): paths = { } prefixes = { } for prefix, path, source_id in [ ('main', 'metadata.calibre', 0), ('card_a', 'metadata.calibre', 1), ('card_b', 'metadata.calibre', 2)]: prefix = getattr(self, '_%s_prefix' % prefix) if prefix is not None and os.path.exists(prefix): paths[source_id] = os.path.join(prefix, *path.split('/')) continue return paths def update_device_database_collections(self, booklists, collections_attributes, oncard): collections_attributes = [ 'tags'] collections = booklists.get_collections(collections_attributes) connection = sqlite.connect(self._main_prefix + '.kobo/KoboReader.sqlite') cursor = connection.cursor() if collections: for category, books in collections.items(): if category == 'Im_Reading': if oncard == 'carda': query = "update content set ReadStatus=0, FirstTimeReading = 'true' where BookID is Null and ReadStatus = 1 and ContentID like 'file:///mnt/sd/%'" elif oncard != 'carda' and oncard != 'cardb': query = "update content set ReadStatus=0, FirstTimeReading = 'true' where BookID is Null and ReadStatus = 1 and ContentID not like 'file:///mnt/sd/%'" try: cursor.execute(query) except: debug_print('Database Exception: Unable to reset Im_Reading list') raise connection.commit() for book in books: book.device_collections = [ 'Im_Reading'] extension = os.path.splitext(book.path)[1] ContentType = None if extension != '' else self.get_content_type_from_path(book.path) ContentID = self.contentid_from_path(book.path, ContentType) datelastread = time.strftime('%Y-%m-%dT%H:%M:%S', time.gmtime()) t = (datelastread, ContentID) try: cursor.execute("update content set ReadStatus=1,FirstTimeReading='false',DateLastRead=? where BookID is Null and ContentID = ?", t) except: debug_print('Database Exception: Unable create Im_Reading list') raise continue connection.commit() if category == 'Read': if oncard == 'carda': query = "update content set ReadStatus=0, FirstTimeReading = 'true' where BookID is Null and ReadStatus = 2 and ContentID like 'file:///mnt/sd/%'" elif oncard != 'carda' and oncard != 'cardb': query = "update content set ReadStatus=0, FirstTimeReading = 'true' where BookID is Null and ReadStatus = 2 and ContentID not like 'file:///mnt/sd/%'" try: cursor.execute(query) except: debug_print('Database Exception: Unable to reset Im_Reading list') raise connection.commit() for book in books: book.device_collections = [ 'Read'] extension = os.path.splitext(book.path)[1] ContentType = None if extension != '' else self.get_content_type_from_path(book.path) ContentID = self.contentid_from_path(book.path, ContentType) t = (ContentID,) try: cursor.execute("update content set ReadStatus=2,FirstTimeReading='true' where BookID is Null and ContentID = ?", t) except: debug_print('Database Exception: Unable set book as Rinished') raise continue connection.commit() else: print 'Reseting ReadStatus to 0' if oncard == 'carda': query = "update content set ReadStatus=0, FirstTimeReading = 'true' where BookID is Null and ContentID like 'file:///mnt/sd/%'" elif oncard != 'carda' and oncard != 'cardb': query = "update content set ReadStatus=0, FirstTimeReading = 'true' where BookID is Null and ContentID not like 'file:///mnt/sd/%'" try: cursor.execute(query) except: debug_print('Database Exception: Unable to reset Im_Reading list') raise connection.commit() cursor.close() connection.close() def sync_booklists(self, booklists, end_session = True): paths = self.get_device_paths() blists = { } for i in paths: if booklists[i] is not None: blists[i] = booklists[i] continue opts = self.settings() for i, blist in blists.items(): if i == 0: oncard = 'main' else: oncard = 'carda' self.update_device_database_collections(blist, collections, oncard) USBMS.sync_booklists(self, booklists, end_session = end_session) def rebuild_collections(self, booklist, oncard): collections_attributes = [] self.update_device_database_collections(booklist, collections_attributes, oncard)