diff --git a/.github/workflows/crawl_tj_all_number.yml b/.github/workflows/crawl_tj_all_number.yml new file mode 100644 index 00000000..31945182 --- /dev/null +++ b/.github/workflows/crawl_tj_all_number.yml @@ -0,0 +1,61 @@ +name: Crawl All TJ Song By Number + +on: + workflow_dispatch: # 수동 실행 전용 + +permissions: + contents: write + +jobs: + run-npm-task: + runs-on: ubuntu-latest + + # 6시간 잡 제한 안에서 전 대역을 커버하기 위해 1만 단위로 구간을 나눠 병렬 실행 + strategy: + fail-fast: false + matrix: + range: + - { start: 1, end: 10000 } + - { start: 10001, end: 20000 } + - { start: 20001, end: 30000 } + - { start: 30001, end: 40000 } + - { start: 40001, end: 50000 } + - { start: 50001, end: 60000 } + - { start: 60001, end: 70000 } + - { start: 70001, end: 80000 } + - { start: 80001, end: 90000 } + - { start: 90001, end: 99999 } + + name: "crawl ${{ matrix.range.start }}-${{ matrix.range.end }}" + + 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 + + - name: run script - crawlAllTJSongByNumber.ts + working-directory: packages/crawling + env: + START_NUMBER: ${{ matrix.range.start }} + END_NUMBER: ${{ matrix.range.end }} + run: pnpm run tj-all-number diff --git a/.github/workflows/crawl_tj_by_number.yml b/.github/workflows/crawl_tj_by_number.yml deleted file mode 100644 index 5d1c7588..00000000 --- a/.github/workflows/crawl_tj_by_number.yml +++ /dev/null @@ -1,40 +0,0 @@ -name: Crawl TJ by Number - -on: - workflow_dispatch: # 수동 실행 전용 - -permissions: - contents: write - -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 - - - name: run script - testCrawlTJSongByNumber.ts - working-directory: packages/crawling - run: pnpm run test-tj-number diff --git a/.github/workflows/tagging_song.yml b/.github/workflows/tagging_song.yml index 7a90b841..bf7ec9e9 100644 --- a/.github/workflows/tagging_song.yml +++ b/.github/workflows/tagging_song.yml @@ -2,7 +2,7 @@ name: Tagging Songs on: schedule: - - cron: "0 10 * * 1" # 매주 월요일 한국 시간 19:00 실행 (UTC+9 → UTC 10:00) + - cron: "0 7 * * *" # 매일 한국 시간 16:00 실행 (UTC+9 → UTC 7:00) workflow_dispatch: permissions: diff --git a/.github/workflows/translation_jpn.yml b/.github/workflows/translation_jpn.yml index a8b15e22..4a72e37a 100644 --- a/.github/workflows/translation_jpn.yml +++ b/.github/workflows/translation_jpn.yml @@ -2,7 +2,7 @@ name: Translation J-POP Songs on: schedule: - - cron: "0 10 * * 5" # 매주 금요일 한국 시간 19:00 실행 (UTC+9 → UTC 10:00) + - cron: "0 10 * * *" # 매일 한국 시간 19:00 실행 (UTC+9 → UTC 10:00) workflow_dispatch: permissions: diff --git a/.github/workflows/verify_ky_youtube.yml b/.github/workflows/verify_ky_youtube.yml index e65f7e86..13efa76f 100644 --- a/.github/workflows/verify_ky_youtube.yml +++ b/.github/workflows/verify_ky_youtube.yml @@ -2,7 +2,7 @@ name: Verify ky by Youtube on: schedule: - - cron: "0 14 * * *" # 한국 시간 23:00 실행 (UTC+9 → UTC 14:00) + - cron: "0 14 * * 1" # 매주 월요일 한국 시간 23:00 실행 (UTC+9 → UTC 14:00) workflow_dispatch: permissions: diff --git a/apps/twa/.gitignore b/apps/twa/.gitignore index dbe1ffa3..92046e3d 100644 --- a/apps/twa/.gitignore +++ b/apps/twa/.gitignore @@ -1,9 +1,27 @@ # Android keystore — 외부 백업 필수, git에 절대 커밋하지 않는다 *.keystore *.jks +*.keystore.bak* +android.keystore.bak* # Bubblewrap 빌드 산출물 android/ output/ +app/ *.aab *.apk +*.apk.idsig + +# Gradle 생성물 +.gradle/ +gradle/ +gradlew +gradlew.bat +build.gradle +settings.gradle +gradle.properties +manifest-checksum.txt +store_icon.png + +# 수동 백업/복사본 +twa-manifest copy.json diff --git a/apps/twa/twa-manifest.json b/apps/twa/twa-manifest.json index bd8173f9..2eebba58 100644 --- a/apps/twa/twa-manifest.json +++ b/apps/twa/twa-manifest.json @@ -1,16 +1,17 @@ { "packageId": "kr.singcode.app", "host": "singcode.kr", - "name": "Singcode - 당신의 노래방 메모장", + "name": "싱코드(singcode) - 노래방 번호 검색", "launcherName": "Singcode", "display": "standalone", - "themeColor": "#1a1a2e", - "navigationColor": "#1a1a2e", - "navigationColorDark": "#1a1a2e", - "navigationDividerColor": "#1a1a2e", - "navigationDividerColorDark": "#1a1a2e", - "backgroundColor": "#1a1a2e", - "enableNotifications": false, + "themeColor": "#1A1A2E", + "themeColorDark": "#1A1A2E", + "navigationColor": "#1A1A2E", + "navigationColorDark": "#1A1A2E", + "navigationDividerColor": "#1A1A2E", + "navigationDividerColorDark": "#1A1A2E", + "backgroundColor": "#1A1A2E", + "enableNotifications": true, "startUrl": "/", "iconUrl": "https://singcode.kr/icons/icon-512.png", "maskableIconUrl": "https://singcode.kr/icons/icon-maskable-512.png", @@ -20,20 +21,13 @@ "path": "./android.keystore", "alias": "singcode" }, - "appVersionCode": 1, - "appVersionName": "1.0.0", + "appVersionName": "3", + "appVersionCode": 3, "shortcuts": [], "generatorApp": "bubblewrap-cli", "webManifestUrl": "https://singcode.kr/manifest.webmanifest", "fallbackType": "customtabs", - "features": { - "locationDelegation": { - "enabled": false - }, - "playBilling": { - "enabled": false - } - }, + "features": {}, "alphaDependencies": { "enabled": false }, @@ -42,5 +36,13 @@ "isMetaQuest": false, "fullScopeUrl": "https://singcode.kr/", "minSdkVersion": 21, - "orientation": "portrait" + "orientation": "portrait", + "fingerprints": [], + "additionalTrustedOrigins": [], + "retainedBundles": [], + "protocolHandlers": [], + "fileHandlers": [], + "launchHandlerClientMode": "", + "displayOverride": [], + "appVersion": "3" } diff --git a/apps/web/public/.well-known/assetlinks.json b/apps/web/public/.well-known/assetlinks.json index 02bc778c..2a14c604 100644 --- a/apps/web/public/.well-known/assetlinks.json +++ b/apps/web/public/.well-known/assetlinks.json @@ -5,7 +5,8 @@ "namespace": "android_app", "package_name": "kr.singcode.app", "sha256_cert_fingerprints": [ - "4A:52:E2:11:4D:E0:6F:B6:E1:E1:B2:BB:72:14:8C:0B:17:97:06:DA:BD:F7:DB:A2:3B:10:59:46:CD:79:3B:E9" + "4A:52:E2:11:4D:E0:6F:B6:E1:E1:B2:BB:72:14:8C:0B:17:97:06:DA:BD:F7:DB:A2:3B:10:59:46:CD:79:3B:E9", + "06:63:E8:E2:C6:97:EE:CE:BF:7C:2D:24:2B:F0:14:61:3F:01:39:4D:8E:B2:B3:E0:F4:65:45:E9:C1:F3:C4:0E" ] } } diff --git a/apps/web/src/app/layout.tsx b/apps/web/src/app/layout.tsx index 678110cb..97d450c7 100644 --- a/apps/web/src/app/layout.tsx +++ b/apps/web/src/app/layout.tsx @@ -19,12 +19,11 @@ import QueryProvider from '@/query'; const isDevelopment = process.env.NODE_ENV === 'development'; export const metadata: Metadata = { - title: 'Singcode - 당신의 노래방 메모장', - description: - '노래방만 가면 부르고 싶었던 노래가 기억 안 날 때? Singcode에서 검색하고 저장하면 걱정 끝!', + title: 'Singcode - 노래방 번호 검색', + description: 'Singcode에서 빠르고 편하게 노래방 번호 검색하세요. J-POP 검색도 지원합니다.', openGraph: { - title: 'Singcode - 노래방에서 부를 곡, 기억하지 말고 저장하세요', - description: '노래방 갈 때마다 잊어버리는 곡번호? Singcode가 대신 기억할게요!', + title: 'Singcode - 노래방 번호 검색', + description: 'Singcode에서 빠르고 편하게 노래방 번호 검색하세요. J-POP 검색도 지원합니다.', url: 'https://www.singcode.kr', siteName: 'Singcode', images: [ diff --git a/packages/crawling/package.json b/packages/crawling/package.json index 7b639baf..f4182493 100644 --- a/packages/crawling/package.json +++ b/packages/crawling/package.json @@ -14,6 +14,7 @@ "recent-tj": "tsx src/cron/crawlRecentTJ.ts", "tag-songs": "tsx src/cron/taggingSongs.ts", "trans-jpn": "tsx src/cron/translationJpn.ts", + "tj-all-number": "tsx src/cron/crawlAllTJSongByNumber.ts", "lint": "eslint .", "test": "vitest run", "format": "prettier --write \"**/*.{ts,tsx,md}\"" diff --git a/packages/crawling/src/crawling/crawlTJSongByNumber.ts b/packages/crawling/src/crawling/crawlTJSongByNumber.ts index 18c52c6a..f49a4d42 100644 --- a/packages/crawling/src/crawling/crawlTJSongByNumber.ts +++ b/packages/crawling/src/crawling/crawlTJSongByNumber.ts @@ -22,8 +22,10 @@ export const crawlTJSongByNumber = async ( const gridContainer = $('.grid-container.list.ico').first(); - const title = gridContainer.find('.grid-item.title3').text().trim(); - const artist = gridContainer.find('.grid-item.title4').text().trim(); + // title3 내부에는 아이콘용
가 div.flex-box > ul 안에 중첩돼 있으므로
+ // 자손 셀렉터(' p') 대신 직계 자식(' > p')만 선택해 실제 제목만 추출한다.
+ const title = gridContainer.find('.grid-item.title3 span').text().trim();
+ const artist = gridContainer.find('.grid-item.title4 span').text().trim();
if (!title || !artist) {
console.log('❌ TJ 검색 결과 없음');
diff --git a/packages/crawling/src/cron/crawlAllTJSongByNumber.ts b/packages/crawling/src/cron/crawlAllTJSongByNumber.ts
index 37c2800a..a161d710 100644
--- a/packages/crawling/src/cron/crawlAllTJSongByNumber.ts
+++ b/packages/crawling/src/cron/crawlAllTJSongByNumber.ts
@@ -9,9 +9,20 @@ import { Song } from '@/types';
dotenv.config();
-// 순회 범위
-const START_NUMBER = 1;
-const END_NUMBER = 99999;
+// 순회 범위 (env로 주입, 미설정 시 전체 범위)
+const parseRange = (value: string | undefined, fallback: number): number => {
+ const parsed = Number(value);
+ return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback;
+};
+
+const START_NUMBER = parseRange(process.env.START_NUMBER, 1);
+const END_NUMBER = parseRange(process.env.END_NUMBER, 99999);
+
+if (START_NUMBER > END_NUMBER) {
+ throw new Error(`잘못된 범위: START_NUMBER(${START_NUMBER}) > END_NUMBER(${END_NUMBER})`);
+}
+
+console.log(`🔢 순회 범위: ${START_NUMBER} ~ ${END_NUMBER}`);
// 1. 최초로 DB의 모든 곡 데이터를 받아서 num_tj 기준 Map으로 보관
const allSongs = await getSongsAllWithTjDB();
diff --git a/packages/crawling/src/supabase/getDB.ts b/packages/crawling/src/supabase/getDB.ts
index 696bdfc3..b5a9445e 100644
--- a/packages/crawling/src/supabase/getDB.ts
+++ b/packages/crawling/src/supabase/getDB.ts
@@ -33,7 +33,7 @@ export async function getSongsJpnDB() {
return hasJapaneseData;
}
-export async function getSongsKyNullDB(max: number = 50000) {
+export async function getSongsKyNullDB(max: number = 100000) {
const supabase = getClient();
const { data, error } = await supabase
@@ -48,7 +48,7 @@ export async function getSongsKyNullDB(max: number = 50000) {
return data;
}
-export async function getSongsKyNotNullDB(max: number = 50000) {
+export async function getSongsKyNotNullDB(max: number = 100000) {
const supabase = getClient();
const { data, error } = await supabase
@@ -85,7 +85,7 @@ export async function getVerifyKySongsDB(): Promise