#!/usr/bin/env python3 """ pipeline_render.py ─────────────────────────────────────────────────────────────── Standalone Rendering Pipeline Usage: python pipeline-render.py /path/to/chapter/folder """ import os import sys import argparse import zipfile import importlib.util from pathlib import Path import cv2 # ✅ Added OpenCV to load the image # ───────────────────────────────────────────── # CONFIG # ───────────────────────────────────────────── DEFAULT_FONT_PATH = "fonts/ComicNeue-Regular.ttf" # ───────────────────────────────────────────── # DYNAMIC MODULE LOADER # ───────────────────────────────────────────── def load_module(name, filepath): spec = importlib.util.spec_from_file_location(name, filepath) if spec is None or spec.loader is None: raise FileNotFoundError(f"Cannot load spec for {filepath}") module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) return module # ───────────────────────────────────────────── # HELPERS # ───────────────────────────────────────────── def sorted_pages(chapter_dir): exts = {".jpg", ".jpeg", ".png", ".webp"} pages = [ p for p in Path(chapter_dir).iterdir() if p.is_file() and p.suffix.lower() in exts ] return sorted(pages, key=lambda p: p.stem) def pack_rendered_cbz(chapter_dir, output_cbz, rendered_files): if not rendered_files: print("⚠️ No rendered pages found — CBZ not created.") return with zipfile.ZipFile(output_cbz, "w", compression=zipfile.ZIP_STORED) as zf: for rp in rendered_files: arcname = rp.name zf.write(rp, arcname) print(f"\n✅ Rendered CBZ saved → {output_cbz}") print(f"📦 Contains: {len(rendered_files)} translated pages ready to read.") # ───────────────────────────────────────────── # PER-PAGE PIPELINE # ───────────────────────────────────────────── def process_render(page_path, workdir, renderer_module, font_path): print(f"\n{'─' * 70}") print(f"🎨 RENDERING: {page_path.name}") print(f"{'─' * 70}") txt_path = workdir / "output.txt" json_path = workdir / "bubbles.json" out_img = workdir / page_path.name if not txt_path.exists() or not json_path.exists(): print(" ⚠️ Missing output.txt or bubbles.json. Did you run the OCR pipeline first?") return None # ✅ FIX: Load the image into memory (as a NumPy array) before passing it img_array = cv2.imread(str(page_path.resolve())) if img_array is None: print(f" ❌ Failed to load image: {page_path.name}") return None orig_dir = os.getcwd() try: os.chdir(workdir) # Pass the loaded image array instead of the string path renderer_module.render_translations( img_array, # 1st arg: Image Data (NumPy array) str(out_img.resolve()), # 2nd arg: Output image path str(txt_path.resolve()), # 3rd arg: Translations text str(json_path.resolve()), # 4th arg: Bubbles JSON font_path # 5th arg: Font Path ) print(" ✅ Render complete") return out_img except Exception as e: print(f" ❌ Failed: {e}") return None finally: os.chdir(orig_dir) # ───────────────────────────────────────────── # MAIN # ───────────────────────────────────────────── def main(): parser = argparse.ArgumentParser(description="Manga Rendering Pipeline") parser.add_argument("chapter_dir", help="Path to the folder containing original manga pages") args = parser.parse_args() chapter_dir = Path(args.chapter_dir).resolve() output_cbz = chapter_dir.parent / f"{chapter_dir.name}_rendered.cbz" script_dir = Path(__file__).parent absolute_font_path = str((script_dir / DEFAULT_FONT_PATH).resolve()) print("Loading renderer module...") try: renderer = load_module("manga_renderer", str(script_dir / "manga-renderer.py")) except Exception as e: print(f"❌ Could not load manga-renderer.py: {e}") sys.exit(1) pages = sorted_pages(chapter_dir) if not pages: print(f"❌ No images found in: {chapter_dir}") sys.exit(1) print(f"\n📖 Chapter : {chapter_dir}") print(f" Pages : {len(pages)}\n") succeeded, failed = [], [] rendered_files = [] for i, page_path in enumerate(pages, start=1): print(f"[{i}/{len(pages)}] Checking data for {page_path.name}...") workdir = Path(chapter_dir) / "translated" / page_path.stem out_file = process_render(page_path, workdir, renderer, absolute_font_path) if out_file: succeeded.append(page_path.name) rendered_files.append(out_file) else: failed.append(page_path.name) print(f"\n{'═' * 70}") print("RENDER PIPELINE COMPLETE") print(f"✅ {len(succeeded)} page(s) rendered successfully") if failed: print(f"❌ {len(failed)} page(s) skipped or failed:") for f in failed: print(f" • {f}") print(f"{'═' * 70}\n") print("Packing final CBZ...") pack_rendered_cbz(chapter_dir, output_cbz, rendered_files) if __name__ == "__main__": main()