buf fix
parent
5dddbaeda2
commit
d4bce083ab
|
|
@ -13,7 +13,7 @@ Song API Router
|
||||||
app.include_router(router, prefix="/api/v1")
|
app.include_router(router, prefix="/api/v1")
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException
|
from fastapi import APIRouter, Depends, HTTPException
|
||||||
from sqlalchemy import func, select
|
from sqlalchemy import func, select
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
|
|
@ -32,7 +32,6 @@ from app.song.schemas.song_schema import (
|
||||||
PollingSongResponse,
|
PollingSongResponse,
|
||||||
SongListItem,
|
SongListItem,
|
||||||
)
|
)
|
||||||
from app.song.worker.song_task import download_and_upload_song_by_suno_task_id
|
|
||||||
from app.utils.pagination import PaginatedResponse
|
from app.utils.pagination import PaginatedResponse
|
||||||
from app.utils.suno import SunoService
|
from app.utils.suno import SunoService
|
||||||
|
|
||||||
|
|
@ -220,7 +219,6 @@ GET /song/status/abc123...
|
||||||
)
|
)
|
||||||
async def get_song_status(
|
async def get_song_status(
|
||||||
suno_task_id: str,
|
suno_task_id: str,
|
||||||
background_tasks: BackgroundTasks,
|
|
||||||
session: AsyncSession = Depends(get_session),
|
session: AsyncSession = Depends(get_session),
|
||||||
) -> PollingSongResponse:
|
) -> PollingSongResponse:
|
||||||
"""suno_task_id로 노래 생성 작업의 상태를 조회합니다.
|
"""suno_task_id로 노래 생성 작업의 상태를 조회합니다.
|
||||||
|
|
@ -236,7 +234,7 @@ async def get_song_status(
|
||||||
parsed_response = suno_service.parse_status_response(result)
|
parsed_response = suno_service.parse_status_response(result)
|
||||||
print(f"[get_song_status] Suno API response - suno_task_id: {suno_task_id}, status: {parsed_response.status}")
|
print(f"[get_song_status] Suno API response - suno_task_id: {suno_task_id}, status: {parsed_response.status}")
|
||||||
|
|
||||||
# SUCCESS 상태인 경우 백그라운드 태스크 실행
|
# SUCCESS 상태인 경우 첫 번째 클립 정보를 DB에 직접 저장
|
||||||
if parsed_response.status == "SUCCESS" and parsed_response.clips:
|
if parsed_response.status == "SUCCESS" and parsed_response.clips:
|
||||||
# 첫 번째 클립(clips[0])의 audioUrl과 duration 사용
|
# 첫 번째 클립(clips[0])의 audioUrl과 duration 사용
|
||||||
first_clip = parsed_response.clips[0]
|
first_clip = parsed_response.clips[0]
|
||||||
|
|
@ -245,7 +243,7 @@ async def get_song_status(
|
||||||
print(f"[get_song_status] Using first clip - id: {first_clip.id}, audio_url: {audio_url}, duration: {clip_duration}")
|
print(f"[get_song_status] Using first clip - id: {first_clip.id}, audio_url: {audio_url}, duration: {clip_duration}")
|
||||||
|
|
||||||
if audio_url:
|
if audio_url:
|
||||||
# suno_task_id로 Song 조회하여 store_name 가져오기
|
# suno_task_id로 Song 조회
|
||||||
song_result = await session.execute(
|
song_result = await session.execute(
|
||||||
select(Song)
|
select(Song)
|
||||||
.where(Song.suno_task_id == suno_task_id)
|
.where(Song.suno_task_id == suno_task_id)
|
||||||
|
|
@ -255,32 +253,15 @@ async def get_song_status(
|
||||||
song = song_result.scalar_one_or_none()
|
song = song_result.scalar_one_or_none()
|
||||||
|
|
||||||
if song and song.status != "completed":
|
if song and song.status != "completed":
|
||||||
# 이미 완료된 경우 백그라운드 작업 중복 실행 방지
|
# 첫 번째 클립의 audio_url과 duration을 직접 DB에 저장
|
||||||
# project_id로 Project 조회하여 store_name 가져오기
|
song.status = "completed"
|
||||||
project_result = await session.execute(
|
song.song_result_url = audio_url
|
||||||
select(Project).where(Project.id == song.project_id)
|
if clip_duration is not None:
|
||||||
)
|
|
||||||
project = project_result.scalar_one_or_none()
|
|
||||||
|
|
||||||
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}, 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":
|
|
||||||
# 이미 완료된 경우에도 duration이 다르면 업데이트
|
|
||||||
if clip_duration is not None and song.duration != clip_duration:
|
|
||||||
print(f"[get_song_status] Updating duration - suno_task_id: {suno_task_id}, old: {song.duration}, new: {clip_duration}")
|
|
||||||
song.duration = clip_duration
|
song.duration = clip_duration
|
||||||
await session.commit()
|
await session.commit()
|
||||||
else:
|
print(f"[get_song_status] Song updated - suno_task_id: {suno_task_id}, status: completed, song_result_url: {audio_url}, duration: {clip_duration}")
|
||||||
print(f"[get_song_status] SKIPPED - Song already completed, suno_task_id: {suno_task_id}")
|
elif song and song.status == "completed":
|
||||||
|
print(f"[get_song_status] SKIPPED - Song already completed, suno_task_id: {suno_task_id}")
|
||||||
|
|
||||||
print(f"[get_song_status] SUCCESS - suno_task_id: {suno_task_id}")
|
print(f"[get_song_status] SUCCESS - suno_task_id: {suno_task_id}")
|
||||||
return parsed_response
|
return parsed_response
|
||||||
|
|
@ -459,23 +440,36 @@ async def get_songs(
|
||||||
try:
|
try:
|
||||||
offset = (pagination.page - 1) * pagination.page_size
|
offset = (pagination.page - 1) * pagination.page_size
|
||||||
|
|
||||||
# 서브쿼리: task_id별 최신 Song의 id 조회 (completed 상태만)
|
# 서브쿼리: task_id별 최신 Song의 id 조회 (completed 상태, created_at 기준)
|
||||||
subquery = (
|
from sqlalchemy import and_
|
||||||
select(func.max(Song.id).label("max_id"))
|
|
||||||
|
# task_id별 최신 created_at 조회
|
||||||
|
latest_subquery = (
|
||||||
|
select(
|
||||||
|
Song.task_id,
|
||||||
|
func.max(Song.created_at).label("max_created_at")
|
||||||
|
)
|
||||||
.where(Song.status == "completed")
|
.where(Song.status == "completed")
|
||||||
.group_by(Song.task_id)
|
.group_by(Song.task_id)
|
||||||
.subquery()
|
.subquery()
|
||||||
)
|
)
|
||||||
|
|
||||||
# 전체 개수 조회 (task_id별 최신 1개만)
|
# 전체 개수 조회 (task_id별 최신 1개만)
|
||||||
count_query = select(func.count()).select_from(subquery)
|
count_query = select(func.count()).select_from(latest_subquery)
|
||||||
total_result = await session.execute(count_query)
|
total_result = await session.execute(count_query)
|
||||||
total = total_result.scalar() or 0
|
total = total_result.scalar() or 0
|
||||||
|
|
||||||
# 데이터 조회 (completed 상태, task_id별 최신 1개만, 최신순)
|
# 데이터 조회 (completed 상태, task_id별 created_at 기준 최신 1개만, 최신순)
|
||||||
query = (
|
query = (
|
||||||
select(Song)
|
select(Song)
|
||||||
.where(Song.id.in_(select(subquery.c.max_id)))
|
.join(
|
||||||
|
latest_subquery,
|
||||||
|
and_(
|
||||||
|
Song.task_id == latest_subquery.c.task_id,
|
||||||
|
Song.created_at == latest_subquery.c.max_created_at
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.where(Song.status == "completed")
|
||||||
.order_by(Song.created_at.desc())
|
.order_by(Song.created_at.desc())
|
||||||
.offset(offset)
|
.offset(offset)
|
||||||
.limit(pagination.page_size)
|
.limit(pagination.page_size)
|
||||||
|
|
|
||||||
|
|
@ -165,6 +165,7 @@ async def generate_video(
|
||||||
detail=f"task_id '{task_id}'에 해당하는 Song을 찾을 수 없습니다.",
|
detail=f"task_id '{task_id}'에 해당하는 Song을 찾을 수 없습니다.",
|
||||||
)
|
)
|
||||||
print(f"[generate_video] Song found - song_id: {song.id}, task_id: {task_id}, duration: {song.duration}")
|
print(f"[generate_video] Song found - song_id: {song.id}, task_id: {task_id}, duration: {song.duration}")
|
||||||
|
print(f"[generate_video] Music URL: {request_body.music_url}, Song duration: {song.duration}")
|
||||||
|
|
||||||
# 4. Video 테이블에 초기 데이터 저장
|
# 4. Video 테이블에 초기 데이터 저장
|
||||||
video = Video(
|
video = Video(
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue