"""render 수동 확인용. 사용: uv run python -m tests.poster_alive.test_render <포스터경로> [클립경로] 클립을 생략하면 test_result/<이름>.clip.mp4 를 쓴다. 나레이션(.tts.mp3)·타임라인(.tts.txt)·BGM(.bgm.mp3)은 있으면 얹고 없으면 건너뛴다. 결과는 <이름>.final.mp4 와 <이름>.thumb.jpg. """ import sys from pathlib import Path from models.tts import NarrationTimeline from services.render import render TEST_RESULT_DIR = Path(__file__).parent.parent / "test_result" / "poster_alive" def read_if_exists(path: Path) -> bytes | None: return path.read_bytes() if path.exists() else None def main() -> None: poster_path = Path(sys.argv[1]) output_base = TEST_RESULT_DIR / poster_path.stem clip_path = Path(sys.argv[2]) if len(sys.argv) > 2 else output_base.with_suffix(".clip.mp4") if not clip_path.exists(): raise SystemExit(f"클립 없음: {clip_path}") timeline_path = output_base.with_suffix(".tts.txt") timeline = (NarrationTimeline.model_validate_json(timeline_path.read_text(encoding="utf-8")) if timeline_path.exists() else None) narration = read_if_exists(output_base.with_suffix(".tts.mp3")) bgm = read_if_exists(output_base.with_suffix(".bgm.mp3")) print(f"클립 {clip_path.name} / 나레이션 {'있음' if narration else '없음'}" f" / BGM {'있음' if bgm else '없음'}") result = render(clip_path.read_bytes(), poster_path, narration=narration, bgm=bgm, timeline=timeline) video_path = output_base.with_suffix(".final.mp4") thumb_path = output_base.with_suffix(".thumb.jpg") video_path.write_bytes(result.video) thumb_path.write_bytes(result.thumbnail) print(f"{result.width}x{result.height} · {result.frames}프레임 · {result.duration:.2f}초 " f"· {len(result.video) / 1e6:.1f}MB") print(f"\n{video_path}\n{thumb_path}") if __name__ == "__main__": main()