날짜: 2026-03-19
레포: parksy-audio / local-agent/bot.py
작업자: 박씨 + Claude Code
텔레그램에서 오디오 파일을 보내고 "포레 스타일로 편곡해줘" 같은 지시를 하면
Claude가 runner.py 파이프라인을 돌리는데, 처리 시간이 길어질 경우:
# Before
await asyncio.wait_for(claude.run(prompt), timeout=300)
# After
await asyncio.wait_for(claude.run(prompt), timeout=1800)
음원 생성 + 마스터링 파이프라인은 10~20분 소요되는 작업이므로 30분으로 확장.
async def _heartbeat():
"""30초마다 경과 시간 표시 — 도구 실행 중 무응답 방지"""
prev_text = ""
while True:
await asyncio.sleep(30)
elapsed = int(asyncio.get_event_loop().time() - start_ts)
mins, secs = divmod(elapsed, 60)
elapsed_str = f"{mins}분 {secs}초" if mins else f"{secs}초"
cur_text = streamer._text
if cur_text == prev_text:
if not cur_text or cur_text.startswith("⏳"):
streamer.on_text(f"⏳ 작업 중... ({elapsed_str} 경과)")
prev_text = streamer._text
await streamer.tick()
문제: _run_claude 함수가 결과물 탐지 시 WAV/MP4만 체크하고 M4A는 체크 안 했음.
runner.py 최종 출력이 outputs/*.m4a 인데 코드가 이를 못 잡아서 텔레그램으로 안 보냄.
# Before: WAV + MP4만 체크
before_renders = set(glob.glob(os.path.join(RENDERS_DIR, "*.wav")))
before_videos = set(glob.glob(os.path.join(VIDEOS_DIR, "*.mp4")))
# After: M4A도 추가
before_m4as = set(glob.glob(os.path.join(M4A_OUT_DIR, "*.m4a")))
새 M4A 감지 시 플로우:
runner.py → M4A 생성
→ _send_m4a() → 텔레그램으로 오디오 파일 전송
→ _pending[chat_id] 등록
→ "✅ 완성! /approve 로 YouTube 비공개 업로드."
파일 드랍 시 캡션 없으면 자동 실행 대신 Claude가 옵션을 물어보는 방식으로 변경:
if caption:
prompt = f"지시에 맞게 옵션 구성 후 파이프라인 실행해줘."
else:
prompt = f"파일 받았다고 알리고, 옵션들 중 어떻게 처리할지 항목별로 짧게 물어봐줘. 한 번에 다 물어보고 답 받은 다음에 실행해."
파이프라인 옵션 가이드(pipeline_guide)를 프롬프트에 내장해서 Claude가 정확히 물어볼 수 있게 함.
사용자 → 오디오 파일 드랍
→ [캡션 있음] Claude가 바로 파이프라인 실행
→ [캡션 없음] Claude가 emotion/style/iterations 등 물어봄
→ 사용자 답변
→ Claude가 runner.py 실행
→ ⏳ 작업 중... (1분 경과) ... (2분 경과) ... 30초마다 업데이트
→ M4A 생성 감지
→ 텔레그램으로 오디오 파일 전송
→ "✅ 완성! /approve 로 YouTube 업로드"
→ 사용자가 /approve
→ YouTube 비공개 업로드
→ URL 텔레그램으로 수신
| 파일 | 변경 내용 | |
| `local-agent/bot.py` | 타임아웃 1800s, 하트비트, M4A 감지, 대화형 파이프라인 |
|---|