- Refactor codebase into src/ (preprocessing, translation, assembly) - Add pipeline/ scripts for individual stages - Externalize configuration to config/config.yaml - Fix Cover Image preservation - Update documentation and manuals
107 lines
3.8 KiB
Python
107 lines
3.8 KiB
Python
import argparse
|
|
import sys
|
|
import os
|
|
from pathlib import Path
|
|
|
|
# Add project root to sys.path
|
|
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
|
|
|
from src.assembly.backfiller import BackfillEngine
|
|
from src.assembly.builder import BilingualBuilder
|
|
from src.translation.manifest_manager import ManifestManager
|
|
from src.translation.llm_client import LLMClient
|
|
from src.common.data_model import BookStructure
|
|
from src.common.exceptions import EpubTranslatorError
|
|
from src.common.utils import setup_logger, ensure_directory
|
|
from src.common.paths import get_work_dirs
|
|
from src.common.config import load_global_config
|
|
from dotenv import load_dotenv
|
|
|
|
logger = setup_logger("pipeline_assemble")
|
|
|
|
async def run_assemble(args):
|
|
input_path = Path(args.input_epub)
|
|
if not input_path.exists():
|
|
logger.error(f"Input file not found: {input_path}")
|
|
sys.exit(1)
|
|
|
|
work = get_work_dirs(input_path)
|
|
work_root = work["root"]
|
|
structure_path = work["structure"]
|
|
manifest_path = work["manifest"]
|
|
|
|
if not structure_path.exists() or not manifest_path.exists():
|
|
logger.error("Missing structure or manifest. Run Step 1.")
|
|
sys.exit(1)
|
|
|
|
output_dir = Path(args.output_dir)
|
|
ensure_directory(output_dir)
|
|
|
|
# Load Config for LLM (Optional for repair)
|
|
load_dotenv()
|
|
config = load_global_config()
|
|
llm_conf = config.get("llm", {})
|
|
api_key = llm_conf.get("api_key") or os.getenv("OPENAI_API_KEY") # Ensure env priority
|
|
|
|
llm_client = None
|
|
if api_key:
|
|
logger.info("Initializing LLM Client for placeholder repair...")
|
|
llm_client = LLMClient(
|
|
api_key=api_key,
|
|
base_url=llm_conf.get("base_url"),
|
|
model=llm_conf.get("model", "gpt-3.5-turbo"),
|
|
requests_per_minute=llm_conf.get("requests_per_minute", 60),
|
|
concurrent_requests=llm_conf.get("concurrent_requests", 5),
|
|
chunk_dir=work["chunks"]
|
|
)
|
|
else:
|
|
logger.warning("No API Key found. Placeholder repair will be disabled.")
|
|
|
|
try:
|
|
# Load Data
|
|
structure = BookStructure.load(structure_path)
|
|
manager = ManifestManager(manifest_path)
|
|
manager.load()
|
|
|
|
# Backfill
|
|
logger.info(f"Backfilling translations (Mode: {args.mode})...")
|
|
backfiller = BackfillEngine(llm_client=llm_client)
|
|
updated_structure = await backfiller.backfill(structure, manager.entries, mode=args.mode)
|
|
|
|
if llm_client:
|
|
await llm_client.close()
|
|
|
|
# Build
|
|
logger.info("Building EPUB...")
|
|
builder = BilingualBuilder(work_root, original_epub_path=input_path)
|
|
|
|
if args.mode == "bilingual":
|
|
output_filename = f"bilingual_{input_path.stem}.epub"
|
|
else:
|
|
output_filename = f"translated_{input_path.stem}.epub"
|
|
|
|
output_path = output_dir / output_filename
|
|
|
|
builder.build(updated_structure, output_path)
|
|
logger.info(f"Assembly complete. Output: {output_path}")
|
|
|
|
except EpubTranslatorError as e:
|
|
logger.error(f"Assembly failed: {e}")
|
|
sys.exit(1)
|
|
except Exception as e:
|
|
logger.critical(f"Unexpected error: {e}")
|
|
import traceback
|
|
traceback.print_exc()
|
|
sys.exit(1)
|
|
|
|
if __name__ == "__main__":
|
|
parser = argparse.ArgumentParser(description="Step 3: Assembly (Backfill + Build)")
|
|
parser.add_argument("input_epub", help="Path to original EPUB")
|
|
parser.add_argument("--output-dir", default="output", help="Output directory")
|
|
parser.add_argument("--bilingual", action="store_true", help="Output bilingual version (default target only)")
|
|
|
|
args = parser.parse_args()
|
|
args.mode = "bilingual" if args.bilingual else "target_only"
|
|
import asyncio
|
|
asyncio.run(run_assemble(args))
|