diff --git a/backend-deno/config.ts b/backend-deno/config.ts index a1e07c0..9ce52ca 100644 --- a/backend-deno/config.ts +++ b/backend-deno/config.ts @@ -9,7 +9,6 @@ export const config = { // 歌词配置 lyrics: { - cacheDir: "./lyrics", apiUrl: "http://123.57.93.143:28883", apiKey: "fzt_tom", }, diff --git a/backend-deno/modules/lyrics.ts b/backend-deno/modules/lyrics.ts index 0fd831c..12a5a95 100644 --- a/backend-deno/modules/lyrics.ts +++ b/backend-deno/modules/lyrics.ts @@ -4,6 +4,7 @@ */ import { MusicStatus } from "./apple_music.ts"; +import { DatabaseSync } from 'node:sqlite' export interface LyricsData { current_lyric_time: number | null; @@ -37,14 +38,23 @@ let lyricsCache: LyricsCache = { }; const DB_PATH = "./lyrics.db"; +const db = new DatabaseSync(DB_PATH); /** * 初始化数据库 */ export async function initDB(): Promise { try { - // 确保歌词目录存在 - await Deno.mkdir("./lyrics", { recursive: true }); + db.exec(` + CREATE TABLE IF NOT EXISTS lyrics ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + track_name TEXT NOT NULL, + artist TEXT NOT NULL, + lyrics_text TEXT NOT NULL, + source TEXT NOT NULL, + deleted BOOLEAN NOT NULL DEFAULT 0 + ) + `); console.log(`数据库初始化完成: ${DB_PATH}`); } catch (error) { console.error(`数据库初始化失败: ${error}`); @@ -72,11 +82,8 @@ async function loadLyricsFromDB( artist: string ): Promise { try { - const filePath = `./lyrics/${encodeURIComponent( - trackName - )}_${encodeURIComponent(artist)}.lrc`; - const content = await Deno.readTextFile(filePath); - return content; + const content = db.prepare(`SELECT lyrics_text FROM lyrics WHERE track_name = ? AND artist = ?`).get(trackName, artist); + return content.lyrics_text; } catch (error) { return null; } @@ -85,25 +92,29 @@ async function loadLyricsFromDB( /** * 保存歌词到数据库 */ -async function saveLyrics( +export async function saveLyrics( trackName: string, artist: string, - lyricsText: string + lyricsText: string, + source: string ): Promise { try { - const filePath = `./lyrics/${encodeURIComponent( - trackName - )}_${encodeURIComponent(artist)}.lrc`; - await Deno.writeTextFile(filePath, lyricsText); + db.prepare(`DELETE FROM lyrics WHERE track_name = ? AND artist = ?`).run(trackName, artist); + db.prepare(`INSERT INTO lyrics (track_name, artist, lyrics_text, source) VALUES (?, ?, ?, ?)`).run(trackName, artist, lyricsText, source); + lyricsCache.track_name = trackName; + lyricsCache.artist = artist; + lyricsCache.lyrics_text = lyricsText; + lyricsCache.source = source; + console.log(`保存歌词成功: ${trackName} - ${artist}`); } catch (error) { console.error(`保存歌词失败: ${error}`); } } /** - * 从网易云音乐 API 搜索歌词 + * 从服务器 API 搜索歌词 */ -async function searchLyrics( +export async function searchLyrics( trackName: string, artistName: string ): Promise { @@ -234,6 +245,18 @@ function formatLrcLyrics( }; } +/** + * 删除歌词 + */ +export async function deleteLyrics(trackName: string, artist: string): Promise { + try { + db.prepare(`DELETE FROM lyrics WHERE track_name = ? AND artist = ?`).run(trackName, artist); + console.log(`删除歌词成功: ${trackName} - ${artist}`); + } catch (error) { + console.error(`删除歌词失败: ${error}`); + } +} + /** * 获取当前歌词数据 */ @@ -276,9 +299,9 @@ export async function getLyricsData(status: MusicStatus): Promise { const lyricsText = await searchLyrics(trackName || "", artist || ""); if (lyricsText) { // 保存到数据库 - await saveLyrics(trackName || "", artist || "", lyricsText); + await saveLyrics(trackName || "", artist || "", lyricsText, "server"); lyricsCache.lyrics_text = lyricsText; - lyricsCache.source = "netease"; + lyricsCache.source = "server"; } } } diff --git a/backend-deno/modules/routes.ts b/backend-deno/modules/routes.ts index d91badd..0d05eca 100644 --- a/backend-deno/modules/routes.ts +++ b/backend-deno/modules/routes.ts @@ -5,10 +5,9 @@ import { Hono } from "hono"; import { cors } from "cors"; -import { logger } from "logger"; import { ConnectionManager } from "./websocket_manager.ts"; import { getMusicStatus, controlMusic } from "./apple_music.ts"; -import { getLyricsData } from "./lyrics.ts"; +import { getLyricsData, searchLyrics, saveLyrics, deleteLyrics } from "./lyrics.ts"; export interface ControlRequest { action: "playpause" | "previous" | "next" | "seek"; @@ -16,7 +15,7 @@ export interface ControlRequest { } export interface SearchLyricsRequest { - title: string; + track_name: string; artist: string; } @@ -25,6 +24,17 @@ export interface GetLyricsFromIdRequest { artist: string; } +export interface SaveLyricsRequest { + track_name: string; + artist: string; + lyrics: string; +} + +export interface DeleteLyricsRequest { + track_name: string; + artist: string; +} + /** * 注册所有路由 */ @@ -89,27 +99,50 @@ export function registerRoutes(app: Hono, manager: ConnectionManager): void { } }); + app.delete("/lyrics", async (c) => { + try { + const body = (await c.req.json()) as DeleteLyricsRequest; + const { track_name, artist } = body; + await deleteLyrics(track_name, artist); + return c.json({ status: "success", message: "歌词删除成功" }); + } catch (error) { + return c.json({ status: "error", error: String(error) }, 500); + } + }); + // 搜索歌词 app.post("/lyrics/search", async (c) => { try { const body = (await c.req.json()) as SearchLyricsRequest; - if (!body.title) { - return c.json({ status: "error", message: "缺少 title 参数" }, 400); - } - - // 这里可以实现歌词搜索逻辑 - // 暂时返回空结果 + const { track_name, artist } = body; + const lyricsData = await searchLyrics(track_name, artist); return c.json({ status: "success", - lyrics: null, - message: "歌词搜索功能待实现", + songs: [{ + name: track_name, + artist: artist, + lyrics: lyricsData, + }], + message: "歌词搜索成功", }); } catch (error) { return c.json({ status: "error", error: String(error) }, 500); } }); + // 保存歌词 + app.post("/lyrics/save", async (c) => { + try { + const body = (await c.req.json()) as SaveLyricsRequest; + const { track_name, artist, lyrics } = body; + await saveLyrics(track_name, artist, lyrics, "search"); + return c.json({ status: "success", message: "歌词保存成功" }); + } catch (error) { + return c.json({ status: "error", error: String(error) }, 500); + } + }); + // 根据歌曲信息获取歌词 app.post("/lyrics/get", async (c) => { try { diff --git a/electron-app/.gitignore b/electron-app/.gitignore index c0da2b4..70acf1f 100644 --- a/electron-app/.gitignore +++ b/electron-app/.gitignore @@ -102,3 +102,6 @@ ENV/ # Local dev configurations config.local.js + +# SQLite database +lyrics.db diff --git a/electron-app/main.js b/electron-app/main.js index c5bd44a..ea6345c 100644 --- a/electron-app/main.js +++ b/electron-app/main.js @@ -199,12 +199,6 @@ app.on("activate", function () { } }); -// 应用退出前 -app.on("before-quit", () => { - // 清理Python进程 - backendService.stopPythonBackend(); -}); - // 监听第二实例启动事件 app.on("second-instance", (event, commandLine, workingDirectory) => { // 当尝试启动第二个实例时,让主窗口获得焦点 diff --git a/electron-app/modules/window-manager.js b/electron-app/modules/window-manager.js index 9c4a07f..7d16d20 100644 --- a/electron-app/modules/window-manager.js +++ b/electron-app/modules/window-manager.js @@ -267,7 +267,7 @@ function createWindow(frontendUrl, frontendPort, backendPort, isDev) { * @returns {Array} 菜单模板数组 */ function getMenuTemplate() { - return [ + const res = [ { label: i18next.t("menu.lockWindow"), click: () => { @@ -286,12 +286,6 @@ function createWindow(frontendUrl, frontendPort, backendPort, isDev) { mainWindow.reload(); }, }, - { - label: i18next.t("menu.openDevTools"), - click: () => { - mainWindow.webContents.openDevTools(); - }, - }, { label: i18next.t("menu.language"), submenu: [ @@ -339,6 +333,16 @@ function createWindow(frontendUrl, frontendPort, backendPort, isDev) { }, }, ]; + if (isDev) { + res.push({ + label: i18next.t("menu.openDevTools"), + click: () => { + mainWindow.webContents.openDevTools(); + searchWindow && searchWindow.webContents.openDevTools(); + }, + }); + } + return res; } /** diff --git a/frontend/src/utils/musicControl.js b/frontend/src/utils/musicControl.js index cc8f25e..c2a8d40 100644 --- a/frontend/src/utils/musicControl.js +++ b/frontend/src/utils/musicControl.js @@ -25,19 +25,13 @@ async function sendControlCommand(action, position = null) { data: payload }); - if (!response.ok) { - throw new Error(`控制请求失败: ${response.status} ${response.statusText}`); - } - - const result = await response.json(); - // 检查结果状态,如果Apple Music未运行,提供明确的错误信息 - if (result.status === 'error' && result.message === 'notrunning') { + if (response.status === 'error' && response.message === 'notrunning') { console.warn('Apple Music未运行,无法执行控制命令'); throw new Error('Apple Music未运行,请先启动Apple Music应用'); } - return result; + return response; } catch (error) { console.error(`${action}控制请求错误:`, error); throw error; diff --git a/frontend/src/views/SearchLyrics.vue b/frontend/src/views/SearchLyrics.vue index 4121ad5..96bc6ba 100644 --- a/frontend/src/views/SearchLyrics.vue +++ b/frontend/src/views/SearchLyrics.vue @@ -50,7 +50,7 @@ >
{{ result.name }}
-
{{ getArtistName(result.artists) }}
+
{{ result.artist }}