Implements seek/time bar
This commit is contained in:
@@ -160,8 +160,12 @@ export class MediaPlayer {
|
||||
public async getNowPlaying(): Promise<PlaylistItem> {
|
||||
const playlist = await this.getPlaylist();
|
||||
const currentlyPlayingSong = playlist.find((item: PlaylistItem) => item.current);
|
||||
const fetchMediaTitle = async (): Promise<string> => {
|
||||
return (await this.writeCommand("get_property", ["media-title"])).data;
|
||||
const fetchMediaTitle = async (): Promise<string | null> => {
|
||||
try {
|
||||
return (await this.writeCommand("get_property", ["media-title"])).data;
|
||||
} catch (err) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
if (currentlyPlayingSong !== undefined) {
|
||||
@@ -169,14 +173,14 @@ export class MediaPlayer {
|
||||
if (currentlyPlayingSong.title === undefined && currentlyPlayingSong.metadata?.title === undefined) {
|
||||
return {
|
||||
...currentlyPlayingSong,
|
||||
title: await fetchMediaTitle()
|
||||
title: await fetchMediaTitle() || currentlyPlayingSong.filename
|
||||
};
|
||||
}
|
||||
|
||||
return currentlyPlayingSong;
|
||||
}
|
||||
|
||||
const mediaTitle = await fetchMediaTitle();
|
||||
const mediaTitle = await fetchMediaTitle() || "";
|
||||
return {
|
||||
id: 0,
|
||||
filename: mediaTitle,
|
||||
@@ -184,11 +188,11 @@ export class MediaPlayer {
|
||||
};
|
||||
}
|
||||
|
||||
public async getCurrentFile(): Promise<string> {
|
||||
public async getCurrentFile(): Promise<string | null> {
|
||||
return this.writeCommand("get_property", ["stream-open-filename"])
|
||||
.then((response) => {
|
||||
return response.data;
|
||||
});
|
||||
}, (reject) => { return null; });
|
||||
}
|
||||
|
||||
public async getPauseState(): Promise<boolean> {
|
||||
@@ -205,6 +209,27 @@ export class MediaPlayer {
|
||||
});
|
||||
}
|
||||
|
||||
public async getTimePosition(): Promise<number | null> {
|
||||
return this.writeCommand("get_property", ["time-pos"])
|
||||
.then((response) => {
|
||||
return response.data;
|
||||
}, (rejected) => { return null; });
|
||||
}
|
||||
|
||||
public async getDuration(): Promise<number | null> {
|
||||
return this.writeCommand("get_property", ["duration"])
|
||||
.then((response) => {
|
||||
return response.data;
|
||||
}, (rejected) => { return null; });
|
||||
}
|
||||
|
||||
public async getSeekable(): Promise<boolean | null> {
|
||||
return this.writeCommand("get_property", ["seekable"])
|
||||
.then((response) => {
|
||||
return response.data;
|
||||
}, (rejected) => { return null; });
|
||||
}
|
||||
|
||||
public async getIdle(): Promise<boolean> {
|
||||
return this.writeCommand("get_property", ["idle"])
|
||||
.then((response) => {
|
||||
@@ -286,6 +311,10 @@ export class MediaPlayer {
|
||||
return this.modify(UserEvent.VolumeUpdate, () => this.writeCommand("set_property", ["volume", volume]));
|
||||
}
|
||||
|
||||
public async seek(time: number) {
|
||||
return this.modify(UserEvent.NowPlayingUpdate, () => this.writeCommand("seek", [time, "absolute"]));
|
||||
}
|
||||
|
||||
public subscribe(ws: WebSocket) {
|
||||
this.eventSubscribers.push(ws);
|
||||
}
|
||||
@@ -338,7 +367,10 @@ export class MediaPlayer {
|
||||
// Notify all subscribers
|
||||
this.handleEvent(event, {});
|
||||
return result;
|
||||
});
|
||||
}, (reject) => {
|
||||
console.log("Error modifying playlist: " + reject);
|
||||
return reject;
|
||||
});
|
||||
}
|
||||
|
||||
private async writeCommand(command: string, args: any[]): Promise<any> {
|
||||
@@ -429,7 +461,12 @@ export class MediaPlayer {
|
||||
if (response.request_id) {
|
||||
const pending = this.pendingCommands.get(response.request_id);
|
||||
if (pending) {
|
||||
pending.resolve(response);
|
||||
if (response.error == "success") {
|
||||
pending.resolve(response);
|
||||
} else {
|
||||
pending.reject(response.error);
|
||||
}
|
||||
|
||||
this.pendingCommands.delete(response.request_id);
|
||||
}
|
||||
} else if (response.event) {
|
||||
|
||||
@@ -43,6 +43,7 @@ const withErrorHandling = (func: (req: any, res: any) => Promise<any>) => {
|
||||
try {
|
||||
await func(req, res);
|
||||
} catch (error: any) {
|
||||
console.log(`Error (${func.name}): ${error}`);
|
||||
res.status(500).send(JSON.stringify({ success: false, error: error.message }));
|
||||
}
|
||||
};
|
||||
@@ -108,6 +109,9 @@ apiRouter.get("/nowplaying", withErrorHandling(async (req, res) => {
|
||||
const pauseState = await mediaPlayer.getPauseState();
|
||||
const volume = await mediaPlayer.getVolume();
|
||||
const idle = await mediaPlayer.getIdle();
|
||||
const timePosition = await mediaPlayer.getTimePosition();
|
||||
const duration = await mediaPlayer.getDuration();
|
||||
const seekable = await mediaPlayer.getSeekable();
|
||||
|
||||
res.send(JSON.stringify({
|
||||
success: true,
|
||||
@@ -115,7 +119,10 @@ apiRouter.get("/nowplaying", withErrorHandling(async (req, res) => {
|
||||
isPaused: pauseState,
|
||||
volume: volume,
|
||||
isIdle: idle,
|
||||
currentFile: currentFile
|
||||
currentFile: currentFile,
|
||||
timePosition: timePosition,
|
||||
duration: duration,
|
||||
seekable: seekable
|
||||
}));
|
||||
}));
|
||||
|
||||
@@ -125,6 +132,12 @@ apiRouter.post("/volume", withErrorHandling(async (req, res) => {
|
||||
res.send(JSON.stringify({ success: true }));
|
||||
}));
|
||||
|
||||
apiRouter.post("/player/seek", withErrorHandling(async (req, res) => {
|
||||
const { time } = req.body as { time: number };
|
||||
await mediaPlayer.seek(time);
|
||||
res.send(JSON.stringify({ success: true }));
|
||||
}));
|
||||
|
||||
apiRouter.ws("/events", (ws, req) => {
|
||||
console.log("Events client connected");
|
||||
mediaPlayer.subscribe(ws);
|
||||
|
||||
Reference in New Issue
Block a user