Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,15 @@ npm-debug.log*
yarn-debug.log*
yarn-error.log*


# Misc
.DS_Store
*.pem

# Crawling
crawlYoutubeSuccess.txt
crawlYoutubeFailed.txt
postByReleaseSuccess.txt
postByReleaseFailed.txt
errorLog.txt
transDataLog.txt
80 changes: 66 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,31 +42,83 @@ sing-code/
```

## ๐Ÿ“ฆ ๋ฐฐํฌ
(๋ฐฐํฌ๋œ ๋งํฌ๋“ค์„ ์ž‘์„ฑ)
[Singcode](https://singcode.vercel.app/)

## โœจ ์ฃผ์š” ๊ธฐ๋Šฅ
(์Šคํฌ๋ฆฐ์ƒท๊ณผ ํ•จ๊ป˜ ์–ด๋–ค ๊ธฐ๋Šฅ์ด ์žˆ๋Š”์ง€ ์„ค๋ช…)


### ๋ถ€๋ฅผ ๊ณก ํŽ˜์ด์ง€

* ์ž์‹ ์ด ์ €์žฅํ•œ ๋ถ€๋ฅผ ๊ณก์„ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค.
* ๋ถ€๋ฅผ ๊ณก์˜ ์ˆœ์„œ๋ฅผ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
* ๊ณก์„ ๋ถ€๋ฅด๊ฑฐ๋‚˜, ๋ถ€๋ฅด์ง€ ์•Š๊ณ  ์‚ญ์ œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

<div style="display: flex; justify-content: center; gap: 10px; flex-wrap: wrap;">
<img src="https://github.com/user-attachments/assets/b29b2dc0-dde3-4d69-9ce5-69a91ffe7985" height=800 style="height:800px; width:auto;" />
</div>

* ์ข‹์•„์š” ํ‘œ์‹œํ•œ ๊ณก์ด๋‚˜ ์ตœ๊ทผ ๋ถ€๋ฅธ ๊ณก ์ค‘์—์„œ ๋ถ€๋ฅผ ๊ณก์œผ๋กœ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


<div style="display: flex; justify-content: center; gap: 10px; flex-wrap: wrap;">
<img src="https://github.com/user-attachments/assets/6a6e9f12-758c-4462-853d-4fadf3fb4851" height=800 style="height:800px; width:auto;"/>
</div>

### ๊ฒ€์ƒ‰ ํŽ˜์ด์ง€
* ์ œ๋ชฉ, ๊ฐ€์ˆ˜ ์ด๋ฆ„์œผ๋กœ ๊ณก์„ ๊ฒ€์ƒ‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
* ๋ช‡๋ช‡ ์ผ๋ณธ๊ณก์€ ํ•œ๊ตญ์–ด๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.
* ๋ฒˆ์—ญ์ด ์ž˜๋ชป๋˜๊ฑฐ๋‚˜ ๋ถ€์ •ํ™•ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐœ๊ฒฌํ•˜์‹ ๋‹ค๋ฉด ์ œ๋ณด ๋ฐ”๋ž๋‹ˆ๋‹ค.

<div style="display: flex; justify-content: center; gap: 10px; flex-wrap: wrap;">
<img src="https://github.com/user-attachments/assets/886efbf5-bfc9-4b88-be8a-6e0e027dffc5" height=800 style="height:800px; width:auto;" />
</div>


### ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํŽ˜์ด์ง€

* ์ž์‹ ์˜ ํ™œ๋™ ๊ธฐ๋ก์„ ์กฐํšŒ ๋ฐ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
* ์ข‹์•„์š” ํ•œ ๊ณก๋“ค์„ ์กฐํšŒํ•˜๊ณ  ์ผ๊ด„ ์‚ญ์ œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
* ์ž์‹ ์ด ๊ฐ€์žฅ ๋งŽ์ด ๋ถ€๋ฅธ ๊ณก์˜ ์ˆœ์œ„๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

<div style="display: flex; justify-content: center; gap: 10px; flex-wrap: wrap;">
<img src="https://github.com/user-attachments/assets/d395522e-e10f-43ba-ab49-6ae299322978" height=800 style="height:800px; width:auto;" />
<img src="https://github.com/user-attachments/assets/8ca69483-ca76-47b2-b719-e301bbec3ee7" height=800 style="height:800px; width:auto;" />
<img src="https://github.com/user-attachments/assets/f2fbe965-470c-4bc0-ae3d-17f6974cceac" height=800 style="height:800px; width:auto;" />
</div>

### ๋กœ๊ทธ์ธ & ํšŒ์›๊ฐ€์ž… ์ง€์›

* Supabase DB์— ์‚ฌ์šฉ์ž ์•„์ด๋””๋ฅผ ์™ธ๋ž˜ํ‚ค๋กœ ํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅ ๋ฐ ๊ด€๋ฆฌํ•˜๊ธฐ์— ๋ชจ๋“  ์„œ๋น„์Šค๋Š” ํšŒ์›๊ฐ€์ž…์ด ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค.
* ์ด๋ฉ”์ผ ์ธ์ฆ ํšŒ์›๊ฐ€์ž…๊ณผ ์นด์นด์˜ค ํšŒ์›๊ฐ€์ž…์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

<div style="display: flex; justify-content: center; gap: 10px; flex-wrap: wrap;">
<img src="https://github.com/user-attachments/assets/4cf4094b-717c-4a88-a5bb-1ae285f3af8e" height=800 style="height:800px; width:auto;" />
<img src="https://github.com/user-attachments/assets/8d7d8e9f-c447-4b2e-800a-dd3007fd0b40" height=800 style="height:800px; width:auto;" />
</div>

## ๐Ÿ“– ํ”„๋กœ์ ํŠธ ๊ธฐ๋ก
(์ž‘์—… ๊ธฐ๊ฐ„์ด ์–ผ๋งˆ๋‚˜ ๊ฑธ๋ ธ๊ณ  ์–ด๋–ค ๊ณผ์ •๋“ค์ด ์žˆ์—ˆ๋Š”์ง€ ๊ธฐ์ˆ )

- 2025.03.12 : ํ”„๋กœ์ ํŠธ ์‹œ์ž‘
- 2025.4.24 : MVP ๋ฒ„์ „ ์™„์„ฑ. (๋ถ€๋ฅผ ๊ณก, ๊ฒ€์ƒ‰, ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ)
- 2025.4.27 : ChatGPT API ํ™œ์šฉ ์ผ๋ณธ์–ด ๊ณก ๋ฒˆ์—ญ ์‹œ๋„, cheerio ๋ฐ puppeteer๋ฅผ ํ™œ์šฉํ•ด์„œ ๊ตญ๋‚ด๊ณก ๋ฐ์ดํ„ฐ ์ถ”๊ฐ€

## ๐Ÿ“ ํšŒ๊ณ 
ํ•ด๊ฒฐํ•˜๊ณ ์ž ํ–ˆ๋˜ ๋ฌธ์ œ
(์–ด๋–ค ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ณ  ์‹ถ์—ˆ๋Š”์ง€)

๋ฌด์—‡์„ ์–ป์—ˆ๋Š”์ง€
(ํ”„๋กœ์ ํŠธ๋ฅผ ํ†ตํ•ด ๋ฐฐ์šด ์ ๊ณผ ๋А๋‚€ ์ )
### ํ•ด๊ฒฐํ•˜๊ณ ์ž ํ–ˆ๋˜ ๋ฌธ์ œ

- ๊ธฐ์กด ์œ ์‚ฌํ•œ ์„œ๋น„์Šค์—์„œ ํ•˜๋‚˜์˜ ๊ณก์„ ๊ฒ€์ƒ‰ํ•  ๋•Œ TJ, ๊ธˆ์˜ API๋กœ ๋”ฐ๋กœ๋”ฐ๋กœ ๊ฐ€์ ธ์˜จ๋‹ค๋Š” ๋ฌธ์ œ๋ฅผ ๋ฐ์ดํ„ฐ๋ฅผ ํฌ๋กค๋งํ•˜์—ฌ ์ž์ฒด DB(Supabase)๋ฅผ ๋งŒ๋“œ๋Š” ๊ฒƒ์œผ๋กœ ํ•ด๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค.
- (์‰ฝ๊ฒŒ ์ผ๋ณธ ๊ณก ๊ฒ€์ƒ‰ํ•œ๋‹ค๋Š” ๋ชฉ์  ๊ฐ•์กฐ??)
- Next.js์˜ API Route๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์„œ๋ฒ„๋ฆฌ์Šค API๋ฅผ ๊ตฌ์ถ•ํ•˜๊ณ  ์™ธ๋ถ€ API์˜ URL์„ ๊ฐ์ถ”๊ณ ์ž ํ•˜์˜€์Šต๋‹ˆ๋‹ค.
- react query๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์„œ๋ฒ„, ์ฆ‰ DB์™€์˜ ๋™๊ธฐํ™”๋ฅผ ๊ตฌ์ถ•ํ•˜๊ณ ์ž ํ•˜์˜€์Šต๋‹ˆ๋‹ค.
- OPEN API๋กœ๋Š” ๊ธˆ์˜ ๋…ธ๋ž˜๋ฐฉ์˜ ๊ณก์„ DB์— ์ €์žฅํ•˜๊ธฐ์— ํ•œ๊ณ„๊ฐ€ ์žˆ์—ˆ๊ธฐ์— puppeteer๋กœ ๊ธˆ์˜ ๋…ธ๋ž˜๋ฐฉ ์œ ํŠœ๋ธŒ ์ฑ„๋„์„ ๋ Œ๋”๋งํ•˜์—ฌ ๊ณก์„ ๊ฒ€์ƒ‰ํ•˜๊ณ  cheerio๋กœ ๊ฒฐ๊ณผ๋ฅผ ํŒŒ์‹ฑํ•˜์—ฌ ํฌ๋กค๋ง ํ”„๋กœ์„ธ์Šค๋ฅผ ์„ค๊ณ„ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

### ๋ฌด์—‡์„ ์–ป์—ˆ๋Š”์ง€

## ๐Ÿ“„ ๋ผ์ด์„ ์Šค
- DMS์ธ Supabase๋ฅผ ํ†ตํ•ด DB๋ฅผ ์ง์ ‘ ๊ตฌ์ถ•ํ•˜๋ฉด์„œ DB Table ๊ตฌ์กฐ์— ๋Œ€ํ•œ ๊ฐœ๋…์„ ์ตํ˜”์Šต๋‹ˆ๋‹ค.
- react query๋ฅผ ๋„์ž…ํ•˜๋ฉฐ ์ฟผ๋ฆฌ ํ‚ค๋ฅผ ํ™œ์šฉํ•œ ๋‚™๊ด€์  ์—…๋ฐ์ดํŠธ, ๋ฐ์ดํ„ฐ ์บ์‹ฑ์— ๋Œ€ํ•œ ์ดํ•ด๋ฅผ ๋†’์˜€์Šต๋‹ˆ๋‹ค.

์ด ํ”„๋กœ์ ํŠธ๋Š” MIT ๋ผ์ด์„ ์Šค๋ฅผ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค.

```
MIT License

Copyright (c) 2025
## ๐Ÿ“„ ๋ผ์ด์„ ์Šค

Permission is hereby granted, free of charge, to any person obtaining a copy
...
์ด ํ”„๋กœ์ ํŠธ๋Š” MIT ๋ผ์ด์„ ์Šค๋ฅผ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค.
13 changes: 11 additions & 2 deletions packages/crawling/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,19 @@
".": "./src/index.js"
},
"dependencies": {
"@supabase/supabase-js": "^2.49.1",
"@repo/open-api": "workspace:*",
"@supabase/supabase-js": "^2.49.1",
"axios": "^1.5.0",
"cheerio": "^1.0.0",
"dotenv": "^16.4.7"
"dotenv": "^16.4.7",
"openai": "^4.96.0",
"puppeteer": "^24.7.2"
},
"devDependencies": {
"@types/node": "^22.14.1",
"ts-node": "^10.9.2",
"tsup": "^8.4.0",
"tsx": "^4.19.3",
"typescript": "^5.8.3"
}
}
38 changes: 0 additions & 38 deletions packages/crawling/src/argList.js

This file was deleted.

52 changes: 52 additions & 0 deletions packages/crawling/src/argList.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { ArgList } from "./types";

const getObject = (
url: string,
artist: string,
titleIndex: number,
tjIndex: number,
kyIndex: number
): ArgList => {
return { url, artist, titleIndex, tjIndex, kyIndex };
};

// ํ…Œ์ด๋ธ” ๊ตฌ์กฐ ์ƒ ๋ถˆ๊ฐ€๋Šฅํ–ˆ๋˜ ๋ฌธ์„œ
// aiko, AKB48, ๋…ธ๊ธฐ์ž์นด46, ๋‹ˆ์‹œ๋…ธ ์นด๋‚˜, ๋ชจ๋‹๊ตฌ ๋ฌด์Šค๋ฉ”, Sound Horizon, ์˜ค์˜ค์ธ ์นด ์•„์ด, ์ผ€์•ผํ‚ค์ž์นด46
// ๋„ˆ๋ฌด ์ ์€ ๋ฐ์ดํ„ฐ
// &TEAM, NiziU

export const argList = [
// url, artist, titleIndex, tjIndex, kyIndex
getObject("AAA(ํ˜ผ์„ฑ๊ทธ๋ฃน)", "AAA", 0, 3, 2),
getObject("Aimer", "์—์ด๋จธ(Aimer)", 2, 0, 1),
getObject("amazarashi", "์•„๋งˆ์ž๋ผ์‹œ(amazarashi)", 0, 1, 2),
getObject("BUMP OF CHICKEN", "BUMP OF CHICKEN", 0, 1, 2),
getObject("DREAMS COME TRUE(๋ฐด๋“œ)", "DREAMS COME TRUE", 0, 1, 2),
getObject("ELLEGARDEN", "์—˜๋ฅด๊ฐ€๋“ (ELLEGARDEN)", 0, 1, 2),
getObject("King Gnu", "ํ‚น๋ˆ„(King Gnu)", 0, 1, 2),
getObject("LiSA", "๋ฆฌ์‚ฌ(LiSA)", 2, 0, 1),
getObject("Mrs. GREEN APPLE", "๋ฏธ์„ธ์Šค๊ทธ๋ฆฐ์• ํ”Œ(Mrs. GREEN APPLE)", 0, 1, 2),
getObject("Official้ซญ็”ทdism", "์˜คํ”ผ์…œํžˆ๊ฒŒ๋‹จ๋””์ฆ˜(Official้ซญ็”ทdism)", 2, 0, 1),
getObject("Perfume", "ํผํ“ธ(Perfume)", 0, 1, 2),
getObject("RADWIMPS", "๋ž˜๋“œ์œ”ํ”„์Šค(RADWIMPS)", 2, 0, 1),
getObject("SEKAI NO OWARI", "์„ธ์นด์ด๋…ธ์˜ค์™€๋ฆฌ(SEKAI NO OWARI)", 0, 1, 2),
getObject("SPYAIR", "์ŠคํŒŒ์ด์—์–ด(SPYAIR)", 2, 0, 1),
getObject("Vaundy", "๋ฐ”์šด๋””(Vaundy)", 2, 0, 1),
getObject("w-inds.", "w-inds.", 0, 1, 2),
getObject("YOASOBI", "์š”์•„์†Œ๋น„(YOASOBI)", 0, 1, 2),
getObject(
"๊ณ„์† ํ•œ๋ฐค์ค‘์ด๋ฉด ์ข‹์„ ํ…๋ฐ.",
"๊ณ„์† ํ•œ๋ฐค์ค‘์ด๋ฉด ์ข‹์„ ํ…๋ฐ.(์ฆ›ํ† ๋งˆ์š”๋‚˜์นด๋ฐ์ด์ด๋…ธ๋‹ˆ)",
2,
0,
1
),
getObject("๋ฒ ๋ฆฌ์ฆˆ์ฝ”๋ณด", "๋ฒ ๋ฆฌ์ฆˆ์ฝ”๋ณด", 0, 1, 2),
getObject("์•„๋ผ์‹œ(์•„์ด๋Œ)", "์•„๋ผ์‹œ", 0, 1, 2),
getObject("์•„์ด๋ฌญ", "์•„์ด๋ฌญ(ใ‚ใ„ใฟใ‚‡ใ‚“)", 0, 1, 2),
getObject("์š”๋„ค์ฆˆ ์ผ„์‹œ", "์š”๋„ค์ฆˆ ์ผ„์‹œ", 2, 0, 1),
getObject("์š”๋ฃจ์‹œ์นด", "์š”๋ฃจ์‹œ์นด(ใƒจใƒซใ‚ทใ‚ซ)", 0, 1, 2),
getObject("์œ ์ด์นด", "์œ ์ด์นด", 0, 1, 2),
getObject("ํ˜ธ์‹œ๋…ธ ๊ฒ", "ํ˜ธ์‹œ๋…ธ ๊ฒ", 0, 1, 2),
getObject("Creepy Nuts", "ํฌ๋ฆฌํ”ผ ๋„›์ธ (Creepy Nuts)", 2, 0, 1),
];
Original file line number Diff line number Diff line change
@@ -1,30 +1,33 @@
import axios from 'axios';
import * as cheerio from 'cheerio';
import dotenv from 'dotenv';
import axios from "axios";
import * as cheerio from "cheerio";
import dotenv from "dotenv";

import { parseNumber, parseJapaneseText, parseText } from './utils.js';
import { ArgList, Song } from "./types";

import { parseNumber, parseJapaneseText, parseText } from "./utils";
dotenv.config();

// โœ… ๋‚˜๋ฌด์œ„ํ‚ค์—์„œ ๋ฐ์ดํ„ฐ ํฌ๋กค๋ง
export async function scrapeSongs(dst) {
export async function scrapeSongs(dst: ArgList) {
try {
const { url, artist, titleIndex, tjIndex, kyIndex } = dst;
if (!url || !artist) {
throw new Error('url ๋˜๋Š” artist๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.');
throw new Error("url ๋˜๋Š” artist๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.");
}

const baseUrl = process.env.NAMU_KARAOKE_URL;
const endURL = process.env.NAMU_KARAOKE_END_URL;
const baseUrl = "https://namu.wiki/w/";
const endURL =
"/%EB%85%B8%EB%9E%98%EB%B0%A9%20%EC%88%98%EB%A1%9D%20%EB%AA%A9%EB%A1%9D";
const fullURL = baseUrl + url + endURL;
console.log(fullURL);
const { data } = await axios.get(fullURL);
// const { data } = await axios.get(url, { headers: { 'User-Agent': 'Mozilla/5.0' } });
const $ = cheerio.load(data);

let songs = [];
let songs: Song[] = [];

$('table tbody tr').each((index, element) => {
const cols = $(element).find('td');
$("table tbody tr").each((index, element) => {
const cols = $(element).find("td");

const title = parseJapaneseText($(cols[titleIndex]).text());
const num_tj = parseNumber($(cols[tjIndex]).text().trim().slice(0, 5));
Expand All @@ -36,29 +39,31 @@ export async function scrapeSongs(dst) {

return songs;
} catch (error) {
console.error('ํฌ๋กค๋ง ์‹คํŒจ:', error);
console.error("ํฌ๋กค๋ง ์‹คํŒจ:", error);
return [];
}
}

export async function scrapeAllSongs(dst) {
export async function scrapeAllSongs() {
try {
const titleIndex = 2;
const artistIndex = 3;
const tjIndex = 0;
const kyIndex = 1;

const baseUrl = process.env.NAMU_KARAOKE_URL;
const url = '์• ๋‹ˆ๋ฉ”์ด์…˜%20์Œ์•…/๋…ธ๋ž˜๋ฐฉ%20์ˆ˜๋ก%20๋ชฉ๋ก/์ „์ฒด๊ณก%20์ผ๋žŒ';
const baseUrl = "https://namu.wiki/w/";
const url = "์• ๋‹ˆ๋ฉ”์ด์…˜%20์Œ์•…/๋…ธ๋ž˜๋ฐฉ%20์ˆ˜๋ก%20๋ชฉ๋ก/์ „์ฒด๊ณก%20์ผ๋žŒ";
const fullURL = baseUrl + url;
console.log(fullURL);
const { data } = await axios.get(fullURL, { headers: { 'User-Agent': 'Mozilla/5.0' } });
const { data } = await axios.get(fullURL, {
headers: { "User-Agent": "Mozilla/5.0" },
});
const $ = cheerio.load(data);

let songs = [];
let songs: Song[] = [];

$('table tbody tr').each((index, element) => {
const cols = $(element).find('td');
$("table tbody tr").each((index, element) => {
const cols = $(element).find("td");

const title = parseText($(cols[titleIndex]).text());
const artist = parseText($(cols[artistIndex]).text());
Expand All @@ -71,7 +76,7 @@ export async function scrapeAllSongs(dst) {

return songs;
} catch (error) {
console.error('ํฌ๋กค๋ง ์‹คํŒจ:', error);
console.error("ํฌ๋กค๋ง ์‹คํŒจ:", error);
return [];
}
}
Expand All @@ -83,17 +88,19 @@ export async function scrapeUtaiteSongs() {
const tjIndex = 0;
const kyIndex = 1;

const baseUrl = process.env.NAMU_KARAOKE_URL;
const url = '์šฐํƒ€์ดํ…Œ ์˜ค๋ฆฌ์ง€๋„ ๊ณก/๋…ธ๋ž˜๋ฐฉ ์ˆ˜๋ก ๋ชฉ๋ก';
const baseUrl = "https://namu.wiki/w/";
const url = "์šฐํƒ€์ดํ…Œ ์˜ค๋ฆฌ์ง€๋„ ๊ณก/๋…ธ๋ž˜๋ฐฉ ์ˆ˜๋ก ๋ชฉ๋ก";
const fullURL = baseUrl + url;
console.log(fullURL);
const { data } = await axios.get(fullURL, { headers: { 'User-Agent': 'Mozilla/5.0' } });
const { data } = await axios.get(fullURL, {
headers: { "User-Agent": "Mozilla/5.0" },
});
const $ = cheerio.load(data);

let songs = [];
let songs: Song[] = [];

$('table tbody tr').each((index, element) => {
const cols = $(element).find('td');
$("table tbody tr").each((index, element) => {
const cols = $(element).find("td");

const title = parseText($(cols[titleIndex]).text());
const artist = parseText($(cols[artistIndex]).text());
Expand All @@ -106,7 +113,7 @@ export async function scrapeUtaiteSongs() {

return songs;
} catch (error) {
console.error('ํฌ๋กค๋ง ์‹คํŒจ:', error);
console.error("ํฌ๋กค๋ง ์‹คํŒจ:", error);
return [];
}
}
Loading