문서: 파이프라인 analyze/draft 분할과 컷별 댓글 추천 근거를 ARCHITECTURE.md에 반영
댓글 매칭을 받아쓰기 뒤로 옮기려고 미리 파이프라인을 analyze/draft 두 조각으로 쪼갠 구조라, 왜 그렇게 했는지·state 이벤트는 내부 전용이라는 것·manifest는 래퍼가 낸다는 함정을 남기지 않으면 다음 사람이 되돌리기 쉽다. captions_for_places와 recommend.py의 is_time_based/quotas 변경, auto.js 렌더러 통합도 같은 이유로 기록. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
e79f2dfaa3
commit
0967e88e7b
@ -30,7 +30,7 @@ capcut2/
|
|||||||
│ ├─ app.py FastAPI. 엔드포인트 4개 + SSE 스트림
|
│ ├─ app.py FastAPI. 엔드포인트 4개 + SSE 스트림
|
||||||
│ └─ static/index.html UI 전체(단일 파일, 탭 3개 + 옵션 + SSE 렌더)
|
│ └─ static/index.html UI 전체(단일 파일, 탭 3개 + 옵션 + SSE 렌더)
|
||||||
└─ capcut_agent/
|
└─ capcut_agent/
|
||||||
├─ pipeline.py ★ 두 파이프라인(process_bg_template / process_paste)
|
├─ pipeline.py ★ 두 파이프라인, 각각 analyze/draft 두 조각 + 얇은 래퍼(§4)
|
||||||
├─ draft.py ★ CapCut 드래프트 생성(pycapcut) + JSON 후처리
|
├─ draft.py ★ CapCut 드래프트 생성(pycapcut) + JSON 후처리
|
||||||
├─ youtube.py yt-dlp 다운로드(단일/다중/정밀) + ffmpeg 병합
|
├─ youtube.py yt-dlp 다운로드(단일/다중/정밀) + ffmpeg 병합
|
||||||
├─ paste.py 붙여넣기 JSON 파서(관대한 파싱)
|
├─ paste.py 붙여넣기 JSON 파서(관대한 파싱)
|
||||||
@ -74,9 +74,27 @@ CapCut 인스펙터 값 ↔ pycapcut 변환:
|
|||||||
|
|
||||||
## 4. 탭별 파이프라인
|
## 4. 탭별 파이프라인
|
||||||
|
|
||||||
|
두 파이프라인 모두 내부적으로 **analyze/draft 두 조각 + 얇은 래퍼**로 나뉜다
|
||||||
|
(`process_bg_template` → `bg_analyze`+`bg_draft`, `process_paste` → `paste_analyze`+`paste_draft`).
|
||||||
|
**왜**: 댓글 매칭을 받아쓰기(ASR) 뒤로 옮기려면 "받아쓰기까지 끝낸 상태"에서 한 번 멈출
|
||||||
|
수 있어야 한다 — analyze 조각이 거기서 멈추고 그 결과를 draft 조각이 이어받는 구조로
|
||||||
|
미리 갈라놨다(1단계 기준으로는 사용자에게 보이는 동작은 그대로).
|
||||||
|
|
||||||
|
- `*_analyze`는 끝나면 다음 조각에 넘길 상태를 실어 `{"type":"state","state":{…}}`를
|
||||||
|
낸다. **이 이벤트는 내부 전용**이라 래퍼가 걸러내고 밖으로 흘리지 않는다 — 기존 UI가
|
||||||
|
모르는 타입이라 흘리면 로그에 정체불명 이벤트가 찍힌다. `t_all`(총 소요 측정
|
||||||
|
시작점)도 이 state에 실려 넘어가 `result.stats.elapsed`(전체 소요시간) 의미를 유지한다.
|
||||||
|
- `manifest`(진행 단계 목록) 이벤트는 조각 안이 아니라 **래퍼가 낸다** — 두 조각이
|
||||||
|
서로 다른 SSE 스트림에 걸쳐 쓰일 수 있어(예: 댓글 매칭이 끼어들면 analyze와 draft가
|
||||||
|
별개 요청이 됨) 각자 다른 manifest가 필요하기 때문. 목록 생성은
|
||||||
|
`bg_steps(youtube)` / `paste_steps(asr_bottom)`가 맡는다.
|
||||||
|
- `bg_draft(..., comment_cards=None)` — 주어지면 폴더에서 읽는 대신 그 목록을 그대로
|
||||||
|
쓴다(댓글 매칭 결과를 다음 단계에서 주입할 자리, 아직 미사용).
|
||||||
|
|
||||||
### 4-A. 📁 파일 / ▶ 유튜브 구간 → `process_bg_template()` (pipeline.py)
|
### 4-A. 📁 파일 / ▶ 유튜브 구간 → `process_bg_template()` (pipeline.py)
|
||||||
|
|
||||||
단계: `[download] → silence → asr → [scene] → draft`
|
단계: `[download] → silence → asr → [scene] → draft` (analyze 조각 = download~asr,
|
||||||
|
draft 조각 = scene~draft)
|
||||||
|
|
||||||
1. **download** (유튜브만): `cut_youtube_multi(url, ranges, out)` —
|
1. **download** (유튜브만): `cut_youtube_multi(url, ranges, out)` —
|
||||||
구간별로 `yt-dlp --download-sections`(h264 우선) 다운로드 → 구간 2개 이상이면
|
구간별로 `yt-dlp --download-sections`(h264 우선) 다운로드 → 구간 2개 이상이면
|
||||||
@ -109,7 +127,8 @@ CapCut 인스펙터 값 ↔ pycapcut 변환:
|
|||||||
|
|
||||||
LLM이 만든 편집안 JSON을 **그대로** 사용. 무음컷·ASR **기본 없음**(옵션으로 무음 제거 가능).
|
LLM이 만든 편집안 JSON을 **그대로** 사용. 무음컷·ASR **기본 없음**(옵션으로 무음 제거 가능).
|
||||||
|
|
||||||
단계: `download(컷 정밀) → [remove_silence] → [scene] → draft`
|
단계: `download(컷 정밀) → [remove_silence] → [asr_bottom] → [scene] → draft`
|
||||||
|
(analyze 조각 = download~[asr_bottom], draft 조각 = [scene]~draft)
|
||||||
|
|
||||||
1. **파싱** (`paste.parse_paste`): 관대한 JSON 파싱 —
|
1. **파싱** (`paste.parse_paste`): 관대한 JSON 파싱 —
|
||||||
`json.loads(strict=False)`(자막 안 실제 줄바꿈 허용), 코드펜스(```) 자동 제거,
|
`json.loads(strict=False)`(자막 안 실제 줄바꿈 허용), 코드펜스(```) 자동 제거,
|
||||||
@ -245,6 +264,24 @@ title_top 서브제목 / title_main 메인제목 / channel 출처 / effect 효
|
|||||||
- 출처: 사용자가 h-lab(https://h-lab.tolag.shop/comment-cards)에서 실제 유튜브 댓글을
|
- 출처: 사용자가 h-lab(https://h-lab.tolag.shop/comment-cards)에서 실제 유튜브 댓글을
|
||||||
카드 PNG로 저장해 폴더에 넣음. (향후: h-lab API 연동해 완전 자동화 아이디어 있음)
|
카드 PNG로 저장해 폴더에 넣음. (향후: h-lab API 연동해 완전 자동화 아이디어 있음)
|
||||||
|
|
||||||
|
#### 컷별 댓글 추천 근거 (recommend.py, 자동 탭)
|
||||||
|
|
||||||
|
- `pipeline.captions_for_places(captions, places, *, cap=500)` — 컷 구간마다 그 구간에
|
||||||
|
걸친 자막을 이어붙여 댓글 추천의 근거 텍스트를 만든다. **`captions`·`places` 둘 다
|
||||||
|
같은(압축) 타임라인 좌표여야 한다** — 좌표계가 다르면 엉뚱한 컷에 엉뚱한 자막이
|
||||||
|
붙는다. 겹치면 포함, 경계에 닿기만 하면 제외. 500자에서 자른다(그 이상은 Gemini
|
||||||
|
토큰만 먹고 매칭 정확도가 안 오름).
|
||||||
|
- `recommend.is_time_based(cuts)` (구 `is_whole`) — **모든 컷의 자막이 비어 있으면**
|
||||||
|
시각 기반 배정으로 판단한다. 예전엔 "컷 1개 + 자막 없음"만 걸렸는데, 자막 없는
|
||||||
|
구간이 여러 개인 경우(▶ 유튜브 구간 탭)도 걸리도록 일반화했다. 통짜(컷 1개) 모드는
|
||||||
|
이 규칙의 특수 케이스가 됐다.
|
||||||
|
- `recommend._time_based_picks(cuts, comments, quotas)` (구 `_whole_picks`) — 컷마다
|
||||||
|
슬롯을 배정하되 `used` 집합을 컷 사이에 공유해 **한 댓글이 두 컷에 중복 배정되지
|
||||||
|
않게** 한다(먼저 도는 컷이 우선).
|
||||||
|
- `recommend.build_highlight_cuts(hl, comments, *, key=None, quotas=None)` — `quotas`를
|
||||||
|
주면 그대로 쓰고, 안 주면 `quotas_for(cuts)`(원본 컷 길이 기준)로 계산한다. 무음
|
||||||
|
제거 뒤 실제 길이 기준 장수를 밖에서 계산해 넘기는 경로를 위한 훅.
|
||||||
|
|
||||||
## 7. yt-dlp 관련 (youtube.py) — 함정 모음
|
## 7. yt-dlp 관련 (youtube.py) — 함정 모음
|
||||||
|
|
||||||
- **JS 런타임 필수**: 최신 유튜브는 JS 챌린지 필요. `_js_runtime_args()`가
|
- **JS 런타임 필수**: 최신 유튜브는 JS 챌린지 필요. `_js_runtime_args()`가
|
||||||
@ -283,6 +320,12 @@ title_top 서브제목 / title_main 메인제목 / channel 출처 / effect 효
|
|||||||
장면분할(**기본 체크**) / 배경 흰색(**기본 체크**) / 무음 제거(붙여넣기용, 기본 꺼짐).
|
장면분할(**기본 체크**) / 배경 흰색(**기본 체크**) / 무음 제거(붙여넣기용, 기본 꺼짐).
|
||||||
- 헤더 우측 고정 링크: ✨ AI Studio(aistudio.google.com), 💬 댓글 카드(h-lab).
|
- 헤더 우측 고정 링크: ✨ AI Studio(aistudio.google.com), 💬 댓글 카드(h-lab).
|
||||||
- 완료 시 결과 카드(총 소요시간 포함) + "완료되면 CapCut 자동 실행" 체크.
|
- 완료 시 결과 카드(총 소요시간 포함) + "완료되면 CapCut 자동 실행" 체크.
|
||||||
|
- `server/static/auto.js`(🤖 자동 탭): 컷별 카드 패널 렌더는 `renderCutPanel(box,
|
||||||
|
panelId, data, opts)` 하나로 통합돼 있다 — 자동 탭(`onResult`)과 ▶ 유튜브 구간 탭
|
||||||
|
`💬 구간 댓글 매칭`(`ytMatch`)이 이 함수를 같이 쓴다. **왜**: 예전엔 두 갈래로 따로
|
||||||
|
구현돼 있었는데, 여기에 📋 붙여넣기 탭까지 더하면 세 갈래가 되어 한 곳만 고치는
|
||||||
|
실수가 나기 쉽다. `data.cuts`가 있으면 컷별 섹션(`컷 N · X초 · 카드 Q장 — 자막`),
|
||||||
|
없으면 기존 ⭐/➕ 폴백을 그린다.
|
||||||
|
|
||||||
## 9. 현재 고정값 치트시트
|
## 9. 현재 고정값 치트시트
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user