From 224921792edcad4735fb293094b4ef814c4ad0ea Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 21 Jul 2017 12:13:59 +0530 Subject: [PATCH] calibre-debug: Change the --explode-book action to only create the exploded directory and not rebuild the file. Add a new --implode-book action to rebuild the file, separately. --- src/calibre/debug.py | 26 +++++++---- src/calibre/ebooks/tweak.py | 90 +++++++++++++++++++++++++------------ 2 files changed, 79 insertions(+), 37 deletions(-) diff --git a/src/calibre/debug.py b/src/calibre/debug.py index fed94785c4..45abd38ffa 100644 --- a/src/calibre/debug.py +++ b/src/calibre/debug.py @@ -82,12 +82,17 @@ Everything after the -- is passed to the script. help=_('Inspect the MOBI file(s) at the specified path(s)')) parser.add_option('-t', '--edit-book', action='store_true', help=_('Launch the calibre "Edit book" tool in debug mode.')) - parser.add_option('-x', '--explode-book', default=None, - help=_('Explode the book (exports the book as a collection of HTML ' + parser.add_option('-x', '--explode-book', default=False, action='store_true', + help=_('Explode the book into the specified directory.\nUsage: ' + '-x file.epub output_dir\n' + 'Exports the book as a collection of HTML ' 'files and metadata, which you can edit using standard HTML ' - 'editing tools, and then rebuilds the file from the edited HTML. ' - 'Makes no additional changes to the HTML, unlike a full calibre ' - 'conversion).')) + 'editing tools. Works with EPUB, AZW3, HTMLZ and DOCX files.')) + parser.add_option('-i', '--implode-book', default=False, action='store_true', help=_( + 'Implode a previously exploded book.\nUsage: -i output_dir file.epub\n' + 'Imports the book from the files in output_dir which must have' + ' been created by a previous call to --explode-book. Be sure to' + ' specify the same file type as was used when exploding.')) parser.add_option('--export-all-calibre-data', default=False, action='store_true', help=_('Export all calibre data (books/settings/plugins)')) parser.add_option('--import-calibre-data', default=False, action='store_true', @@ -277,9 +282,14 @@ def main(args=sys.argv): elif opts.edit_book: from calibre.gui_launch import ebook_edit ebook_edit(['ebook-edit'] + args[1:]) - elif opts.explode_book: - from calibre.ebooks.tweak import tweak - tweak(opts.explode_book) + elif opts.explode_book or opts.implode_book: + from calibre.ebooks.tweak import explode, implode + try: + a1, a2 = args[1:] + except Exception: + raise SystemExit('Must provide exactly two arguments') + f = explode if opts.explode_book else implode + f(a1, a2) elif opts.test_build: from calibre.test_build import test test() diff --git a/src/calibre/ebooks/tweak.py b/src/calibre/ebooks/tweak.py index 3a566ad18b..b4464e7020 100644 --- a/src/calibre/ebooks/tweak.py +++ b/src/calibre/ebooks/tweak.py @@ -7,11 +7,11 @@ __license__ = 'GPL v3' __copyright__ = '2012, Kovid Goyal ' __docformat__ = 'restructuredtext en' -import sys, os, shlex, subprocess, shutil, unicodedata +import sys, os, unicodedata from calibre import prints, as_unicode, walk from calibre.constants import iswindows, __appname__ -from calibre.ptempfile import TemporaryDirectory, TemporaryFile +from calibre.ptempfile import TemporaryDirectory from calibre.libunzip import extract as zipextract from calibre.utils.zipfile import ZipFile, ZIP_DEFLATED, ZIP_STORED from calibre.utils.ipc.simple_worker import WorkerError @@ -102,6 +102,61 @@ def get_tools(fmt): return ans +def explode(ebook_file, output_dir): + if not os.path.exists(output_dir): + os.makedirs(output_dir) + if not os.path.isdir(output_dir): + raise SystemExit('%s is not a directory' % output_dir) + output_dir = os.path.abspath(output_dir) + fmt = ebook_file.rpartition('.')[-1].lower() + exploder, rebuilder = get_tools(fmt) + if exploder is None: + raise SystemExit('Cannot tweak %s files. Supported formats are: EPUB, HTMLZ, AZW3, MOBI, DOCX' % fmt.upper()) + try: + opf = exploder(ebook_file, output_dir, question=ask_cli_question) + except WorkerError as e: + prints('Failed to unpack', ebook_file) + prints(e.orig_tb) + raise SystemExit(1) + except Error as e: + prints(as_unicode(e), file=sys.stderr) + raise SystemExit(1) + + if opf is None: + # The question was answered with No + return + h = '_' if iswindows else '.' + with lopen(os.path.join(output_dir, h + '__explode_fmt__'), 'wb') as f: + f.write(fmt) + prints('Book extracted to', output_dir) + prints('Make your changes and once you are done, use --implode-book to rebuild') + + +def implode(output_dir, ebook_file): + output_dir = os.path.abspath(output_dir) + fmt = ebook_file.rpartition('.')[-1].lower() + exploder, rebuilder = get_tools(fmt) + if rebuilder is None: + raise SystemExit('Cannot tweak %s files. Supported formats are: EPUB, HTMLZ, AZW3, MOBI, DOCX' % fmt.upper()) + h = '_' if iswindows else '.' + efmt_path = os.path.join(output_dir, h + '__explode_fmt__') + try: + with lopen(efmt_path, 'rb') as f: + efmt = f.read() + except Exception: + raise SystemExit('The directory %s does not seem to have been created by --explode-book' % output_dir) + if efmt != fmt: + raise SystemExit('You must use the same format of file as was used when exploding the book') + os.remove(efmt_path) + try: + rebuilder(output_dir, ebook_file) + except WorkerError as e: + prints('Failed to rebuild', ebook_file) + prints(e.orig_tb) + raise SystemExit(1) + prints(ebook_file, 'successfully rebuilt') + + def tweak(ebook_file): ''' Command line interface to the Tweak Book tool ''' fmt = ebook_file.rpartition('.')[-1].lower() @@ -127,33 +182,11 @@ def tweak(ebook_file): # The question was answered with No return - ed = os.environ.get('EDITOR', 'dummy') - cmd = shlex.split(ed) - isvim = bool([x for x in cmd[0].split('/') if x.endswith('vim')]) - - proceed = False prints('Book extracted to', tdir) - - if not isvim: - prints('Make your tweaks and once you are done,', __appname__, - 'will rebuild', ebook_file, 'from', tdir) - print() - proceed = ask_cli_question('Rebuild ' + ebook_file + '?') - else: - base = os.path.basename(ebook_file) - with TemporaryFile(base+'.zip') as zipf: - with ZipFile(zipf, 'w') as zf: - zf.add_dir(tdir) - try: - subprocess.check_call(cmd + [zipf]) - except: - prints(ed, 'failed, aborting...') - raise SystemExit(1) - with ZipFile(zipf, 'r') as zf: - shutil.rmtree(tdir) - os.mkdir(tdir) - zf.extractall(path=tdir) - proceed = True + prints('Make your tweaks and once you are done,', __appname__, + 'will rebuild', ebook_file, 'from', tdir) + print() + proceed = ask_cli_question('Rebuild ' + ebook_file + '?') if proceed: prints('Rebuilding', ebook_file, 'please wait ...') @@ -164,4 +197,3 @@ def tweak(ebook_file): prints(e.orig_tb) raise SystemExit(1) prints(ebook_file, 'successfully tweaked') -