29 lines
1.6 KiB
JavaScript
29 lines
1.6 KiB
JavaScript
/** Metadata probes belong to the active playback, and share an ordered lease across replacements. */
|
|
class PlaybackProbe {
|
|
constructor(fetchImpl = fetch) {
|
|
this.fetch = fetchImpl;
|
|
this.sequence = 0;
|
|
this.leaseId = Array.from(globalThis.crypto.getRandomValues(new Uint8Array(16)),
|
|
byte => byte.toString(16).padStart(2, '0')).join('');
|
|
}
|
|
async read(url, { program = null, userAgent } = {}) {
|
|
this.cancel();
|
|
const controller = this.controller = new AbortController();
|
|
const query = new URLSearchParams({ url, probeLeaseId: this.leaseId, requestSequence: String(++this.sequence) });
|
|
if (program != null) query.set('program', program);
|
|
if (userAgent !== undefined) query.set('ua', userAgent);
|
|
try {
|
|
const response = await this.fetch('/api/probe?' + query, { signal: controller.signal, credentials: 'same-origin' });
|
|
const info = await response.json();
|
|
if (controller.signal.aborted || this.controller !== controller || response.status === 409) {
|
|
throw Object.assign(new Error('Playback probe was superseded'), { name: 'AbortError' });
|
|
}
|
|
if (response.ok === false) throw new Error(info.error || 'Unable to probe stream');
|
|
return info;
|
|
} finally { if (this.controller === controller) this.controller = null; }
|
|
}
|
|
cancel() { this.controller?.abort(); this.controller = null; }
|
|
}
|
|
if (typeof window !== 'undefined') window.PlaybackProbe = PlaybackProbe;
|
|
if (typeof module !== 'undefined') module.exports = PlaybackProbe;
|