diff --git a/bun.lock b/bun.lock index bf77f28..3471259 100644 --- a/bun.lock +++ b/bun.lock @@ -14,6 +14,7 @@ "@unocss/preset-icons": "^66.5.6", "@unocss/reset": "^66.5.6", "chart.js": "^4.5.1", + "franc": "^6.2.0", "m3-svelte": "^5.14.1", "mongodb": "^7.0.0", "openai": "^6.9.0", @@ -436,6 +437,8 @@ "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], + "collapse-white-space": ["collapse-white-space@2.1.0", "", {}, "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw=="], + "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], @@ -604,6 +607,8 @@ "forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="], + "franc": ["franc@6.2.0", "", { "dependencies": { "trigram-utils": "^2.0.0" } }, "sha512-rcAewP7PSHvjq7Kgd7dhj82zE071kX5B4W1M4ewYMf/P+i6YsDQmj62Xz3VQm9zyUzUXwhIde/wHLGCMrM+yGg=="], + "fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="], "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], @@ -758,6 +763,8 @@ "music-metadata": ["music-metadata@11.10.0", "", { "dependencies": { "@borewit/text-codec": "^0.2.0", "@tokenizer/token": "^0.3.0", "content-type": "^1.0.5", "debug": "^4.4.3", "file-type": "^21.0.0", "media-typer": "^1.1.0", "strtok3": "^10.3.4", "token-types": "^6.1.1", "uint8array-extras": "^1.5.0" } }, "sha512-alZYPjpqAPFgVZaFQob0PMq/9tSqaR+3m159vavrptxj09P0GcyBkDQI/wuCyn4uz/TDCrS8gN+9SzURlahmdQ=="], + "n-gram": ["n-gram@2.0.2", "", {}, "sha512-S24aGsn+HLBxUGVAUFOwGpKs7LBcG4RudKU//eWzt/mQ97/NMKQxDWHyHx63UNWk/OOdihgmzoETn1tf5nQDzQ=="], + "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], @@ -984,6 +991,8 @@ "tr46": ["tr46@5.1.1", "", { "dependencies": { "punycode": "^2.3.1" } }, "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw=="], + "trigram-utils": ["trigram-utils@2.0.1", "", { "dependencies": { "collapse-white-space": "^2.0.0", "n-gram": "^2.0.0" } }, "sha512-nfWIXHEaB+HdyslAfMxSqWKDdmqY9I32jS7GnqpdWQnLH89r6A5sdk3fDVYqGAZ0CrT8ovAFSAo6HRiWcWNIGQ=="], + "ts-api-utils": ["ts-api-utils@2.1.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ=="], "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], diff --git a/package.json b/package.json index dd401a6..dc57678 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "@unocss/preset-icons": "^66.5.6", "@unocss/reset": "^66.5.6", "chart.js": "^4.5.1", + "franc": "^6.2.0", "m3-svelte": "^5.14.1", "mongodb": "^7.0.0", "openai": "^6.9.0", diff --git a/src/lib/server/songs.ts b/src/lib/server/songs.ts index 71e5fea..132b034 100644 --- a/src/lib/server/songs.ts +++ b/src/lib/server/songs.ts @@ -2,6 +2,8 @@ import * as ne from '@neteasecloudmusicapienhanced/api' import { aiParseLyrics } from './tools/lyrics' import type { NeteaseSongBrief } from '../../shared/types' import { db } from './db' +import { franc } from 'franc' +import { error } from '@sveltejs/kit' /** * Functional wrapper to cache API results to MongoDB. @@ -73,15 +75,20 @@ export async function getSongsFromPlaylist(ref: string): Promise<{meta: any, son return {meta: plData.playlist, songs: plData.playlist.tracks.map(parseBrief)} } -interface NeteaseLyricsResponse { lrc: { lyric: string } } +interface NeteaseLyricsResponse { lrc: { lyric: string }, lang: string } export const getLyricsRaw = cached('lyrics_raw', - async (songId: number) => (await ne.lyric({ id: songId })).body as any as NeteaseLyricsResponse + async (songId: number) => { + const raw = (await ne.lyric({ id: songId })).body as any as NeteaseLyricsResponse + const lang = franc(raw.lrc.lyric) + return { ...raw, lang } + } ) export const getLyricsProcessed = cached('lyrics_processed', async (songId: number) => { const raw = await getLyricsRaw(songId) + if (raw.lang !== 'ja') throw error(400, 'Lyrics are not in Japanese') console.log(`Processing lyrics for song ${songId}`) return aiParseLyrics(raw.lrc.lyric) })