process_paste를 paste_analyze/paste_draft 두 조각으로 분할

댓글 매칭을 받아쓰기(asr_bottom) 다음에 넣으려면 파이프라인이
"받아쓰기까지" 상태에서 한 번 멈출 수 있어야 한다. 앞서 process_bg_template에
적용한 것과 같은 패턴(analyze/draft 분리 + state 이벤트 + 얇은 래퍼)을
process_paste에도 적용했다. 로직은 옮기기만 했고 바꾸지 않았다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
hehihoho3@gmail.com 2026-08-04 17:36:50 +09:00
parent 475b7b0f71
commit e79f2dfaa3

View File

@ -460,26 +460,30 @@ def _template_pos(white: bool = False):
return frame, bg, pos return frame, bg, pos
async def process_paste( def paste_steps(asr_bottom: bool) -> List[Dict[str, str]]:
"""붙여넣기 파이프라인의 manifest 스텝."""
steps = [{"id": "download", "label": "컷 정밀 다운로드·병합"}]
if asr_bottom:
steps.append({"id": "asr", "label": "받아쓰기 (Whisper)"})
steps.append({"id": "draft", "label": "템플릿 드래프트 생성"})
return steps
async def paste_analyze(
payload: dict, payload: dict,
draft_name: str, draft_name: str,
*, *,
video_scale: float = 1.0,
flip_horizontal: bool = False,
scene_split: bool = False,
comments_dir: str = "",
cards_fixed: bool = False,
card_cuts: Optional[List[int]] = None,
bg_white: bool = False,
remove_silence: bool = False, remove_silence: bool = False,
asr_bottom: bool = False, asr_bottom: bool = False,
name_suffix: str = "", name_suffix: str = "",
) -> AsyncIterator[dict]: ) -> AsyncIterator[dict]:
"""붙여넣기(JSON) 파이프라인: 컷 정밀 다운로드·병합 → 공급된 자막 2트랙으로 드래프트. """붙여넣기 파이프라인 앞부분: 컷 정밀 다운로드·병합 → probe → 컷 배치·자막 계산 →
[무음 제거] [asr_bottom 받아쓰기].
자막 배치 시간 = 순서 누적. asr_bottom=True JSON bottom 대신 병합본을 자막 배치 시간 = 순서 누적. asr_bottom=True JSON bottom 대신 병합본을
Whisper로 받아써 실제 발화 타이밍에 맞춘 하단 자막을 생성(effect/제목은 JSON 유지). Whisper로 받아써 실제 발화 타이밍에 맞춘 하단 자막을 생성(effect/제목은 JSON 유지).
payload: paste.parse_paste 결과 dict. payload: paste.parse_paste 결과 dict.
마지막에 `paste_draft` 이어줄 `{"type": "state", ...}` 낸다.
""" """
t_all = time.perf_counter() t_all = time.perf_counter()
cuts = payload["cuts"] # [(s, e, bottom, effect)] cuts = payload["cuts"] # [(s, e, bottom, effect)]
@ -488,12 +492,6 @@ async def process_paste(
title_main = payload.get("title_main", "") title_main = payload.get("title_main", "")
channel = payload.get("channel", "") channel = payload.get("channel", "")
steps = [{"id": "download", "label": "컷 정밀 다운로드·병합"}]
if asr_bottom:
steps.append({"id": "asr", "label": "받아쓰기 (Whisper)"})
steps.append({"id": "draft", "label": "템플릿 드래프트 생성"})
yield {"type": "manifest", "steps": steps}
# ── 컷 정밀 다운로드 + 병합 ── # ── 컷 정밀 다운로드 + 병합 ──
yield {"type": "step", "id": "download", "status": "start"} yield {"type": "step", "id": "download", "status": "start"}
yield {"type": "log", "msg": f"{len(cuts)}개 컷 정밀 다운로드·병합 중… (프레임 정확 컷)"} yield {"type": "log", "msg": f"{len(cuts)}개 컷 정밀 다운로드·병합 중… (프레임 정확 컷)"}
@ -587,6 +585,47 @@ async def process_paste(
"elapsed": round(time.perf_counter() - t, 1), "elapsed": round(time.perf_counter() - t, 1),
"detail": f"{method} · 자막 {len(bottom_caps)}"} "detail": f"{method} · 자막 {len(bottom_caps)}"}
yield {"type": "state", "state": {
"video_path": video_path, "meta": meta, "dur": dur, "cuts": cuts,
"placements": placements, "card_places": card_places,
"video_clips": video_clips, "timeline_dur": timeline_dur,
"bottom_caps": bottom_caps, "eff_caps": eff_caps,
"draft_name": draft_name, "title_top": title_top, "title_main": title_main,
"channel": channel, "t_all": t_all,
}}
async def paste_draft(
state: dict,
*,
video_scale: float = 1.0,
flip_horizontal: bool = False,
scene_split: bool = False,
comments_dir: str = "",
cards_fixed: bool = False,
card_cuts: Optional[List[int]] = None,
bg_white: bool = False,
) -> AsyncIterator[dict]:
"""붙여넣기 파이프라인 뒷부분: [장면분할] → 댓글 카드 → 드래프트 생성 → result.
`paste_analyze` `state` 이어받는다.
"""
video_path = state["video_path"]
meta = state["meta"]
dur = state["dur"]
cuts = state["cuts"]
placements = state["placements"]
card_places = state["card_places"]
video_clips = state["video_clips"]
timeline_dur = state["timeline_dur"]
bottom_caps = state["bottom_caps"]
eff_caps = state["eff_caps"]
draft_name = state["draft_name"]
title_top = state["title_top"]
title_main = state["title_main"]
channel = state["channel"]
t_all = state["t_all"]
# ── draft ── # ── draft ──
yield {"type": "step", "id": "draft", "status": "start"} yield {"type": "step", "id": "draft", "status": "start"}
t = time.perf_counter() t = time.perf_counter()
@ -646,6 +685,31 @@ async def process_paste(
} }
async def process_paste(payload, draft_name, *, video_scale=1.0, flip_horizontal=False,
scene_split=False, comments_dir="", cards_fixed=False,
card_cuts=None, bg_white=False, remove_silence=False,
asr_bottom=False, name_suffix="") -> AsyncIterator[dict]:
"""붙여넣기(JSON) 파이프라인 — 기존 호출부용 얇은 래퍼.
analyze/draft 조각을 연달아 부른다. `state` 이벤트는 밖으로 흘린다.
"""
yield {"type": "manifest", "steps": paste_steps(asr_bottom)}
state = None
async for ev in paste_analyze(payload, draft_name, remove_silence=remove_silence,
asr_bottom=asr_bottom, name_suffix=name_suffix):
if ev.get("type") == "state":
state = ev["state"]
continue
yield ev
if state is None:
return
async for ev in paste_draft(state, video_scale=video_scale,
flip_horizontal=flip_horizontal, scene_split=scene_split,
comments_dir=comments_dir, cards_fixed=cards_fixed,
card_cuts=card_cuts, bg_white=bg_white):
yield ev
STEPS: List[Dict[str, str]] = [ STEPS: List[Dict[str, str]] = [
{"id": "silence", "label": "무음·발화 분석"}, {"id": "silence", "label": "무음·발화 분석"},
{"id": "draft", "label": "점프컷 드래프트 생성"}, {"id": "draft", "label": "점프컷 드래프트 생성"},