diff --git a/app/song/api/routers/v1/song.py b/app/song/api/routers/v1/song.py index b80e501..9c9b6e7 100644 --- a/app/song/api/routers/v1/song.py +++ b/app/song/api/routers/v1/song.py @@ -238,9 +238,10 @@ async def get_song_status( # SUCCESS 상태인 경우 백그라운드 태스크 실행 if parsed_response.status == "SUCCESS" and parsed_response.clips: - # 첫 번째 클립의 audioUrl 가져오기 + # 첫 번째 클립의 audioUrl과 duration 가져오기 first_clip = parsed_response.clips[0] audio_url = first_clip.audio_url + clip_duration = first_clip.duration if audio_url: # suno_task_id로 Song 조회하여 store_name 가져오기 @@ -263,12 +264,13 @@ async def get_song_status( store_name = project.store_name if project else "song" # 백그라운드 태스크로 MP3 다운로드 및 Blob 업로드, DB 업데이트 (suno_task_id 사용) - print(f"[get_song_status] Background task args - suno_task_id: {suno_task_id}, audio_url: {audio_url}, store_name: {store_name}") + print(f"[get_song_status] Background task args - suno_task_id: {suno_task_id}, audio_url: {audio_url}, store_name: {store_name}, duration: {clip_duration}") background_tasks.add_task( download_and_upload_song_by_suno_task_id, suno_task_id=suno_task_id, audio_url=audio_url, store_name=store_name, + duration=clip_duration, ) elif song and song.status == "completed": print(f"[get_song_status] SKIPPED - Song already completed, suno_task_id: {suno_task_id}") diff --git a/app/song/models.py b/app/song/models.py index d599353..702ad76 100644 --- a/app/song/models.py +++ b/app/song/models.py @@ -100,6 +100,11 @@ class Song(Base): comment="노래 결과 URL", ) + duration: Mapped[Optional[float]] = mapped_column( + nullable=True, + comment="노래 재생 시간 (초)", + ) + language: Mapped[str] = mapped_column( String(50), nullable=False, diff --git a/app/song/worker/song_task.py b/app/song/worker/song_task.py index 087b013..f37fafc 100644 --- a/app/song/worker/song_task.py +++ b/app/song/worker/song_task.py @@ -210,6 +210,7 @@ async def download_and_upload_song_by_suno_task_id( suno_task_id: str, audio_url: str, store_name: str, + duration: float | None = None, ) -> None: """suno_task_id로 Song을 조회하여 노래를 다운로드하고 Azure Blob Storage에 업로드한 뒤 Song 테이블을 업데이트합니다. @@ -217,8 +218,9 @@ async def download_and_upload_song_by_suno_task_id( suno_task_id: Suno API 작업 ID audio_url: 다운로드할 오디오 URL store_name: 저장할 파일명에 사용할 업체명 + duration: 노래 재생 시간 (초) """ - print(f"[download_and_upload_song_by_suno_task_id] START - suno_task_id: {suno_task_id}, store_name: {store_name}") + print(f"[download_and_upload_song_by_suno_task_id] START - suno_task_id: {suno_task_id}, store_name: {store_name}, duration: {duration}") temp_file_path: Path | None = None task_id: str | None = None @@ -287,8 +289,10 @@ async def download_and_upload_song_by_suno_task_id( if song: song.status = "completed" song.song_result_url = blob_url + if duration is not None: + song.duration = duration await session.commit() - print(f"[download_and_upload_song_by_suno_task_id] SUCCESS - suno_task_id: {suno_task_id}, status: completed") + print(f"[download_and_upload_song_by_suno_task_id] SUCCESS - suno_task_id: {suno_task_id}, status: completed, duration: {duration}") else: print(f"[download_and_upload_song_by_suno_task_id] Song NOT FOUND in DB - suno_task_id: {suno_task_id}") diff --git a/app/video/api/routers/v1/video.py b/app/video/api/routers/v1/video.py index 3b11911..21c1022 100644 --- a/app/video/api/routers/v1/video.py +++ b/app/video/api/routers/v1/video.py @@ -164,7 +164,7 @@ async def generate_video( status_code=404, detail=f"task_id '{task_id}'에 해당하는 Song을 찾을 수 없습니다.", ) - print(f"[generate_video] Song found - song_id: {song.id}, task_id: {task_id}") + print(f"[generate_video] Song found - song_id: {song.id}, task_id: {task_id}, duration: {song.duration}") # 4. Video 테이블에 초기 데이터 저장 video = Video( @@ -181,9 +181,12 @@ async def generate_video( # 5. Creatomate API 호출 (POC 패턴 적용) print(f"[generate_video] Creatomate API generation started - task_id: {task_id}") - # orientation에 따른 템플릿과 duration 자동 설정 - creatomate_service = CreatomateService(orientation=request_body.orientation) - print(f"[generate_video] Using template_id: {creatomate_service.template_id}, duration: {creatomate_service.target_duration}") + # orientation에 따른 템플릿 선택, duration은 Song에서 가져옴 (없으면 config 기본값 사용) + creatomate_service = CreatomateService( + orientation=request_body.orientation, + target_duration=song.duration, # Song의 duration 사용 (None이면 config 기본값) + ) + print(f"[generate_video] Using template_id: {creatomate_service.template_id}, duration: {creatomate_service.target_duration} (song duration: {song.duration})") # 5-1. 템플릿 조회 (비동기, CreatomateService에서 orientation에 맞는 template_id 사용) template = await creatomate_service.get_one_template_data_async(creatomate_service.template_id)