def delete_single_book(self, path): try: tp = self.thumbpath_from_filepath(path) if tp: try: os.remove(tp) except EnvironmentError as err: if err.errno != errno.ENOENT: prints(u'Failed to delete thumbnail for {!r} at {!r} with error: {}'.format(path, tp, err)) except Exception: import traceback traceback.print_exc() USBMS.delete_single_book(self, path)
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
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
def sync_booklists(self, booklists, end_session=True): debug_print('PRS505: started sync_booklists') c = self.initialize_XML_cache() blists = {} for i in c.paths: try: if booklists[i] is not None: blists[i] = booklists[i] except IndexError: pass opts = self.settings() if opts.extra_customization: collections = [ x.strip() for x in opts.extra_customization[ self.OPT_COLLECTIONS].split(',') ] else: collections = [] debug_print('PRS505: collection fields:', collections) pb = None if self.plugboard_func: pb = self.plugboard_func(self.__class__.__name__, 'device_db', self.plugboards) debug_print('PRS505: use plugboards', pb) c.update(blists, collections, pb) c.write() if opts.extra_customization[self.OPT_REFRESH_COVERS]: debug_print('PRS505: uploading covers in sync_booklists') for idx, bl in blists.items(): prefix = self._card_a_prefix if idx == 1 else \ self._card_b_prefix if idx == 2 \ else self._main_prefix for book in bl: try: p = os.path.join(prefix, book.lpath) self._upload_cover( os.path.dirname(p), os.path.splitext(os.path.basename(p))[0], book, p) except: debug_print('FAILED to upload cover', prefix, book.lpath) else: debug_print('PRS505: NOT uploading covers in sync_booklists') USBMS.sync_booklists(self, booklists, end_session=end_session) debug_print('PRS505: finished sync_booklists')
def sync_booklists(self, booklists, end_session=True): debug_print("PRST1: starting sync_booklists") opts = self.settings() if opts.extra_customization: collections = [x.strip() for x in opts.extra_customization[self.OPT_COLLECTIONS].split(",")] else: collections = [] debug_print("PRST1: collection fields:", collections) if booklists[0] is not None: self.update_device_database(booklists[0], collections, None) if len(booklists) > 1 and booklists[1] is not None: self.update_device_database(booklists[1], collections, "carda") USBMS.sync_booklists(self, booklists, end_session=end_session) debug_print("PRST1: finished sync_booklists")
def sync_booklists(self, booklists, end_session=True): debug_print('PRS505: started sync_booklists') c = self.initialize_XML_cache() blists = {} for i in c.paths: try: if booklists[i] is not None: blists[i] = booklists[i] except IndexError: pass opts = self.settings() if opts.extra_customization: collections = [x.strip() for x in opts.extra_customization[self.OPT_COLLECTIONS].split(',')] else: collections = [] debug_print('PRS505: collection fields:', collections) pb = None if self.plugboard_func: pb = self.plugboard_func(self.__class__.__name__, 'device_db', self.plugboards) debug_print('PRS505: use plugboards', pb) c.update(blists, collections, pb) c.write() if opts.extra_customization[self.OPT_REFRESH_COVERS]: debug_print('PRS505: uploading covers in sync_booklists') for idx,bl in blists.items(): prefix = self._card_a_prefix if idx == 1 else \ self._card_b_prefix if idx == 2 \ else self._main_prefix for book in bl: try: p = os.path.join(prefix, book.lpath) self._upload_cover(os.path.dirname(p), os.path.splitext(os.path.basename(p))[0], book, p) except: debug_print('FAILED to upload cover', prefix, book.lpath) else: debug_print('PRS505: NOT uploading covers in sync_booklists') USBMS.sync_booklists(self, booklists, end_session=end_session) debug_print('PRS505: finished sync_booklists')
def sync_booklists(self, booklists, end_session=True): debug_print('PRST1: starting sync_booklists') opts = self.settings() if opts.extra_customization: collections = [x.strip() for x in opts.extra_customization[self.OPT_COLLECTIONS].split(',')] else: collections = [] debug_print('PRST1: collection fields:', collections) if booklists[0] is not None: self.update_device_database(booklists[0], collections, None) if len(booklists) > 1 and booklists[1] is not None: self.update_device_database(booklists[1], collections, 'carda') USBMS.sync_booklists(self, booklists, end_session=end_session) debug_print('PRST1: finished sync_booklists')
def create_upload_path(self, path, mdata, fname, create_dirs=True): is_news = mdata.tags and _('News') in mdata.tags subdir = 'Magazines' if is_news else 'Books' path = os.path.join(path, subdir) return USBMS.create_upload_path(self, path, mdata, fname, create_dirs=create_dirs)
def delete_single_book(self, path): try: tp1 = self.thumbpath_from_filepath(path) if tp1: tp2 = os.path.join(self.amazon_cover_bug_cache_dir(), os.path.basename(tp1)) for tp in (tp1, tp2): try: os.remove(tp) except EnvironmentError as err: if err.errno != errno.ENOENT: prints( 'Failed to delete thumbnail for {!r} at {!r} with error: {}' .format(path, tp, err)) except Exception: import traceback traceback.print_exc() USBMS.delete_single_book(self, path)
def __init__(self, path): if not os.path.isdir(path): raise OSError('Path is not a folder') path = USBMS.normalize_path(path) if path.endswith(os.sep): self._main_prefix = path else: self._main_prefix = path + os.sep self.booklist_class = BookList self.is_connected = True
def __init__(self, path): if not os.path.isdir(path): raise IOError, 'Path is not a folder' path = USBMS.normalize_path(path) if path.endswith(os.sep): self._main_prefix = path else: self._main_prefix = path + os.sep self.booklist_class = BookList self.is_connected = True
def books(self, oncard=None, end_session=True): bl = USBMS.books(self, oncard=oncard, end_session=end_session) # Read collections information collections = os.path.join(self._main_prefix, 'system', 'collections.json') if os.access(collections, os.R_OK): try: self.kindle_update_booklist(bl, collections) except: import traceback traceback.print_exc() return bl
def delete_single_book(self, path): if path.replace('\\', '/').endswith('.sdr/assets/metadata.kfx'): kfx_path = get_kfx_path(path) if DEBUG: prints('Kindle driver: Attempting to delete kfx: %r -> %r' % (path, kfx_path)) if os.path.exists(kfx_path): os.unlink(kfx_path) sdr_path = kfx_path.rpartition('.')[0] + '.sdr' if os.path.exists(sdr_path): shutil.rmtree(sdr_path) try: os.removedirs(os.path.dirname(kfx_path)) except Exception: pass else: return USBMS.delete_single_book(self, path)
def delete_single_book(self, path): USBMS.delete_single_book(self, path)
def initialize(self): self.plugin_needs_delayed_initialization = True USBMS.initialize(self)
def books(self, oncard=None, end_session=True): import sqlite3 as sqlite dummy_bl = BookList(None, None, None) if ( (oncard == 'carda' and not self._card_a_prefix) or (oncard and oncard != 'carda') ): self.report_progress(1.0, _('Getting list of books on device...')) return dummy_bl prefix = self._card_a_prefix if oncard == 'carda' else self._main_prefix # Let parent driver get the books self.booklist_class.rebuild_collections = self.rebuild_collections bl = USBMS.books(self, oncard=oncard, end_session=end_session) dbpath = self.normalize_path(prefix + DBPATH) debug_print("SQLite DB Path: " + dbpath) with closing(sqlite.connect(dbpath)) as connection: # Replace undecodable characters in the db instead of erroring out connection.text_factory = lambda x: unicode(x, "utf-8", "replace") cursor = connection.cursor() # Query collections query = ''' SELECT books._id, collection.title FROM collections LEFT OUTER JOIN books LEFT OUTER JOIN collection WHERE collections.content_id = books._id AND collections.collection_id = collection._id ''' cursor.execute(query) bl_collections = {} for i, row in enumerate(cursor): bl_collections.setdefault(row[0], []) bl_collections[row[0]].append(row[1]) # collect information on offsets, but assume any # offset we already calculated is correct if self.device_offset is None: query = 'SELECT file_path, modified_date FROM books' cursor.execute(query) time_offsets = {} for i, row in enumerate(cursor): try: comp_date = int(os.path.getmtime(self.normalize_path(prefix + row[0])) * 1000); except (OSError, IOError, TypeError): # In case the db has incorrect path info continue device_date = int(row[1]); offset = device_date - comp_date time_offsets.setdefault(offset, 0) time_offsets[offset] = time_offsets[offset] + 1 try: device_offset = max(time_offsets,key = lambda a: time_offsets.get(a)) debug_print("Device Offset: %d ms"%device_offset) self.device_offset = device_offset except ValueError: debug_print("No Books To Detect Device Offset.") for idx, book in enumerate(bl): query = 'SELECT _id, thumbnail FROM books WHERE file_path = ?' t = (book.lpath,) cursor.execute (query, t) for i, row in enumerate(cursor): book.device_collections = bl_collections.get(row[0], None) thumbnail = row[1] if thumbnail is not None: thumbnail = self.normalize_path(prefix + thumbnail) book.thumbnail = ImageWrapper(thumbnail) cursor.close() return bl
def formats_to_scan_for(self): ans = USBMS.formats_to_scan_for(self) | {'azw3'} return ans
def books(self, oncard=None, end_session=True): import sqlite3 as sqlite dummy_bl = BookList(None, None, None) if ( (oncard == 'carda' and not self._card_a_prefix) or (oncard and oncard != 'carda') ): self.report_progress(1.0, _('Getting list of books on device...')) return dummy_bl prefix = self._card_a_prefix if oncard == 'carda' else self._main_prefix # Let parent driver get the books self.booklist_class.rebuild_collections = self.rebuild_collections bl = USBMS.books(self, oncard=oncard, end_session=end_session) dbpath = self.normalize_path(prefix + DBPATH) debug_print("SQLite DB Path: " + dbpath) with closing(sqlite.connect(dbpath)) as connection: # Replace undecodable characters in the db instead of erroring out connection.text_factory = lambda x: unicode(x, "utf-8", "replace") cursor = connection.cursor() # Query collections query = ''' SELECT books._id, collection.title FROM collections LEFT OUTER JOIN books LEFT OUTER JOIN collection WHERE collections.content_id = books._id AND collections.collection_id = collection._id ''' cursor.execute(query) bl_collections = {} for i, row in enumerate(cursor): bl_collections.setdefault(row[0], []) bl_collections[row[0]].append(row[1]) # collect information on offsets, but assume any # offset we already calculated is correct if self.device_offset is None: query = 'SELECT file_path, modified_date FROM books' cursor.execute(query) time_offsets = {} for i, row in enumerate(cursor): try: comp_date = int(os.path.getmtime(self.normalize_path(prefix + row[0])) * 1000) except (OSError, IOError, TypeError): # In case the db has incorrect path info continue device_date = int(row[1]) offset = device_date - comp_date time_offsets.setdefault(offset, 0) time_offsets[offset] = time_offsets[offset] + 1 try: device_offset = max(time_offsets, key=lambda a: time_offsets.get(a)) debug_print("Device Offset: %d ms"%device_offset) self.device_offset = device_offset except ValueError: debug_print("No Books To Detect Device Offset.") for idx, book in enumerate(bl): query = 'SELECT _id, thumbnail FROM books WHERE file_path = ?' t = (book.lpath,) cursor.execute(query, t) for i, row in enumerate(cursor): book.device_collections = bl_collections.get(row[0], None) thumbnail = row[1] if thumbnail is not None: thumbnail = self.normalize_path(prefix + thumbnail) book.thumbnail = ImageWrapper(thumbnail) cursor.close() return bl
def _fetch_annotations(self): self._log_location("Start!!!!") count_bookmark_query = (''' SELECT COUNT(*) AS num_bookmarks FROM Tags t WHERE TagID = 102 AND VAL IS NOT 'bookmark' AND ItemID IN (SELECT OID from Items WHERE State = 0) ''') books_metadata_query = (''' SELECT b.OID AS book_oid, i.format, p.Path || f.filename AS filepath, Title, Authors FROM Books b LEFT JOIN (SELECT MAX(OID), BookID, PathID, Name AS filename FROM Files GROUP BY BookID) f ON b.OID = f.BookID LEFT JOIN (SELECT ItemID, Val AS format FROM Tags WHERE TagID = 17) i ON b.OID = i.ItemID LEFT JOIN Paths p ON p.OID = PathID WHERE b.OID IN (SELECT DISTINCT ParentID FROM Items WHERE TypeID = 4 ORDER BY ParentID) GROUP BY b.OID ORDER BY b.OID ''') # For 104 (highlight_txt) either use json_extract(text), or import JSON using python # as notes edited in the PB notes app loose their Begin/End JSON fields. annotation_data_query = (''' SELECT i.OID AS item_oid, i.TimeAlt, t.TagID, t.Val FROM Items i LEFT JOIN Tags t ON i.OID=t.ItemID WHERE ParentID = ? AND State = 0 ORDER BY i.OID, t.TagID ''') def get_device_path_from_id(id_): paths = [] for x in ('memory', 'card_a', 'card_b'): x = getattr(self.opts.gui, x + '_view').model() paths += x.paths_for_db_ids(set([id_]), as_map=True)[id_] return paths[0].path if paths else None # Modified. def generate_annotation_paths(ids, mode="default"): path_map = {} for id in ids: fullpath = get_device_path_from_id(id) if not fullpath: continue if mode == "default": pbmainroot = "/mnt/ext1/" pbcardroot = "/mnt/ext2/" if self.device._main_prefix in fullpath: path_map[os.path.join( pbmainroot, os.path.relpath(fullpath, start=self.device._main_prefix))] = id elif self.device._card_a_prefix in fullpath: path_map[os.path.join( pbcardroot, os.path.relpath( fullpath, start=self.device._card_a_prefix))] = id elif fullpath.startswith(('/var/', '/tmp/')): continue else: self._log("Path not matched: %s" % (fullpath)) return path_map def generate_title_map(ids, db): title_map = {} for id in ids: title = db.get_metadata(id, index_is_id=True).title if title: title_map[title] = {} title_map[title]['book_id'] = id title_map[title]['authors'] = db.get_metadata( id, index_is_id=True).format_authors() else: continue return title_map # Get DB location (only stock or default profile) self._log("Getting DB location") locations = [ os.path.join(self.device._main_prefix, 'system/config/books.db'), os.path.join(self.device._main_prefix, 'system/profiles/default/config/books.db') ] paths = [path for path in locations if os.path.exists(path)] if paths: db_location = USBMS.normalize_path(paths[0]) else: self._log( "No DB found. Currently only supports default profiles, with DB based notes. Stopping" ) return # Borrowed from Kobo db = self.opts.gui.library_view.model().db if not self.onDeviceIds: self.onDeviceIds = set( db.search_getting_ids('ondevice:True', None, sort_results=False, use_virtual_library=False)) if len(self.onDeviceIds) == 0: return self._log("_fetch_annotations - onDeviceIds={0}".format( self.onDeviceIds)) path_map = generate_annotation_paths(self.onDeviceIds) title_map = generate_title_map(self.onDeviceIds, db) # Start fetching annotations from contextlib import closing import apsw with closing(apsw.Connection(db_location)) as connection: self.opts.pb.set_label(_("Fetch annotations from database")) #connection.setrowtrace(self.row_factory) cursor = connection.cursor() cursor.execute(count_bookmark_query) try: result = next(cursor) count_bookmarks = result[0] except StopIteration: count_bookmarks = 0 self._log( "_fetch_annotations - Total number of bookmarks={0}".format( count_bookmarks)) self._log("_fetch_annotations - About to get annotations") self._read_database_annotations(connection, books_metadata_query, annotation_data_query, path_map, title_map, fetchbookmarks=False) self._log("_fetch_annotations - Finished getting annotations") self._log_location("Finish!!!!")