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