feat: 播放器核心集成;MediaSession集成

This commit is contained in:
lqtmcstudio
2026-02-02 21:27:48 +08:00
parent 9fc08c59d7
commit 6965858ae3
8 changed files with 286 additions and 75 deletions

View File

@@ -1,27 +1,168 @@
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 { ipcMain, BrowserWindow, app, Menu } from "electron"; import { ipcMain, BrowserWindow, app, Menu } from "electron";
import { createRequire } from "node:module"; import { createRequire } from "node:module";
import { fileURLToPath } from "node:url"; import { fileURLToPath } from "node:url";
import path from "node:path"; import path$1 from "node:path";
import { spawn } from "child_process";
import { Socket } from "net";
import { EventEmitter } from "events";
import path from "path";
class MpvController extends EventEmitter {
constructor(ipcPath) {
super();
__publicField(this, "process", null);
__publicField(this, "socket", null);
__publicField(this, "ipcPath");
__publicField(this, "messageBuffer", "");
this.ipcPath = ipcPath || this.getIpcPath();
}
getIpcPath() {
if (process.platform === "win32") {
return "\\\\.\\pipe\\qzmusic_mpv_socket";
}
return "/tmp/qzmusic_mpv_socket";
}
getMpvPath() {
const appRoot = process.env.APP_ROOT || process.cwd();
if (process.platform === "win32") {
return path.join(appRoot, "core", "mpv.exe");
}
return "mpv";
}
start() {
const mpvPath = this.getMpvPath();
console.log("Starting MPV from:", mpvPath);
this.process = spawn(mpvPath, [
"--idle",
"--force-window=no",
"--no-media-controls",
`--input-ipc-server=${this.ipcPath}`,
"--no-terminal"
]);
this.process.on("error", (err) => {
console.error("Failed to start MPV:", err);
this.emit("error", err);
});
this.process.on("exit", (code, signal) => {
var _a;
console.log(`MPV exited with code ${code} and signal ${signal}`);
this.emit("exit", { code, signal });
(_a = this.socket) == null ? void 0 : _a.destroy();
});
this.tryConnect();
}
tryConnect(retries = 10) {
if (retries <= 0) {
console.error("Could not connect to MPV socket after multiple attempts.");
return;
}
setTimeout(() => {
this.socket = new Socket();
this.socket.on("connect", () => {
console.log("Connected to MPV IPC socket");
this.emit("ready");
this.send(["observe_property", 1, "pause"]);
this.send(["observe_property", 2, "time-pos"]);
this.send(["observe_property", 3, "duration"]);
this.send(["observe_property", 4, "idle-active"]);
this.send(["observe_property", 5, "eof-reached"]);
});
this.socket.on("data", (data) => {
this.handleData(data);
});
this.socket.on("error", (err) => {
var _a;
(_a = this.socket) == null ? void 0 : _a.destroy();
this.tryConnect(retries - 1);
});
this.socket.connect(this.ipcPath);
}, 500);
}
handleData(data) {
const raw = data.toString();
this.messageBuffer += raw;
const messages = this.messageBuffer.split("\n");
this.messageBuffer = messages.pop() || "";
for (const msg of messages) {
if (!msg.trim()) continue;
console.log("[IPC]", msg);
try {
const json = JSON.parse(msg);
this.emit("message", json);
if (json.event) {
this.emit("event", json);
}
} catch (e) {
console.error("Failed to parse MPV message:", msg);
}
}
}
async send(command) {
if (!this.socket || this.socket.destroyed) {
console.warn("MPV socket not connected");
return;
}
const payload = JSON.stringify({ command });
console.log("[MPV TX]", payload);
this.socket.write(payload + "\n");
}
// Convenience methods
async load(url) {
return this.send(["loadfile", url]);
}
async play() {
return this.send(["set_property", "pause", false]);
}
async pause() {
return this.send(["set_property", "pause", true]);
}
async togglePause() {
return this.send(["cycle", "pause"]);
}
async stop() {
return this.send(["stop"]);
}
async setVolume(vol) {
return this.send(["set_property", "volume", vol]);
}
async seek(seconds) {
return this.send(["seek", seconds, "absolute"]);
}
destroy() {
if (this.process) {
console.log("Killing MPV process...");
this.process.kill();
this.process = null;
}
if (this.socket) {
this.socket.destroy();
this.socket = null;
}
}
}
createRequire(import.meta.url); createRequire(import.meta.url);
const __dirname$1 = path.dirname(fileURLToPath(import.meta.url)); const __dirname$1 = path$1.dirname(fileURLToPath(import.meta.url));
process.env.APP_ROOT = path.join(__dirname$1, ".."); process.env.APP_ROOT = path$1.join(__dirname$1, "..");
const VITE_DEV_SERVER_URL = process.env["VITE_DEV_SERVER_URL"]; const VITE_DEV_SERVER_URL = process.env["VITE_DEV_SERVER_URL"];
const MAIN_DIST = path.join(process.env.APP_ROOT, "dist-electron"); const MAIN_DIST = path$1.join(process.env.APP_ROOT, "dist-electron");
const RENDERER_DIST = path.join(process.env.APP_ROOT, "dist"); const RENDERER_DIST = path$1.join(process.env.APP_ROOT, "dist");
process.env.VITE_PUBLIC = VITE_DEV_SERVER_URL ? path.join(process.env.APP_ROOT, "public") : RENDERER_DIST; process.env.VITE_PUBLIC = VITE_DEV_SERVER_URL ? path$1.join(process.env.APP_ROOT, "public") : RENDERER_DIST;
let win; let win;
let mpv;
function createWindow() { function createWindow() {
win = new BrowserWindow({ win = new BrowserWindow({
frame: false, frame: false,
minWidth: 950, minWidth: 950,
minHeight: 700, minHeight: 800,
width: 1e3, width: 1e3,
height: 800, height: 800,
icon: path.join(process.env.VITE_PUBLIC, "electron-vite.svg"), icon: path$1.join(process.env.VITE_PUBLIC, "electron-vite.svg"),
webPreferences: { webPreferences: {
preload: path.join(__dirname$1, "preload.mjs"), preload: path$1.join(__dirname$1, "preload.mjs")
nodeIntegration: false, // nodeIntegration: false,
contextIsolation: true // contextIsolation: true,
} }
}); });
win.webContents.on("did-finish-load", () => { win.webContents.on("did-finish-load", () => {
@@ -30,7 +171,7 @@ function createWindow() {
if (VITE_DEV_SERVER_URL) { if (VITE_DEV_SERVER_URL) {
win.loadURL(VITE_DEV_SERVER_URL); win.loadURL(VITE_DEV_SERVER_URL);
} else { } else {
win.loadFile(path.join(RENDERER_DIST, "index.html")); win.loadFile(path$1.join(RENDERER_DIST, "index.html"));
} }
registerZoomShortcuts(win); registerZoomShortcuts(win);
} }
@@ -41,12 +182,29 @@ 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-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.on("window-close", () => win == null ? void 0 : win.close());
ipcMain.handle("window-is-maximized", () => (win == null ? void 0 : win.isMaximized()) || false); ipcMain.handle("window-is-maximized", () => (win == null ? void 0 : win.isMaximized()) || false);
ipcMain.handle("mpv-command", async (_, command) => {
if (mpv) {
mpv.send(command);
}
});
ipcMain.handle("mpv-load", (_, url) => mpv == null ? void 0 : mpv.load(url));
ipcMain.handle("mpv-play", () => mpv == null ? void 0 : mpv.play());
ipcMain.handle("mpv-pause", () => mpv == null ? void 0 : mpv.pause());
ipcMain.handle("mpv-toggle-pause", () => mpv == null ? void 0 : mpv.togglePause());
ipcMain.handle("mpv-stop", () => mpv == null ? void 0 : mpv.stop());
ipcMain.handle("mpv-set-volume", (e, vol) => mpv == null ? void 0 : mpv.setVolume(vol));
ipcMain.handle("mpv-seek", (_, time) => mpv == null ? void 0 : mpv.seek(time));
app.on("window-all-closed", () => { app.on("window-all-closed", () => {
if (process.platform !== "darwin") { if (process.platform !== "darwin") {
app.quit(); app.quit();
win = null; win = null;
} }
}); });
app.on("will-quit", () => {
if (mpv) {
mpv.destroy();
}
});
function registerZoomShortcuts(win2) { function registerZoomShortcuts(win2) {
win2.webContents.on("before-input-event", (event, input) => { win2.webContents.on("before-input-event", (event, input) => {
if (input.control || input.meta) { if (input.control || input.meta) {
@@ -75,6 +233,13 @@ app.on("activate", () => {
app.whenReady().then(() => { app.whenReady().then(() => {
Menu.setApplicationMenu(null); Menu.setApplicationMenu(null);
createWindow(); createWindow();
mpv = new MpvController();
mpv.start();
mpv.on("event", (data) => {
if (win && !win.isDestroyed()) {
win.webContents.send("mpv-event", data);
}
});
}); });
export { export {
MAIN_DIST, MAIN_DIST,

View File

@@ -5,5 +5,16 @@ electron.contextBridge.exposeInMainWorld("electronAPI", {
minimizeWindow: () => electron.ipcRenderer.send("window-minimize"), minimizeWindow: () => electron.ipcRenderer.send("window-minimize"),
maximizeWindow: () => electron.ipcRenderer.send("window-maximize"), maximizeWindow: () => electron.ipcRenderer.send("window-maximize"),
closeWindow: () => electron.ipcRenderer.send("window-close"), closeWindow: () => electron.ipcRenderer.send("window-close"),
isMaximized: () => electron.ipcRenderer.invoke("window-is-maximized") isMaximized: () => electron.ipcRenderer.invoke("window-is-maximized"),
// MPV Control
mpv: {
load: (url) => electron.ipcRenderer.invoke("mpv-load", url),
play: () => electron.ipcRenderer.invoke("mpv-play"),
pause: () => electron.ipcRenderer.invoke("mpv-pause"),
togglePause: () => electron.ipcRenderer.invoke("mpv-toggle-pause"),
stop: () => electron.ipcRenderer.invoke("mpv-stop"),
setVolume: (vol) => electron.ipcRenderer.invoke("mpv-set-volume", vol),
seek: (time) => electron.ipcRenderer.invoke("mpv-seek", time),
onEvent: (callback) => electron.ipcRenderer.on("mpv-event", callback)
}
}); });

View File

@@ -2,6 +2,7 @@ import { app, BrowserWindow, Menu, ipcMain } from 'electron'
import { createRequire } from 'node:module' import { createRequire } from 'node:module'
import { fileURLToPath } from 'node:url' import { fileURLToPath } from 'node:url'
import path from 'node:path' import path from 'node:path'
import { MpvController } from './mpvController'
// @ts-ignore // @ts-ignore
const require = createRequire(import.meta.url) const require = createRequire(import.meta.url)
@@ -16,6 +17,7 @@ export 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 process.env.VITE_PUBLIC = VITE_DEV_SERVER_URL ? path.join(process.env.APP_ROOT, 'public') : RENDERER_DIST
let win: BrowserWindow | null let win: BrowserWindow | null
let mpv: MpvController | null
// === Electron 窗口逻辑 === // === Electron 窗口逻辑 ===
@@ -23,14 +25,14 @@ function createWindow() {
win = new BrowserWindow({ win = new BrowserWindow({
frame: false, frame: false,
minWidth: 950, minWidth: 950,
minHeight: 700, minHeight: 800,
width: 1000, width: 1000,
height: 800, height: 800,
icon: path.join(process.env.VITE_PUBLIC, 'electron-vite.svg'), icon: path.join(process.env.VITE_PUBLIC, 'electron-vite.svg'),
webPreferences: { webPreferences: {
preload: path.join(__dirname, 'preload.mjs'), preload: path.join(__dirname, 'preload.mjs'),
nodeIntegration: false, // nodeIntegration: false,
contextIsolation: true, // contextIsolation: true,
}, },
}) })
@@ -54,6 +56,23 @@ ipcMain.on('window-maximize', () => win?.isMaximized() ? win.unmaximize() : win?
ipcMain.on('window-close', () => win?.close()) ipcMain.on('window-close', () => win?.close())
ipcMain.handle('window-is-maximized', () => win?.isMaximized() || false) ipcMain.handle('window-is-maximized', () => win?.isMaximized() || false)
// --- MPV IPC Handlers ---
ipcMain.handle('mpv-command', async (_, command: any[]) => {
if (mpv) {
mpv.send(command)
}
})
// Quick Helpers
ipcMain.handle('mpv-load', (_, url) => mpv?.load(url))
ipcMain.handle('mpv-play', () => mpv?.play())
ipcMain.handle('mpv-pause', () => mpv?.pause())
ipcMain.handle('mpv-toggle-pause', () => mpv?.togglePause())
ipcMain.handle('mpv-stop', () => mpv?.stop())
ipcMain.handle('mpv-set-volume', (e, vol) => mpv?.setVolume(vol))
ipcMain.handle('mpv-seek', (_, time) => mpv?.seek(time))
app.on('window-all-closed', () => { app.on('window-all-closed', () => {
if (process.platform !== 'darwin') { if (process.platform !== 'darwin') {
app.quit() app.quit()
@@ -61,6 +80,12 @@ app.on('window-all-closed', () => {
} }
}) })
app.on('will-quit', () => {
if (mpv) {
mpv.destroy()
}
})
function registerZoomShortcuts(win: BrowserWindow) { function registerZoomShortcuts(win: BrowserWindow) {
win.webContents.on('before-input-event', (event, input) => { win.webContents.on('before-input-event', (event, input) => {
if (input.control || input.meta) { if (input.control || input.meta) {
@@ -92,4 +117,15 @@ app.on('activate', () => {
app.whenReady().then(() => { app.whenReady().then(() => {
Menu.setApplicationMenu(null) Menu.setApplicationMenu(null)
createWindow() createWindow()
// Start MPV
mpv = new MpvController()
mpv.start()
mpv.on('event', (data) => {
// Forward MPV events to Render Process
if (win && !win.isDestroyed()) {
win.webContents.send('mpv-event', data)
}
})
}) })

View File

@@ -6,4 +6,16 @@ contextBridge.exposeInMainWorld('electronAPI', {
maximizeWindow: () => ipcRenderer.send('window-maximize'), maximizeWindow: () => ipcRenderer.send('window-maximize'),
closeWindow: () => ipcRenderer.send('window-close'), closeWindow: () => ipcRenderer.send('window-close'),
isMaximized: () => ipcRenderer.invoke('window-is-maximized'), isMaximized: () => ipcRenderer.invoke('window-is-maximized'),
// MPV Control
mpv: {
load: (url: string) => ipcRenderer.invoke('mpv-load', url),
play: () => ipcRenderer.invoke('mpv-play'),
pause: () => ipcRenderer.invoke('mpv-pause'),
togglePause: () => ipcRenderer.invoke('mpv-toggle-pause'),
stop: () => ipcRenderer.invoke('mpv-stop'),
setVolume: (vol: number) => ipcRenderer.invoke('mpv-set-volume', vol),
seek: (time: number) => ipcRenderer.invoke('mpv-seek', time),
onEvent: (callback: (event: any, data: any) => void) => ipcRenderer.on('mpv-event', callback)
}
}) })

57
package-lock.json generated
View File

@@ -21,7 +21,6 @@
"element-plus": "^2.13.0", "element-plus": "^2.13.0",
"jss": "^10.10.0", "jss": "^10.10.0",
"jss-preset-default": "^10.10.0", "jss-preset-default": "^10.10.0",
"node-mpv": "^1.5.0",
"pinia": "^3.0.4", "pinia": "^3.0.4",
"tdesign-vue-next": "^1.17.7", "tdesign-vue-next": "^1.17.7",
"vue": "^3.4.21", "vue": "^3.4.21",
@@ -2670,12 +2669,6 @@
"dev": true, "dev": true,
"license": "Python-2.0" "license": "Python-2.0"
}, },
"node_modules/asap": {
"version": "2.0.6",
"resolved": "https://registry.npmmirror.com/asap/-/asap-2.0.6.tgz",
"integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==",
"license": "MIT"
},
"node_modules/assert-plus": { "node_modules/assert-plus": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmmirror.com/assert-plus/-/assert-plus-1.0.0.tgz", "resolved": "https://registry.npmmirror.com/assert-plus/-/assert-plus-1.0.0.tgz",
@@ -2844,12 +2837,6 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/browser-fingerprint": {
"version": "0.0.1",
"resolved": "https://registry.npmmirror.com/browser-fingerprint/-/browser-fingerprint-0.0.1.tgz",
"integrity": "sha512-b8SXP7yOlzLUJXF8WUvIjmbJzkJC0X6OHe7J9a/SHqEBC7a9Eglag6AANSTJz82h5U582kuxm/5TPudnD68EPA==",
"license": "MIT"
},
"node_modules/buffer": { "node_modules/buffer": {
"version": "5.7.1", "version": "5.7.1",
"resolved": "https://registry.npmmirror.com/buffer/-/buffer-5.7.1.tgz", "resolved": "https://registry.npmmirror.com/buffer/-/buffer-5.7.1.tgz",
@@ -3317,13 +3304,6 @@
"url": "https://github.com/sponsors/mesqueeb" "url": "https://github.com/sponsors/mesqueeb"
} }
}, },
"node_modules/core-js": {
"version": "1.2.7",
"resolved": "https://registry.npmmirror.com/core-js/-/core-js-1.2.7.tgz",
"integrity": "sha512-ZiPp9pZlgxpWRu0M+YWbm6+aQ84XEfH1JRXvfOc/fILWI0VKhLC2LX13X1NYq4fULzLMq7Hfh43CSo2/aIaUPA==",
"deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.",
"license": "MIT"
},
"node_modules/core-util-is": { "node_modules/core-util-is": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.2.tgz", "resolved": "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.2.tgz",
@@ -3402,18 +3382,6 @@
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/cuid": {
"version": "1.3.8",
"resolved": "https://registry.npmmirror.com/cuid/-/cuid-1.3.8.tgz",
"integrity": "sha512-MoL67ZZuBetDMxzrZtO+Iq1ATajFACQCP52QRinBgd3yTjYdv54mJO8ibUrh06fojKCoX5P2i7KkEatm4VTIOQ==",
"deprecated": "Cuid and other k-sortable and non-cryptographic ids (Ulid, ObjectId, KSUID, all UUIDs) are all insecure. Use @paralleldrive/cuid2 instead.",
"license": "MIT",
"dependencies": {
"browser-fingerprint": "0.0.1",
"core-js": "^1.1.1",
"node-fingerprint": "0.0.2"
}
},
"node_modules/dayjs": { "node_modules/dayjs": {
"version": "1.11.19", "version": "1.11.19",
"resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.19.tgz", "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.19.tgz",
@@ -5503,22 +5471,6 @@
"license": "MIT", "license": "MIT",
"optional": true "optional": true
}, },
"node_modules/node-fingerprint": {
"version": "0.0.2",
"resolved": "https://registry.npmmirror.com/node-fingerprint/-/node-fingerprint-0.0.2.tgz",
"integrity": "sha512-vPFfTD5EBJieQ4SI3v61fWxlV1kav3m9Dbejd6CjWhOJn8s+XMxpOOosCNAyIrUQ/jJOlPndfrZ0lSw4+RgwcA==",
"license": "MIT"
},
"node_modules/node-mpv": {
"version": "1.5.0",
"resolved": "https://registry.npmmirror.com/node-mpv/-/node-mpv-1.5.0.tgz",
"integrity": "sha512-kvLo+PcWHZ/Sg7t9XeFDi5KJrNOL9XJOEljCEh5wBNOHiE6Wa/txwIsYWKmNaIFuncbEhgjnoOavE4T5YBNV8Q==",
"dependencies": {
"cuid": "^1.3.8",
"lodash": ">= 4.0.0",
"promise": "^7.1.1"
}
},
"node_modules/normalize-path": { "node_modules/normalize-path": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz", "resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz",
@@ -5765,15 +5717,6 @@
"node": ">=0.4.0" "node": ">=0.4.0"
} }
}, },
"node_modules/promise": {
"version": "7.3.1",
"resolved": "https://registry.npmmirror.com/promise/-/promise-7.3.1.tgz",
"integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
"license": "MIT",
"dependencies": {
"asap": "~2.0.3"
}
},
"node_modules/promise-retry": { "node_modules/promise-retry": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmmirror.com/promise-retry/-/promise-retry-2.0.1.tgz", "resolved": "https://registry.npmmirror.com/promise-retry/-/promise-retry-2.0.1.tgz",

View File

@@ -22,7 +22,6 @@
"element-plus": "^2.13.0", "element-plus": "^2.13.0",
"jss": "^10.10.0", "jss": "^10.10.0",
"jss-preset-default": "^10.10.0", "jss-preset-default": "^10.10.0",
"node-mpv": "^1.5.0",
"pinia": "^3.0.4", "pinia": "^3.0.4",
"tdesign-vue-next": "^1.17.7", "tdesign-vue-next": "^1.17.7",
"vue": "^3.4.21", "vue": "^3.4.21",

View File

@@ -20,6 +20,16 @@ const router = createRouter({
path: '/local', path: '/local',
name: 'Local', name: 'Local',
component: () => import('./views/LocalMusic.vue') component: () => import('./views/LocalMusic.vue')
},
{
path: '/liked',
name: 'Liked',
component: () => import('./views/Playlist.vue')
},
{
path: '/recent',
name: 'Recent',
component: () => import('./views/Playlist.vue')
} }
] ]
}) })
@@ -29,3 +39,28 @@ app.use(pinia)
app.use(router) app.use(router)
app.use(TDesign) app.use(TDesign)
app.mount('#app') app.mount('#app')
// --- TEST: Auto Play Specific Song ---
import { usePlayerStore, PlayMode } from './stores/player'
import type { Song } from './types/song'
const playerStore = usePlayerStore()
// Set Single Mode
playerStore.playMode = PlayMode.Single;
const testSong: Song = {
id: '9999',
name: 'Test FLAC',
artist: 'Netease',
picUrl: 'http://p1.music.126.net/btYBbFLd5mf9w0lDpfNs6w==/109951171506809884.jpg?param=130y130',
url: 'http://m801.music.126.net/20260202213928/189743bba596f8fd999bc44fd51d11fd/jdymusic/obj/wo3DlMOGwrbDjj7DisKw/61393856655/24be/6a68/77e4/fb898a5378682427bf7a4fb55640e610.flac',
duration: '03:30',
source: 'netease',
type: 'Remote'
};
// Auto Play
playerStore.playSong(testSong).then(() => {
playerStore.setPlaylist([testSong]);
});

View File

@@ -3,6 +3,16 @@ export interface IElectronAPI {
maximizeWindow: () => void; maximizeWindow: () => void;
closeWindow: () => void; closeWindow: () => void;
isMaximized: () => Promise<boolean>; isMaximized: () => Promise<boolean>;
mpv: {
load: (url: string) => Promise<void>;
play: () => Promise<void>;
pause: () => Promise<void>;
togglePause: () => Promise<void>;
stop: () => Promise<void>;
setVolume: (vol: number) => Promise<void>;
seek: (time: number) => Promise<void>;
onEvent: (callback: (event: any, data: any) => void) => void;
}
} }
declare global { declare global {