Files
jellytau/src/lib/components/player/mediaReady.ts
T
dtourolle ad48d89dfe chore(format): run prettier over src/ and scripts/
Formatting was configured but never enforced: `bun run format:check`
reported 199 unformatted files and ran in no workflow and in no git hook,
so .prettierrc (printWidth 100, trailing commas) described an intention
rather than the tree.

This is the one-time sweep that makes the check gateable. Whitespace and
token-reflow only -- no behavioural change: `bun run check` reports 0
errors and all 1053 frontend tests pass before and after.

Kept out of every other commit on purpose. A 199-file diff mixed with
real changes is unreviewable, and the next commit turns format:check
into a hard CI gate so this cannot silently accumulate again.
2026-08-21 17:41:44 +02:00

48 lines
2.2 KiB
TypeScript

/**
* When the video area may be revealed on the **native** (ExoPlayer) path.
*
* TRACES: UR-003, UR-004 | DR-182 | UT-184
*
* VideoPlayer draws an opaque `bg-black` poster/title card over the video area
* until `isMediaReady`. Every signal that clears it is emitted by the HTML5
* `<video>` element — `canplay`, `loadedmetadata`, hls.js `FRAG_BUFFERED`, the
* `playing` event, and two `readyState` timeouts. The native path has no such
* element, so on Android nothing ever cleared the card: ExoPlayer decoded to a
* live SurfaceView behind a black div, which is the "audio with no picture"
* report of DR-172 and is indistinguishable from a compositing failure.
*
* The backend's own events are the equivalent signals, and this is the rule for
* reading them. It is a pure function rather than a branch inside the component
* because the component cannot be exercised without a DOM and a mounted player,
* and this decision is exactly the part that was missing and needs a guard.
*/
/** A player event that might mean "the surface has a picture on it". */
export type NativeRevealSignal =
{ kind: "state"; state: string } | { kind: "position"; position: number; duration: number };
/**
* Whether `signal` proves the native backend is rendering, and the poster card
* should therefore come down.
*
* Two signals qualify, mirroring the HTML5 path's primary event and its
* backstop:
*
* - **`state === "playing"`** — the direct equivalent of the `<video>`
* `playing` event. ExoPlayer reports this once it is actually drawing.
* - **a position tick carrying a real position or duration** — the equivalent
* of the `readyState` fallbacks. It covers a first state event that is
* dropped or arrives before the listener is attached; a tick means the media
* is loaded and the surface has content.
*
* Everything else — `buffering`, `paused`, `stopped`, `error` — leaves the card
* up. Revealing on `error` in particular would replace the title card with a
* transparent hole showing the launcher through the app.
*/
export function nativeSignalRevealsVideo(signal: NativeRevealSignal): boolean {
if (signal.kind === "state") {
return signal.state === "playing";
}
return signal.duration > 0 || signal.position > 0;
}