Use new betterhelp.
This commit is contained in:
parent
c23335733c
commit
039610dec2
1 changed files with 229 additions and 190 deletions
419
epubfile.py
419
epubfile.py
|
@ -1,17 +1,22 @@
|
||||||
|
import argparse
|
||||||
|
import bs4
|
||||||
import copy
|
import copy
|
||||||
import functools
|
import functools
|
||||||
|
import html
|
||||||
import io
|
import io
|
||||||
import mimetypes
|
import mimetypes
|
||||||
import os
|
import os
|
||||||
|
import random
|
||||||
import re
|
import re
|
||||||
|
import string
|
||||||
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
|
import tinycss2
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
import uuid
|
import uuid
|
||||||
import zipfile
|
import zipfile
|
||||||
|
|
||||||
import bs4
|
from voussoirkit import betterhelp
|
||||||
import tinycss2
|
|
||||||
|
|
||||||
from voussoirkit import interactive
|
from voussoirkit import interactive
|
||||||
from voussoirkit import pathclass
|
from voussoirkit import pathclass
|
||||||
from voussoirkit import pipeable
|
from voussoirkit import pipeable
|
||||||
|
@ -1388,156 +1393,6 @@ class Epub:
|
||||||
|
|
||||||
# COMMAND LINE TOOLS
|
# COMMAND LINE TOOLS
|
||||||
################################################################################
|
################################################################################
|
||||||
import argparse
|
|
||||||
import html
|
|
||||||
import random
|
|
||||||
import string
|
|
||||||
import sys
|
|
||||||
|
|
||||||
from voussoirkit import betterhelp
|
|
||||||
|
|
||||||
DOCSTRING = '''
|
|
||||||
Epubfile
|
|
||||||
========
|
|
||||||
|
|
||||||
A simple Python .epub scripting tool.
|
|
||||||
|
|
||||||
{addfile}
|
|
||||||
|
|
||||||
{covercomesfirst}
|
|
||||||
|
|
||||||
{exec}
|
|
||||||
|
|
||||||
{generate_toc}
|
|
||||||
|
|
||||||
{holdit}
|
|
||||||
|
|
||||||
{merge}
|
|
||||||
|
|
||||||
{new}
|
|
||||||
|
|
||||||
{normalize}
|
|
||||||
|
|
||||||
{setfont}
|
|
||||||
|
|
||||||
TO SEE DETAILS ON EACH COMMAND, RUN
|
|
||||||
> epubfile.py <command> --help
|
|
||||||
'''
|
|
||||||
|
|
||||||
SUB_DOCSTRINGS = dict(
|
|
||||||
addfile='''
|
|
||||||
addfile:
|
|
||||||
Add files into the book.
|
|
||||||
|
|
||||||
> epubfile.py addfile book.epub page1.html image.jpg
|
|
||||||
'''.strip(),
|
|
||||||
|
|
||||||
covercomesfirst='''
|
|
||||||
covercomesfirst:
|
|
||||||
Rename the cover image file so that it is the alphabetically-first image.
|
|
||||||
|
|
||||||
> epubfile.py covercomesfirst book.epub
|
|
||||||
|
|
||||||
I use CBXShell to get thumbnails of epub files on Windows, and because it
|
|
||||||
is generalized for zip files and doesn't read epub metadata, alphabetized
|
|
||||||
mode works best for getting epub covers as icons.
|
|
||||||
|
|
||||||
In my testing, CBXShell considers the image's whole path and not just the
|
|
||||||
basename, so you may want to consider normalizing the directory structure
|
|
||||||
first, otherwise some /a/image.jpg will always be before /images/cover.jpg.
|
|
||||||
'''.strip(),
|
|
||||||
|
|
||||||
exec='''
|
|
||||||
exec:
|
|
||||||
Execute a snippet of Python code against the book.
|
|
||||||
|
|
||||||
> epubfile.py exec book.epub --command "book._____()"
|
|
||||||
'''.strip(),
|
|
||||||
|
|
||||||
generate_toc='''
|
|
||||||
generate_toc:
|
|
||||||
Regenerate the toc.ncx and nav.xhtml based on html <hX> headers in the text.
|
|
||||||
|
|
||||||
> epubfile.py generate_toc book.epub <flags>
|
|
||||||
|
|
||||||
flags:
|
|
||||||
--max_level X:
|
|
||||||
Only generate toc entries for headers up to level X.
|
|
||||||
That is, h1, h2, ... hX.
|
|
||||||
'''.strip(),
|
|
||||||
|
|
||||||
holdit='''
|
|
||||||
holdit:
|
|
||||||
Extract the book so that you can manually edit the files on disk, then
|
|
||||||
compress them back into the original file.
|
|
||||||
|
|
||||||
This is helpful when you want to do some file processing that is outside of
|
|
||||||
epubfile's scope. epubfile will save you the effort of extracting and
|
|
||||||
compressing the epub so you can focus on doing the file operations.
|
|
||||||
|
|
||||||
> epubfile.py holdit book.epub
|
|
||||||
'''.strip(),
|
|
||||||
|
|
||||||
merge='''
|
|
||||||
merge:
|
|
||||||
Merge multiple books into one.
|
|
||||||
|
|
||||||
> epubfile.py merge book1.epub book2.epub --output final.epub <flags>
|
|
||||||
|
|
||||||
flags:
|
|
||||||
--demote_headers:
|
|
||||||
All h1 in the book will be demoted to h2, and so forth, so that the
|
|
||||||
headerfiles are the only h1s and the table of contents will generate
|
|
||||||
with a good hierarchy.
|
|
||||||
|
|
||||||
--headerfile:
|
|
||||||
Add a file before each book with an <h1> containing its title.
|
|
||||||
|
|
||||||
--number_headerfile:
|
|
||||||
In the headerfile, the <h1> will start with the book's index, like
|
|
||||||
"01. First Book"
|
|
||||||
|
|
||||||
--yes:
|
|
||||||
Overwrite the output file without prompting.
|
|
||||||
'''.strip(),
|
|
||||||
|
|
||||||
new='''
|
|
||||||
new:
|
|
||||||
Create a new, blank epub file.
|
|
||||||
|
|
||||||
> epubfile.py new book.epub <flags>
|
|
||||||
|
|
||||||
flags:
|
|
||||||
--yes:
|
|
||||||
Overwrite the file without prompting.
|
|
||||||
'''.strip(),
|
|
||||||
|
|
||||||
normalize='''
|
|
||||||
normalize:
|
|
||||||
Rename files and directories in the book to match a common structure.
|
|
||||||
|
|
||||||
Moves all book content from / into /OEBPS and sorts files into
|
|
||||||
subdirectories by type: Text, Images, Styles, etc.
|
|
||||||
|
|
||||||
> epubfile.py normalize book.epub
|
|
||||||
'''.strip(),
|
|
||||||
|
|
||||||
setfont='''
|
|
||||||
setfont:
|
|
||||||
Set the font for every page in the whole book.
|
|
||||||
|
|
||||||
A stylesheet called epubfile_setfont.css will be created that sets
|
|
||||||
* { font-family: ... !important } with a font file of your choice.
|
|
||||||
|
|
||||||
> epubfile.py setfont book.epub font.ttf <flags>
|
|
||||||
|
|
||||||
flags:
|
|
||||||
--yes:
|
|
||||||
Overwrite the epubfile_setfont.css without prompting.
|
|
||||||
'''.strip(),
|
|
||||||
)
|
|
||||||
|
|
||||||
DOCSTRING = betterhelp.add_previews(DOCSTRING, SUB_DOCSTRINGS)
|
|
||||||
|
|
||||||
def random_string(length, characters=string.ascii_lowercase):
|
def random_string(length, characters=string.ascii_lowercase):
|
||||||
return ''.join(random.choices(characters, k=length))
|
return ''.join(random.choices(characters, k=length))
|
||||||
|
@ -1624,7 +1479,7 @@ def generate_toc_argparse(args):
|
||||||
books = []
|
books = []
|
||||||
for epub in epubs:
|
for epub in epubs:
|
||||||
book = Epub(epub)
|
book = Epub(epub)
|
||||||
book.generate_toc(max_level=int(args.max_level) if args.max_level else None)
|
book.generate_toc(max_level=args.max_level)
|
||||||
book.save(epub)
|
book.save(epub)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
@ -1835,62 +1690,246 @@ def setfont_argparse(args):
|
||||||
|
|
||||||
@vlogging.main_decorator
|
@vlogging.main_decorator
|
||||||
def main(argv):
|
def main(argv):
|
||||||
parser = argparse.ArgumentParser(description=__doc__)
|
parser = argparse.ArgumentParser(
|
||||||
|
description='''
|
||||||
|
A simple Python .epub scripting tool.
|
||||||
|
''',
|
||||||
|
)
|
||||||
subparsers = parser.add_subparsers()
|
subparsers = parser.add_subparsers()
|
||||||
|
|
||||||
p_addfile = subparsers.add_parser('addfile')
|
################################################################################################
|
||||||
p_addfile.add_argument('epub')
|
|
||||||
p_addfile.add_argument('files', nargs='+')
|
p_addfile = subparsers.add_parser(
|
||||||
|
'addfile',
|
||||||
|
description='''
|
||||||
|
Add files into the book.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
p_addfile.add_argument(
|
||||||
|
'epub',
|
||||||
|
)
|
||||||
|
p_addfile.add_argument(
|
||||||
|
'files',
|
||||||
|
nargs='+',
|
||||||
|
)
|
||||||
p_addfile.set_defaults(func=addfile_argparse)
|
p_addfile.set_defaults(func=addfile_argparse)
|
||||||
|
|
||||||
p_covercomesfirst = subparsers.add_parser('covercomesfirst')
|
################################################################################################
|
||||||
p_covercomesfirst.add_argument('epubs', nargs='+')
|
|
||||||
|
p_covercomesfirst = subparsers.add_parser(
|
||||||
|
'covercomesfirst',
|
||||||
|
description='''
|
||||||
|
Rename the cover image file so that it is the alphabetically-first image.
|
||||||
|
|
||||||
|
I use CBXShell to get thumbnails of epub files on Windows, and because it
|
||||||
|
is generalized for zip files and doesn't read epub metadata, alphabetized
|
||||||
|
mode works best for getting epub covers as icons.
|
||||||
|
|
||||||
|
In my testing, CBXShell considers the image's whole path and not just the
|
||||||
|
basename, so you may want to consider normalizing the directory structure
|
||||||
|
first, otherwise some /a/image.jpg will always be before /images/cover.jpg.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
p_covercomesfirst.add_argument(
|
||||||
|
'epubs',
|
||||||
|
nargs='+',
|
||||||
|
)
|
||||||
p_covercomesfirst.set_defaults(func=covercomesfirst_argparse)
|
p_covercomesfirst.set_defaults(func=covercomesfirst_argparse)
|
||||||
|
|
||||||
p_exec = subparsers.add_parser('exec')
|
################################################################################################
|
||||||
p_exec.add_argument('epubs', nargs='+')
|
|
||||||
p_exec.add_argument('--command', default=None, required=True)
|
p_exec = subparsers.add_parser(
|
||||||
|
'exec',
|
||||||
|
description='''
|
||||||
|
Execute a snippet of Python code against the book. The book will be saved
|
||||||
|
again after the command has finished.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
p_exec.examples = [
|
||||||
|
['mybook.epub', '--command', 'print(book.get_authors())'],
|
||||||
|
['*.epub', '--command', 'book.remove_cover_image()'],
|
||||||
|
]
|
||||||
|
p_exec.add_argument(
|
||||||
|
'epubs',
|
||||||
|
nargs='+',
|
||||||
|
)
|
||||||
|
p_exec.add_argument(
|
||||||
|
'--command',
|
||||||
|
default=None,
|
||||||
|
required=True,
|
||||||
|
help='''
|
||||||
|
The variable `book` will be the Epub object.
|
||||||
|
''',
|
||||||
|
)
|
||||||
p_exec.set_defaults(func=exec_argparse)
|
p_exec.set_defaults(func=exec_argparse)
|
||||||
|
|
||||||
p_generate_toc = subparsers.add_parser('generate_toc')
|
################################################################################################
|
||||||
p_generate_toc.add_argument('epubs', nargs='+')
|
|
||||||
p_generate_toc.add_argument('--max_level', '--max-level', default=None)
|
p_generate_toc = subparsers.add_parser(
|
||||||
|
'generate_toc',
|
||||||
|
description='''
|
||||||
|
Regenerate the toc.ncx and nav.xhtml based on html <hX> headers in the text.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
p_generate_toc.add_argument(
|
||||||
|
'epubs',
|
||||||
|
nargs='+',
|
||||||
|
)
|
||||||
|
p_generate_toc.add_argument(
|
||||||
|
'--max_level',
|
||||||
|
'--max-level',
|
||||||
|
type=int,
|
||||||
|
default=None,
|
||||||
|
help='''
|
||||||
|
Only generate toc entries for headers up to level X.
|
||||||
|
That is, h1, h2, ... hX.
|
||||||
|
''',
|
||||||
|
)
|
||||||
p_generate_toc.set_defaults(func=generate_toc_argparse)
|
p_generate_toc.set_defaults(func=generate_toc_argparse)
|
||||||
|
|
||||||
p_holdit = subparsers.add_parser('holdit')
|
################################################################################################
|
||||||
p_holdit.add_argument('epubs', nargs='+')
|
|
||||||
|
p_holdit = subparsers.add_parser(
|
||||||
|
'holdit',
|
||||||
|
description='''
|
||||||
|
Extract the book so that you can manually edit the files on disk, then
|
||||||
|
compress them back into the original file.
|
||||||
|
|
||||||
|
This is helpful when you want to do some file processing that is outside of
|
||||||
|
epubfile's scope. epubfile will save you the effort of extracting and
|
||||||
|
compressing the epub so you can focus on doing the file operations.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
p_holdit.add_argument(
|
||||||
|
'epubs',
|
||||||
|
nargs='+',
|
||||||
|
)
|
||||||
p_holdit.set_defaults(func=holdit_argparse)
|
p_holdit.set_defaults(func=holdit_argparse)
|
||||||
|
|
||||||
p_merge = subparsers.add_parser('merge')
|
################################################################################################
|
||||||
p_merge.add_argument('epubs', nargs='+')
|
|
||||||
p_merge.add_argument('--output', required=True)
|
p_merge = subparsers.add_parser(
|
||||||
p_merge.add_argument('--headerfile', action='store_true')
|
'merge',
|
||||||
p_merge.add_argument('--demote_headers', '--demote-headers', action='store_true')
|
description='''
|
||||||
p_merge.add_argument('--number_headerfile', '--number-headerfile', action='store_true')
|
Merge multiple books into one.
|
||||||
p_merge.add_argument('-y', '--yes', '--autoyes', dest='autoyes', action='store_true')
|
''',
|
||||||
|
)
|
||||||
|
p_merge.add_argument(
|
||||||
|
'epubs',
|
||||||
|
nargs='+',
|
||||||
|
)
|
||||||
|
p_merge.add_argument(
|
||||||
|
'--output',
|
||||||
|
required=True,
|
||||||
|
)
|
||||||
|
p_merge.add_argument(
|
||||||
|
'--headerfile',
|
||||||
|
action='store_true',
|
||||||
|
help='''
|
||||||
|
Add a file before each book with an <h1> containing its title.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
p_merge.add_argument(
|
||||||
|
'--demote_headers',
|
||||||
|
'--demote-headers',
|
||||||
|
action='store_true',
|
||||||
|
help='''
|
||||||
|
All h1 in the book will be demoted to h2, and so forth, so that the
|
||||||
|
headerfiles are the only h1s and the table of contents will generate
|
||||||
|
with a good hierarchy.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
p_merge.add_argument(
|
||||||
|
'--number_headerfile',
|
||||||
|
'--number-headerfile',
|
||||||
|
action='store_true',
|
||||||
|
help='''
|
||||||
|
In the headerfile, the <h1> will start with the book's index, like
|
||||||
|
"01. First Book"
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
p_merge.add_argument(
|
||||||
|
'-y',
|
||||||
|
'--yes',
|
||||||
|
'--autoyes',
|
||||||
|
dest='autoyes',
|
||||||
|
action='store_true',
|
||||||
|
help='''
|
||||||
|
Overwrite the output file without prompting.
|
||||||
|
''',
|
||||||
|
)
|
||||||
p_merge.set_defaults(func=merge_argparse)
|
p_merge.set_defaults(func=merge_argparse)
|
||||||
|
|
||||||
p_new = subparsers.add_parser('new')
|
################################################################################################
|
||||||
p_new.add_argument('epub')
|
|
||||||
p_new.add_argument('-y', '--yes', '--autoyes', dest='autoyes', action='store_true')
|
p_new = subparsers.add_parser(
|
||||||
|
'new',
|
||||||
|
description='''
|
||||||
|
Create a new, blank epub file.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
p_new.add_argument(
|
||||||
|
'epub',
|
||||||
|
)
|
||||||
|
p_new.add_argument(
|
||||||
|
'-y',
|
||||||
|
'--yes',
|
||||||
|
'--autoyes',
|
||||||
|
dest='autoyes',
|
||||||
|
action='store_true',
|
||||||
|
help='''
|
||||||
|
Overwrite the file without prompting.
|
||||||
|
''',
|
||||||
|
)
|
||||||
p_new.set_defaults(func=new_book_argparse)
|
p_new.set_defaults(func=new_book_argparse)
|
||||||
|
|
||||||
p_normalize = subparsers.add_parser('normalize')
|
################################################################################################
|
||||||
p_normalize.add_argument('epubs', nargs='+')
|
|
||||||
|
p_normalize = subparsers.add_parser(
|
||||||
|
'normalize',
|
||||||
|
description='''
|
||||||
|
Rename files and directories in the book to match a common structure.
|
||||||
|
|
||||||
|
Moves all book content from / into /OEBPS and sorts files into
|
||||||
|
subdirectories by type: Text, Images, Styles, etc.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
p_normalize.add_argument(
|
||||||
|
'epubs',
|
||||||
|
nargs='+',
|
||||||
|
)
|
||||||
p_normalize.set_defaults(func=normalize_argparse)
|
p_normalize.set_defaults(func=normalize_argparse)
|
||||||
|
|
||||||
p_setfont = subparsers.add_parser('setfont')
|
################################################################################################
|
||||||
p_setfont.add_argument('epubs', nargs='+')
|
|
||||||
p_setfont.add_argument('--font', required=True)
|
p_setfont = subparsers.add_parser(
|
||||||
p_setfont.add_argument('--yes', dest='autoyes', action='store_true')
|
'setfont',
|
||||||
|
description='''
|
||||||
|
Set the font for every page in the whole book.
|
||||||
|
|
||||||
|
A stylesheet called epubfile_setfont.css will be created that sets
|
||||||
|
* { font-family: ... !important } with a font file of your choice.
|
||||||
|
''',
|
||||||
|
)
|
||||||
|
p_setfont.add_argument(
|
||||||
|
'epubs',
|
||||||
|
nargs='+',
|
||||||
|
)
|
||||||
|
p_setfont.add_argument(
|
||||||
|
'--font',
|
||||||
|
required=True,
|
||||||
|
)
|
||||||
|
p_setfont.add_argument(
|
||||||
|
'--yes',
|
||||||
|
dest='autoyes',
|
||||||
|
action='store_true',
|
||||||
|
help='''
|
||||||
|
Overwrite the epubfile_setfont.css without prompting.
|
||||||
|
''',
|
||||||
|
)
|
||||||
p_setfont.set_defaults(func=setfont_argparse)
|
p_setfont.set_defaults(func=setfont_argparse)
|
||||||
|
|
||||||
return betterhelp.subparser_main(
|
return betterhelp.go(parser, argv)
|
||||||
argv,
|
|
||||||
parser,
|
|
||||||
main_docstring=DOCSTRING,
|
|
||||||
sub_docstrings=SUB_DOCSTRINGS,
|
|
||||||
)
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
raise SystemExit(main(sys.argv[1:]))
|
raise SystemExit(main(sys.argv[1:]))
|
||||||
|
|
Loading…
Reference in a new issue