blastoise-archive/stream.ts

98 lines
2.1 KiB
TypeScript

import type { ServerWebSocket } from "bun";
export interface Track {
filename: string;
title: string;
duration: number;
}
export interface StreamConfig {
id: string;
name: string;
tracks: Track[];
}
export class Stream {
id: string;
name: string;
playlist: Track[];
currentIndex: number = 0;
startedAt: number = Date.now();
clients: Set<ServerWebSocket<{ streamId: string }>> = new Set();
paused: boolean = false;
pausedAt: number = 0;
constructor(config: StreamConfig) {
this.id = config.id;
this.name = config.name;
this.playlist = config.tracks;
}
get currentTrack(): Track | null {
if (this.playlist.length === 0) return null;
return this.playlist[this.currentIndex];
}
get currentTimestamp(): number {
if (this.paused) return this.pausedAt;
return (Date.now() - this.startedAt) / 1000;
}
tick(): boolean {
if (this.paused) return false;
const track = this.currentTrack;
if (!track) return false;
if (this.currentTimestamp >= track.duration) {
this.advance();
return true;
}
return false;
}
advance() {
if (this.playlist.length === 0) return;
this.currentIndex = (this.currentIndex + 1) % this.playlist.length;
this.startedAt = Date.now();
this.broadcast();
}
getState() {
return {
track: this.currentTrack,
currentTimestamp: this.currentTimestamp,
streamName: this.name,
paused: this.paused,
};
}
pause() {
if (this.paused) return;
this.pausedAt = this.currentTimestamp;
this.paused = true;
this.broadcast();
}
unpause() {
if (!this.paused) return;
this.paused = false;
this.startedAt = Date.now() - this.pausedAt * 1000;
this.broadcast();
}
broadcast() {
const msg = JSON.stringify(this.getState());
for (const ws of this.clients) {
ws.send(msg);
}
}
addClient(ws: ServerWebSocket<{ streamId: string }>) {
this.clients.add(ws);
ws.send(JSON.stringify(this.getState()));
}
removeClient(ws: ServerWebSocket<{ streamId: string }>) {
this.clients.delete(ws);
}
}