class OCFZipReader(OCFReader): def __init__(self, stream, mode='r', root=None): if isinstance(stream, (LocalZipFile, ZipFile)): self.archive = stream else: try: self.archive = ZipFile(stream, mode=mode) except BadZipfile: raise EPubException("not a ZIP .epub OCF container") self.root = root if self.root is None: name = getattr(stream, 'name', False) if name: self.root = os.path.abspath(os.path.dirname(name)) else: self.root = getcwd() super().__init__() def open(self, name): if isinstance(self.archive, LocalZipFile): return self.archive.open(name) return io.BytesIO(self.archive.read(name)) def read_bytes(self, name): return self.archive.read(name)
class OCFZipReader(OCFReader): def __init__(self, stream, mode='r', root=None): if isinstance(stream, (LocalZipFile, ZipFile)): self.archive = stream else: try: self.archive = ZipFile(stream, mode=mode) except BadZipfile: raise EPubException("not a ZIP .epub OCF container") self.root = root if self.root is None: name = getattr(stream, 'name', False) if name: self.root = os.path.abspath(os.path.dirname(name)) else: self.root = getcwd() super(OCFZipReader, self).__init__() def open(self, name, mode='r'): if isinstance(self.archive, LocalZipFile): return self.archive.open(name) return io.BytesIO(self.archive.read(name)) def read_bytes(self, name): return self.archive.read(name)
def get_comic_metadata_from_cbz(self): ''' Reads the comic metadata from the comic cbz file as comictagger metadata ''' self.make_temp_cbz_file() # open the zipfile zf = ZipFile(self.file) # get cix metadata for name in zf.namelist(): if name.lower() == "comicinfo.xml": self.cix_metadata = ComicInfoXml().metadataFromString( zf.read(name)) self.zipinfo = name if not prefs['auto_count_pages']: break elif prefs['auto_count_pages'] and name.lower().rpartition( '.')[-1] in IMG_EXTENSIONS: self.pages += 1 # get the cbi metadata if ComicBookInfo().validateString(zf.comment): self.cbi_metadata = ComicBookInfo().metadataFromString(zf.comment) zf.close() # get combined metadata self._get_combined_metadata()
def run(self, archive): from calibre.utils.zipfile import ZipFile is_rar = archive.lower().endswith('.rar') if is_rar: from calibre.utils.unrar import extract_member, names else: zf = ZipFile(archive, 'r') if is_rar: with open(archive, 'rb') as rf: fnames = list(names(rf)) else: fnames = zf.namelist() def fname_ok(fname): bn = os.path.basename(fname).lower() if bn == 'thumbs.db': return False if '.' not in bn: return False if bn.rpartition('.')[-1] in {'diz', 'nfo'}: return False if '__MACOSX' in fname.split('/'): return False return True fnames = list(filter(fname_ok, fnames)) if is_comic(fnames): ext = '.cbr' if is_rar else '.cbz' of = self.temporary_file('_archive_extract' + ext) with open(archive, 'rb') as f: of.write(f.read()) of.close() return of.name if len(fnames) > 1 or not fnames: return archive fname = fnames[0] ext = os.path.splitext(fname)[1][1:] if ext.lower() not in { 'lit', 'epub', 'mobi', 'prc', 'rtf', 'pdf', 'mp3', 'pdb', 'azw', 'azw1', 'azw3', 'fb2', 'docx', 'doc', 'odt' }: return archive of = self.temporary_file('_archive_extract.' + ext) with closing(of): if is_rar: with open(archive, 'rb') as f: data = extract_member(f, match=None, name=fname)[1] of.write(data) else: of.write(zf.read(fname)) return of.name
def run(self, archive): from calibre.utils.zipfile import ZipFile is_rar = archive.lower().endswith('.rar') if is_rar: from calibre.utils.unrar import extract_member, names else: zf = ZipFile(archive, 'r') if is_rar: with open(archive, 'rb') as rf: fnames = list(names(rf)) else: fnames = zf.namelist() def fname_ok(fname): bn = os.path.basename(fname).lower() if bn == 'thumbs.db': return False if '.' not in bn: return False if bn.rpartition('.')[-1] in {'diz', 'nfo'}: return False if '__MACOSX' in fname.split('/'): return False return True fnames = list(filter(fname_ok, fnames)) if is_comic(fnames): ext = '.cbr' if is_rar else '.cbz' of = self.temporary_file('_archive_extract'+ext) with open(archive, 'rb') as f: of.write(f.read()) of.close() return of.name if len(fnames) > 1 or not fnames: return archive fname = fnames[0] ext = os.path.splitext(fname)[1][1:] if ext.lower() not in { 'lit', 'epub', 'mobi', 'prc', 'rtf', 'pdf', 'mp3', 'pdb', 'azw', 'azw1', 'azw3', 'fb2', 'docx', 'doc', 'odt'}: return archive of = self.temporary_file('_archive_extract.'+ext) with closing(of): if is_rar: with open(archive, 'rb') as f: data = extract_member(f, match=None, name=fname)[1] of.write(data) else: of.write(zf.read(fname)) return of.name
class OCFZipReader(OCFReader): def __init__(self, stream, mode='r', root=None): try: self.archive = ZipFile(stream, mode=mode) except BadZipfile: raise EPubException("not a ZIP .epub OCF container") self.root = root if self.root is None: name = getattr(stream, 'name', False) if name: self.root = os.path.abspath(os.path.dirname(name)) else: self.root = os.getcwdu() super(OCFZipReader, self).__init__() def open(self, name, mode='r'): return StringIO(self.archive.read(name))
def run(self, archive): from calibre.utils.zipfile import ZipFile is_rar = archive.lower().endswith('.rar') if is_rar: from calibre.utils.unrar import extract_member, names else: zf = ZipFile(archive, 'r') if is_rar: with open(archive, 'rb') as rf: fnames = list(names(rf)) else: fnames = zf.namelist() fnames = [ x for x in fnames if '.' in x and x.lower().rpartition('/')[-1] != 'thumbs.db' ] if is_comic(fnames): ext = '.cbr' if is_rar else '.cbz' of = self.temporary_file('_archive_extract' + ext) with open(archive, 'rb') as f: of.write(f.read()) of.close() return of.name if len(fnames) > 1 or not fnames: return archive fname = fnames[0] ext = os.path.splitext(fname)[1][1:] if ext.lower() not in ('lit', 'epub', 'mobi', 'prc', 'rtf', 'pdf', 'mp3', 'pdb', 'azw', 'azw1', 'azw3', 'fb2'): return archive of = self.temporary_file('_archive_extract.' + ext) with closing(of): if is_rar: with open(archive, 'rb') as f: data = extract_member(f, match=None, name=fname)[1] of.write(data) else: of.write(zf.read(fname)) return of.name
def get_comic_metadata_from_cbz(self): ''' Reads the comic metadata from the comic cbz file as comictagger metadata ''' self.make_temp_cbz_file() # open the zipfile zf = ZipFile(self.file) # get cix metadata for name in zf.namelist(): if name.lower() == "comicinfo.xml": self.cix_metadata = ComicInfoXml().metadataFromString(zf.read(name)) self.zipinfo = name break # get the cbi metadata if ComicBookInfo().validateString(zf.comment): self.cbi_metadata = ComicBookInfo().metadataFromString(zf.comment) zf.close() # get combined metadata self._get_combined_metadata()
def run(self, archive): from calibre.utils.zipfile import ZipFile is_rar = archive.lower().endswith('.rar') if is_rar: from calibre.utils.unrar import extract_member, names else: zf = ZipFile(archive, 'r') if is_rar: with open(archive, 'rb') as rf: fnames = list(names(rf)) else: fnames = zf.namelist() fnames = [x for x in fnames if '.' in x and x.lower().rpartition('/')[-1] != 'thumbs.db'] if is_comic(fnames): ext = '.cbr' if is_rar else '.cbz' of = self.temporary_file('_archive_extract'+ext) with open(archive, 'rb') as f: of.write(f.read()) of.close() return of.name if len(fnames) > 1 or not fnames: return archive fname = fnames[0] ext = os.path.splitext(fname)[1][1:] if ext.lower() not in ('lit', 'epub', 'mobi', 'prc', 'rtf', 'pdf', 'mp3', 'pdb', 'azw', 'azw1', 'azw3', 'fb2'): return archive of = self.temporary_file('_archive_extract.'+ext) with closing(of): if is_rar: with open(archive, 'rb') as f: data = extract_member(f, match=None, name=fname)[1] of.write(data) else: of.write(zf.read(fname)) return of.name
def _get_epub_toc(self, cid=None, path=None, prepend_title=None): ''' Given a calibre id, return the epub TOC indexed by section If cid, use copy in library, else use path to copy on device ''' toc = None # if cid is not None: # mi = self.opts.gui.current_db.get_metadata(cid, index_is_id=True) # toc = None # if 'EPUB' in mi.formats: # fpath = self.opts.gui.current_db.format(cid, 'EPUB', index_is_id=True, as_path=True) # else: # return toc # elif path is not None: # fpath = path # else: # return toc fpath = path # iBooks stores books unzipped # Marvin stores books zipped # Need spine, ncx_tree to construct toc if self.ios.stat(fpath) and self.ios.stat( fpath)['st_ifmt'] == 'S_IFDIR': # Find the OPF in the unzipped ePub fp = '/'.join([fpath, 'META-INF', 'container.xml']) cf = io.BytesIO(self.ios.read(fp)) container = etree.parse(cf) opf_file = container.xpath('.//*[local-name()="rootfile"]')[0].get( 'full-path') oebps = opf_file.rpartition('/')[0] fp = '/'.join([fpath, opf_file]) opf = io.BytesIO(self.ios.read(fp)) opf_tree = etree.parse(opf) spine = opf_tree.xpath('.//*[local-name()="spine"]')[0] ncx_fs = spine.get('toc') manifest = opf_tree.xpath('.//*[local-name()="manifest"]')[0] ncx_file = manifest.find('.//*[@id="%s"]' % ncx_fs).get('href') fp = '/'.join([fpath, oebps, ncx_file]) ncxf = io.BytesIO(self.ios.read(fp)) ncx_tree = etree.parse(ncxf) #self._log(etree.tostring(ncx_tree, pretty_print=True)) else: # Find the OPF file in the zipped ePub zfo = io.BytesIO(self.ios.read(fpath, mode='rb')) try: zf = ZipFile(zfo, 'r') container = etree.fromstring(zf.read('META-INF/container.xml')) opf_tree = etree.fromstring( zf.read( container.xpath('.//*[local-name()="rootfile"]') [0].get('full-path'))) spine = opf_tree.xpath('.//*[local-name()="spine"]')[0] ncx_fs = spine.get('toc') manifest = opf_tree.xpath('.//*[local-name()="manifest"]')[0] ncx = manifest.find('.//*[@id="%s"]' % ncx_fs).get('href') # Find the ncx file fnames = zf.namelist() _ncx = [x for x in fnames if ncx in x][0] ncx_tree = etree.fromstring(zf.read(_ncx)) except: import traceback self._log_location() self._log(" unable to unzip '%s'" % fpath) self._log(traceback.format_exc()) return toc # fpath points to epub (zipped or unzipped dir) # spine, ncx_tree populated try: toc = OrderedDict() # 1. capture idrefs from spine for i, el in enumerate(spine): toc[str(i)] = el.get('idref') # 2. Resolve <spine> idrefs to <manifest> hrefs for el in toc: toc[el] = manifest.find('.//*[@id="%s"]' % toc[el]).get('href') # 3. Build a dict of src:toc_entry src_map = OrderedDict() navMap = ncx_tree.xpath('.//*[local-name()="navMap"]')[0] for navPoint in navMap: # Get the first-level entry src = re.sub( r'#.*$', '', navPoint.xpath('.//*[local-name()="content"]')[0].get( 'src')) toc_entry = navPoint.xpath('.//*[local-name()="text"]')[0].text src_map[src] = toc_entry # Get any nested navPoints nested_navPts = navPoint.xpath('.//*[local-name()="navPoint"]') for nnp in nested_navPts: src = re.sub( r'#.*$', '', nnp.xpath('.//*[local-name()="content"]')[0].get( 'src')) toc_entry = nnp.xpath('.//*[local-name()="text"]')[0].text src_map[src] = toc_entry # Resolve src paths to toc_entry for section in toc: if toc[section] in src_map: if prepend_title: toc[section] = "%s · %s" % ( prepend_title, src_map[toc[section]]) else: toc[section] = src_map[toc[section]] else: toc[section] = None # 5. Fill in the gaps current_toc_entry = None for section in toc: if toc[section] is None: toc[section] = current_toc_entry else: current_toc_entry = toc[section] except: import traceback self._log_location() self._log("{:~^80}".format(" error parsing '%s' " % fpath)) self._log(traceback.format_exc()) self._log("{:~^80}".format(" end traceback ")) return toc
def _generate_thumbnail(self, book): ''' Fetch the cover image, generate a thumbnail, cache Extracts covers from zipped epubs ''' self._log_location(book.title) #self._log("book_path: %s" % book.path) #self._log("book: '%s' by %s uuid: %s" % (book.title, book.author, book.uuid)) # Parse the cover from the connected device, model Fetch_Annotations:_get_epub_toc() thumb_data = None thumb_path = book.path.rpartition('.')[0] + '.jpg' # Try getting the cover from the cache try: zfr = ZipFile(self.archive_path) thumb_data = zfr.read(thumb_path) if thumb_data == 'None': self._log("returning None from cover cache") zfr.close() return None except: self._log("opening cover cache for appending") zfw = ZipFile(self.archive_path, mode='a') else: self._log("returning thumb from cover cache") return thumb_data # Get the cover from the book try: stream = cStringIO.StringIO(self.ios.read(book.path, mode='rb')) mi = get_metadata(stream) if mi.cover_data is not None: img_data = cStringIO.StringIO(mi.cover_data[1]) except: if self.verbose: self._log("ERROR: unable to get cover from '%s'" % book.title) import traceback #traceback.print_exc() exc_type, exc_value, exc_traceback = sys.exc_info() self._log(traceback.format_exception_only(exc_type, exc_value)[0].strip()) return thumb_data # Generate a thumb try: im = PILImage.open(img_data) scaled, width, height = fit_image(im.size[0], im.size[1], 60, 80) im = im.resize((int(width), int(height)), PILImage.ANTIALIAS) thumb = cStringIO.StringIO() im.convert('RGB').save(thumb, 'JPEG') thumb_data = thumb.getvalue() thumb.close() self._log("SUCCESS: generated thumb for '%s', caching" % book.title) # Cache the tagged thumb zfw.writestr(thumb_path, thumb_data) except: if self.verbose: self._log("ERROR generating thumb for '%s', caching empty marker" % book.title) import traceback exc_type, exc_value, exc_traceback = sys.exc_info() self._log(traceback.format_exception_only(exc_type, exc_value)[0].strip()) # Cache the empty cover zfw.writestr(thumb_path, 'None') finally: img_data.close() zfw.close() return thumb_data
def _get_epub_toc(self, cid=None, path=None, prepend_title=None): ''' Given a calibre id, return the epub TOC indexed by section If cid, use copy in library, else use path to copy on device ''' toc = None # if cid is not None: # mi = self.opts.gui.current_db.get_metadata(cid, index_is_id=True) # toc = None # if 'EPUB' in mi.formats: # fpath = self.opts.gui.current_db.format(cid, 'EPUB', index_is_id=True, as_path=True) # else: # return toc # elif path is not None: # fpath = path # else: # return toc fpath = path # iBooks stores books unzipped # Marvin stores books zipped # Need spine, ncx_tree to construct toc if self.ios.stat(fpath) and self.ios.stat(fpath)['st_ifmt'] == 'S_IFDIR': # Find the OPF in the unzipped ePub fp = '/'.join([fpath, 'META-INF', 'container.xml']) cf = cStringIO.StringIO(self.ios.read(fp)) container = etree.parse(cf) opf_file = container.xpath('.//*[local-name()="rootfile"]')[0].get('full-path') oebps = opf_file.rpartition('/')[0] fp = '/'.join([fpath, opf_file]) opf = cStringIO.StringIO(self.ios.read(fp)) opf_tree = etree.parse(opf) spine = opf_tree.xpath('.//*[local-name()="spine"]')[0] ncx_fs = spine.get('toc') manifest = opf_tree.xpath('.//*[local-name()="manifest"]')[0] ncx_file = manifest.find('.//*[@id="%s"]' % ncx_fs).get('href') fp = '/'.join([fpath, oebps, ncx_file]) ncxf = cStringIO.StringIO(self.ios.read(fp)) ncx_tree = etree.parse(ncxf) #self._log(etree.tostring(ncx_tree, pretty_print=True)) else: # Find the OPF file in the zipped ePub zfo = cStringIO.StringIO(self.ios.read(fpath, mode='rb')) try: zf = ZipFile(zfo, 'r') container = etree.fromstring(zf.read('META-INF/container.xml')) opf_tree = etree.fromstring(zf.read(container.xpath('.//*[local-name()="rootfile"]')[0].get('full-path'))) spine = opf_tree.xpath('.//*[local-name()="spine"]')[0] ncx_fs = spine.get('toc') manifest = opf_tree.xpath('.//*[local-name()="manifest"]')[0] ncx = manifest.find('.//*[@id="%s"]' % ncx_fs).get('href') # Find the ncx file fnames = zf.namelist() _ncx = [x for x in fnames if ncx in x][0] ncx_tree = etree.fromstring(zf.read(_ncx)) except: import traceback self._log_location() self._log(" unable to unzip '%s'" % fpath) self._log(traceback.format_exc()) return toc # fpath points to epub (zipped or unzipped dir) # spine, ncx_tree populated try: toc = OrderedDict() # 1. capture idrefs from spine for i, el in enumerate(spine): toc[str(i)] = el.get('idref') # 2. Resolve <spine> idrefs to <manifest> hrefs for el in toc: toc[el] = manifest.find('.//*[@id="%s"]' % toc[el]).get('href') # 3. Build a dict of src:toc_entry src_map = OrderedDict() navMap = ncx_tree.xpath('.//*[local-name()="navMap"]')[0] for navPoint in navMap: # Get the first-level entry src = re.sub(r'#.*$', '', navPoint.xpath('.//*[local-name()="content"]')[0].get('src')) toc_entry = navPoint.xpath('.//*[local-name()="text"]')[0].text src_map[src] = toc_entry # Get any nested navPoints nested_navPts = navPoint.xpath('.//*[local-name()="navPoint"]') for nnp in nested_navPts: src = re.sub(r'#.*$', '', nnp.xpath('.//*[local-name()="content"]')[0].get('src')) toc_entry = nnp.xpath('.//*[local-name()="text"]')[0].text src_map[src] = toc_entry # Resolve src paths to toc_entry for section in toc: if toc[section] in src_map: if prepend_title: toc[section] = "%s · %s" % (prepend_title, src_map[toc[section]]) else: toc[section] = src_map[toc[section]] else: toc[section] = None # 5. Fill in the gaps current_toc_entry = None for section in toc: if toc[section] is None: toc[section] = current_toc_entry else: current_toc_entry = toc[section] except: import traceback self._log_location() self._log("{:~^80}".format(" error parsing '%s' " % fpath)) self._log(traceback.format_exc()) self._log("{:~^80}".format(" end traceback ")) return toc
def _generate_thumbnail(self, book, cover_path): ''' Fetch the cover image, generate a thumbnail, cache Specific implementation for iBooks ''' self._log_location(book.title) self._log_diagnostic(" book_path: %s" % book.path) self._log_diagnostic("cover_path: %s" % repr(cover_path)) thumb_data = None thumb_path = book.path.rpartition('.')[0] + '.jpg' # Try getting the cover from the cache try: zfr = ZipFile(self.archive_path) thumb_data = zfr.read(thumb_path) if thumb_data == 'None': self._log_diagnostic("returning None from cover cache") zfr.close() return None except: self._log_diagnostic("opening cover cache for appending") zfw = ZipFile(self.archive_path, mode='a') else: self._log_diagnostic("returning thumb from cover cache") return thumb_data ''' # Is book.path a directory (iBooks) or an epub? stats = self.ios.stat(book.path) if stats['st_ifmt'] == 'S_IFDIR': # *** This needs to fetch the cover data from the directory *** self._log_diagnostic("returning None, can't read iBooks covers yet") return thumb_data # Get the cover from the book try: stream = cStringIO.StringIO(self.ios.read(book.path, mode='rb')) mi = get_metadata(stream) if mi.cover_data is not None: img_data = cStringIO.StringIO(mi.cover_data[1]) except: if self.verbose: self._log_diagnostic("ERROR: unable to get cover from '%s'" % book.title) import traceback #traceback.print_exc() exc_type, exc_value, exc_traceback = sys.exc_info() self._log_diagnostic(traceback.format_exception_only(exc_type, exc_value)[0].strip()) return thumb_data ''' try: img_data = cStringIO.StringIO(self.ios.read(cover_path, mode='rb')) except: if self.verbose: self._log_diagnostic("ERROR fetching cover data for '%s', caching empty marker" % book.title) import traceback exc_type, exc_value, exc_traceback = sys.exc_info() self._log_diagnostic(traceback.format_exception_only(exc_type, exc_value)[0].strip()) # Cache the empty cover zfw.writestr(thumb_path, 'None') return thumb_data # Generate a thumb try: im = PILImage.open(img_data) scaled, width, height = fit_image(im.size[0], im.size[1], 60, 80) im = im.resize((int(width), int(height)), PILImage.ANTIALIAS) thumb = cStringIO.StringIO() im.convert('RGB').save(thumb, 'JPEG') thumb_data = thumb.getvalue() thumb.close() self._log_diagnostic("SUCCESS: generated thumb for '%s', caching" % book.title) # Cache the tagged thumb zfw.writestr(thumb_path, thumb_data) except: if self.verbose: self._log_diagnostic("ERROR generating thumb for '%s', caching empty marker" % book.title) import traceback exc_type, exc_value, exc_traceback = sys.exc_info() self._log_diagnostic(traceback.format_exception_only(exc_type, exc_value)[0].strip()) # Cache the empty cover zfw.writestr(thumb_path, 'None') finally: #img_data.close() zfw.close() return thumb_data
def books(self, oncard=None, end_session=True): ''' Return a list of ebooks on the device. @param oncard: If 'carda' or 'cardb' return a list of ebooks on the specific storage card, otherwise return list of ebooks in main memory of device. If a card is specified and no books are on the card return empty list. @return: A BookList. ''' if oncard: return BookList() self._log_location() booklist = BookList() cached_books = {} # Fetch current assets from Media folder assets_profile = self._localize_database_path(self.assets_subpath) #Fetch current metadata from iBooks's DB db_profile = self._localize_database_path(self.books_subpath) con = sqlite3.connect(db_profile['path']) # Mount the Media folder self.ios.mount_ios_media_folder() # Get Books.plist so we can find the covers books_plist = {} if True: raw_plist = XmlPropertyListParser().parse(self.ios.read('/Books/Sync/Books.plist'))['Books'] for book in raw_plist: if not 'Path' in book: print(" No 'Path' element found for '%s' by '%s'" % (book['Name'], book['Artist'])) #print(book) #print continue if 'Cover Path' in book: books_plist['/'.join(['/Books', book['Path']])] = unicode('/'.join(['/Books', book['Path'], book['Cover Path']])) else: books_plist['/'.join(['/Books', book['Path']])] = unicode('/'.join(['/Books', 'Sync', 'Artwork', book['Persistent ID']])) # Process any outliers raw_plist = XmlPropertyListParser().parse(self.ios.read('/Books/Books.plist'))['Books'] for book in raw_plist: if not 'Path' in book: print(" No 'Path' element found for '%s' by '%s'" % (book['Name'], book['Artist'])) #print(book) #print continue # Don't overwrite existing cover_paths if not '/'.join(['/Books', book['Path']]) in books_plist: if 'Cover Path' in book and not ['/'.join(['/Books', book['Path']])] in book_plist: books_plist['/'.join(['/Books', book['Path']])] = unicode('/'.join(['/Books', book['Path'], book['Cover Path']])) else: books_plist['/'.join(['/Books', book['Path']])] = unicode('/'.join(['/Books', 'Sync', 'Artwork', book['Persistent ID']])) raw_plist = XmlPropertyListParser().parse(self.ios.read('/Books/Purchases/Purchases.plist'))['Books'] for book in raw_plist: if not 'Path' in book: print(" No 'Path' element found for '%s' by '%s'" % (book['Name'], book['Artist'])) print(book) print continue # Don't overwrite existing cover_paths if not '/'.join(['/Books', book['Path']]) in books_plist: if 'Cover Path' in book: books_plist['/'.join(['/Books/Purchases', book['Path']])] = unicode('/'.join(['/Books/Purchases', book['Path'], book['Cover Path']])) else: books_plist['/'.join(['/Books/Purchases', book['Path']])] = unicode('/'.join(['/Books', 'Sync', 'Artwork', book['Persistent ID']])) else: raw_plist = XmlPropertyListParser().parse(self.ios.read('/Books/Books.plist'))['Books'] for book in raw_plist: if not 'Path' in book: print(" No 'Path' element found for '%s' by '%s'" % (book['Name'], book['Artist'])) print(book) print continue if 'Cover Path' in book: books_plist['/'.join(['/Books', book['Path']])] = unicode('/'.join(['/Books', book['Path'], book['Cover Path']])) else: books_plist['/'.join(['/Books', book['Path']])] = unicode('/'.join(['/Books', 'Sync', 'Artwork', book['Persistent ID']])) raw_plist = XmlPropertyListParser().parse(self.ios.read('/Books/Purchases/Purchases.plist'))['Books'] for book in raw_plist: if not 'Path' in book: print(" No 'Path' element found for '%s' by '%s'" % (book['Name'], book['Artist'])) print(book) print continue if 'Cover Path' in book: books_plist['/'.join(['/Books/Purchases', book['Path']])] = unicode('/'.join(['/Books/Purchases', book['Path'], book['Cover Path']])) else: books_plist['/'.join(['/Books/Purchases', book['Path']])] = unicode('/'.join(['/Books', 'Sync', 'Artwork', book['Persistent ID']])) print(books_plist) with con: con.row_factory = sqlite3.Row # Build a collection map collections_map = {} # Get the books cur = con.cursor() #cur.execute("ATTACH DATABASE '{0}' as 'ASSETS'".format(assets_profile['path']) cur.execute('''SELECT ZASSETURL, ZBOOKAUTHOR, ZSORTAUTHOR, ZBOOKTITLE, ZSORTTITLE, ZDATABASEKEY, ZDATEADDED FROM ZBKBOOKINFO WHERE ZASSETURL LIKE 'file://localhost%' AND ZASSETURL LIKE '%.epub/' ''') rows = cur.fetchall() book_count = len(rows) for i, row in enumerate(rows): book_id = row[b'ZDATABASEKEY'] # Get the collection assignments collections = [] # Get the primary metadata this_book = Book(row[b'ZBOOKTITLE'], row[b'ZBOOKAUTHOR']) original_path = row[b'ZASSETURL'] path = original_path[original_path.find('Media/') + len('Media'):-1] this_book.path = path.replace('%20', ' ') timestamp = int(row[b'ZDATEADDED']) + NSTimeIntervalSince1970 this_book.datetime = datetime.fromtimestamp(timestamp).timetuple() this_book.device_collections = collections this_book.uuid = None this_book.thumbnail = self._generate_thumbnail(this_book, books_plist[this_book.path]) # Retrieve folder size from cache or compute and cache try: zfr = ZipFile(self.folder_archive_path) file_size = zfr.read(this_book.path) this_book.size = int(file_size) self._log_diagnostic("returning folder size from cache") except: self._log_diagnostic("opening folder cache for appending") zfw = ZipFile(self.folder_archive_path, mode='a') stats = self.ios.stat(this_book.path) this_book.size = self.ios.get_folder_size(this_book.path) zfw.writestr(this_book.path, str(this_book.size)) zfw.close() finally: zfr.close() booklist.add_book(this_book, False) if self.report_progress is not None: self.report_progress(float((i + 1)*100 / book_count)/100, '%(num)d of %(tot)d' % dict(num=i + 1, tot=book_count)) cached_books[this_book.path] = { 'title': this_book.title, 'author': this_book.author, 'authors': this_book.author.split(' & '), 'uuid': this_book.uuid } cur.close() # Close the connection self.ios.dismount_ios_media_folder() if self.report_progress is not None: self.report_progress(1.0, _('finished')) self.cached_books = cached_books return booklist