1# -*- coding: utf-8 -*- 2 3 4__license__ = 'GPL v3' 5__copyright__ = '2011, John Schember <john@nachtimwald.com>' 6 7''' 8Read meta information from extZ (TXTZ, HTMLZ...) files. 9''' 10 11import io 12import os 13 14from calibre.ebooks.metadata import MetaInformation 15from calibre.ebooks.metadata.opf2 import OPF 16from calibre.ptempfile import PersistentTemporaryFile 17from calibre.utils.zipfile import ZipFile, safe_replace 18 19 20def get_metadata(stream, extract_cover=True): 21 ''' 22 Return metadata as a L{MetaInfo} object 23 ''' 24 mi = MetaInformation(_('Unknown'), [_('Unknown')]) 25 stream.seek(0) 26 try: 27 with ZipFile(stream) as zf: 28 opf_name = get_first_opf_name(zf) 29 opf_stream = io.BytesIO(zf.read(opf_name)) 30 opf = OPF(opf_stream) 31 mi = opf.to_book_metadata() 32 if extract_cover: 33 cover_href = opf.raster_cover 34 if not cover_href: 35 for meta in opf.metadata.xpath('//*[local-name()="meta" and @name="cover"]'): 36 val = meta.get('content') 37 if val.rpartition('.')[2].lower() in {'jpeg', 'jpg', 'png'}: 38 cover_href = val 39 break 40 if cover_href: 41 try: 42 mi.cover_data = (os.path.splitext(cover_href)[1], zf.read(cover_href)) 43 except Exception: 44 pass 45 except Exception: 46 return mi 47 return mi 48 49 50def set_metadata(stream, mi): 51 replacements = {} 52 53 # Get the OPF in the archive. 54 with ZipFile(stream) as zf: 55 opf_path = get_first_opf_name(zf) 56 opf_stream = io.BytesIO(zf.read(opf_path)) 57 opf = OPF(opf_stream) 58 59 # Cover. 60 new_cdata = None 61 try: 62 new_cdata = mi.cover_data[1] 63 if not new_cdata: 64 raise Exception('no cover') 65 except: 66 try: 67 with open(mi.cover, 'rb') as f: 68 new_cdata = f.read() 69 except: 70 pass 71 if new_cdata: 72 cpath = opf.raster_cover 73 if not cpath: 74 cpath = 'cover.jpg' 75 new_cover = _write_new_cover(new_cdata, cpath) 76 replacements[cpath] = open(new_cover.name, 'rb') 77 mi.cover = cpath 78 79 # Update the metadata. 80 opf.smart_update(mi, replace_metadata=True) 81 newopf = io.BytesIO(opf.render()) 82 safe_replace(stream, opf_path, newopf, extra_replacements=replacements, add_missing=True) 83 84 # Cleanup temporary files. 85 try: 86 if cpath is not None: 87 replacements[cpath].close() 88 os.remove(replacements[cpath].name) 89 except: 90 pass 91 92 93def get_first_opf_name(zf): 94 names = zf.namelist() 95 opfs = [] 96 for n in names: 97 if n.endswith('.opf') and '/' not in n: 98 opfs.append(n) 99 if not opfs: 100 raise Exception('No OPF found') 101 opfs.sort() 102 return opfs[0] 103 104 105def _write_new_cover(new_cdata, cpath): 106 from calibre.utils.img import save_cover_data_to 107 new_cover = PersistentTemporaryFile(suffix=os.path.splitext(cpath)[1]) 108 new_cover.close() 109 save_cover_data_to(new_cdata, new_cover.name) 110 return new_cover 111