bg_analyze가 구간 위치를 ranges_sec/raw_places/places로 state에 담는다

구간 탭에서 댓글 카드를 컷 단위로 배치하려면 "구간 N이 압축 타임라인의
어디인지"를 알아야 한다. 지금까지는 유튜브 구간을 다운로드·병합만 하고
그 경계 정보를 버렸다. 원본 시각(ranges_sec), 병합본 누적 구간
(raw_places), 무음 제거 후 압축 타임라인 구간(places)을 state에 추가한다.
파일 탭은 세 키 모두 빈 리스트로 유지되어 동작이 바뀌지 않는다(회귀 검증 완료).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
hehihoho3@gmail.com 2026-08-05 10:28:30 +09:00
parent bfba87288f
commit 58fab2b2a4

View File

@ -12,7 +12,7 @@ from __future__ import annotations
import asyncio import asyncio
import os import os
import time import time
from typing import AsyncIterator, Dict, List from typing import AsyncIterator, Dict, List, Tuple
from typing import Optional from typing import Optional
@ -227,9 +227,21 @@ async def bg_analyze(
t_all = time.perf_counter() t_all = time.perf_counter()
use_gemini = has_gemini_key() use_gemini = has_gemini_key()
# 구간 탭이 "구간 N이 타임라인의 어디인지" 알아야 자막·배치·장수를 컷 단위로 낼 수 있다.
# 파일 탭(유튜브 아님)은 빈 리스트 → 아무 데서도 안 쓰인다.
ranges_sec: List[Tuple[float, float]] = []
raw_places: List[Tuple[float, float]] = []
# ── 유튜브 여러 구간 다운로드 + 병합 (URL 입력 시) ── # ── 유튜브 여러 구간 다운로드 + 병합 (URL 입력 시) ──
if youtube: if youtube:
ranges = youtube.get("ranges") or [(youtube.get("start", ""), youtube.get("end", ""))] ranges = youtube.get("ranges") or [(youtube.get("start", ""), youtube.get("end", ""))]
from .paste import parse_time
c = 0.0
for s, e in ranges:
ss, ee = parse_time(str(s)), parse_time(str(e))
ranges_sec.append((ss, ee))
raw_places.append((c, c + (ee - ss)))
c += ee - ss
yield {"type": "step", "id": "download", "status": "start"} yield {"type": "step", "id": "download", "status": "start"}
rng_txt = ", ".join(f"{s}~{e}" for s, e in ranges) rng_txt = ", ".join(f"{s}~{e}" for s, e in ranges)
yield {"type": "log", "msg": f"유튜브 {len(ranges)}개 구간 다운로드·병합 중… [{rng_txt}]"} yield {"type": "log", "msg": f"유튜브 {len(ranges)}개 구간 다운로드·병합 중… [{rng_txt}]"}
@ -264,6 +276,8 @@ async def bg_analyze(
yield {"type": "error", "message": "오디오가 없거나 전부 무음입니다."} yield {"type": "error", "message": "오디오가 없거나 전부 무음입니다."}
return return
kept = sum(e - s for s, e in keep) kept = sum(e - s for s, e in keep)
# raw_places(병합본 좌표) → 압축 타임라인 좌표. 자막·배치·장수는 전부 이걸 쓴다.
places = _remap_placements(raw_places, keep) if raw_places else []
await _floor(t) await _floor(t)
yield {"type": "step", "id": "silence", "status": "done", yield {"type": "step", "id": "silence", "status": "done",
"elapsed": round(time.perf_counter() - t, 1), "elapsed": round(time.perf_counter() - t, 1),
@ -307,6 +321,7 @@ async def bg_analyze(
"video_clips": video_clips, "captions": captions, "total": total, "video_clips": video_clips, "captions": captions, "total": total,
"draft_name": draft_name, "title_top": title_top, "title_main": title_main, "draft_name": draft_name, "title_top": title_top, "title_main": title_main,
"channel": channel, "t_all": t_all, "channel": channel, "t_all": t_all,
"ranges_sec": ranges_sec, "raw_places": raw_places, "places": places,
}} }}