Files
bitvid/js/services/playbackHelpers.js
T
thePR0M3TH3ANandClaude Opus 4.8 76ba750708 refactor(playback): extract playback helpers to shrink playbackService (1/10)
File-size decomposition (no behavior change). Move the self-contained
SimpleEventEmitter and extractWebSeedsFromMagnet (+ its WEBSEED_PARAM_KEYS and
the now-sole safeDecodeURIComponent consumer) out of playbackService.js into a
new js/services/playbackHelpers.js and import them back.

playbackService.js: 1350 -> 1274 lines (now under its original 1289 baseline).
Re-recorded the smaller baseline in check-file-size.mjs to lock the reduction.

Build + full unit suite green.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-19 11:04:16 -04:00

99 lines
2.6 KiB
JavaScript

// js/services/playbackHelpers.js
//
// Small, self-contained helpers extracted from playbackService.js to keep that
// orchestrator under the file-size budget. Pure logic with no dependency on the
// playback session state.
import { safeDecodeURIComponent } from "../utils/safeDecode.js";
/**
* Minimal synchronous event emitter used by the playback session to surface
* status/fallback/sourcechange events to the coordinator. Errors thrown by a
* listener are swallowed (and optionally logged) so one bad listener can't
* abort the rest of the playback pipeline.
*/
export class SimpleEventEmitter {
constructor(logger = null) {
this.logger = typeof logger === "function" ? logger : null;
this.listeners = new Map();
}
on(eventName, handler) {
if (typeof handler !== "function") {
return () => {};
}
if (!this.listeners.has(eventName)) {
this.listeners.set(eventName, new Set());
}
const handlers = this.listeners.get(eventName);
handlers.add(handler);
return () => {
handlers.delete(handler);
if (!handlers.size) {
this.listeners.delete(eventName);
}
};
}
emit(eventName, detail) {
const handlers = this.listeners.get(eventName);
if (!handlers || !handlers.size) {
return;
}
for (const handler of Array.from(handlers)) {
try {
handler(detail);
} catch (err) {
if (this.logger) {
this.logger("[PlaybackService] Listener error", err);
}
}
}
}
}
const WEBSEED_PARAM_KEYS = new Set(["ws", "webseed"]);
/**
* Extract every web seed URL (`ws=`/`webseed=`) from a magnet's query string,
* in order, decoded. Used to seed WebTorrent's urlList from the magnet.
*
* @param {string} magnetUri
* @returns {string[]}
*/
export const extractWebSeedsFromMagnet = (magnetUri) => {
if (typeof magnetUri !== "string") {
return [];
}
const trimmed = magnetUri.trim();
if (!trimmed) {
return [];
}
const [withoutFragment] = trimmed.split("#", 1);
const [, query = ""] = withoutFragment.split("?", 2);
if (!query) {
return [];
}
const webSeeds = [];
for (const segment of query.split("&")) {
if (!segment) {
continue;
}
const [rawKey, rawValue = ""] = segment.split("=", 2);
if (!rawKey) {
continue;
}
const key = rawKey.trim().toLowerCase();
if (!WEBSEED_PARAM_KEYS.has(key)) {
continue;
}
const decodedValue = safeDecodeURIComponent(rawValue.replace(/\+/g, "%20"));
const candidate = decodedValue.trim();
if (candidate) {
webSeeds.push(candidate);
}
}
return webSeeds;
};