forked from miao-moe/QZMusic_PC
feat: 播放器核心集成;MediaSession集成
This commit is contained in:
@@ -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,
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -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
57
package-lock.json
generated
@@ -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",
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
35
src/main.ts
35
src/main.ts
@@ -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]);
|
||||||
|
});
|
||||||
10
src/types/electron.d.ts
vendored
10
src/types/electron.d.ts
vendored
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user