o2o-castad-backend/app/song/worker/song_task.py

102 lines
3.9 KiB
Python

"""
Song Background Tasks
노래 생성 관련 백그라운드 태스크를 정의합니다.
"""
from datetime import date
from pathlib import Path
import aiofiles
import httpx
from sqlalchemy import select
from app.database.session import AsyncSessionLocal
from app.song.models import Song
from app.utils.common import generate_task_id
from config import prj_settings
async def download_and_save_song(
task_id: str,
audio_url: str,
store_name: str,
) -> None:
"""백그라운드에서 노래를 다운로드하고 Song 테이블을 업데이트합니다.
Args:
task_id: 프로젝트 task_id
audio_url: 다운로드할 오디오 URL
store_name: 저장할 파일명에 사용할 업체명
"""
print(f"[download_and_save_song] START - task_id: {task_id}, store_name: {store_name}")
try:
# 저장 경로 생성: media/song/{날짜}/{uuid7}/{store_name}.mp3
today = date.today().strftime("%Y-%m-%d")
unique_id = await generate_task_id()
# 파일명에 사용할 수 없는 문자 제거
safe_store_name = "".join(
c for c in store_name if c.isalnum() or c in (" ", "_", "-")
).strip()
safe_store_name = safe_store_name or "song"
file_name = f"{safe_store_name}.mp3"
# 절대 경로 생성
media_dir = Path("media") / "song" / today / unique_id
media_dir.mkdir(parents=True, exist_ok=True)
file_path = media_dir / file_name
print(f"[download_and_save_song] Directory created - path: {file_path}")
# 오디오 파일 다운로드
print(f"[download_and_save_song] Downloading audio - task_id: {task_id}, url: {audio_url}")
async with httpx.AsyncClient() as client:
response = await client.get(audio_url, timeout=60.0)
response.raise_for_status()
async with aiofiles.open(str(file_path), "wb") as f:
await f.write(response.content)
print(f"[download_and_save_song] File saved - task_id: {task_id}, path: {file_path}")
# 프론트엔드에서 접근 가능한 URL 생성
relative_path = f"/media/song/{today}/{unique_id}/{file_name}"
base_url = f"http://{prj_settings.PROJECT_DOMAIN}"
file_url = f"{base_url}{relative_path}"
print(f"[download_and_save_song] URL generated - task_id: {task_id}, url: {file_url}")
# Song 테이블 업데이트 (새 세션 사용)
async with AsyncSessionLocal() as session:
# 여러 개 있을 경우 가장 최근 것 선택
result = await session.execute(
select(Song)
.where(Song.task_id == task_id)
.order_by(Song.created_at.desc())
.limit(1)
)
song = result.scalar_one_or_none()
if song:
song.status = "completed"
song.song_result_url = file_url
await session.commit()
print(f"[download_and_save_song] SUCCESS - task_id: {task_id}, status: completed")
else:
print(f"[download_and_save_song] Song NOT FOUND in DB - task_id: {task_id}")
except Exception as e:
print(f"[download_and_save_song] EXCEPTION - task_id: {task_id}, error: {e}")
# 실패 시 Song 테이블 업데이트
async with AsyncSessionLocal() as session:
# 여러 개 있을 경우 가장 최근 것 선택
result = await session.execute(
select(Song)
.where(Song.task_id == task_id)
.order_by(Song.created_at.desc())
.limit(1)
)
song = result.scalar_one_or_none()
if song:
song.status = "failed"
await session.commit()
print(f"[download_and_save_song] FAILED - task_id: {task_id}, status updated to failed")