Created
November 23, 2023 06:05
-
-
Save ssokolow/c2e24fd3ae7f1bf83b601156c20e6c69 to your computer and use it in GitHub Desktop.
Quick script to split manga CBZs scanned as two-page spreads so they'll convert with proper page order in Calibre
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# -*- coding: utf-8 -*- | |
"""A quick helper for splitting manga pages so Calibre doesn't split them for | |
you and present them in the wrong order. | |
Takes and produces .cbz files. | |
Currently assumes first image will be a cover and doesn't split it. | |
""" | |
# NOTE: This is a quick hack. Most of the polish is from a template that | |
# I start all my quick hacks from. | |
__author__ = "Stephan Sokolow (deitarion/SSokolow)" | |
__appname__ = "Manga Page Splitter" | |
__version__ = "0.1" | |
__license__ = "MIT" | |
import logging, os # noqa: E401 | |
from zipfile import ZipFile | |
from PIL import Image # type: ignore | |
log = logging.getLogger(__name__) | |
IMAGE_EXTS = Image.registered_extensions() # Dict[extension, format name] | |
def process_arg(path): | |
log.info("Processing archive %s...", path) | |
zbase, zext = os.path.splitext(path) | |
outpath = '{}_split{}'.format(zbase, zext) | |
cover_seen = False | |
with ZipFile(path) as zobj_in, ZipFile(outpath, 'w') as zobj_out: | |
for zinfo in sorted(zobj_in.infolist(), key=lambda x: x.filename): | |
ibase, iext = os.path.splitext(zinfo.filename) | |
iext = iext.lower() | |
if zinfo.is_dir(): | |
log.info("Skipping directory entry: %s", zinfo.filename) | |
continue # ZipInfo.mkdir was only added in Python 3.11 | |
elif iext in IMAGE_EXTS: | |
if not cover_seen: | |
log.info("Copying cover verbatim: %s", zinfo.filename) | |
zobj_out.writestr(zinfo, zobj_in.read(zinfo)) | |
cover_seen = True | |
continue | |
log.info("Processing image: %s", zinfo.filename) | |
im = Image.open(zobj_in.open(zinfo.filename, 'r')) | |
fname_right = '{}_1{}'.format(ibase, iext) | |
fname_left = '{}_2{}'.format(ibase, iext) | |
im_format = IMAGE_EXTS[iext] | |
split_point = im.size[0] // 2 | |
# im.crop will ideally just take a reference to `im` | |
# | |
# I'm not sure if this will duplicate the column of pixels at | |
# split_point, but it doesn't matter | |
page_right = im.crop((split_point, 0, im.size[0], im.size[1])) | |
page_right.save(zobj_out.open(fname_right, 'w'), im_format) | |
page_right.close() | |
page_left = im.crop((0, 0, split_point, im.size[1])) | |
page_left.save(zobj_out.open(fname_left, 'w'), im_format) | |
page_left.close() | |
else: | |
log.info("Copying non-image verbatim: %s", zinfo.filename) | |
zobj_out.writestr(zinfo, zobj_in.read(zinfo)) | |
def main(): | |
"""The main entry point, compatible with setuptools entry points.""" | |
from argparse import ArgumentParser, RawDescriptionHelpFormatter | |
parser = ArgumentParser(formatter_class=RawDescriptionHelpFormatter, | |
description=__doc__.replace('\r\n', '\n').split('\n--snip--\n')[0]) | |
parser.add_argument('-V', '--version', action='version', | |
version="%%(prog)s v%s" % __version__) | |
parser.add_argument('-v', '--verbose', action="count", | |
default=3, help="Increase the verbosity. Use twice for extra effect.") | |
parser.add_argument('-q', '--quiet', action="count", | |
default=0, help="Decrease the verbosity. Use twice for extra effect.") | |
parser.add_argument('path', action="store", nargs="+", | |
help="Path to operate on") | |
args = parser.parse_args() | |
# Set up clean logging to stderr | |
log_levels = [logging.CRITICAL, logging.ERROR, logging.WARNING, | |
logging.INFO, logging.DEBUG] | |
args.verbose = min(args.verbose - args.quiet, len(log_levels) - 1) | |
args.verbose = max(args.verbose, 0) | |
logging.basicConfig(level=log_levels[args.verbose], | |
format='%(levelname)s: %(message)s') | |
for path in args.path: | |
process_arg(path) | |
if __name__ == '__main__': # pragma: nocover | |
main() | |
# vim: set sw=4 sts=4 expandtab : |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment