Commit Graph

113 Commits

Author SHA1 Message Date
528b48280c feat(comment-cards): 제외 단어 필터 추가
입력한 단어가 포함된 댓글을 카드·분석 양쪽에서 제외.
쉼표/공백 다중 지정, 대량 댓글 대비 입력 디바운스(250ms).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 16:07:40 +09:00
28b072df42 feat(comment-cards): 타임라인 분석에 시간순 정렬 옵션 추가
많이 언급된 타임라인 섹션에 '언급순/시간순' 선택 추가.
시간순은 선별된 상위 구간을 재생 순서대로 재정렬해 보여줌.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 12:52:39 +09:00
92726b1c08 feat(comment-cards): 전체 댓글 수집 + 인사이트 분석 패널 추가
- 수집 상한 1,000→20,000개(안전 상한)로 상향해 전체 댓글 기준 분석
- 분석 패널: 많이 언급된 타임라인(클릭 시 해당 시점 유튜브 이동), 자주 나온 단어(조사 제거+불용어, 좋아요 가중 토글), 요약 통계(총/평균 좋아요·참여자·최다 좋아요 댓글·월별 댓글량)
- 모자이크 기본 ON, '모서리 둥글게' 토글 추가(기본 각진 모서리)
- 카드 렌더는 상위 2,000개로 제한(브라우저 보호), 분석은 전체 기준

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 12:37:15 +09:00
b1b23a45f2 feat(comment-cards): 댓글 내용 실시간 검색 필터 추가
필터바에 검색 입력창 추가 → 입력 즉시 댓글 내용에 그 단어가 포함된 카드만 노출
(작성자 제외, 내용만 매칭). 클라이언트 필터라 추가 API 호출 없음. JS 캐시버스터 갱신.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 12:41:20 +09:00
3a3eeef87a feat(comment-cards): 댓글 수집 상한 500→1,000개로 상향
MAX_PAGES 5→10 (페이지당 100개). commentThreads는 호출당 1유닛이라
1,000개=10유닛으로 쿼터 부담 미미.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 11:46:35 +09:00
ba3776eb7e feat(comment-cards): 카드 PNG 파일 저장(다운로드) 버튼 추가
캡컷은 클립보드 이미지 붙여넣기를 못 받아 파일 불러오기가 필요 → 카드별 '⬇ 저장' 버튼으로
PNG 다운로드 제공. 저장 버튼은 cc-noexport+modern-screenshot filter로 캡처 이미지에서 제외.
카드 클릭=클립보드 복사는 유지.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 12:45:46 +09:00
7de8f954ad fix(comment-cards): 복사 버튼 제거, 카드 클릭으로 복사
복사 버튼이 캡처 이미지에 함께 찍히던 문제 해결. 버튼을 없애고 카드 자체 클릭으로
복사하도록 변경, 완료 피드백은 카드 밖 토스트로 표시(캡처에 미포함). JS 캐시버스터 갱신.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 18:31:25 +09:00
ee5a13163b fix(comment-cards): 카드 메이슨리 레이아웃으로 짧은 글 흰 여백 제거
grid가 행 단위로 최대 높이에 맞춰 늘어나던 것을 CSS 다단(column)으로 변경.
각 카드가 내용 높이에 맞고 빈틈없이 쌓이도록 break-inside:avoid + margin-bottom 적용.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 18:24:13 +09:00
6136eb83e9 fix(comment-cards): 일시적 YouTube 403/5xx 자동 재시도
commentThreads가 order=relevance에서 간헐적으로 403 forbidden을 반환하는 문제 대응.
generic 403/rateLimit/5xx는 백오프 후 최대 2회 재시도, commentsDisabled/quotaExceeded는
재시도 없이 즉시 안내. 재시도 성공/비재시도 단위 테스트 추가.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 18:13:19 +09:00
6f59554b6d fix(comment-cards): YouTube API 오류를 reason별로 구분 처리
403을 무조건 '댓글 비활성화'로 표시하던 문제 수정. error.errors[0].reason을 파싱해
commentsDisabled/quotaExceeded/기타(keyInvalid 등)를 각각 안내하고, 비-403 오류가
opaque 500으로 새던 것도 의미있는 메시지로 노출. 단위 테스트 추가.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 17:54:50 +09:00
0ee343ceca feat(comment-cards): 카드 PNG 클립보드 복사 추가
modern-screenshot로 카드 캡처→클립보드(투명배경, 모자이크 반영), 미지원 시 다운로드 폴백

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 17:07:13 +09:00
418242f47d feat(comment-cards): 댓글 카드 페이지·사이드바·렌더/정렬/필터 추가
/comment-cards 라우트, 유튜브 스타일 카드 렌더, 정렬/임계값 필터, 전체 모자이크 토글

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 17:06:53 +09:00
0e84f5de08 feat(comment-cards): 댓글 fetch + 프로필 프록시 API 추가
POST /api/comment-cards/fetch, GET /api/comment-cards/avatar(구글 도메인 화이트리스트)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 17:05:27 +09:00
15a655d7f9 feat(comment-cards): commentThreads 댓글 수집 서비스 추가
CommentCardDto + YoutubeCommentService(최대 5페이지, 쿼터가드, 댓글비활성 처리)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 17:05:27 +09:00
39e2b42995 feat(comment-cards): 유튜브 videoId 파서 추가
watch/youtu.be/shorts/embed/순수ID 추출, 단위 테스트 포함

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 17:04:15 +09:00
4776905745 docs(comment-cards): 구현 계획서 추가
5개 태스크(videoId 파서·수집서비스·API·페이지/렌더·PNG복사) 단계별 TDD/수동검증 계획

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 16:48:05 +09:00
f309db46b6 docs(comment-cards): 유튜브 댓글 카드 기능 설계서 추가
링크 입력→댓글 수집(commentThreads)→유튜브 스타일 카드 렌더→전체 모자이크→카드별 PNG 클립보드 복사 흐름 정의

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 16:42:20 +09:00
e2caf9a0ba docs(board): 안내 문구에 모바일 상태 드롭다운 사용법 추가
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 16:04:45 +09:00
e7418e7c3c feat(board): 모바일에서 카드 상태 변경 드롭다운 추가(터치로 칸반 이동)
HTML5 드래그가 터치에서 동작 안 하는 문제 — 카드마다 상태 드롭다운을 두어
터치로도 컬럼 이동 가능. 데스크톱은 드래그 유지(드롭다운은 모바일/터치에서만 노출).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 16:03:10 +09:00
f7641cd06b chore: push 테스트(빈 커밋)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 15:56:53 +09:00
cf3476e8a1 chore: .playwright-mcp 캡쳐 로그·미디어 산출물 git 추적 제외
실수로 커밋된 .playwright-mcp/*.yml 제거 + .gitignore 에 캡쳐/mp4 등 추가.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 15:54:25 +09:00
db8452d1ea feat(channels): 전체 선택을 현재 활성 국가 탭으로 한정
전체 선택/카운트/선택 영상 보기를 보이는 카드 기준으로 동작, 탭 전환 시
숨겨진 카드는 자동 선택 해제.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 15:45:09 +09:00
24f964364a fix(channels): 국가 탭 라벨 한글화(국기 이모지 미렌더 대응)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 15:25:59 +09:00
84155a69f8 feat(channels): 국가별 탭(KR/JP/US/기타) + Channel.country 수집·백필
Channel 에 country 추가(YouTube snippet.country 저장), 추천 채널 region 으로 기존 채널
백필(POST /api/channels/backfill-country), /channels 에 국가 탭+카운트 필터.
선택 영상 보기 버튼 한글화.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 15:24:40 +09:00
fec4c77cb9 feat(channels): 채널 영상 모아보기 카드형 재디자인 + 한국어 + 정렬 드롭다운
테이블→카드 그리드(썸네일·배율 배지·조회수·구독자·날짜), 한국어 라벨, 정렬 드롭다운
(배율/조회수/최신/구독자), 미리보기 모달(9:16), 선택/전체 담기·다운로드 기능 보존.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 15:02:53 +09:00
f3c6d20442 fix(channels): 전체 선택 시 체크 시각화 깨지던 문제(lucide가 i를 svg로 치환)
querySelector('i')가 lucide 치환 후 null→TypeError로 시각 토글이 첫 카드에서 멈춰
전체 선택이 안 되는 것처럼 보였음. querySelector('svg, i') + null 가드로 수정.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 14:44:53 +09:00
fd18a8981a feat(discover): 제외 목록 보기 + 복원 기능
- 목록 API status 파라미터(NEW|EXCLUDED|REGISTERED), POST /{id}/restore(→NEW)
- recommend.html: 헤더 '제외 목록' 토글로 EXCLUDED 채널 보기, 카드에 '복원' 버튼
  (제외 보기에선 등록/제외 대신 복원+채널이동만), 지역 탭은 그대로 적용

검증: 제외→EXCLUDED 목록 노출→복원→NEW 복귀 end-to-end 확인.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 11:48:34 +09:00
1d7d50f50b feat(discover): 추천 채널 카드에 '채널 이동'(유튜브 채널 새 탭) 버튼 추가
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 11:39:07 +09:00
08d50230c4 feat(discover): 추천 채널 지역(KR/JP/US) 탭 + 발견 지역 태깅
- region 을 채널 국가가 아닌 '검색 발견 지역(regionCode)'으로 태깅(UNKNOWN 해소)
- upsert: 같은 배율이면 갱신(지역 등 메타 최신화)
- recommend.html: 전체/KR/JP/US 탭으로 필터, 등록/제외 시 목록 갱신

검증: 재발굴 후 KR14·JP7·US1 로 분포, 탭 필터 동작.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 11:34:43 +09:00
3bfb7a7495 feat(discover): 추천 채널 썸네일 클릭 시 미리보기 모달(다른 페이지와 동일)
YouTube 새 탭 대신 인라인 iframe 모달로 영상 재생.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 11:26:11 +09:00
e9b192eabb feat(discover): 추천 채널 전용 페이지 /recommend + 사이드바 + 발굴 조회수순 보강
- recommend.html(카드: 썸네일·구독자·배율·대표영상, 등록/제외) + WebController 라우트 + 사이드바 메뉴
- 발굴 검색 order=viewCount 강제(키워드 없을 때 최신순→조회수순) — 떡상 채널이 잡히게
  (YoutubeSearchCondition.order 추가, YoutubeSearchService 반영)

검증: /run → KR,JP,US 26개 떡상 채널 발굴(구독 76~7천, 배율 1500~9만x).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 10:49:42 +09:00
35944a8fd6 feat(discover): 발굴 일별 스케줄 연결 + 추천 채널 REST(목록/등록/제외/수동)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 10:44:27 +09:00
02fa5be25b feat(discover): ChannelDiscoveryService(지역 Shorts 검색→떡상 채널 upsert)+설정
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 10:43:24 +09:00
507ae7c760 feat(discover): RecommendedChannel 엔티티+Repo, DiscoveryRanker 랭킹로직(TDD)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 10:41:52 +09:00
ee4d556851 docs(plan): 추천 채널 발굴 구현 계획(6 태스크)
RecommendedChannel 엔티티 → DiscoveryRanker(TDD) → ChannelDiscoveryService(검색+upsert)
→ 스케줄러 → REST → /recommend 페이지. 기존 검색/쿼터 가드 재사용.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 10:31:10 +09:00
9fed1e04b9 docs(spec): 추천 채널 발굴(떡상 Shorts) 설계 추가
지역(KR,JP,US) 인기 Shorts 주기 검색 → 작은구독자·고배율 떡상 채널 발굴 →
RecommendedChannel 저장 → /recommend 페이지에서 등록/제외. 쿼터 가드·기존 검색 재사용.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 10:07:23 +09:00
279cb4c9d0 chore(rework): 제거된 버튼의 잔여 안내문구·죽은 JS 정리
URL자막 버튼 제거에 따라 안내문/placeholder의 'URL자막' 언급 삭제,
미사용 함수 extractScript/translateToEditor 제거(copyToEditor는 유지).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 09:49:00 +09:00
0decc4439c chore(rework): 상단 버튼 정리 — URL자막·번역→재작성·[00:00]번역 제거
AI 자막(음성/화면) 기능이 번역을 대체해 중복되는 버튼 3개 삭제.
원본 다운로드·자막 추출(음성)·받은 원본 삭제·영상 업로드·에디터로 복사는 유지.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 09:46:44 +09:00
d16f23a4c0 feat(rework): AI 자막 패널에 원문+번역 비교 표시
화면(Gemini)·음성 패널에서 원문과 한국어 번역을 함께 보여줘 비교 가능하게 함.
- ScriptResponseDto.Segment: original 필드 추가(NON_NULL)
- Gemini 프롬프트/스키마: original(화면 원문) + text(한국어 번역) 둘 다 요청,
  text 는 반드시 한국어(영어 방지) 명시
- rework.html: 각 줄에 한국어(위)+원문(아래 회색) 표시, 음성 패널은 Whisper 원문 사용

검증: 470 화면자막 → 원문 일본어 + 한국어 번역 + 진행형 타임스탬프 정상.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 09:12:31 +09:00
b151204267 fix(gemini): 화면 자막 타임스탬프 0/빈결과 문제 해결
원인: Gemini 2.5 Flash의 thinking(타임스탬프 추론)+JSON 출력이 자막 많은 영상에서
출력 토큰을 초과해 JSON이 잘림 → 0개 또는 타임스탬프 전부 0.

- maxOutputTokens 65536(최대)로 상향 — thinking+JSON 잘림 방지(핵심)
- temperature 0 — 일관성
- 프롬프트: 시작/끝 시각 정확히, 전부 0 금지, 누락 없이 추출하도록 보강
- 빈 결과면 최대 3회 재시도(영상이해 비결정성 보완)

검증: 470·560(자막 많은 영상) 모두 진행형 타임스탬프로 정상 추출.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 18:43:47 +09:00
99dbe80fdb fix(rework): 자막 하이라이트가 페이지 전체를 스크롤해 영상이 흔들리던 문제
재생 중 활성 세그먼트로 이동할 때 scrollIntoView가 페이지를 스크롤해 sticky 영상이
위아래로 튀던 현상 수정. 세그먼트 박스(#segmentList) 내부에서만 스크롤하도록 변경.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 18:25:41 +09:00
31b7720af9 feat(rework): '전사' → '자막 추출(음성)' 용어 변경 + 왼쪽 영상 패널 sticky
- 이해하기 쉽게 모든 '전사' 문구를 '자막 추출'로 변경(메인 버튼은 '자막 추출(음성)')
- 좌측 영상 카드 position:sticky(top:1rem) → 스크롤해도 상단에 고정되어 항상 보임

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 18:12:26 +09:00
7b94515180 fix(rework): AI 자막 - 음성 없음은 오류 아닌 안내(회색)로 표시
말소리 없는 영상(음악·화면자막만)의 음성 패널을 빨강 오류가 아니라 회색 안내
('음성 없음 — 말소리가 없거나 전사 미실행')로 표시. renderAiPanel에 note 인자 추가.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 17:31:02 +09:00
6380a28a6f fix(rework): Gemini 실패 사유를 사용자에게 명확히 + 음성 없음 메시지 정정
- GeminiSubtitleService: 403(프로젝트 차단)/429(한도)/400(키 무효)을 한국어 메시지로
  IllegalArgumentException throw → 'Internal Server Error' 대신 실제 사유 노출
- rework.html: 음성 패널 빈 경우 '말소리 없음(음악/화면자막만) 또는 전사 미실행'으로 정정

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 17:16:29 +09:00
447645c19a feat(rework): AI 자막 2종(음성 Whisper / 화면 Gemini)→한국어, 좌우 분할 + SRT
재가공 화면에 'AI 자막(음성/화면)' 카드 추가. '생성' 한 번에:
- 왼쪽(음성): 저장된 Whisper 세그먼트(정밀 타임스탬프) → LibreTranslate 한국어
- 오른쪽(화면): 유튜브 URL → Gemini가 화면 박힌 자막 추출+한국어 번역
각 패널을 [00:00] 한국어 리스트로 표시, 각각 SRT 다운로드(audio_ko/screen_ko).

- GeminiSubtitleService 신규: youtube URL을 generativelanguage API에 전송,
  responseMimeType=json 구조화 응답 파싱. buildRequest/extractSegments/sanitizeApiKey 순수+단위테스트
- sanitizeApiKey: 잘못 붙은 선행 '='·공백 정리(env var 오타 방어)
- geminiRestTemplate 빈(5분), POST /{id}/gemini-subtitles, CurationService.geminiScreenSubtitles
- 설정 gemini.api-key/model(gemini-2.5-flash), rework.html 분할 카드+JS
- 음성 한국어는 기존 translate(format=segments) 재사용

검증: 470 화면자막 → 한국어 27세그먼트 30초 추출(스타일 일본어 자막도 정확).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 17:04:24 +09:00
c44b16a7f4 docs(spec): AI 자막 2종(음성 Whisper / 화면 Gemini)→한국어 SRT 로 설계 갱신
좌우 분할: 왼쪽=음성(Whisper 정밀+한국어), 오른쪽=화면(Gemini 한국어). 각각 SRT 다운로드.
음성 한국어는 기존 전사+LibreTranslate 재사용, 화면은 Gemini 신규(transient).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 16:44:04 +09:00
aa8dc17dd8 docs(spec): Gemini URL 자막(유튜브 URL→한국어 SRT) 설계 추가
'Gemini 자막' 버튼으로 유튜브 URL을 Gemini에 보내 화면/음성 자막을 추출+한국어 번역,
[00:00] 한국어 세그먼트로 저장 → SRT 내보내기가 곧 한국어 SRT. 제거된 OCR의 대체.
무료 티어(하루 8시간) 사용, 타임스탬프는 ~1초 근사.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 16:34:30 +09:00
7ebee1caa3 revert(rework): 화면 자막 OCR 기능 제거
Tesseract 기반 OCR이 실제 쇼츠의 스타일 자막을 거의 못 읽어 실효성이 없고,
URL→Gemini 방향으로 전환하기로 해 h-lab의 OCR 연동을 제거한다.

- 컨트롤러 POST /{id}/ocr 제거
- CurationService.ocrScreenSubtitles 제거
- ChannelService.ocrFromCached/joinSegmentText 제거(persistScript는 전사가 계속 사용)
- rework.html '화면 자막 OCR' 버튼·crop/fps 입력·ocrScreen() 제거
- docs/python-service/ocr_video_endpoint.py(videocr 예시) 삭제

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 16:19:32 +09:00
fbce1ec8ea feat(rework): 화면 자막 OCR 속도(sample_fps) 컨트롤 추가, 기본 1
저사양 서버(N150)에서 sample_fps=3은 프레임 과다로 10분 타임아웃 발생.
OCR 영역(crop %) 옆에 'fps' 입력을 추가하고 기본을 1로 낮춰 빠르게 끝나게 함
(자막이 빨리 바뀌면 2~3으로 올림). ocrScreen()이 sampleFps 를 함께 전달.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 15:42:57 +09:00
32b2eae62c feat(rework): 받아진 상태면 '원본 다운로드' 숨기고 '전사' 버튼 노출
캐시(받은 원본) 보유 시 '원본 다운로드'를 숨기고, 그 자리에 '전사' 버튼을 띄운다
(전사만 실패했을 때 재다운로드 없이 재시도). 캐시 없으면 다시 '원본 다운로드' 표시.

- updateCacheUI: downloadBtn 숨김/표시 대칭 토글 + transcribeCachedBtn 노출
- runTranscribeCached 헬퍼 추출(다운로드 2단계 전사와 '전사' 버튼이 공유)
- '전사' 버튼 onclick=transcribeCached()

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 15:25:35 +09:00