fix(music-lab): pipeline 동기 작업을 asyncio.to_thread로 — 이벤트 루프 블로킹 해결

video.generate/thumb.generate/youtube.upload_video는 동기 함수로 ffmpeg subprocess(최대 5분)와
google-api-python-client(최대 10분)를 호출함. async run_step에서 직접 호출하면 이벤트 루프가
블로킹돼 후속 요청이 504로 타임아웃되고 텔레그램 폴링도 끊김.

asyncio.to_thread로 감싸 스레드 풀에서 실행 — 이벤트 루프 자유.
This commit is contained in:
2026-05-08 22:57:33 +09:00
parent 2c13e7cc85
commit 6d416aab78

View File

@@ -1,4 +1,5 @@
"""파이프라인 오케스트레이터 — 단계별 BackgroundTask 등록 및 산출물 → DB 반영."""
import asyncio
import json
import logging
import os
@@ -106,7 +107,8 @@ async def _run_video(p, track):
vd = setup["visual_defaults"]
audio_path = track.get("file_path") or _local_path(track.get("audio_url", ""))
cover_path = _local_path(p["cover_url"])
out = video.generate(
out = await asyncio.to_thread(
video.generate,
pipeline_id=p["id"], audio_path=audio_path, cover_path=cover_path,
genre=track.get("genre", "default"),
duration_sec=track.get("duration_sec", 120),
@@ -117,8 +119,11 @@ async def _run_video(p, track):
async def _run_thumb(p, track, feedback):
video_path = _local_path(p["video_url"])
out = thumb.generate(pipeline_id=p["id"], video_path=video_path,
track_title=track.get("title", ""), overlay_text=True)
out = await asyncio.to_thread(
thumb.generate,
pipeline_id=p["id"], video_path=video_path,
track_title=track.get("title", ""), overlay_text=True,
)
return {"next_state": "thumb_pending", "fields": {"thumbnail_url": out["url"]}}
@@ -152,7 +157,8 @@ async def _run_publish(p, track):
setup = db.get_youtube_setup()
meta = json.loads(p["metadata_json"]) if p.get("metadata_json") else {}
privacy = setup["publish_policy"].get("privacy", "private")
result = youtube.upload_video(
result = await asyncio.to_thread(
youtube.upload_video,
video_path=_local_path(p["video_url"]),
thumbnail_path=_local_path(p["thumbnail_url"]) if p.get("thumbnail_url") else None,
metadata=meta, privacy=privacy,