TRACES: | DR-204 484 ungated `console.*` calls across 63 non-test frontend files shipped to end users with no way to turn them off. Mechanical substitution, no control flow, error handling or message semantics changed: console.log / console.debug -> log.debug console.info -> log.info console.warn -> log.warn console.error -> log.error Hand-written `"[Scope] …"` prefixes are dropped where the logger's scope now carries them; scope names that already existed are preserved verbatim (`[Auth]`, `[VideoPlayer]`, `[PiP]`, …) and inferred from the filename where a file had none. `src/routes/player/[id]/+page.svelte` keeps its `NextEpisode` and `AutoPlay` sub-scopes as separate loggers rather than flattening them into the page scope. `grep -rn 'console\.' src/` now matches nothing outside the tests and the facade itself.
97 lines
3.3 KiB
TypeScript
97 lines
3.3 KiB
TypeScript
/**
|
|
* An {@link AdapterHost} implementation that forwards a player adapter's outward
|
|
* lifecycle events into the Rust `PlayerController` via the `player_report_*`
|
|
* commands. The controller re-emits the same `PlayerStatusEvent`s the native
|
|
* backends emit, so the frontend `player` store is fed from ONE pipeline
|
|
* (playerEvents.ts) in both native and HTML5 modes — keeping Rust the single
|
|
* source of truth.
|
|
*
|
|
* This is the sole place that talks to the report commands; adapters depend only
|
|
* on the {@link AdapterHost} interface, never on `commands` directly, which keeps
|
|
* them unit-testable with a mock host.
|
|
*
|
|
* TRACES: UR-003, UR-005 | DR-001, DR-028
|
|
*/
|
|
|
|
import { commands } from "$lib/api/bindings";
|
|
import type { AdapterHost } from "./types";
|
|
import { createLogger } from "$lib/utils/logger";
|
|
|
|
const log = createLogger("rustReportHost");
|
|
|
|
const POSITION_REPORT_INTERVAL_MS = 250;
|
|
|
|
/** Report options that let a caller bypass throttling for discrete events. */
|
|
export interface ReportPositionOptions {
|
|
force?: boolean;
|
|
}
|
|
|
|
/**
|
|
* Low-level report helpers, exported so the legacy `$lib/player/html5Adapter`
|
|
* shim can keep its function-style API while there are still direct callers.
|
|
* Prefer {@link createRustReportHost} for new adapter code.
|
|
*/
|
|
let lastPositionReport = 0;
|
|
|
|
export async function reportState(
|
|
state: "playing" | "paused" | "loading" | "stopped" | "idle",
|
|
mediaId: string | null
|
|
): Promise<void> {
|
|
try {
|
|
await commands.playerReportState(state, mediaId);
|
|
} catch (err) {
|
|
log.warn("Failed to report state:", err);
|
|
}
|
|
}
|
|
|
|
export async function reportPosition(
|
|
position: number,
|
|
duration: number,
|
|
{ force = false }: ReportPositionOptions = {}
|
|
): Promise<void> {
|
|
const now = Date.now();
|
|
if (!force && now - lastPositionReport < POSITION_REPORT_INTERVAL_MS) {
|
|
return;
|
|
}
|
|
lastPositionReport = now;
|
|
try {
|
|
await commands.playerReportPosition(position, Number.isFinite(duration) ? duration : 0);
|
|
} catch (err) {
|
|
log.warn("Failed to report position:", err);
|
|
}
|
|
}
|
|
|
|
export async function reportMediaLoaded(duration: number): Promise<void> {
|
|
try {
|
|
await commands.playerReportMediaLoaded(Number.isFinite(duration) ? duration : 0);
|
|
} catch (err) {
|
|
log.warn("Failed to report media loaded:", err);
|
|
}
|
|
}
|
|
|
|
export function resetReporting(): void {
|
|
lastPositionReport = 0;
|
|
}
|
|
|
|
/**
|
|
* Build an {@link AdapterHost} bound to a specific media id that forwards adapter
|
|
* events to Rust. `onStreamUrlChanged`, `onBuffering`, and `onReady` are wired by
|
|
* the owning view (they affect the `<video src>` / spinner), so this host accepts
|
|
* optional view callbacks and defaults them to no-ops.
|
|
*/
|
|
export function createRustReportHost(
|
|
mediaId: string,
|
|
view: Partial<Pick<AdapterHost, "onStreamUrlChanged" | "onBuffering" | "onReady" | "onEnded" | "onError">> = {}
|
|
): AdapterHost {
|
|
return {
|
|
onState: (state) => void reportState(state, mediaId),
|
|
onPosition: (position, duration) => void reportPosition(position, duration),
|
|
onMediaLoaded: (duration) => void reportMediaLoaded(duration),
|
|
onEnded: view.onEnded ?? (() => {}),
|
|
onError: view.onError ?? ((message) => log.warn("adapter error:", message)),
|
|
onStreamUrlChanged: view.onStreamUrlChanged ?? (() => {}),
|
|
onBuffering: view.onBuffering ?? (() => {}),
|
|
onReady: view.onReady ?? (() => {}),
|
|
};
|
|
}
|