diff --git a/docs/superpowers/specs/2026-08-04-구간탭-2단계-댓글추천-design.md b/docs/superpowers/specs/2026-08-04-남은두탭-댓글추천-design.md similarity index 69% rename from docs/superpowers/specs/2026-08-04-구간탭-2단계-댓글추천-design.md rename to docs/superpowers/specs/2026-08-04-남은두탭-댓글추천-design.md index c349b9f..c920ed5 100644 --- a/docs/superpowers/specs/2026-08-04-구간탭-2단계-댓글추천-design.md +++ b/docs/superpowers/specs/2026-08-04-남은두탭-댓글추천-design.md @@ -1,9 +1,24 @@ -# 유튜브 구간 탭 — 2단계 분할 + 구간별 댓글 추천 (설계) +# 컷별 댓글 추천을 남은 두 탭에 — 구간 탭(2단계 분할) + 붙여넣기 탭 (설계) 작성일: 2026-08-04 관련 코드: `capcut_agent/pipeline.py`, `capcut_agent/recommend.py`, `server/app.py`, `server/static/auto.js`, `server/static/index.html` 선행 스펙: `2026-08-04-컷별-댓글-추천-design.md` (자동 탭. 추천 엔진 `recommend.py`가 여기서 나왔다) +## 0. 목표 한 줄 + +**댓글 매칭이 일어나는 모든 곳에서 같은 추천·배치가 돌게 한다.** + +| 탭 | 상태 | 이 스펙에서 | +|---|---|---| +| 🤖 자동 | ✅ 완료(선행 스펙) | 손대지 않음 | +| ▶ 유튜브 구간 | 분:초만 봄 | **2단계로 쪼개고** Whisper 자막으로 추천 — §2~§12 | +| 📋 붙여넣기 | 댓글 매칭 UI 자체가 없음 | **매칭 화면 추가** — §13 | +| 📁 파일 | 로컬 파일이라 유튜브 URL이 없음 | 제외 — §14 | + +두 탭의 난이도가 크게 다르다. 구간 탭은 파이프라인을 쪼개는 구조 변경이고, +붙여넣기 탭은 **파이프라인·추천 엔진을 하나도 안 건드린다**(이미 컷 자막이 있고 +`process_paste`가 `card_cuts`를 받는다). 그래서 한 스펙에 같이 담는다. + ## 1. 배경 ▶ 유튜브 구간 탭의 `💬 구간 댓글 매칭`은 **댓글 본문의 분:초만** 본다. 실측(2026-08-04): @@ -194,28 +209,79 @@ Gemini 내용 매칭 경로**로 간다. 타임스탬프 우선·중복 금지 **어떤 경우에도 드래프트 생성을 막지 않는다**(다운로드 실패 제외 — 그건 영상이 없다). -## 13. 하지 않는 것 +## 13. 📋 붙여넣기 탭 -- **📋 붙여넣기 탭** — 별도 스펙. 구간 탭에서 만든 재사용 부품 위에 얹는 게 순서다. -- **📁 파일 탭 흐름 변경** — 내부만 쪼개고 겉보기는 그대로. +구간 탭과 달리 **2단계로 쪼갤 필요가 없다.** 자막이 붙여넣은 JSON 안에 이미 있어서 +영상을 받기 전에 추천이 끝난다. 그래서 지금 구간 탭이 쓰던 방식 — `💬 댓글 매칭` +버튼으로 먼저 고르고 `편집 시작` — 을 그대로 쓴다. + +**파이프라인과 추천 엔진은 한 줄도 안 건드린다:** +- 컷마다 `bottom` 자막이 있다 → `build_highlight_cuts()`가 그대로 걸려 Gemini 내용 매칭이 돈다 +- `process_paste(..., card_cuts=…)`는 선행 작업에서 이미 받는다 + +**새로 만드는 것:** + +| 위치 | 변경 | +|---|---| +| `POST /paste/comments` (신규) | 폼 `data`(붙여넣은 JSON) → `parse_paste` → `fetch_comments(url)` → `build_highlight_cuts` → `{cuts, need, comments, candidates, warnings}` | +| `POST /paste` | `cards`(PNG 다수)·`card_cuts` 폼 필드 추가. 저장 로직은 기존 `/youtube`의 것을 그대로 옮긴다 | +| `index.html` | 붙여넣기 탭에 `💬 댓글 매칭` 버튼 + 결과 영역 | +| `auto.js` | `window.pasteCC = {active, capture}` 다리 추가 (기존 `window.ytCC`와 같은 모양) | + +**주의**: 붙여넣기 탭의 `무음 제거`·`받아쓰기 자동 생성` 옵션을 켜면 타임라인이 줄거나 +자막이 바뀐다. 추천은 붙여넣은 JSON의 `bottom` 기준으로 하되, 배치 장수는 자동 탭과 +똑같이 `_cards_by_cut`이 압축 후 길이로 캡하고 버린 장수를 로그에 남긴다. 별도 처리 없음. + +## 13-1. 렌더러를 하나로 모은다 + +지금 카드 패널을 그리는 코드가 자동 탭(`onResult`)과 구간 탭(`ytMatch`) 두 군데에 +갈라져 있다. 여기에 붙여넣기 탭까지 더하면 세 갈래가 되어 한 곳만 고치는 실수가 난다. + +**`renderCutPanel(panelId, data, opts)` 하나로 모은다** — 컷/구간별 섹션, ⭐🤖➕ 배지, +검색창, `➕ 채우기` 섹션, 선택 상한을 전부 담당한다. 세 탭이 `panelId`만 달리해서 부른다 +(`hl.id` / `"yt"` / `"paste"`). 라벨 접두사(`컷` vs `구간`)는 `opts`로 넘긴다. + +이건 이번 작업에 필요해서 하는 정리이지, 무관한 리팩터링이 아니다. + +## 14. 하지 않는 것 + +- **📁 파일 탭** — 로컬 파일이라 댓글을 가져올 유튜브 URL이 없다. URL 칸을 따로 받으면 + 자막 기반 추천(🤖)은 가능하지만, 로컬 파일의 시각이 원본 영상 시각과 달라 + **분:초 매칭(⭐)은 원리적으로 불가**하다. 사용자가 제외를 선택했다. +- **📁 파일 탭 흐름 변경** — `process_bg_template` 내부만 쪼개고 겉보기는 그대로. - **유튜브 자동자막** — Whisper 받아쓰기를 쓰기로 했으므로 불필요. -- **🤖 자동 탭** — 손대지 않는다. +- **🤖 자동 탭** — 렌더러 통합(§13-1) 외에는 동작을 바꾸지 않는다. - **다운로드 캐시 추가** — 파이프라인을 쪼개면 재다운로드가 없어지므로 필요 없다. - **구간별 제목·출처 개별 지정** — 구간을 이어붙여 영상 하나를 만드는 것이므로 제목도 하나다. +- **붙여넣기 탭 2단계 분할** — 자막이 JSON에 있어 쪼갤 이유가 없다. -## 14. 검증 +## 15. 검증 자동 테스트 스위트가 없다(CLAUDE.md). 다음으로 확인한다. -1. `is_time_based()` / 구간별 `_whole_picks` 중복 방지 — 인라인 assert -2. 구간 자막 추출(압축 좌표 범위 → 캡션 이어붙이기) — 인라인 assert -3. `quotas` 외부 주입 시 `build_highlight_cuts` 동작 — 인라인 assert -4. 📁 파일 탭 회귀: 분할 전후 SSE 이벤트 순서·내용이 같은가 (실제 파일 1건) -5. 구간 2개짜리 실제 영상 1건 → 검토 화면에 구간별 섹션·자동 선택이 뜨는가 -6. 생성된 `draft_content.json`에서 카드 시간이 **각 구간의 압축 타임라인 범위 안**인가 -7. 표시한 `need` 장수 = 실제 배치된 카드 수 (버려지는 카드 0) -8. Gemini 키를 비워 폴백이 도는가 +**순수 함수 (인라인 assert, 네트워크·서버 없이)** +1. `is_time_based()` — 자막이 전부 비면 참, 하나라도 차 있으면 거짓. 컷 1개 통짜도 참 +2. 구간별 `_whole_picks` — 구간 간 댓글 중복 배정이 없는가 +3. 구간 자막 추출 — 압축 좌표 범위에 걸친 캡션만 이어붙이는가, 500자에서 자르는가 +4. `quotas` 외부 주입 시 `build_highlight_cuts` 동작 (안 넘기면 기존과 동일) -## 15. 열린 질문 +**회귀 (실행 확인)** +5. 📁 파일 탭: 분할 전후 SSE 이벤트 순서·내용이 같은가 (실제 파일 1건). `state` 이벤트가 밖으로 새지 않는가 +6. 🤖 자동 탭: 렌더러 통합 후 컷별 섹션·배지·검색·캡처가 그대로인가 + +**구간 탭 (실사용)** +7. 구간 2개짜리 실제 영상 1건 → 검토 화면에 구간별 섹션·자동 선택이 뜨는가 +8. `draft_content.json`에서 카드 시간이 **각 구간의 압축 타임라인 범위 안**인가 +9. 표시한 `need` 장수 = 실제 배치된 카드 수 (버려지는 카드 0) + +**붙여넣기 탭 (실사용)** +10. `💬 댓글 매칭` → 컷별 섹션·추천이 뜨는가 +11. 카드가 해당 컷 위에 깔리는가 +12. 댓글을 하나도 안 고르고 `편집 시작`해도 기존처럼 생성되는가 (하위호환) + +**공통** +13. Gemini 키를 비워 세 탭 모두 폴백이 도는가 + +## 16. 열린 질문 없음. 미결이 생기면 여기에 적고 구현 전에 사용자에게 묻는다.