mirror of
https://github.com/lqtmcstudio/QZMusic_PC.git
synced 2026-06-21 15:54:25 +08:00
feat: 迁移架构&实现功能
- 持久化音量设置 - 搜索页支持自定义每页显示数量(持久化) - vite-electron-plugin迁移electron-vite
This commit is contained in:
166
src/main/qzpController.ts
Normal file
166
src/main/qzpController.ts
Normal file
@@ -0,0 +1,166 @@
|
||||
//@ts-ignore
|
||||
import { spawn, ChildProcess } from 'child_process';
|
||||
import { Socket } from 'net';
|
||||
import { EventEmitter } from 'events';
|
||||
import path from 'path';
|
||||
|
||||
export class QzpController extends EventEmitter {
|
||||
private process: ChildProcess | null = null;
|
||||
private socket: Socket | null = null;
|
||||
private ipcPath: string;
|
||||
private messageBuffer: string = '';
|
||||
|
||||
constructor(ipcPath?: string) {
|
||||
super();
|
||||
this.ipcPath = ipcPath || this.getIpcPath();
|
||||
}
|
||||
|
||||
private getIpcPath(): string {
|
||||
if (process.platform === 'win32') {
|
||||
return '\\\\.\\pipe\\qzplayer';
|
||||
}
|
||||
return '/tmp/qzmusic_mpv_socket';
|
||||
}
|
||||
|
||||
private getCorePath(): string {
|
||||
const appRoot = process.env.APP_ROOT || process.cwd();
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
return path.join(appRoot, 'core', 'qzplayer.exe');
|
||||
}
|
||||
return "qzplayer"
|
||||
}
|
||||
|
||||
start() {
|
||||
const playerPath = this.getCorePath();
|
||||
console.log('Starting QZPlayer from:', playerPath);
|
||||
|
||||
this.process = spawn(playerPath);
|
||||
|
||||
this.process.on('error', (err) => {
|
||||
console.error('Failed to start QZPlayer:', err);
|
||||
this.emit('error', err);
|
||||
});
|
||||
|
||||
this.process.on('exit', (code, signal) => {
|
||||
console.log(`QZPlayer exited with code ${code} and signal ${signal}`);
|
||||
this.emit('exit', { code, signal });
|
||||
this.socket?.destroy();
|
||||
});
|
||||
|
||||
this.tryConnect();
|
||||
}
|
||||
|
||||
private tryConnect(retries = 10) {
|
||||
if (retries <= 0) {
|
||||
console.error('Could not connect to QZPlayer socket after multiple attempts.');
|
||||
return;
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
this.socket = new Socket();
|
||||
|
||||
this.socket.on('connect', () => {
|
||||
console.log('Connected to QZPlayer 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.send(['set_property', 'volume', 50])
|
||||
});
|
||||
|
||||
this.socket.on('data', (data) => {
|
||||
this.handleData(data);
|
||||
});
|
||||
|
||||
this.socket.on('error', (_) => {
|
||||
this.socket?.destroy();
|
||||
this.tryConnect(retries - 1);
|
||||
});
|
||||
|
||||
this.socket.connect(this.ipcPath);
|
||||
}, 500);
|
||||
}
|
||||
|
||||
private handleData(data: Buffer) {
|
||||
// Determine message boundaries by newline
|
||||
const raw = data.toString();
|
||||
|
||||
this.messageBuffer += raw;
|
||||
const messages = this.messageBuffer.split('\n');
|
||||
|
||||
// The last part might be an incomplete message, save it back to buffer
|
||||
this.messageBuffer = messages.pop() || '';
|
||||
|
||||
for (const msg of messages) {
|
||||
if (!msg.trim()) continue;
|
||||
//console.log('[IPC]', msg); // User requested raw communication
|
||||
try {
|
||||
const json = JSON.parse(msg);
|
||||
this.emit('message', json);
|
||||
|
||||
// Handle specific events
|
||||
if (json.event) {
|
||||
this.emit('event', json);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Failed to parse QZPlayer message:', msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async send(command: any[]) {
|
||||
if (!this.socket || this.socket.destroyed) {
|
||||
console.warn('QZPlayer socket not connected');
|
||||
return;
|
||||
}
|
||||
|
||||
const payload = JSON.stringify({ command });
|
||||
console.log('[QZPlayer TX]', payload); // User requested raw communication
|
||||
this.socket.write(payload + '\n');
|
||||
}
|
||||
|
||||
// Convenience methods
|
||||
async load(url: string) {
|
||||
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: number) {
|
||||
return this.send(['set_property', 'volume', vol]);
|
||||
}
|
||||
|
||||
async seek(seconds: number) {
|
||||
return this.send(['seek', seconds, 'absolute']);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
if (this.process) {
|
||||
console.log('Killing QZPlayer process...');
|
||||
this.process.kill();
|
||||
this.process = null;
|
||||
}
|
||||
if (this.socket) {
|
||||
this.socket.destroy();
|
||||
this.socket = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user