Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
66e6b6d
fix : repo/query 제거
GulSam00 Mar 8, 2026
8bd72f8
chore : crawlYoutubeTemp, crawlYoutubeUbuntu 임시 파일 및 관련 스크립트 제거
GulSam00 Mar 8, 2026
35bd241
feat : isValidKYExistNumber AI 기반 곡 검증으로 교체 및 validateSongMatch 유틸 추가
GulSam00 Mar 8, 2026
ef80587
refactor : crawling log 시스템 정리 및 replaceSupabaseFailed 버그 수정
GulSam00 Mar 8, 2026
928d98b
doc : packages/crawling CLAUDE.md 추가
GulSam00 Mar 8, 2026
b654aa8
refactor : crawlYoutubeVerify 파일 체크포인트를 verify_ky_songs DB 기반으로 전환
GulSam00 Mar 8, 2026
f183afb
refactor : logData 파일 체크포인트 사용 주석처리
GulSam00 Mar 8, 2026
2a8f4b4
Merge pull request #158 from GulSam00/refactor/crawling
GulSam00 Mar 8, 2026
5959ace
fix : useSearchSong 자동완성 label 정확 일치 시에만 alias 치환
GulSam00 Mar 8, 2026
63d3bce
chore : 아티스트 별칭 및 한일 매핑 데이터 업데이트
GulSam00 Mar 8, 2026
9f91863
chore : .gitignore Claude Code 로컬 설정 파일 제외 추가
GulSam00 Mar 8, 2026
a72ccba
Merge pull request #159 from GulSam00/fix/autoComplete
GulSam00 Mar 8, 2026
8bfda37
fix : yml 액션 파일 환경 변수 받게 수정
GulSam00 Mar 14, 2026
8ed06fd
Merge pull request #160 from GulSam00/fix/env
GulSam00 Mar 14, 2026
9aa7634
chore : ky-valid → ky-verify 스크립트 전환
GulSam00 Mar 14, 2026
6d05c1e
chore : ky-verify 수동 실행 GitHub Actions 워크플로우 추가
GulSam00 Mar 14, 2026
bdeef97
Merge pull request #161 from GulSam00/chore/kyVerifyAction
GulSam00 Mar 14, 2026
50b9252
fix : crawlYoutubeVerify 우분투 환경 Puppeteer 샌드박스 옵션 추가
GulSam00 Mar 14, 2026
d59423a
Merge pull request #162 from GulSam00/chore/kyVerifyAction
GulSam00 Mar 14, 2026
1b50f6f
fix : 레이아웃 간격 조정
GulSam00 Mar 15, 2026
f927cc4
Merge pull request #163 from GulSam00/fix/layout
GulSam00 Mar 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/update_ky_youtube.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ jobs:
run: |
echo "SUPABASE_URL=${{ secrets.SUPABASE_URL }}" >> .env
echo "SUPABASE_KEY=${{ secrets.SUPABASE_KEY }}" >> .env
echo "OPENAI_API_KEY=${{ secrets.OPENAI_API_KEY }}" >> .env

- name: run update script - packages/crawling/crawlYoutube.ts
working-directory: packages/crawling
Expand Down
41 changes: 41 additions & 0 deletions .github/workflows/verify_ky_youtube.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Verify ky by Youtube

on:
workflow_dispatch:

permissions:
contents: write # push 권한을 위해 필요

jobs:
run-npm-task:
runs-on: ubuntu-latest

steps:
- name: Checkout branch
uses: actions/checkout@v4

- name: Use Node.js 20
uses: actions/setup-node@v4
with:
node-version: "20"

- name: Install pnpm
uses: pnpm/action-setup@v2
with:
version: 9
run_install: false

- name: Install dependencies
working-directory: packages/crawling
run: pnpm install

- name: Create .env file
working-directory: packages/crawling
run: |
echo "SUPABASE_URL=${{ secrets.SUPABASE_URL }}" >> .env
echo "SUPABASE_KEY=${{ secrets.SUPABASE_KEY }}" >> .env
echo "OPENAI_API_KEY=${{ secrets.OPENAI_API_KEY }}" >> .env

- name: run verify script - packages/crawling
working-directory: packages/crawling
run: pnpm run ky-verify
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,8 @@ yarn-error.log*
.cursorrules
.gitmessage.txt

# Claude Code local settings (contains secrets)
.claude/*

temp/
.vscode
20 changes: 20 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,26 @@ See [apps/web/CLAUDE.md](apps/web/CLAUDE.md) for full detail. Key points:
- **Tailwind CSS v4** + **shadcn/ui** in `src/components/ui/` (do not modify directly)
- Path alias `@/` → `src/`

## Git Workflows

### "branch 정리해줘"

원격에서 merge 후 삭제된 브랜치를 로컬에서도 동기화한다.

```bash
# 1. 원격 삭제된 브랜치 참조 정리
git fetch --prune

# 2. 원격에 없는 로컬 브랜치 목록 확인
git branch -vv | grep ': gone]'

# 3. 삭제 대상 브랜치 제거 (main, develop, 현재 브랜치 제외)
git branch -vv | grep ': gone]' | awk '{print $1}' | xargs -r git branch -d
```

- `-d` 플래그 사용 (merge되지 않은 브랜치는 삭제 안 됨, 안전)
- 삭제 전 목록을 사용자에게 보여주고 확인 후 진행

## Git Conventions

Branch format: `<type>/<camelCaseName>` — flow: `feat/*` → `develop` → `main`
Expand Down
1 change: 0 additions & 1 deletion apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
"@radix-ui/react-tabs": "^1.1.3",
"@radix-ui/react-tooltip": "^1.2.6",
"@repo/open-api": "workspace:*",
"@repo/query": "workspace:*",
"@supabase/ssr": "^0.6.1",
"@supabase/supabase-js": "^2.49.1",
"@tanstack/react-query": "^5.68.0",
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/app/info/like/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export default function LikePage() {

<Separator className="mb-4" />

<ScrollArea className="h-[calc(100vh-16rem)]">
<ScrollArea className="h-[calc(100vh-20rem)]">
{likedSongs.map(song => (
<SongItem
key={song.song_id}
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/app/popular/PopularRankingList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export default function PopularRankingList() {
/>
</CardHeader>

<ScrollArea className="h-[calc(100vh-20rem)]">
<ScrollArea className="h-[calc(100vh-22rem)]">
<CardContent className="pt-0">
<div className="space-y-0">
{data && data.length > 0 ? (
Expand Down
13 changes: 6 additions & 7 deletions apps/web/src/constants/artistAlias.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export const artistAlias = {
'Creepy Nuts': ['크리피 너츠'],

// [Gemini 추천 아티스트 추가] 한국 인기 최상위
Ado: ['아도', '우세와'],
Ado: ['아도', '우세와', '신시대'],
imase: ['이마세', '나이트댄서'],
'ONE OK ROCK': ['원오크락', '원오크', '원옥'],
'X JAPAN': ['엑스재팬', '엑스제팬'],
Expand All @@ -25,16 +25,18 @@ export const artistAlias = {
"L'Arc~en~Ciel": ['라르크', '라르크 앙 시엘'],
松田聖子: ['마츠다 세이코'],
椎名林檎: ['시이나 링고'],
Eve: ['eve', '이브'],
美波: ['미나미', 'minami', '373'],
'ASIAN KUNG-FU GENERATION': ['아시안 쿵푸 제너레이션', '아지캉', '아쿵제'],

// 기존 데이터
嵐: ['아라시'],
東方神起: ['동방신기'],
あいみょん: ['아이묭'],
米津玄師: ['요네즈 켄시'],
浜崎あゆみ: ['하마사키 아유미'],
水樹奈々: ['미즈키 나나'],
'モーニング娘。': ['모닝구 무스메'],
Official髭男dism: ['오피셜히게단디즘', '히게단'],
Official髭男dism: ['오피셜히게단디즘', '히게단', '프리텐더'],
ヨルシカ: ['요루시카'],
中島美嘉: ['나카시마 미카'],
宇多田ヒカル: ['우타다 히카루'],
Expand All @@ -50,14 +52,11 @@ export const artistAlias = {
坂本真綾: ['사카모토 마아야'],
結束バンド: ['결속 밴드'],
乃木坂46: ['노기자카46'],
防弾少年団: ['방탄소년단'],
少女時代: ['소녀시대'],
林原めぐみ: ['하야시바라 메구미'],
ポルノグラフィティ: ['포르노그라피티'],
初音ミク: ['하츠네 미쿠'],
'DECO*27(Feat.初音ミク)': ['DECO27', '데코니나'],
'DECO*27': ['DECO27', '데코니나'],
松浦亜弥: ['마츠우라 아야'],
ユンナ: ['윤하'],
堀江由衣: ['호리에 유이'],
コブクロ: ['코부쿠로'],
きゃりーぱみゅぱみゅ: ['캬리 파뮤파뮤'],
Expand Down
4 changes: 4 additions & 0 deletions apps/web/src/constants/krToJpnArtist.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ export const krToJpnArtistSort = {
// A-Z
Ado: 'Ado',
Aimer: 'Aimer',
'ASIAN KUNG-FU GENERATION': '아시안 쿵푸 제너레이션',

DECO27: 'DECO*27',
Eve: 'Eve',
HoneyWorks: 'HoneyWorks',

imase: 'imase',
Expand All @@ -22,6 +25,7 @@ export const krToJpnArtistSort = {

// 마
'미세스 그린 애플': 'Mrs. GREEN APPLE',
미나미: '美波',

// 바
바운디: 'Vaundy',
Expand Down
6 changes: 4 additions & 2 deletions apps/web/src/hooks/useSearchSong.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,10 @@ export default function useSearchSong() {
let parsedSearch = trimSearch;

if (autoCompleteList.length === 1) {
// 자동완성 리스트가 하나(정확히 일치하면) 해당 alias의 value로 자동 치환
parsedSearch = autoCompleteList[0].value;
if (autoCompleteList[0].label === trimSearch) {
// 자동완성 리스트가 하나(정확히 일치하면)고 label도 일치하면 해당 alias의 value로 자동 치환
parsedSearch = autoCompleteList[0].value;
}
} else {
// 한글이 있다면 공백 제거
const hasKorean = /[가-힣ㄱ-ㅎㅏ-ㅣ]/.test(trimSearch);
Expand Down
113 changes: 113 additions & 0 deletions packages/crawling/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Overview

일회성 데이터 수집/처리 스크립트 모음. 빌드 결과물을 배포하지 않으며, `tsx`로 스크립트를 직접 실행한다.

## Commands

```bash
pnpm ky-open # Open API(금영)로 KY 번호 수집
pnpm ky-youtube # YouTube 크롤링으로 KY 번호 수집 + AI 검증
pnpm ky-verify # 기존 KY 번호의 실제 존재 여부 재검증 (체크포인트 지원)
pnpm ky-update # ky-youtube + ky-verify 병렬 실행
pnpm trans # 일본어 아티스트명 → 한국어 번역 후 DB 저장
pnpm test # vitest 실행
pnpm lint # ESLint
```

스크립트는 반드시 **`packages/crawling/`** 디렉토리에서 실행해야 한다. 로그 파일 및 assets 경로가 상대 경로 기준이기 때문.

## Environment Variables

`.env` 파일 필요 (루트가 아닌 `packages/crawling/`에 위치):

```
SUPABASE_URL=
SUPABASE_KEY=
OPENAI_API_KEY=
```

## Architecture

### 데이터 흐름

모든 스크립트는 **Supabase `songs` 테이블**을 중심으로 동작한다.

```
[songs 테이블]
title, artist, num_tj(TJ번호), num_ky(KY번호)

주요 목표: num_ky가 null인 곡에 KY 번호를 채우는 것
```

**KY 번호 수집 (메인 파이프라인)**

```
crawlYoutube.ts
└─ getSongsKyNullDB() # num_ky가 null인 곡 조회
└─ YouTube @KARAOKEKY 채널 검색 # puppeteer + cheerio로 번호 스크래핑
└─ isValidKYExistNumber() # kysing.kr에서 번호 실존 여부 확인
└─ validateSongMatch() # OpenAI gpt-4o-mini로 제목/아티스트 일치 판단
└─ updateSongsKyDB() # 성공 시 DB 업데이트
└─ postInvalidKYSongsDB() # 실패 시 invalid_ky_songs 테이블에 기록
```

**KY 번호 검증 (기존 데이터 재확인)**

```
crawlYoutubeVerify.ts
└─ getSongsKyNotNullDB() # num_ky가 있는 곡 조회
└─ getVerifyKySongsDB() # 이미 검증된 ID 로드 (체크포인트)
└─ isValidKYExistNumber() # KY 사이트에서 실존 여부 재확인
└─ 유효하면 postVerifyKySongsDB() # verify_ky_songs 테이블에 insert
└─ 유효하지 않으면 num_ky = null로 초기화
```

**Open API 방식 (보조)**

```
findKYByOpen.ts
└─ @repo/open-api의 getSong()으로 금영 API 직접 조회
└─ 제목 + 아티스트 문자열 비교로 KY 번호 매칭
```

**일본어 번역**

```
postTransDictionary.ts
└─ getSongsJpnDB() # 일본어 포함된 곡 필터링
└─ transChatGPT() # GPT-4-turbo로 아티스트명 번역
└─ postTransDictionariesDB() # trans_dictionaries 테이블에 저장
```

### 핵심 패턴: 진행 상태 저장 (체크포인트)

장시간 실행되는 스크립트가 중단됐을 때 재시작하면 처음부터 다시 하지 않도록, `src/assets/`에 텍스트 파일로 진행 상태를 기록한다.

| 파일 | 용도 |
| ----------------------------------------- | ---------------------------------- |
| `src/assets/transList.txt` | 이미 번역 시도한 일본어 아티스트명 |
| `src/assets/crawlKYValidList.txt` | 검증 완료된 (제목-아티스트) 쌍 |
| `src/assets/crawlKYYoutubeFailedList.txt` | YouTube 크롤링 실패 목록 |

`logData.ts`의 `save*` / `load*` 함수로 관리. 스크립트 시작 시 로드해 `Set`으로 변환 후 O(1) 검색으로 스킵 처리.

### Path Alias

`@/` → `src/` (tsconfig의 paths 설정)

### Supabase 테이블

| 테이블 | 용도 |
| -------------------- | -------------------------------- |
| `songs` | 메인 곡 데이터 (TJ/KY 번호 포함) |
| `invalid_ky_songs` | KY 번호 수집 실패 목록 |
| `trans_dictionaries` | 일본어 → 한국어 번역 사전 |

### AI 유틸

- `utils/validateSongMatch.ts` — `gpt-4o-mini`로 두 (제목, 아티스트) 쌍이 같은 곡인지 판단. `temperature: 0`, `max_tokens: 20`, 완전 일치 시 API 호출 생략.
- `utils/transChatGPT.ts` — `gpt-4-turbo`로 일본어 → 한국어 번역.
5 changes: 2 additions & 3 deletions packages/crawling/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@
"scripts": {
"ky-open": "tsx src/findKYByOpen.ts",
"ky-youtube": "tsx src/crawling/crawlYoutube.ts",
"ky-youtube-ubuntu": "tsx src/crawling/crawlYoutubeUbuntu.ts",
"ky-valid": "tsx src/crawling/crawlYoutubeValid.ts",
"ky-update": "pnpm run ky-youtube & pnpm run ky-valid",
"ky-verify": "tsx src/crawling/crawlYoutubeVerify.ts",
"ky-update": "pnpm run ky-youtube & pnpm run ky-verify",

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Ky-update premature exit 🐞 Bug ⛯ Reliability

packages/crawling의 ky-update가 &로 ky-youtube를 백그라운드 실행하고 wait하지 않아, ky-verify 종료 시점에 전체 스크립트가 끝나면서
ky-youtube가 중간에 종료되거나 결과가 불완전해질 수 있습니다.
Agent Prompt
### Issue description
`ky-update` 스크립트가 `&`로 백그라운드 실행을 만들고 `wait` 없이 종료되어, 한 작업이 끝나기 전에 스크립트가 종료되거나 실패가 전파되지 않습니다.

### Issue Context
현재 `ky-update`는 ky-youtube(백그라운드) + ky-verify(포그라운드) 형태입니다.

### Fix Focus Areas
- packages/crawling/package.json[9-19]

### Suggested implementation
- 옵션 A: `pnpm run ky-youtube & pnpm run ky-verify; wait`
- 옵션 B(권장): `concurrently -k -s first "pnpm run ky-youtube" "pnpm run ky-verify"` 추가 및 exit code 전파

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

"trans": "tsx src/postTransDictionary.ts",
"recent-tj": "tsx src/crawling/crawlRecentTJ.ts",
"lint": "eslint . --ext .ts,.js",
Expand Down
4 changes: 1 addition & 3 deletions packages/crawling/src/crawling/crawlRecentTJ.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import puppeteer from 'puppeteer';

import { postSongsDB } from '@/supabase/postDB';
import { LogData, Song } from '@/types';
import { updateDataLog } from '@/utils/logData';
import { parseNumber } from '@/utils/parseNumber';
import { parseText } from '@/utils/parseString';

Expand Down Expand Up @@ -61,7 +60,6 @@ console.log('실패 개수 : ', result.failed.length);
console.log('성공 데이터 : ', result.success);
console.log('실패 데이터 : ', result.failed);

updateDataLog(result.success, 'postByRecentTJSuccess.txt');
updateDataLog(result.failed, 'postByRecentTJFailed.txt');


await browser.close();
Loading
Loading