1#!/usr/local/bin/python3.8 2# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai 3 4 5__license__ = 'GPL v3' 6__copyright__ = '2012, Kovid Goyal <kovid@kovidgoyal.net>' 7__docformat__ = 'restructuredtext en' 8 9import os, glob 10 11from calibre import CurrentDir 12from calibre.ebooks.mobi import MobiError 13from calibre.ebooks.mobi.reader.mobi6 import MobiReader 14from calibre.ebooks.mobi.reader.headers import MetadataHeader 15from calibre.utils.logging import default_log 16from calibre.ebooks import DRMError 17from calibre.ebooks.mobi.reader.mobi8 import Mobi8Reader 18from calibre.ebooks.conversion.plumber import Plumber, create_oebbook 19from calibre.customize.ui import (plugin_for_input_format, 20 plugin_for_output_format) 21from calibre.utils.ipc.simple_worker import fork_job 22 23 24class BadFormat(ValueError): 25 pass 26 27 28def do_explode(path, dest): 29 with open(path, 'rb') as stream: 30 mr = MobiReader(stream, default_log, None, None) 31 32 with CurrentDir(dest): 33 mr = Mobi8Reader(mr, default_log) 34 opf = os.path.abspath(mr()) 35 try: 36 os.remove('debug-raw.html') 37 except: 38 pass 39 40 return opf 41 42 43def explode(path, dest, question=lambda x:True): 44 with open(path, 'rb') as stream: 45 raw = stream.read(3) 46 stream.seek(0) 47 if raw == b'TPZ': 48 raise BadFormat(_('This is not a MOBI file. It is a Topaz file.')) 49 50 try: 51 header = MetadataHeader(stream, default_log) 52 except MobiError: 53 raise BadFormat(_('This is not a MOBI file.')) 54 55 if header.encryption_type != 0: 56 raise DRMError(_('This file is locked with DRM. It cannot be tweaked.')) 57 58 kf8_type = header.kf8_type 59 60 if kf8_type is None: 61 raise BadFormat(_('This MOBI file does not contain a KF8 format ' 62 'book. KF8 is the new format from Amazon. calibre can ' 63 'only tweak MOBI files that contain KF8 books. Older ' 64 'MOBI files without KF8 are not tweakable.')) 65 66 if kf8_type == 'joint': 67 if not question(_('This MOBI file contains both KF8 and ' 68 'older Mobi6 data. Tweaking it will remove the Mobi6 data, which ' 69 'means the file will not be usable on older Kindles. Are you ' 70 'sure?')): 71 return None 72 73 return fork_job('calibre.ebooks.mobi.tweak', 'do_explode', args=(path, 74 dest), no_output=True)['result'] 75 76 77def set_cover(oeb): 78 if 'cover' not in oeb.guide or oeb.metadata['cover']: 79 return 80 cover = oeb.guide['cover'] 81 if cover.href in oeb.manifest.hrefs: 82 item = oeb.manifest.hrefs[cover.href] 83 oeb.metadata.clear('cover') 84 oeb.metadata.add('cover', item.id) 85 86 87def do_rebuild(opf, dest_path): 88 plumber = Plumber(opf, dest_path, default_log) 89 plumber.setup_options() 90 inp = plugin_for_input_format('azw3') 91 outp = plugin_for_output_format('azw3') 92 93 plumber.opts.mobi_passthrough = True 94 oeb = create_oebbook(default_log, opf, plumber.opts) 95 set_cover(oeb) 96 outp.convert(oeb, dest_path, inp, plumber.opts, default_log) 97 98 99def rebuild(src_dir, dest_path): 100 opf = glob.glob(os.path.join(src_dir, '*.opf')) 101 if not opf: 102 raise ValueError('No OPF file found in %s'%src_dir) 103 opf = opf[0] 104 # For debugging, uncomment the following two lines 105 # def fork_job(a, b, args=None, no_output=True): 106 # do_rebuild(*args) 107 fork_job('calibre.ebooks.mobi.tweak', 'do_rebuild', args=(opf, dest_path), 108 no_output=True) 109 110