Better socket handling, loadfile api update for mpv 0.38.0

This commit is contained in:
2025-05-30 16:40:20 -07:00
parent 035d74d412
commit 3552c9c476

View File

@@ -38,8 +38,9 @@ enum UserEvent {
}
export class MediaPlayer {
private playerProcess: ChildProcess;
private socket: Socket;
private playerProcess: ChildProcess | null = null;
private socket: Promise<Socket>;
private eventSubscribers: WebSocket[] = [];
private favoritesStore: FavoritesStore;
@@ -49,9 +50,19 @@ export class MediaPlayer {
private metadata: Map<string, LinkMetadata> = new Map();
constructor() {
this.socket = this.tryRespawnPlayerProcess();
this.favoritesStore = new FavoritesStore();
this.favoritesStore.onFavoritesChanged = (favorites) => {
this.handleEvent(UserEvent.FavoritesUpdate, { favorites });
};
}
private tryRespawnPlayerProcess(): Promise<Socket> {
const socketFilename = Math.random().toString(36).substring(2, 10);
const socketPath = `/tmp/mpv-${socketFilename}`;
const enableVideo = process.env.ENABLE_VIDEO || false;
const logfilePath = `/tmp/mpv-logfile.txt`;
console.log("Starting player process (video: " + (enableVideo ? "enabled" : "disabled") + ")");
this.playerProcess = spawn("mpv", [
@@ -59,22 +70,26 @@ export class MediaPlayer {
"--fullscreen",
"--no-terminal",
"--idle=yes",
"--input-ipc-server=" + socketPath
"--input-ipc-server=" + socketPath,
"--log-file=" + logfilePath,
"--msg-level=all=v"
]);
this.socket = new Socket();
let socketReady!: (s: Socket) => void;
let socketPromise = new Promise<Socket>(resolve => {
socketReady = resolve;
});
this.playerProcess.on("spawn", () => {
console.log(`Player process spawned, opening socket @ ${socketPath}`);
setTimeout(() => {
this.connectToSocket(socketPath);
let socket = this.connectToSocket(socketPath);
socketReady(socket);
}, 500);
});
this.favoritesStore = new FavoritesStore();
this.favoritesStore.onFavoritesChanged = (favorites) => {
this.handleEvent(UserEvent.FavoritesUpdate, { favorites });
};
return socketPromise;
}
public async getPlaylist(): Promise<PlaylistItem[]> {
@@ -247,7 +262,7 @@ export class MediaPlayer {
}
private async loadFile(url: string, mode: string, fetchMetadata: boolean = true, options: string[] = []) {
this.modify(UserEvent.PlaylistUpdate, () => this.writeCommand("loadfile", [url, mode, options.join(',')]));
this.modify(UserEvent.PlaylistUpdate, () => this.writeCommand("loadfile", [url, mode, "-1", options.join(',')]));
if (fetchMetadata) {
this.fetchMetadataAndNotify(url).catch(error => {
@@ -266,6 +281,9 @@ export class MediaPlayer {
}
private async writeCommand(command: string, args: any[]): Promise<any> {
// Wait for socket to become available.
let socket = await this.socket;
return new Promise((resolve, reject) => {
const id = this.requestId++;
@@ -274,8 +292,13 @@ export class MediaPlayer {
request_id: id
});
try {
this.pendingCommands.set(id, { resolve, reject });
this.socket.write(commandObject + '\n');
socket.write(commandObject + '\n');
} catch (e: any) {
console.error(`Error writing to socket: ${e}. Trying to respawn.`)
this.tryRespawnPlayerProcess();
}
// Add timeout to prevent hanging promises
setTimeout(() => {
@@ -313,9 +336,12 @@ export class MediaPlayer {
}
}
private connectToSocket(path: string) {
this.socket.connect(path);
this.socket.on("data", data => this.receiveData(data.toString()));
private connectToSocket(path: string): Socket {
let socket = new Socket();
socket.connect(path);
socket.on("data", data => this.receiveData(data.toString()));
return socket;
}
private handleEvent(event: string, data: any) {