feat(streamer): icecast listener-count poller

This commit is contained in:
devilreef 2026-04-30 15:21:49 +06:00
parent 6bcf18c1bf
commit 870a41a2f7
Signed by: devilreef
SSH key fingerprint: SHA256:UZisRr4iuXx+IhkbZnR655L2RWAT6o2rgbGv5F/6m3Y

43
streamer/src/icecast.ts Normal file
View file

@ -0,0 +1,43 @@
import { EventEmitter } from "node:events";
export interface IcecastOpts {
statusUrl: string;
mountName: string;
intervalMs?: number;
}
export interface IcecastListeners { current: number; peak: number; }
export class IcecastPoller extends EventEmitter {
private timer: NodeJS.Timeout | null = null;
private peak = 0;
constructor(private opts: IcecastOpts) { super(); }
start(): void {
this.poll();
this.timer = setInterval(() => this.poll(), this.opts.intervalMs ?? 30000);
}
stop(): void {
if (this.timer) clearInterval(this.timer);
}
private async poll(): Promise<void> {
try {
const r = await fetch(this.opts.statusUrl, { signal: AbortSignal.timeout(5000) });
if (!r.ok) throw new Error(`icecast status ${r.status}`);
const j = await r.json() as {
icestats: { source?: { listenurl: string; listeners: number }[] | { listenurl: string; listeners: number } };
};
const sources = j.icestats.source;
const list = Array.isArray(sources) ? sources : sources ? [sources] : [];
const found = list.find((s) => s.listenurl.endsWith(this.opts.mountName));
const current = found?.listeners ?? 0;
this.peak = Math.max(this.peak, current);
this.emit("listeners", { current, peak: this.peak });
} catch (err) {
this.emit("warn", err);
}
}
}