From 9fc08c59d738da369ba08970ab35e07a2a274a14 Mon Sep 17 00:00:00 2001 From: lqtmcstudio Date: Mon, 2 Feb 2026 13:54:31 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=87=8D=E6=9E=84=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dist-electron/main.js | 160 +----- dist-electron/preload.mjs | 13 +- electron/main.ts | 194 +------- electron/preload.ts | 20 - src/App.vue | 289 +---------- src/components/layout/PlayerBar.vue | 488 ------------------- src/components/layout/SettingsOverlay.vue | 361 -------------- src/components/layout/SideBar.vue | 190 -------- src/components/layout/WindowControls.vue | 57 --- src/main.ts | 17 +- src/stores/playerStore.ts | 188 ------- src/types/electron.d.ts | 12 - src/views/HomeView.vue | 383 --------------- src/views/PlaylistDetailView.vue | 568 ---------------------- 14 files changed, 65 insertions(+), 2875 deletions(-) delete mode 100644 src/components/layout/PlayerBar.vue delete mode 100644 src/components/layout/SettingsOverlay.vue delete mode 100644 src/components/layout/SideBar.vue delete mode 100644 src/components/layout/WindowControls.vue delete mode 100644 src/stores/playerStore.ts delete mode 100644 src/views/HomeView.vue delete mode 100644 src/views/PlaylistDetailView.vue diff --git a/dist-electron/main.js b/dist-electron/main.js index 0c12c18..fc9c43d 100644 --- a/dist-electron/main.js +++ b/dist-electron/main.js @@ -1,13 +1,7 @@ -var __defProp = Object.defineProperty; -var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; -var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); -import { app, ipcMain, BrowserWindow, Menu } from "electron"; +import { ipcMain, BrowserWindow, app, Menu } from "electron"; import { createRequire } from "node:module"; import { fileURLToPath } from "node:url"; import path from "node:path"; -import os from "node:os"; -import net from "node:net"; -import { spawn } from "node:child_process"; createRequire(import.meta.url); const __dirname$1 = path.dirname(fileURLToPath(import.meta.url)); process.env.APP_ROOT = path.join(__dirname$1, ".."); @@ -16,131 +10,6 @@ const MAIN_DIST = path.join(process.env.APP_ROOT, "dist-electron"); const RENDERER_DIST = path.join(process.env.APP_ROOT, "dist"); process.env.VITE_PUBLIC = VITE_DEV_SERVER_URL ? path.join(process.env.APP_ROOT, "public") : RENDERER_DIST; let win; -class MpvController { - // 用于处理 JSON 数据粘包 - constructor() { - __publicField(this, "process", null); - __publicField(this, "socket", null); - __publicField(this, "socketPath"); - __publicField(this, "buffer", ""); - const socketName = `mpv-socket-${Date.now()}`; - this.socketPath = process.platform === "win32" ? `\\\\.\\pipe\\${socketName}` : path.join(os.tmpdir(), socketName); - } - start(binaryPath) { - console.log(`[MPV] Starting from: ${binaryPath}`); - console.log(`[MPV] IPC Socket: ${this.socketPath}`); - this.process = spawn(binaryPath, [ - "--idle", - // 空闲时不退出 - "--no-video", - // 纯音频模式 - "--keep-open=yes", - // 播放结束不退出 - `--input-ipc-server=${this.socketPath}` - // 指定 IPC 监听地址 - ]); - this.process.on("error", (err) => { - console.error("[MPV] Process Error:", err); - }); - this.process.on("exit", (code) => { - var _a; - console.log(`[MPV] Process exited with code ${code}`); - (_a = this.socket) == null ? void 0 : _a.destroy(); - }); - this.connectSocket(); - } - connectSocket(retries = 10) { - setTimeout(() => { - const socket = net.createConnection(this.socketPath); - socket.on("connect", () => { - console.log("[MPV] IPC Connected!"); - this.socket = socket; - this.setupObservers(); - }); - socket.on("data", (data) => this.handleData(data)); - socket.on("error", (err) => { - if (retries > 0) { - this.connectSocket(retries - 1); - } else { - console.error("[MPV] Failed to connect to IPC socket:", err); - } - }); - }, 500); - } - // 处理接收到的数据 (解决 TCP 数据包粘连或截断问题) - handleData(data) { - this.buffer += data.toString(); - const lines = this.buffer.split("\n"); - this.buffer = lines.pop() || ""; - lines.forEach((line) => { - if (!line.trim()) return; - try { - const message = JSON.parse(line); - console.log(message); - this.handleMessage(message); - } catch (e) { - console.error("[MPV] JSON Parse Error:", e); - } - }); - } - // 处理解析后的 JSON 消息 - handleMessage(msg) { - if (!win) return; - if (msg.event === "property-change") { - switch (msg.name) { - case "time-pos": - win.webContents.send("mpv-time-update", msg.data); - break; - case "duration": - win.webContents.send("mpv-duration", msg.data); - break; - case "pause": - win.webContents.send("mpv-play-state", !msg.data); - break; - } - } - if (msg.event === "end-file") { - win.webContents.send("mpv-ended"); - win.webContents.send("mpv-play-state", false); - } - } - // 初始化监听属性 - setupObservers() { - this.send(["observe_property", 1, "time-pos"]); - this.send(["observe_property", 2, "duration"]); - this.send(["observe_property", 3, "pause"]); - } - // 发送命令给 MPV - send(command) { - if (!this.socket || this.socket.destroyed) return; - const payload = JSON.stringify({ command }); - this.socket.write(payload + "\n"); - } - // === 业务方法 === - load(url, autoPlay) { - if (autoPlay) { - this.send(["set_property", "pause", false]); - this.send(["loadfile", url, "replace"]); - } else { - this.send(["set_property", "pause", true]); - this.send(["loadfile", url, "replace"]); - } - } - play() { - this.send(["set_property", "pause", false]); - } - pause() { - this.send(["set_property", "pause", true]); - } - seek(time) { - this.send(["seek", time, "absolute"]); - } - setVolume(volume) { - this.send(["set_property", "volume", volume]); - } -} -const mpv = new MpvController(); -const mpvExecutablePath = app.isPackaged ? path.join(process.resourcesPath, "core", "mpv.exe") : path.join(process.env.APP_ROOT, "core", "mpv.exe"); function createWindow() { win = new BrowserWindow({ frame: false, @@ -163,6 +32,7 @@ function createWindow() { } else { win.loadFile(path.join(RENDERER_DIST, "index.html")); } + registerZoomShortcuts(win); } ipcMain.on("window-minimize", (event) => { var _a; @@ -171,17 +41,32 @@ ipcMain.on("window-minimize", (event) => { ipcMain.on("window-maximize", () => (win == null ? void 0 : win.isMaximized()) ? win.unmaximize() : win == null ? void 0 : win.maximize()); ipcMain.on("window-close", () => win == null ? void 0 : win.close()); ipcMain.handle("window-is-maximized", () => (win == null ? void 0 : win.isMaximized()) || false); -ipcMain.on("mpv-load", (_, url, autoPlay = true) => mpv.load(url, autoPlay)); -ipcMain.on("mpv-play", () => mpv.play()); -ipcMain.on("mpv-pause", () => mpv.pause()); -ipcMain.on("mpv-seek", (_, time) => mpv.seek(time)); -ipcMain.on("mpv-volume", (_, volume) => mpv.setVolume(volume)); app.on("window-all-closed", () => { if (process.platform !== "darwin") { app.quit(); win = null; } }); +function registerZoomShortcuts(win2) { + win2.webContents.on("before-input-event", (event, input) => { + if (input.control || input.meta) { + if (input.key.toLowerCase() === "=" || input.key === "+") { + let currentZoom = win2.webContents.getZoomFactor(); + win2.webContents.setZoomFactor(currentZoom + 0.1); + event.preventDefault(); + } else if (input.key === "-" || input.key === "_") { + let currentZoom = win2.webContents.getZoomFactor(); + if (currentZoom > 0.5) { + win2.webContents.setZoomFactor(currentZoom - 0.1); + } + event.preventDefault(); + } else if (input.key === "0") { + win2.webContents.setZoomFactor(1); + event.preventDefault(); + } + } + }); +} app.on("activate", () => { if (BrowserWindow.getAllWindows().length === 0) { createWindow(); @@ -189,7 +74,6 @@ app.on("activate", () => { }); app.whenReady().then(() => { Menu.setApplicationMenu(null); - mpv.start(mpvExecutablePath); createWindow(); }); export { diff --git a/dist-electron/preload.mjs b/dist-electron/preload.mjs index 4454d43..674120e 100644 --- a/dist-electron/preload.mjs +++ b/dist-electron/preload.mjs @@ -5,16 +5,5 @@ electron.contextBridge.exposeInMainWorld("electronAPI", { minimizeWindow: () => electron.ipcRenderer.send("window-minimize"), maximizeWindow: () => electron.ipcRenderer.send("window-maximize"), closeWindow: () => electron.ipcRenderer.send("window-close"), - isMaximized: () => electron.ipcRenderer.invoke("window-is-maximized"), - // MPV 控制 (Renderer -> Main) - mpvLoad: (url, autoPlay = true) => electron.ipcRenderer.send("mpv-load", url, autoPlay), - mpvPlay: () => electron.ipcRenderer.send("mpv-play"), - mpvPause: () => electron.ipcRenderer.send("mpv-pause"), - mpvSeek: (time) => electron.ipcRenderer.send("mpv-seek", time), - mpvSetVolume: (volume) => electron.ipcRenderer.send("mpv-volume", volume), - // MPV 事件 (Main -> Renderer) - onMpvTimeUpdate: (callback) => electron.ipcRenderer.on("mpv-time-update", (_, time) => callback(time)), - onMpvDuration: (callback) => electron.ipcRenderer.on("mpv-duration", (_, duration) => callback(duration)), - onMpvPlayState: (callback) => electron.ipcRenderer.on("mpv-play-state", (_, isPlaying) => callback(isPlaying)), - onMpvEnded: (callback) => electron.ipcRenderer.on("mpv-ended", () => callback()) + isMaximized: () => electron.ipcRenderer.invoke("window-is-maximized") }); diff --git a/electron/main.ts b/electron/main.ts index a798760..17b0747 100644 --- a/electron/main.ts +++ b/electron/main.ts @@ -2,10 +2,8 @@ import { app, BrowserWindow, Menu, ipcMain } from 'electron' import { createRequire } from 'node:module' import { fileURLToPath } from 'node:url' import path from 'node:path' -import os from 'node:os' -import net from 'node:net' -import { spawn, type ChildProcess } from 'node:child_process' +// @ts-ignore const require = createRequire(import.meta.url) const __dirname = path.dirname(fileURLToPath(import.meta.url)) @@ -19,162 +17,8 @@ process.env.VITE_PUBLIC = VITE_DEV_SERVER_URL ? path.join(process.env.APP_ROOT, let win: BrowserWindow | null - -class MpvController { - private process: ChildProcess | null = null; - private socket: net.Socket | null = null; - private socketPath: string; - private buffer: string = ''; - - constructor() { - const socketName = `mpv-socket-${Date.now()}`; - this.socketPath = process.platform === 'win32' - ? `\\\\.\\pipe\\${socketName}` - : path.join(os.tmpdir(), socketName); - } - - start(binaryPath: string) { - console.log(`[MPV] Starting from: ${binaryPath}`); - console.log(`[MPV] IPC Socket: ${this.socketPath}`); - - // 启动 MPV 进程 - this.process = spawn(binaryPath, [ - '--idle', // 空闲时不退出 - '--no-video', // 纯音频模式 - '--keep-open=yes', // 播放结束不退出 - `--input-ipc-server=${this.socketPath}` - ]); - - this.process.on('error', (err) => { - console.error('[MPV] Process Error:', err); - }); - - this.process.on('exit', (code) => { - console.log(`[MPV] Process exited with code ${code}`); - this.socket?.destroy(); - }); - - this.connectSocket(); - } - - private connectSocket(retries = 10) { - setTimeout(() => { - const socket = net.createConnection(this.socketPath); - - socket.on('connect', () => { - console.log('[MPV] IPC Connected!'); - this.socket = socket; - this.setupObservers(); - }); - - socket.on('data', (data) => this.handleData(data)); - - socket.on('error', (err) => { - if (retries > 0) { - // MPV 还没准备好,继续重试 - this.connectSocket(retries - 1); - } else { - console.error('[MPV] Failed to connect to IPC socket:', err); - } - }); - }, 500); // 500ms 重试间隔 - } - - // 处理接收到的数据 - private handleData(data: Buffer) { - this.buffer += data.toString(); - - // MPV 的消息以 \n 分隔 - const lines = this.buffer.split('\n'); - // 最后一个元素可能是不完整的行,留到下一次处理 - this.buffer = lines.pop() || ''; - - lines.forEach(line => { - if (!line.trim()) return; - try { - const message = JSON.parse(line); - console.log(message); - this.handleMessage(message); - } catch (e) { - console.error('[MPV] JSON Parse Error:', e); - } - }); - } - - // 处理解析后的 JSON 消息 - private handleMessage(msg: any) { - if (!win) return; - - // 处理属性变更事件 - if (msg.event === 'property-change') { - switch (msg.name) { - case 'time-pos': - win.webContents.send('mpv-time-update', msg.data); - break; - case 'duration': - win.webContents.send('mpv-duration', msg.data); - break; - case 'pause': - win.webContents.send('mpv-play-state', !msg.data); // data=true 意味着暂停 - break; - } - } - - // 处理播放结束事件 - if (msg.event === 'end-file') { - win.webContents.send('mpv-ended'); - win.webContents.send('mpv-play-state', false); - } - } - - // 初始化监听属性 - private setupObservers() { - this.send(['observe_property', 1, 'time-pos']); - this.send(['observe_property', 2, 'duration']); - this.send(['observe_property', 3, 'pause']); - } - - // 发送命令给 MPV - send(command: any[]) { - if (!this.socket || this.socket.destroyed) return; - - const payload = JSON.stringify({ command }); - this.socket.write(payload + '\n'); - } - - // === 业务方法 === - - load(url: string, autoPlay: boolean) { - if (autoPlay) { - // 确保取消暂停 (防止之前是暂停状态) - this.send(['set_property', 'pause', false]); - // 加载文件 - this.send(['loadfile', url, 'replace']); - } else { - // 先设置为暂停 (这样后续加载的文件会继承这个暂停状态) - this.send(['set_property', 'pause', true]); - // 加载文件 - this.send(['loadfile', url, 'replace']); - } - } - - play() { this.send(['set_property', 'pause', false]); } - - pause() { this.send(['set_property', 'pause', true]); } - - seek(time: number) { this.send(['seek', time, 'absolute']); } - - setVolume(volume: number) { this.send(['set_property', 'volume', volume]); } -} - -const mpv = new MpvController(); - // === Electron 窗口逻辑 === -const mpvExecutablePath = app.isPackaged - ? path.join(process.resourcesPath, 'core', 'mpv.exe') - : path.join(process.env.APP_ROOT, 'core', 'mpv.exe'); - function createWindow() { win = new BrowserWindow({ frame: false, @@ -199,6 +43,8 @@ function createWindow() { } else { win.loadFile(path.join(RENDERER_DIST, 'index.html')) } + + registerZoomShortcuts(win) } // === IPC 监听 === @@ -208,14 +54,6 @@ ipcMain.on('window-maximize', () => win?.isMaximized() ? win.unmaximize() : win? ipcMain.on('window-close', () => win?.close()) ipcMain.handle('window-is-maximized', () => win?.isMaximized() || false) -// MPV 控制指令 -ipcMain.on('mpv-load', (_, url, autoPlay = true) => mpv.load(url, autoPlay)) -ipcMain.on('mpv-play', () => mpv.play()) -ipcMain.on('mpv-pause', () => mpv.pause()) -ipcMain.on('mpv-seek', (_, time) => mpv.seek(time)) -ipcMain.on('mpv-volume', (_, volume) => mpv.setVolume(volume)) - - app.on('window-all-closed', () => { if (process.platform !== 'darwin') { app.quit() @@ -223,6 +61,28 @@ app.on('window-all-closed', () => { } }) +function registerZoomShortcuts(win: BrowserWindow) { + win.webContents.on('before-input-event', (event, input) => { + if (input.control || input.meta) { + if (input.key.toLowerCase() === '=' || input.key === '+') { + let currentZoom = win.webContents.getZoomFactor(); + win.webContents.setZoomFactor(currentZoom + 0.1); + event.preventDefault(); + } else if (input.key === '-' || input.key === '_') { + let currentZoom = win.webContents.getZoomFactor(); + // Limit minimum zoom to avoid making it too small to see + if (currentZoom > 0.5) { + win.webContents.setZoomFactor(currentZoom - 0.1); + } + event.preventDefault(); + } else if (input.key === '0') { + win.webContents.setZoomFactor(1); + event.preventDefault(); + } + } + }); +} + app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) { createWindow() @@ -231,7 +91,5 @@ app.on('activate', () => { app.whenReady().then(() => { Menu.setApplicationMenu(null) - // 启动 MPV - mpv.start(mpvExecutablePath); createWindow() -}) \ No newline at end of file +}) diff --git a/electron/preload.ts b/electron/preload.ts index a339652..87d627e 100644 --- a/electron/preload.ts +++ b/electron/preload.ts @@ -6,24 +6,4 @@ contextBridge.exposeInMainWorld('electronAPI', { maximizeWindow: () => ipcRenderer.send('window-maximize'), closeWindow: () => ipcRenderer.send('window-close'), isMaximized: () => ipcRenderer.invoke('window-is-maximized'), - - // MPV 控制 (Renderer -> Main) - mpvLoad: (url: string,autoPlay: boolean = true) => ipcRenderer.send('mpv-load', url, autoPlay), - mpvPlay: () => ipcRenderer.send('mpv-play'), - mpvPause: () => ipcRenderer.send('mpv-pause'), - mpvSeek: (time: number) => ipcRenderer.send('mpv-seek', time), - mpvSetVolume: (volume: number) => ipcRenderer.send('mpv-volume', volume), - - // MPV 事件 (Main -> Renderer) - onMpvTimeUpdate: (callback: (time: number) => void) => - ipcRenderer.on('mpv-time-update', (_, time) => callback(time)), - - onMpvDuration: (callback: (duration: number) => void) => - ipcRenderer.on('mpv-duration', (_, duration) => callback(duration)), - - onMpvPlayState: (callback: (isPlaying: boolean) => void) => - ipcRenderer.on('mpv-play-state', (_, isPlaying) => callback(isPlaying)), - - onMpvEnded: (callback: () => void) => - ipcRenderer.on('mpv-ended', () => callback()), }) \ No newline at end of file diff --git a/src/App.vue b/src/App.vue index 1da6d0b..1929fbf 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,290 +1,11 @@ - + \ No newline at end of file diff --git a/src/components/layout/PlayerBar.vue b/src/components/layout/PlayerBar.vue deleted file mode 100644 index 9e0df9d..0000000 --- a/src/components/layout/PlayerBar.vue +++ /dev/null @@ -1,488 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/layout/SettingsOverlay.vue b/src/components/layout/SettingsOverlay.vue deleted file mode 100644 index 6b2317a..0000000 --- a/src/components/layout/SettingsOverlay.vue +++ /dev/null @@ -1,361 +0,0 @@ - - - - - diff --git a/src/components/layout/SideBar.vue b/src/components/layout/SideBar.vue deleted file mode 100644 index 2f2fb04..0000000 --- a/src/components/layout/SideBar.vue +++ /dev/null @@ -1,190 +0,0 @@ - - - - - diff --git a/src/components/layout/WindowControls.vue b/src/components/layout/WindowControls.vue deleted file mode 100644 index 05b0b5b..0000000 --- a/src/components/layout/WindowControls.vue +++ /dev/null @@ -1,57 +0,0 @@ - - - - - diff --git a/src/main.ts b/src/main.ts index d1ac18e..fdd6220 100644 --- a/src/main.ts +++ b/src/main.ts @@ -4,9 +4,6 @@ import { createPinia } from 'pinia' import { createRouter, createWebHashHistory } from 'vue-router' import App from './App.vue' -import HomeView from './views/HomeView.vue' -import PlaylistDetailView from './views/PlaylistDetailView.vue' - import TDesign from 'tdesign-vue-next' import 'tdesign-vue-next/es/style/index.css' @@ -14,8 +11,16 @@ const pinia = createPinia() const router = createRouter({ history: createWebHashHistory(), routes: [ - { path: '/', component: HomeView }, - { path: '/playlist/:id', component: PlaylistDetailView }, + { + path: '/', + name: 'Home', + component: () => import('./views/Home.vue') + }, + { + path: '/local', + name: 'Local', + component: () => import('./views/LocalMusic.vue') + } ] }) @@ -23,4 +28,4 @@ const app = createApp(App) app.use(pinia) app.use(router) app.use(TDesign) -app.mount('#app') +app.mount('#app') \ No newline at end of file diff --git a/src/stores/playerStore.ts b/src/stores/playerStore.ts deleted file mode 100644 index 8e7b6bd..0000000 --- a/src/stores/playerStore.ts +++ /dev/null @@ -1,188 +0,0 @@ -import { defineStore } from 'pinia'; -import { ref, computed, watch } from 'vue'; - -export const usePlayerStore = defineStore('player', () => { - const currentSong = ref({ - id: 1, - title: "Example Track", - artist: "AI Artist", - cover: "http://p2.music.126.net/h2vun-h_uGBYzGvQoLKiBw==/109951165966921437.jpg?param=130y130", - url: "https://m704.music.126.net/20260115210245/494fafc7ecc89da85365b1e6533cdb30/jdyyaac/obj/w5rDlsOJwrLDjj7CmsOj/32280537391/40ea/84dd/db94/451cfc92afa4a12926f40b1183eca3cd.m4a?vuutv=yqFO4JPFSDRDeqVLCjH3fPvuLTHnPPNLPMIBbHWfYTZOmqP5/RFh7UnxA2sG1X9+MLdjYsrkG5vUIUjV6t+y1pniceMN5lePyr33C0D1Aho=&authSecret=0000019bc1a9710615420a3283920006&cdntag=bWFyaz1vc193ZWIscXVhbGl0eV9leGhpZ2g", - duration: 0 - }); - - const isPlaying = ref(false); - const currentTime = ref(0); - const volume = ref(80); - const showPlaylist = ref(false); - const showSettings = ref(false); - const darkMode = ref(false); - const themeColors = ref({ primary: '#6366f1', secondary: '#a855f7' }); - const progressColor = ref('#ffffff'); - - // 标记是否已初始化监听器,防止重复绑定 - let isInitialized = false; - - // --- 初始化函数:在组件挂载时调用一次 --- - function init() { - if (isInitialized) return; - - // 监听时间更新 - window.electronAPI.onMpvTimeUpdate((time) => { - currentTime.value = time; - }); - - // 监听时长更新 (MPV 加载完元数据后会发送) - window.electronAPI.onMpvDuration((duration) => { - currentSong.value.duration = duration; - }); - - // 监听播放状态 (用于同步 MPV 内部状态和 UI) - window.electronAPI.onMpvPlayState((playing) => { - isPlaying.value = playing; - }); - - // 监听结束 - window.electronAPI.onMpvEnded(() => { - isPlaying.value = false; - currentTime.value = 0; - // 这里可以添加自动播放下一首的逻辑 - }); - - // 初始化音量 - window.electronAPI.mpvSetVolume(volume.value); - - // 加载初始歌曲 - loadCurrentSong(false); - - isInitialized = true; - } - - - function loadCurrentSong(autoPlay:boolean=true) { - if(currentSong.value.url) { - window.electronAPI.mpvLoad(currentSong.value.url,autoPlay); - } - } - - function togglePlay() { - if (isPlaying.value) { - window.electronAPI.mpvPause(); - } else { - window.electronAPI.mpvPlay(); - } - isPlaying.value = !isPlaying.value; - } - - function seek(time: number) { - window.electronAPI.mpvSeek(time); - currentTime.value = time; // 立即更新 UI 防止跳变 - } - - // 监听音量变化 - watch(volume, (newVol) => { - window.electronAPI.mpvSetVolume(newVol); - }); - - // 监听歌曲 URL 变化 (切歌) - watch(() => currentSong.value.url, () => { - loadCurrentSong(true); - }); - - // 监听封面变化提取颜色 (保持原有逻辑不变) - watch(() => currentSong.value.cover, () => { - extractColors(); - }, { immediate: true }); - - // --- 颜色提取逻辑 (保持不变) --- - function extractColors() { - const img = new Image(); - img.crossOrigin = 'Anonymous'; - img.src = currentSong.value.cover + '?t=' + Date.now(); - - img.onload = () => { - const canvas = document.createElement('canvas'); - const ctx = canvas.getContext('2d'); - if (!ctx) return; - - const scale = 0.1; - canvas.width = Math.floor(img.width * scale); - canvas.height = Math.floor(img.height * scale); - ctx.drawImage(img, 0, 0, canvas.width, canvas.height); - - const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height).data; - const colors: { r: number; g: number; b: number; brightness: number }[] = []; - - for (let i = 0; i < imageData.length; i += 4) { - const r = imageData[i]; - const g = imageData[i + 1]; - const b = imageData[i + 2]; - const brightness = (r + g + b) / 3; - colors.push({ r, g, b, brightness }); - } - - const colorFrequency: Record = {}; - colors.forEach(color => { - const key = `${Math.floor(color.r / 16)}${Math.floor(color.g / 16)}${Math.floor(color.b / 16)}`; - colorFrequency[key] = (colorFrequency[key] || 0) + 1; - }); - - const sortedColors = Object.entries(colorFrequency) - .map(([key, count]) => { - const r = parseInt(key[0], 16) * 16; - const g = parseInt(key[1], 16) * 16; - const b = parseInt(key[2], 16) * 16; - return { r, g, b, count }; - }) - .sort((a, b) => b.count - a.count); - - if (sortedColors.length > 0) { - const primary = sortedColors[0]; - const primaryColor = `rgb(${primary.r}, ${primary.g}, ${primary.b})`; - - let secondary = sortedColors[1] || sortedColors[0]; - let maxBrightnessDiff = Math.abs(primary.brightness - secondary.brightness); - - for (let i = 1; i < Math.min(10, sortedColors.length); i++) { - const currentBrightness = (sortedColors[i].r + sortedColors[i].g + sortedColors[i].b) / 3; - const brightnessDiff = Math.abs(primary.brightness - currentBrightness); - - if (brightnessDiff > maxBrightnessDiff) { - maxBrightnessDiff = brightnessDiff; - secondary = sortedColors[i]; - } - } - - const secondaryColor = `rgb(${secondary.r}, ${secondary.g}, ${secondary.b})`; - - themeColors.value = { - primary: primaryColor, - secondary: secondaryColor - }; - - const darkPrimary = { - r: Math.floor(primary.r * 0.7), - g: Math.floor(primary.g * 0.7), - b: Math.floor(primary.b * 0.7) - }; - progressColor.value = `rgb(${darkPrimary.r}, ${darkPrimary.g}, ${darkPrimary.b})`; - } else { - themeColors.value = { primary: '#6366f1', secondary: '#a855f7' }; - progressColor.value = '#ffffff'; - } - }; - } - - const progressPercentage = computed(() => - currentSong.value.duration ? (currentTime.value / currentSong.value.duration) * 100 : 0 - ); - - return { - currentSong, isPlaying, currentTime, volume, showPlaylist, - themeColors, progressPercentage, progressColor, - togglePlay, seek, extractColors, - showSettings, - darkMode, - init - }; -}); \ No newline at end of file diff --git a/src/types/electron.d.ts b/src/types/electron.d.ts index 767825c..bcb3eb0 100644 --- a/src/types/electron.d.ts +++ b/src/types/electron.d.ts @@ -3,18 +3,6 @@ export interface IElectronAPI { maximizeWindow: () => void; closeWindow: () => void; isMaximized: () => Promise; - - mpvLoad: (url: string,autoPlay?: boolean) => void; - mpvPlay: () => void; - mpvPause: () => void; - mpvResume: () => void; - mpvSeek: (time: number) => void; - mpvSetVolume: (volume: number) => void; - - onMpvTimeUpdate: (callback: (time: number) => void) => void; - onMpvDuration: (callback: (duration: number) => void) => void; - onMpvPlayState: (callback: (isPlaying: boolean) => void) => void; - onMpvEnded: (callback: () => void) => void; } declare global { diff --git a/src/views/HomeView.vue b/src/views/HomeView.vue deleted file mode 100644 index 3ab1e43..0000000 --- a/src/views/HomeView.vue +++ /dev/null @@ -1,383 +0,0 @@ - - - - - diff --git a/src/views/PlaylistDetailView.vue b/src/views/PlaylistDetailView.vue deleted file mode 100644 index 54fc235..0000000 --- a/src/views/PlaylistDetailView.vue +++ /dev/null @@ -1,568 +0,0 @@ - - - - - \ No newline at end of file