# task-1094.1 완료 보고서: YouTube 요약 파이프라인 수정

**S**: InsuWiki YouTube 요약 파이프라인이 Track A(InnerTube Android API → FAILED_PRECONDITION), Track B(HTML 파싱 → URL 필드 제거)로 인해 모든 영상이 Track C(제목+설명만)로 빠지면서 요약 품질이 저하되고 있다.

**C**: YouTube가 API 응답 구조를 변경하여 기존 Android 클라이언트 파라미터와 HTML 기반 오디오 URL 추출이 모두 차단됨. yt-dlp 기반 Whisper 전사는 수동 테스트에서 정상 작동 확인됨.

**Q**: yt-dlp 기반 오디오 추출 + InnerTube API 업데이트 + 처리 로그 시스템으로 자막 파이프라인을 정상화할 수 있는가?

**A**: 3가지 수정을 완료했다. (1) Whisper GPU 서비스에 `/v1/youtube-transcribe` 엔드포인트 추가 (yt-dlp→Whisper 일괄 처리), (2) InnerTube API를 WEB→TVHTML5 다단계 fallback으로 변경, (3) Google Drive 처리 로그 + 메타데이터 강화. 테스트 27건 전체 통과, pyright 에러 0건.

---

## 수정 내용

### 1. Track B 수정 — yt-dlp 기반 오디오 추출

**server.py** (`/home/jay/workspace/services/whisper-gpu/server.py`)
- `POST /v1/youtube-transcribe` 엔드포인트 추가
- `video_id` 검증 (정규식 `^[a-zA-Z0-9_-]{11}$`)
- `asyncio.create_subprocess_exec`로 yt-dlp 비동기 실행 (300초 timeout)
- `run_transcription()`으로 전사 → `{ text, source: "whisper_local_ytdlp", duration }` 반환
- finally 블록에서 임시 파일 삭제

**whisperStt.ts** (insuwiki worktree)
- 기존 HTML 파싱/오디오 다운로드/직접 Whisper 호출 코드 제거 (451행 → 91행, 360행 감소)
- `callYoutubeTranscribe()`: `POST ${LOCAL_WHISPER_URL}/v1/youtube-transcribe` (600초 timeout)
- `whisperTranscribe()` 시그니처 동일 유지 (기존 호출처 변경 불필요)

### 2. Track A 수정 — InnerTube API 업데이트

**crawlYoutubeChannels.ts**
- `fetchYouTubeTranscript()` → 다단계 fallback:
  - 방법 1: WEB 클라이언트 (`clientName: 'WEB'`, `clientVersion: '2.20240313.00.00'`)
  - 방법 2: TVHTML5_SIMPLY_EMBEDDED_PLAYER (`clientVersion: '2.0'`, embedUrl)
  - 둘 다 실패 시 null → Whisper STT fallback (기존)
- `fetchUrlContentWithUA()` User-Agent: Android → Chrome 웹 브라우저로 변경
- 반환 타입: `string | null` → `TranscriptResult | null` (captionLanguage, captionType 포함)

### 3. 처리 로그 시스템 (신규)

- `VideoProcessingLog` 인터페이스: 영상별 처리 방식/길이/상태/시간 수집
- `buildProcessingLogMarkdown()`: 마크다운 테이블 로그 생성
- 파이프라인 완료 시 `04_유튜브요약/_처리로그/YYYY-MM-DD_크롤링로그.md` Drive 업로드
- `buildMarkdownContent()` 메타데이터 강화: 전사 방식/언어/유형/길이 표시
- Firestore `youtube_knowledge` 필드 추가: `transcriptionLength`, `transcriptionModel`, `processingTimeMs`

---

## 수정 파일 목록

| 파일 | 변경 | 위치 |
|------|------|------|
| `services/whisper-gpu/server.py` | youtube-transcribe 엔드포인트 추가 (+113행) | workspace 메인 |
| `functions/src/whisperStt.ts` | yt-dlp API 호출로 전환 (-398행, +91행) | insuwiki worktree |
| `functions/src/crawlYoutubeChannels.ts` | InnerTube fallback + 처리 로그 (+245행) | insuwiki worktree |
| `functions/src/__tests__/youtubeWhisper.test.ts` | 신규 테스트 12건 추가 (+206행) | insuwiki worktree |

---

## 테스트 결과

- **vitest**: 27/27 통과 (기존 15 + 신규 12), 223ms
- **tsc --noEmit**: whisperStt.ts 에러 0건, crawlYoutubeChannels.ts 기존 findNearest 타입 미인식 1건만 (기존 issue, Firestore vector search 타입 정의 미흡)
- **pyright (server.py)**: 0 errors, 0 warnings

---

## 발견 이슈 및 해결

### 자체 해결 (3건)
1. **fetchUrlContentWithUA의 Android UA 잔존** — 웹 브라우저 UA로 통일 (자막 요청 시에도 WEB 클라이언트와 일관성 유지)
2. **buildMarkdownContent 하위호환성** — 기존 호출처가 7개 매개변수만 전달해도 동작하도록 신규 매개변수를 optional로 설계
3. **server.py의 _VIDEO_ID_RE를 함수 내부에서 매번 컴파일** — 정규식 컴파일이 경량이고, 엔드포인트 호출 빈도가 낮아 성능 영향 미미. 모듈 레벨로 이동 가능하나 현재 구조에서는 불필요한 최적화

### 범위 외 미해결 (1건)
1. **crawlYoutubeChannels.ts line 932 findNearest 타입 에러** — Firestore SDK의 vector search 타입 정의가 아직 불완전한 기존 이슈. 런타임에서는 정상 동작.

---

## 머지 판단

- **머지 필요**: Yes
- **브랜치**: task/task-1094.1-dev2
- **워크트리 경로**: /home/jay/workspace/projects/insuwiki/.worktrees/task-1094.1-dev2
- **머지 의견**: 테스트 27건 전체 통과, 신규 에러 0건. whisperStt.ts의 인터페이스 변경 없음(기존 호출처 영향 없음). server.py는 insuwiki 외부(workspace/services/)이므로 별도 커밋 완료. InnerTube API 변경은 실제 YouTube 응답과의 통합 테스트(라이브)가 필요하나, 코드 레벨에서는 검증 완료.

---

## QC 자동 검증

```
overall: WARN → black 적용 후 해결
6 PASS, 5 SKIP, 1 WARN (style_check: black 포매팅 → 즉시 수정 커밋)

- file_check: PASS (4개 파일 모두 존재, 정상 크기)
- data_integrity: PASS
- pyright_check: PASS (0 errors, 0 warnings)
- spec_compliance: PASS (5/5 항목 커버)
- critical_gap: PASS
- duplicate_check: PASS (최대 유사도 8.0%)
```

Gate PASS → .done 파일 자동 생성 완료
