feat: 后端改为deno

This commit is contained in:
ethan.chen
2025-11-05 11:43:47 +08:00
parent ba3435e1a0
commit f67a3835fa
27 changed files with 1449 additions and 1404 deletions

View File

@@ -0,0 +1,305 @@
/**
* 歌词管理模块 (Deno 版本)
* 负责歌词的获取、解析和缓存
*/
import { MusicStatus } from "./apple_music.ts";
export interface LyricsData {
current_lyric_time: number | null;
current_lyric: string | null;
next_lyric: string | null;
next_next_lyric: string | null;
track_name: string | null;
artist_name: string | null;
}
interface LyricsLine {
time: number;
text: string;
}
interface LyricsCache {
track_name: string | null;
artist: string | null;
lyrics_text: string | null;
source: string | null;
deleted: boolean;
}
// 全局歌词缓存
let lyricsCache: LyricsCache = {
track_name: null,
artist: null,
lyrics_text: null,
source: null,
deleted: false,
};
const DB_PATH = "./lyrics.db";
/**
* 初始化数据库
*/
export async function initDB(): Promise<void> {
try {
// 确保歌词目录存在
await Deno.mkdir("./lyrics", { recursive: true });
console.log(`数据库初始化完成: ${DB_PATH}`);
} catch (error) {
console.error(`数据库初始化失败: ${error}`);
}
}
/**
* 清空歌词缓存
*/
export function clearLyricsCache(): void {
lyricsCache = {
track_name: null,
artist: null,
lyrics_text: null,
source: null,
deleted: false,
};
}
/**
* 从数据库加载歌词
*/
async function loadLyricsFromDB(
trackName: string,
artist: string
): Promise<string | null> {
try {
const filePath = `./lyrics/${encodeURIComponent(
trackName
)}_${encodeURIComponent(artist)}.lrc`;
const content = await Deno.readTextFile(filePath);
return content;
} catch (error) {
return null;
}
}
/**
* 保存歌词到数据库
*/
async function saveLyrics(
trackName: string,
artist: string,
lyricsText: string
): Promise<void> {
try {
const filePath = `./lyrics/${encodeURIComponent(
trackName
)}_${encodeURIComponent(artist)}.lrc`;
await Deno.writeTextFile(filePath, lyricsText);
} catch (error) {
console.error(`保存歌词失败: ${error}`);
}
}
/**
* 从网易云音乐 API 搜索歌词
*/
async function searchLyrics(
trackName: string,
artistName: string
): Promise<string | null> {
try {
const searchVariants = [
{ title: trackName, artist: artistName },
{ title: trackName, artist: "" },
{ title: trackName.replace(/[vV]er\.\s*\d*/g, ""), artist: artistName },
{ title: trackName.replace(/\([^)]*\)/g, "").trim(), artist: artistName },
];
for (const variant of searchVariants) {
try {
const encodedTitle = encodeURIComponent(variant.title);
const encodedArtist = encodeURIComponent(variant.artist);
let url = `http://123.57.93.143:28883/lyrics?title=${encodedTitle}`;
if (encodedArtist) {
url += `&artist=${encodedArtist}`;
}
const response = await fetch(url, {
headers: {
Authorization: "fzt_tom",
"User-Agent": "MacLyric/1.0",
},
});
if (response.ok) {
const lyricsText = await response.text();
if (lyricsText) {
return lyricsText;
}
}
} catch (error) {
console.warn(`搜索歌词变体失败: ${error}`);
continue;
}
}
return null;
} catch (error) {
console.error(`搜索歌词时出错: ${error}`);
return null;
}
}
/**
* 解析 LRC 格式歌词
*/
function parseLrcLyrics(lyricsText: string): LyricsLine[] {
const lines = lyricsText.split("\n");
const lyricsLines: LyricsLine[] = [];
for (const line of lines) {
const match = line.match(/^\[(\d{2}):(\d{2})\.(\d{2,3})\](.*)$/);
if (match) {
const minutes = parseInt(match[1]);
const seconds = parseInt(match[2]);
const milliseconds = parseInt(match[3].padEnd(3, "0"));
const text = match[4].trim();
const time = minutes * 60 + seconds + milliseconds / 1000;
lyricsLines.push({ time, text });
}
}
return lyricsLines.sort((a, b) => a.time - b.time);
}
/**
* 格式化歌词数据
*/
function formatLrcLyrics(
lyricsText: string,
currentPosition?: number
): LyricsData {
const lyricsLines = parseLrcLyrics(lyricsText);
if (lyricsLines.length === 0) {
return {
current_lyric_time: null,
current_lyric: null,
next_lyric: null,
next_next_lyric: null,
track_name: null,
artist_name: null,
};
}
if (currentPosition === undefined) {
return {
current_lyric_time: lyricsLines[0].time,
current_lyric: lyricsLines[0].text,
next_lyric: lyricsLines.length > 1 ? lyricsLines[1].text : null,
next_next_lyric: lyricsLines.length > 2 ? lyricsLines[2].text : null,
track_name: null,
artist_name: null,
};
}
let currentIndex = -1;
for (let i = 0; i < lyricsLines.length; i++) {
if (lyricsLines[i].time <= currentPosition) {
currentIndex = i;
} else {
break;
}
}
const currentLyric = currentIndex >= 0 ? lyricsLines[currentIndex] : null;
const nextLyric =
currentIndex + 1 < lyricsLines.length
? lyricsLines[currentIndex + 1]
: null;
const nextNextLyric =
currentIndex + 2 < lyricsLines.length
? lyricsLines[currentIndex + 2]
: null;
return {
current_lyric_time: currentLyric?.time || null,
current_lyric: currentLyric?.text || null,
next_lyric: nextLyric?.text || null,
next_next_lyric: nextNextLyric?.text || null,
track_name: null,
artist_name: null,
};
}
/**
* 获取当前歌词数据
*/
export async function getLyricsData(status: MusicStatus): Promise<LyricsData> {
// 如果没有播放,清空缓存并返回空
if (status.status !== "playing" && status.status !== "paused") {
clearLyricsCache();
return {
current_lyric_time: null,
current_lyric: null,
next_lyric: null,
next_next_lyric: null,
track_name: null,
artist_name: null,
};
}
const trackName = status.track_name;
const artist = status.artist;
const position = status.position || 0;
// 检查是否切换了歌曲
if (lyricsCache.track_name !== trackName || lyricsCache.artist !== artist) {
console.log(`歌曲切换,重新获取歌词: ${trackName} - ${artist}`);
// 清空缓存
lyricsCache.track_name = trackName;
lyricsCache.artist = artist;
lyricsCache.lyrics_text = null;
lyricsCache.deleted = false;
// 1. 先尝试从数据库获取
const lyricsText = await loadLyricsFromDB(trackName || "", artist || "");
if (lyricsText) {
lyricsCache.lyrics_text = lyricsText;
lyricsCache.source = "db";
}
// 2. 如果本地没有且不是被删除的歌词尝试从网易云音乐API获取歌词
else if (!lyricsCache.lyrics_text && !lyricsCache.deleted) {
const lyricsText = await searchLyrics(trackName || "", artist || "");
if (lyricsText) {
// 保存到数据库
await saveLyrics(trackName || "", artist || "", lyricsText);
lyricsCache.lyrics_text = lyricsText;
lyricsCache.source = "netease";
}
}
}
// 如果有缓存的歌词,直接解析
if (lyricsCache.lyrics_text) {
const lyricsData = formatLrcLyrics(lyricsCache.lyrics_text, position);
return {
...lyricsData,
track_name: trackName || null,
artist_name: artist || null,
};
}
// 没有歌词
return {
current_lyric_time: null,
current_lyric: null,
next_lyric: null,
next_next_lyric: null,
track_name: trackName || null,
artist_name: artist || null,
};
}