playreel/backend/tests/poster_alive/test_render.py

51 lines
1.9 KiB
Python

"""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()