Compare commits
5 Commits
a638ad7d7a
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 613496daf5 | |||
| 9237d70289 | |||
| b50c75865d | |||
| 978f13ebed | |||
| 46f24cd349 |
@@ -1 +1,2 @@
|
||||
.venv/
|
||||
__pycache__/
|
||||
|
||||
+112
-118
@@ -1,140 +1,134 @@
|
||||
# Created on iPad.
|
||||
# Comic to PDF Converter
|
||||
from utils import Sorter
|
||||
from utils import src, dest, verbose
|
||||
from utils import log
|
||||
|
||||
# version 1.0
|
||||
import os
|
||||
import img2pdf
|
||||
import tempfile
|
||||
import patoolib
|
||||
import shutil
|
||||
|
||||
import os, re, argparse, tqdm, img2pdf, pathlib, tempfile, patoolib
|
||||
from tqdm import tqdm
|
||||
from send2trash import send2trash
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Converts comic book zipfiles to PDF.",
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
)
|
||||
parser.add_argument("-src", help="location of file(s) to convert")
|
||||
parser.add_argument("-dest", help="destination of converted files")
|
||||
parser.add_argument(
|
||||
"-t",
|
||||
"--trash-old",
|
||||
action="store_true",
|
||||
help="places converted cbz and cbr to the trash",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-d",
|
||||
"--delete-old",
|
||||
action="store_true",
|
||||
help="delete cbz and cbr that are converted",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
src = args.src
|
||||
dest = args.dest
|
||||
delete_old = args.delete_old
|
||||
trash_old = args.trash_old
|
||||
|
||||
print(f"The source file is: {args.source}")
|
||||
print(f"The destination file is: {args.dest}")
|
||||
|
||||
class Comic2PDF:
|
||||
def __init__(self, src, dest):
|
||||
# huh? this needs to be fixed. I can't read it
|
||||
# horrible use of OOP
|
||||
src = self.check_src(src, dest)
|
||||
dest = self.check_dest(src, dest)
|
||||
def __init__(self, src, dest, verbose=False, use_tempfile=True):
|
||||
self.sort = Sorter()
|
||||
self.use_tempfile = use_tempfile
|
||||
|
||||
if src.endswith(".cbz") or src.endswith(".cbr"):
|
||||
self.convert_single_file(src, dest)
|
||||
self.src = src.rstrip('/')
|
||||
self.dest = dest
|
||||
self.dest_temp_dir = os.path.join(dest, "tmp")
|
||||
|
||||
if use_tempfile:
|
||||
self.define_temp = tempfile.TemporaryDirectory()
|
||||
self.temp_dir = self.define_temp.name
|
||||
else:
|
||||
self.batch_convert(src, dest)
|
||||
self.define_temp = self.dest_temp_dir
|
||||
self.temp_dir = self.define_temp
|
||||
os.makedirs(self.dest_temp_dir, exist_ok=True)
|
||||
|
||||
def check_src(self, src, dest):
|
||||
if (src is None and dest is None) or (src is None and dest is not None):
|
||||
# src = os.path.realpath(__file__)
|
||||
src = os.getcwd()
|
||||
src = os.path.dirname(src)
|
||||
return src
|
||||
os.makedirs(self.dest, exist_ok=True)
|
||||
os.makedirs(self.temp_dir, exist_ok=True)
|
||||
|
||||
log.info(f"Source: {self.src}")
|
||||
log.info(f"Destination: {self.dest}")
|
||||
log.debug(f"Temp Dir: {self.temp_dir}")
|
||||
log.debug(f"Dest Temp Dir: {self.dest_temp_dir}")
|
||||
|
||||
def __del__(self):
|
||||
if isinstance(self.define_temp, tempfile.TemporaryDirectory):
|
||||
log.debug(f"Deleting: {self.temp_dir}")
|
||||
self.define_temp.cleanup()
|
||||
else:
|
||||
return src
|
||||
delete_it = True
|
||||
try:
|
||||
if delete_it and self.use_tempfile is False:
|
||||
log.debug(f"Deleting: {self.dest_temp_dir}")
|
||||
shutil.rmtree(self.dest_temp_dir)
|
||||
except Exception as e:
|
||||
log.error(e)
|
||||
|
||||
def run(self):
|
||||
if os.path.isfile(self.src) and (
|
||||
self.src.endswith(".cbz") or self.src.endswith(".cbr")
|
||||
):
|
||||
self.extract(self.src)
|
||||
self.convert(self.src)
|
||||
|
||||
elif os.path.isdir(self.src):
|
||||
queue = 1
|
||||
scanned_folder = self.scan(self.src)
|
||||
log.debug(f"scanned folder: {scanned_folder}")
|
||||
|
||||
# BUG
|
||||
# batch conversion is probably recursive
|
||||
for root, dirs, files in os.walk(self.src):
|
||||
for file in files:
|
||||
txt = f" [{queue}/{len(scanned_folder)}] Progress "
|
||||
centered_txt = txt.center(45, "=")
|
||||
log.info(centered_txt)
|
||||
|
||||
self.__del__()
|
||||
os.makedirs(self.temp_dir, exist_ok=True)
|
||||
log.debug(f"Created {self.temp_dir}")
|
||||
|
||||
self.extract(file)
|
||||
self.convert(file, extract_to_folder=True)
|
||||
|
||||
queue += 1
|
||||
|
||||
log.info("=== ✅ Finished ===")
|
||||
|
||||
def check_dest(self, src, dest):
|
||||
if dest is None and src is None:
|
||||
# dest = os.path.realpath(__file__)
|
||||
dest = os.getcwd()
|
||||
dest = os.path.dirname(dest)
|
||||
return dest
|
||||
elif dest is None and src is not None:
|
||||
if src.endswith(".cbz") or src.endswith(".cbr"):
|
||||
dest = pathlib.Path(src).parents[0]
|
||||
else:
|
||||
dest = pathlib.Path(src)
|
||||
return dest
|
||||
else:
|
||||
return dest
|
||||
log.error("Unknown file type.")
|
||||
exit()
|
||||
|
||||
def scan_folder(self, src):
|
||||
filesIn_folder = []
|
||||
def scan(self, dir):
|
||||
log.info(f"Scanning {dir}")
|
||||
|
||||
for root, dirs, files in os.walk(src):
|
||||
files_in_folder = []
|
||||
# TODO
|
||||
# replace os.walk() for more flexibility
|
||||
|
||||
for root, dirs, files in os.walk(dir):
|
||||
for name in files:
|
||||
if name.endswith((".png", ".jpg", ".jpeg", ".cbz", ".cbr")):
|
||||
filesIn_folder.append(os.path.join(root, name))
|
||||
files_in_folder.append(os.path.join(root, name))
|
||||
|
||||
filesIn_folder.sort(key=natural_keys)
|
||||
return filesIn_folder
|
||||
files_in_folder.sort(key=self.sort.natural_keys)
|
||||
log.info(f"Found {len(files_in_folder)} files.")
|
||||
return files_in_folder
|
||||
|
||||
def convert_to_pdf(self, src, dest, name):
|
||||
def extract(self, file):
|
||||
log.info(f"Extracting... {file}")
|
||||
|
||||
path_file = os.path.join(self.src, file)
|
||||
patoolib.extract_archive(path_file, outdir=self.temp_dir, verbosity=-1)
|
||||
|
||||
log.info("Extraction Complete")
|
||||
|
||||
def convert(self, file, extract_to_folder=True):
|
||||
log.info("Converting to pdf...")
|
||||
imgs = []
|
||||
imgs = self.scan_folder(src)
|
||||
pdf_path = f"{dest}/{name}.pdf"
|
||||
imgs = self.scan(self.temp_dir)
|
||||
basename = os.path.basename(file)
|
||||
name = os.path.splitext(basename)[0]
|
||||
|
||||
if extract_to_folder:
|
||||
folder_basename = os.path.basename(self.src)
|
||||
folder = os.path.join(self.dest, f"{folder_basename}")
|
||||
os.makedirs(folder, exist_ok=True)
|
||||
pdf_path = os.path.join(folder, f"{name}.pdf")
|
||||
else:
|
||||
pdf_path = os.path.join(self.dest, f"{name}.pdf")
|
||||
|
||||
log.info(f"Path to conversion: {pdf_path}")
|
||||
log.info(f"Extract to folder is set to {extract_to_folder}")
|
||||
with open(pdf_path, "wb") as f:
|
||||
f.write(img2pdf.convert(imgs))
|
||||
|
||||
def convert_single_file(self, src, dest):
|
||||
with tempfile.TemporaryDirectory() as temp_dict:
|
||||
folder_parents = pathlib.Path(temp_dict)
|
||||
name = pathlib.Path(src).stem
|
||||
patoolib.extract_archive(src, outdir=temp_dict)
|
||||
list_folderInside = next(os.walk(folder_parents))[1]
|
||||
|
||||
try:
|
||||
storage = f"{temp_dict}/{list_folderInside[0]}"
|
||||
except:
|
||||
storage = f"{temp_dict}/"
|
||||
|
||||
self.convert_to_pdf(src=storage, dest=dest, name=name)
|
||||
|
||||
if trash_old is True:
|
||||
send2trash(f"{src}")
|
||||
|
||||
if delete_old is True:
|
||||
os.remove(f"{src}")
|
||||
|
||||
def batch_convert(self, src, dest):
|
||||
for root, dir, files in os.walk(src):
|
||||
pstat = [f for f in files if f.endswith(".cbr") or f.endswith(".cbz")]
|
||||
pbar = tqdm(total=len(pstat))
|
||||
queue = 0
|
||||
|
||||
for name in files:
|
||||
if name.endswith(".cbz") or name.endswith(".cbr"):
|
||||
pbar.set_description(f"Converting {name}")
|
||||
self.convert_single_file(os.path.join(root, name), dest)
|
||||
pbar.update(1)
|
||||
queue += 1
|
||||
else:
|
||||
pass
|
||||
break
|
||||
log.info("Conversion complete")
|
||||
|
||||
|
||||
# exported from the internet;
|
||||
# sorts folder name to human order
|
||||
def atoi(text):
|
||||
return int(text) if text.isdigit() else text
|
||||
|
||||
|
||||
def natural_keys(text):
|
||||
return [atoi(c) for c in re.split(r"(d )", text)]
|
||||
|
||||
|
||||
Comic2PDF(src, dest)
|
||||
if __name__ == "__main__":
|
||||
comic2pdf = Comic2PDF(src, dest, verbose)
|
||||
comic2pdf.run()
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
img2pdf==0.4.4
|
||||
patool==1.12
|
||||
Send2Trash==1.8.0
|
||||
tqdm==4.64.1
|
||||
@@ -0,0 +1,36 @@
|
||||
import re
|
||||
import argparse
|
||||
import logging
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description="A comic archive to pdf converter.",
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
)
|
||||
|
||||
parser.add_argument("source", type=str, help="source of file/folder with .cbz or .cbr format")
|
||||
parser.add_argument(
|
||||
"destination", type=str, help="ouptput of the converted files"
|
||||
)
|
||||
parser.add_argument("-v", "--verbose", action="store_true", help="debug mode")
|
||||
|
||||
args = parser.parse_args()
|
||||
src = args.source
|
||||
dest = args.destination
|
||||
verbose = args.verbose
|
||||
|
||||
if verbose:
|
||||
logging.basicConfig(level=logging.DEBUG, format="[%(levelname)s] - %(message)s")
|
||||
else:
|
||||
logging.basicConfig(level=logging.INFO, format="[%(levelname)s] - %(message)s")
|
||||
log = logging.getLogger()
|
||||
logging.getLogger('img2pdf').setLevel(logging.CRITICAL)
|
||||
logging.getLogger('patoolib').setLevel(logging.CRITICAL)
|
||||
|
||||
# got this from the internet
|
||||
class Sorter:
|
||||
def atoi(self, text):
|
||||
return int(text) if text.isdigit() else text
|
||||
|
||||
def natural_keys(self, text):
|
||||
return [self.atoi(c) for c in re.split(r"(d )", text)]
|
||||
|
||||
Reference in New Issue
Block a user