feat: 구간 JSON에 title_top/title_main 선택 필드 — 검토 화면 제목 자동 채움
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
b49946a4cd
commit
f38776d71f
@ -93,10 +93,12 @@ def _extract_json_str(text: str) -> str:
|
||||
|
||||
|
||||
def parse_candidates(text: str, *, src: str = "Step 1 응답") -> List[Dict]:
|
||||
"""`{"candidates":[{id,start_time,end_time,reason}, …]}` → [{id,start,end,reason}] (초).
|
||||
"""`{"candidates":[{id,start_time,end_time,reason}, …]}` → [{id,start,end,reason,title_top,title_main}] (초).
|
||||
|
||||
Gemini Step1 응답과 사용자가 직접 붙여넣는 구간 JSON이 **같은 형식**이라 둘이 공유한다.
|
||||
코드펜스(```)·앞뒤 잡텍스트는 `_extract_json_str`이 걷어낸다.
|
||||
`title_top`/`title_main`은 선택 — 구간 JSON에 있으면 검토 화면 제목 입력칸에
|
||||
미리 채워진다(Step1 응답에는 없으므로 빈 문자열).
|
||||
"""
|
||||
try:
|
||||
data = json.loads(_extract_json_str(text), strict=False)
|
||||
@ -116,7 +118,9 @@ def parse_candidates(text: str, *, src: str = "Step 1 응답") -> List[Dict]:
|
||||
continue
|
||||
if e > s:
|
||||
out.append({"id": int(c.get("id") or i), "start": s, "end": e,
|
||||
"reason": str(c.get("reason") or "").strip()})
|
||||
"reason": str(c.get("reason") or "").strip(),
|
||||
"title_top": str(c.get("title_top") or "").strip(),
|
||||
"title_main": str(c.get("title_main") or "").strip()})
|
||||
if not out:
|
||||
raise RuntimeError(f"{src}: 유효한 구간이 하나도 없습니다.")
|
||||
return out
|
||||
|
||||
@ -526,13 +526,16 @@ async def auto_stream(aid: str) -> StreamingResponse:
|
||||
|
||||
def _whole_hl(c: dict) -> dict:
|
||||
"""구간 통짜 하이라이트 — 컷 편집 없이 구간 전체가 컷 1개.
|
||||
제목은 비워 두고 `editable_title` 로 UI에서 직접 입력받는다.
|
||||
제목은 `editable_title` 로 UI에서 직접 입력받되, 구간 JSON에
|
||||
title_top/title_main 이 있으면 그 값을 입력칸에 미리 채운다.
|
||||
whole(Gemini Step1) 과 wpaste(구간 JSON 붙여넣기) 가 공유."""
|
||||
total = c["end"] - c["start"]
|
||||
return {
|
||||
"id": c["id"], "start": c["start"], "end": c["end"],
|
||||
"reason": c["reason"],
|
||||
"paste": {"url": url, "title_top": "", "title_main": "",
|
||||
"paste": {"url": url,
|
||||
"title_top": c.get("title_top") or "",
|
||||
"title_main": c.get("title_main") or "",
|
||||
"channel": "",
|
||||
"cuts": [{"start": c["start"], "end": c["end"],
|
||||
"bottom": "", "effect": ""}]},
|
||||
|
||||
@ -532,15 +532,15 @@ function curMode(){
|
||||
const MODE_HELP={
|
||||
full:"Gemini가 영상에서 하이라이트 구간 5개를 골라 편집안을 만들고, h-lab에서 그 구간을 언급한 댓글을 찾아옵니다. 무음 제거·받아쓰기(Whisper)는 댓글 매칭을 위해 항상 적용됩니다(끌 수 없음).",
|
||||
whole:"Gemini가 구간 5개만 고르고(Step1), 각 구간을 컷 편집 없이 통짜로 만듭니다. 무음 제거·받아쓰기(Whisper)는 댓글 매칭을 위해 항상 적용됩니다(끌 수 없음).",
|
||||
wpaste:"Gemini를 쓰지 않습니다. 구간 JSON(candidates 5개)을 붙여넣으면 그 구간을 그대로 통짜로 만듭니다. 제목(윗줄·아랫줄)은 댓글 선택 화면에서 ID별로 직접 씁니다. 무음 제거·받아쓰기(Whisper)는 댓글 매칭을 위해 항상 적용됩니다(끌 수 없음).",
|
||||
wpaste:"Gemini를 쓰지 않습니다. 구간 JSON(candidates 5개)을 붙여넣으면 그 구간을 그대로 통짜로 만듭니다. JSON에 title_top·title_main이 있으면 제목(윗줄·아랫줄)이 자동으로 채워지고, 검토 화면에서 ID별로 고칠 수 있습니다. 무음 제거·받아쓰기(Whisper)는 댓글 매칭을 위해 항상 적용됩니다(끌 수 없음).",
|
||||
paste:"Gemini를 쓰지 않습니다. 오팔에서 받은 JSON 5개를 통째로 붙여넣으면 댓글 선택 화면으로 갑니다. URL을 비우면 JSON 안의 url을 씁니다. 무음 제거·받아쓰기(Whisper)는 댓글 매칭을 위해 항상 적용됩니다(끌 수 없음).",
|
||||
};
|
||||
const PASTE_UI={
|
||||
paste:{label:"오팔 JSON 붙여넣기 (여러 개를 통째로 — 사이에 구분선·타이틀 후보가 섞여 있어도 됨)",
|
||||
ph:'오팔 Step 3 결과(JSON 코드블록) 5개를 순서대로 전부 붙여넣으세요.\n블록 ②·③ 텍스트가 섞여 들어와도 자동으로 JSON만 골라냅니다.',
|
||||
note:"(선택 — 비우면 JSON의 url 사용)"},
|
||||
wpaste:{label:"구간 JSON 붙여넣기 (candidates 배열 — 구간 5개)",
|
||||
ph:'{\n "candidates": [\n {"id": 1, "start_time": "14:28", "end_time": "16:15", "reason": "구간 선정 이유"},\n … (총 5개)\n ]\n}',
|
||||
wpaste:{label:"구간 JSON 붙여넣기 (candidates 배열 — 구간 5개 · title_top/title_main 선택)",
|
||||
ph:'{\n "candidates": [\n {"id": 1, "start_time": "14:28", "end_time": "16:15", "reason": "구간 선정 이유",\n "title_top": "상단 제목 (선택)", "title_main": "메인 제목 (선택)"},\n … (총 5개)\n ]\n}',
|
||||
note:"(필수 — 구간 JSON에는 URL이 없습니다)"},
|
||||
};
|
||||
function applyMode(){
|
||||
@ -654,6 +654,11 @@ function onResult(ev){
|
||||
(t.kind?" — "+esc(t.kind):"")+"</option>").join("")+"</select></div>";
|
||||
}
|
||||
card.innerHTML=html;
|
||||
if(hl.editable_title){ // 구간 JSON의 title_top/title_main 미리 채움 (.value라 따옴표 이스케이프 불필요)
|
||||
const ti=card.querySelector("#hltop-"+hl.id),mi=card.querySelector("#hlmain-"+hl.id);
|
||||
if(ti) ti.value=hl.paste.title_top||"";
|
||||
if(mi) mi.value=hl.paste.title_main||"";
|
||||
}
|
||||
card.dataset.titles=JSON.stringify(opts);
|
||||
card.appendChild(cutsSection(hl)); // 영상 편집안(컷 목록·JSON) — 기본 접힘
|
||||
R.appendChild(card);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user