fix(critical): 유튜브 구간 탭 시간 형식 불일치로 전면 불능이던 것 수정
ytRanges()가 구간을 초 숫자로 /yt/analyze에 보내는데 다운로드 경로의 cut_youtube()→valid_time()은 MM:SS/HH:MM:SS만 통과시켜, 모든 분석이 다운로드 단계에서 "시간 형식 오류"로 죽고 있었다. 프런트는 입력칸에 이미 들어있는 fmtTime 포맷 문자열을 그대로 보내도록 되돌리고, 서버 (_parse_ranges)도 parse_time()으로 초 환산 후 valid_time이 통과하는 형식으로 재포맷해 프런트가 무엇을 보내든 다운로드 경로까지 안전하게 닿도록 방어를 추가했다. 스크래치패드 스크립트로 정상 케이스·구 버그 케이스·1시간 이상 구간까지 valid_time 통과를 확인했다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
77265f822e
commit
6ab3d998e9
@ -25,7 +25,7 @@ from capcut_agent.pipeline import (
|
||||
paste_steps as pipeline_paste_steps, _remap_caps,
|
||||
bg_analyze, bg_draft, bg_steps, _cards_by_cut, _card_paths,
|
||||
)
|
||||
from capcut_agent.paste import parse_paste
|
||||
from capcut_agent.paste import parse_paste, parse_time
|
||||
from capcut_agent.draft import DEFAULT_DRAFT_ROOT, list_drafts, repair_layers
|
||||
from capcut_agent import comments as hlab
|
||||
from capcut_agent import recommend
|
||||
@ -97,8 +97,28 @@ def _truthy(v: str) -> bool:
|
||||
return str(v).strip().lower() in ("1", "true", "on", "yes")
|
||||
|
||||
|
||||
def _fmt_range_time(sec: float) -> str:
|
||||
"""초 → `M:SS`(1시간 이상은 `H:MM:SS`). youtube.valid_time()이 강제하는 형식과
|
||||
맞춰야 한다 — youtube._fmt_hms()는 밀리초까지 붙어(`H:MM:SS.mmm`) valid_time을
|
||||
통과 못 하므로 여기 전용 포맷 함수를 따로 둔다."""
|
||||
sec = max(0, round(sec))
|
||||
h, rem = divmod(int(sec), 3600)
|
||||
m, s = divmod(rem, 60)
|
||||
return f"{h}:{m:02d}:{s:02d}" if h else f"{m}:{s:02d}"
|
||||
|
||||
|
||||
def _parse_ranges(raw: str) -> list[tuple[str, str]]:
|
||||
"""JSON [["mm:ss","mm:ss"],...] → [(start,end)]. 빈 값/파싱실패는 []."""
|
||||
"""JSON [[시작,끝],...] → [(M:SS, M:SS)]. 항목은 "16:07" 문자열/967 같은 초 숫자
|
||||
모두 허용해 parse_time()으로 초로 바꾼 뒤 valid_time()이 통과하는 M:SS/H:MM:SS로
|
||||
재포맷한다.
|
||||
|
||||
⚠ 함정: 프런트(ytRanges())가 초 숫자를 보내던 시절 이 정규화 없이 그대로
|
||||
저장했다가, 다운로드 경로의 cut_youtube()→valid_time()이 "967" 같은 순수
|
||||
숫자 문자열을 시간 형식으로 인정하지 않아 매 분석이 다운로드 단계에서
|
||||
"시간 형식 오류"로 죽었다(구간 탭 전면 불능). 여기서 형식을 통일해두면
|
||||
프런트가 무엇을 보내든(문자열이든 숫자든) 다운로드 경로까지 안전하게 도달한다.
|
||||
빈 값/파싱실패 항목은 건너뛴다.
|
||||
"""
|
||||
raw = (raw or "").strip()
|
||||
if not raw:
|
||||
return []
|
||||
@ -109,9 +129,11 @@ def _parse_ranges(raw: str) -> list[tuple[str, str]]:
|
||||
out: list[tuple[str, str]] = []
|
||||
for item in arr if isinstance(arr, list) else []:
|
||||
if isinstance(item, (list, tuple)) and len(item) == 2:
|
||||
s, e = str(item[0]).strip(), str(item[1]).strip()
|
||||
if s and e:
|
||||
out.append((s, e))
|
||||
try:
|
||||
s_sec, e_sec = parse_time(item[0]), parse_time(item[1])
|
||||
except (ValueError, TypeError):
|
||||
continue
|
||||
out.append((_fmt_range_time(s_sec), _fmt_range_time(e_sec)))
|
||||
return out
|
||||
|
||||
|
||||
|
||||
@ -935,13 +935,20 @@ function ytSec(v){
|
||||
return p.length===3?p[0]*3600+p[1]*60+p[2]:p.length===2?p[0]*60+p[1]:p[0];
|
||||
}
|
||||
function ytRanges(){
|
||||
// ⚠ 함정: 여기서 초 숫자로 바꿔 보내면 다운로드 경로(cut_youtube→valid_time)가
|
||||
// "MM:SS"/"HH:MM:SS"만 받아들여 매 분석이 다운로드 단계에서 죽는다(구간 탭
|
||||
// 전면 불능 — 서버는 방어적으로 재정규화하지만 프런트도 옛 fmtTime 동작대로
|
||||
// 문자열 그대로 보내는 게 안전하다). 입력칸은 index.html의 focusout 리스너가
|
||||
// 이미 fmtTime()으로 "16:07" 형식을 넣어 두므로 그 문자열을 그대로 쓴다.
|
||||
const out=[];
|
||||
document.querySelectorAll("#ranges .rng").forEach(row=>{
|
||||
const s=ytSec(row.querySelector(".rstart").value);
|
||||
if(s==null) return;
|
||||
let e=ytSec(row.querySelector(".rend").value);
|
||||
if(e==null) e=s+90; // 끝 비면 시작+1:30 (실행 로직과 동일)
|
||||
if(e>s) out.push([s,e]);
|
||||
const sv=(row.querySelector(".rstart").value||"").trim();
|
||||
const sSec=ytSec(sv);
|
||||
if(sSec==null) return;
|
||||
let ev2=(row.querySelector(".rend").value||"").trim();
|
||||
let eSec=ytSec(ev2);
|
||||
if(eSec==null){ eSec=sSec+90; ev2=fmtT(eSec); } // 끝 비면 시작+1:30 (실행 로직과 동일)
|
||||
if(eSec>sSec) out.push([sv,ev2]);
|
||||
});
|
||||
return out;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user