diff --git a/music-lab/app/video_producer.py b/music-lab/app/video_producer.py index af2d033..d70e30a 100644 --- a/music-lab/app/video_producer.py +++ b/music-lab/app/video_producer.py @@ -84,7 +84,7 @@ def _build_slideshow_cmd( filter_str = ";".join(filter_parts) prev = "v0" for i in range(1, n): - offset = max(0.0, duration_per_image * i - xd) + offset = max(0.0, duration_per_image * i - xd * i) nxt = "out" if i == n - 1 else f"xf{i}" filter_str += ( f";[{prev}][v{i}]xfade=transition=fade:" @@ -159,7 +159,7 @@ def _generate_metadata(genre: str, moods: list, lyrics: str, target_countries: l start, end = text.find("{"), text.rfind("}") + 1 return json.loads(text[start:end]) except Exception: - return {"yt_title": f"{genre or 'Music'} - Chill Beats", "yt_description": "", "yt_tags": [genre]} + return {"yt_title": f"{genre or 'Music'} - Chill Beats", "yt_description": "", "yt_tags": [genre] if genre else []} def _render_visualizer(track: dict, proj: dict, output_path: str) -> None: diff --git a/music-lab/tests/test_video_producer.py b/music-lab/tests/test_video_producer.py index 30807b2..8826125 100644 --- a/music-lab/tests/test_video_producer.py +++ b/music-lab/tests/test_video_producer.py @@ -1,7 +1,6 @@ # music-lab/tests/test_video_producer.py import os from unittest.mock import patch, MagicMock -import pytest def test_build_visualizer_cmd(): @@ -59,10 +58,9 @@ def test_build_slideshow_cmd_multiple_images(): assert "/tmp/out.mp4" in cmd -def test_produce_video_visualizer_calls_ffmpeg(tmp_db, tmp_path): +def test_produce_video_visualizer_calls_ffmpeg(tmp_db, tmp_path, monkeypatch): """produce_video가 visualizer 포맷으로 FFmpeg를 호출하는지 확인.""" from app.db import init_db, create_video_project - import sqlite3 init_db() @@ -82,7 +80,7 @@ def test_produce_video_visualizer_calls_ffmpeg(tmp_db, tmp_path): create_video_project({"track_id": 1, "format": "visualizer", "target_countries": ["BR"]}) import app.video_producer as vp - vp.VIDEO_DATA_DIR = str(tmp_path / "videos") + monkeypatch.setattr("app.video_producer.VIDEO_DATA_DIR", str(tmp_path / "videos")) with patch("app.video_producer.subprocess.run") as mock_run, \ patch("app.video_producer._generate_metadata", return_value={ @@ -96,3 +94,18 @@ def test_produce_video_visualizer_calls_ffmpeg(tmp_db, tmp_path): proj = get_video_project(1) assert proj["status"] == "done" assert mock_run.called + + +def test_build_slideshow_cmd_offset_calculation(): + from app.video_producer import _build_slideshow_cmd + imgs = ["/tmp/img0.jpg", "/tmp/img1.jpg", "/tmp/img2.jpg"] + cmd = _build_slideshow_cmd(imgs, "/tmp/audio.mp3", "/tmp/out.mp4", duration_per_image=30.0) + # filter_complex 문자열 추출 + fc_idx = cmd.index("-filter_complex") + fc = cmd[fc_idx + 1] + # xfade이 2번 등장해야 함 (이미지 3개 → 전환 2번) + assert fc.count("xfade") == 2 + # 첫 번째 xfade offset: 30*1 - 1*1 = 29.0 + assert "offset=29.00" in fc + # 두 번째 xfade offset: 30*2 - 1*2 = 58.0 + assert "offset=58.00" in fc