#!/usr/bin/env python3 import asyncio import sys import argparse from pathlib import Path from loguru import logger from src.translator import EPUBTranslator from src.epub_parser import EPUBParser from src.toc_parser import TOCParser from src.utils import load_config, setup_logging def parse_args(): parser = argparse.ArgumentParser(description="EPUB 双语翻译工具") parser.add_argument("epub_path", help="输入 EPUB 文件路径") parser.add_argument("--provider", "-p", default="openrouter", help="LLM 供应商 (config.json 中 providers 的 key)") parser.add_argument("--mode", "-m", default="bilingual", choices=["bilingual", "chinese"], help="输出模式: bilingual (双语对照) 或 chinese (纯中文,保留格式)") parser.add_argument("--test", action="store_true", help="测试模式(仅翻译前几段)") parser.add_argument("--output", "-o", help="输出目录") parser.add_argument("--no-cache", action="store_true", help="禁用缓存(强制重新翻译)") parser.add_argument("--clear-cache", action="store_true", help="清理所有缓存文件") # TOC 章节选择 parser.add_argument("--show-toc", action="store_true", help="显示书籍目录结构") parser.add_argument("--from", dest="from_chapter", help="起始章节标题") parser.add_argument("--to", dest="to_chapter", help="结束章节标题") return parser.parse_args() def flatten_provider_config(config: dict, provider_name: str) -> dict: """ 将选定的 provider 配置扁平化到 config['llm'] 中, 以便下游模块统一调用。 """ providers = config.get('providers', {}) if provider_name not in providers: available = list(providers.keys()) logger.error(f"未找到供应商 '{provider_name}'。可用供应商: {available}") sys.exit(1) selected_config = providers[provider_name] logger.info(f"使用 LLM 供应商: {provider_name} ({selected_config.get('base_url')})") # 注入到 config['llm'] config['llm'] = selected_config return config async def run_translation(args): try: # 1. 加载配置 config = load_config() # 2. 处理 Provider 选择 config = flatten_provider_config(config, args.provider) # 3. 设置日志 setup_logging(config) logger.info("程序启动") # 4. 初始化翻译器 translator = EPUBTranslator(config, use_cache=not args.no_cache) # 5. 执行翻译 (传递章节范围参数) await translator.translate_epub( args.epub_path, test_mode=args.test, output_dir=args.output, mode=args.mode, from_chapter=args.from_chapter, to_chapter=args.to_chapter ) except Exception as e: import traceback traceback.print_exc() logger.error(f"翻译失败: {e}") sys.exit(1) def show_toc(epub_path: str): """显示 EPUB 的目录结构""" parser = EPUBParser(epub_path) toc_parser = TOCParser(parser.book) print(f"\n📖 {parser.metadata.get('title', 'Unknown')} - {parser.metadata.get('author', 'Unknown')}") print(toc_parser.format_toc_table()) print("提示: 使用 --from \"章节名\" --to \"章节名\" 指定翻译范围") print() def main(): args = parse_args() if args.clear_cache: import shutil cache_dir = Path("cache") if cache_dir.exists(): shutil.rmtree(cache_dir) print("缓存已清理") # sys.exit(0) # 移除退出,允许继续执行 if args.show_toc: show_toc(args.epub_path) sys.exit(0) asyncio.run(run_translation(args)) if __name__ == "__main__": main()