Files
ai-trade/services/music-render/providers/local.py
gahusb eb34cbc0f7 fix(music-render): raise_for_status on MusicGen MP3 download (T6 follow-up)
Code review found: non-200 response from /audio/ endpoint was silently
written as MP3 body → corrupt file. Match T5 suno.py download pattern.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 04:57:14 +09:00

107 lines
4.1 KiB
Python

"""Local MusicGen Provider — Windows AI 머신의 native MusicGen 서버(:8765) 호출.
NAS music-lab/app/local_provider.py 이식. DB 호출만 webhook으로 변환.
"""
from __future__ import annotations
import logging
import os
import time
import requests
from nas_client import webhook_update_task, webhook_add_track
logger = logging.getLogger(__name__)
MUSIC_AI_SERVER_URL = os.getenv("MUSIC_AI_SERVER_URL", "")
MUSIC_MEDIA_ROOT = os.getenv("MUSIC_MEDIA_ROOT", "/mnt/nas/webpage/data/music")
MUSIC_MEDIA_BASE = os.getenv("MUSIC_MEDIA_URL_PREFIX", "/media/music")
def run_local_generation(task_id: str, params: dict) -> None:
"""MusicGen 생성 → /mnt/nas/.../music/{task_id}.mp3 저장 → add_track."""
try:
webhook_update_task(task_id, "processing", 10, "AI 서버에 연결 중...")
if not MUSIC_AI_SERVER_URL:
webhook_update_task(task_id, "failed", 0, "", error="MUSIC_AI_SERVER_URL 미설정")
return
webhook_update_task(task_id, "processing", 30, "음악 생성 중...")
resp = requests.post(f"{MUSIC_AI_SERVER_URL}/generate", json=params, timeout=30)
if resp.status_code != 200:
webhook_update_task(task_id, "failed", 0, "",
error=f"AI 서버 오류: {resp.status_code} {resp.text[:200]}")
return
ai_task_id = resp.json().get("task_id")
if not ai_task_id:
webhook_update_task(task_id, "failed", 0, "", error="AI 서버 응답에 task_id 없음")
return
remote_url = None
for _ in range(120):
time.sleep(5)
sr = requests.get(f"{MUSIC_AI_SERVER_URL}/status/{ai_task_id}", timeout=10)
sd = sr.json()
st = sd.get("status")
prog = sd.get("progress", 0)
msg = sd.get("message", "음악 생성 중...")
scaled = 30 + int(prog * 0.49)
webhook_update_task(task_id, "processing", scaled, msg)
if st == "succeeded":
remote_url = sd.get("audio_url")
break
elif st == "failed":
webhook_update_task(task_id, "failed", 0, "",
error=sd.get("error", "AI 서버 생성 실패"))
return
if not remote_url:
webhook_update_task(task_id, "failed", 0, "", error="AI 서버 타임아웃 (10분)")
return
webhook_update_task(task_id, "processing", 80, "파일 저장 중...")
filename = f"{task_id}.mp3"
os.makedirs(MUSIC_MEDIA_ROOT, exist_ok=True)
file_path = os.path.join(MUSIC_MEDIA_ROOT, filename)
dl = requests.get(remote_url, timeout=120, stream=True)
dl.raise_for_status()
with open(file_path, "wb") as f:
for chunk in dl.iter_content(chunk_size=8192):
f.write(chunk)
audio_url = f"{MUSIC_MEDIA_BASE}/{filename}"
genre = params.get("genre", "")
moods = params.get("moods", [])
mood_str = moods[0] if moods else "Original"
title = params.get("title") or (
f"{genre}{mood_str} Mix" if genre else f"{mood_str} Mix"
)
track = {
"title": title,
"genre": genre,
"moods": moods,
"instruments": params.get("instruments", []),
"duration_sec": params.get("duration_sec"),
"bpm": params.get("bpm"),
"key": params.get("key", ""),
"scale": params.get("scale", ""),
"prompt": params.get("prompt", ""),
"audio_url": audio_url,
"file_path": f"/app/data/{filename}",
"task_id": task_id,
"provider": "local",
}
webhook_add_track(task_id, "succeeded", 100, "생성 완료",
audio_url=audio_url, track=track)
except requests.Timeout:
webhook_update_task(task_id, "failed", 0, "", error="AI 서버 타임아웃")
except Exception as e:
logger.exception("local generation error task=%s", task_id)
webhook_update_task(task_id, "failed", 0, "", error=str(e))