feat(music-render): providers/local.py — MusicGen client (SP-5)
NAS music-lab/app/local_provider.py 이식. DB 호출 webhook 변환. MusicGen 호스트는 host.docker.internal:8765 (Windows native). 결과 MP3는 /mnt/nas/webpage/data/music/에 직접 저장. Plan-B-Music Phase 2. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
105
services/music-render/providers/local.py
Normal file
105
services/music-render/providers/local.py
Normal file
@@ -0,0 +1,105 @@
|
||||
"""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)
|
||||
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))
|
||||
Reference in New Issue
Block a user