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.
105 lines
3.9 KiB
TypeScript
105 lines
3.9 KiB
TypeScript
/**
|
|
* Native video surface compositing, Android only.
|
|
*
|
|
* TRACES: UR-003, UR-004 | DR-150, DR-151, DR-183
|
|
*
|
|
* On Android, ExoPlayer renders video into a SurfaceView that sits *behind* the
|
|
* Tauri WebView (`setZOrderMediaOverlay(false)`, added at index 0 of the content
|
|
* view by VideoOverlayManager). For that video to be visible, two independent
|
|
* opaque layers have to be cleared:
|
|
*
|
|
* 1. The **WebView widget's own background** — reachable only from Kotlin, via
|
|
* the `AndroidVideoSurface` @JavascriptInterface installed by MainActivity.
|
|
* 2. The **web page's backgrounds** — the `html`/`body` colour in app.css and
|
|
* the app shell's `bg-[var(--color-background)]`. Handled by the
|
|
* `data-native-video` attribute, which $lib/stores/nativeVideo.ts sets and
|
|
* app.css keys its transparency rules off.
|
|
*
|
|
* Clearing only one leaves a black screen with audio, which is exactly the
|
|
* failure mode the old INTERIM override in VideoPlayer.svelte was working
|
|
* around. Both must be toggled together, so this module owns both halves.
|
|
*
|
|
* Everything here is a no-op off Android — the bridge is simply absent.
|
|
*/
|
|
|
|
import { nativeVideoActive } from "$lib/stores/nativeVideo";
|
|
import { createLogger } from "$lib/utils/logger";
|
|
|
|
const log = createLogger("videoSurface");
|
|
|
|
interface AndroidVideoSurfaceBridge {
|
|
setTransparent(transparent: boolean): void;
|
|
isSupported(): boolean;
|
|
}
|
|
|
|
declare global {
|
|
interface Window {
|
|
AndroidVideoSurface?: AndroidVideoSurfaceBridge;
|
|
}
|
|
}
|
|
|
|
function bridge(): AndroidVideoSurfaceBridge | undefined {
|
|
if (typeof window === "undefined") return undefined;
|
|
return window.AndroidVideoSurface;
|
|
}
|
|
|
|
/**
|
|
* Whether the native-surface bridge exists on this platform. This reports only
|
|
* that the *plumbing* is present; whether native video should actually be used
|
|
* is Rust's decision (`player_get_capabilities`) gated by the user's
|
|
* `experimentalNativeVideo` flag.
|
|
*/
|
|
export function isNativeSurfaceBridgeAvailable(): boolean {
|
|
try {
|
|
return bridge()?.isSupported() ?? false;
|
|
} catch (err) {
|
|
log.warn("isSupported check failed:", err);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Make the webview transparent so the video surface behind it shows through.
|
|
*
|
|
* MUST be paired with {@link disableNativeVideoCompositing} on teardown — a
|
|
* transparent window left behind shows the launcher through the whole app.
|
|
*/
|
|
export function enableNativeVideoCompositing(): void {
|
|
// Page layer first: if the Kotlin call succeeded but this threw, the user
|
|
// would see through the app to the home screen.
|
|
nativeVideoActive.set(true);
|
|
const androidVideoSurface = bridge();
|
|
if (!androidVideoSurface) {
|
|
// Say so loudly. Every bridge call in this file is optional-chained, so a
|
|
// missing bridge is silent — and a silently-skipped setTransparent(true) is
|
|
// indistinguishable on screen from a compositing failure: ExoPlayer renders
|
|
// correctly behind a WebView that never stopped painting its own opaque
|
|
// background. That ambiguity is what DR-172 was left holding. MainActivity's
|
|
// console bridge forwards this to logcat under the JellyTauWeb tag.
|
|
log.error(
|
|
"AndroidVideoSurface bridge is MISSING - the webview will " +
|
|
"stay opaque and native video will play as audio with no picture"
|
|
);
|
|
return;
|
|
}
|
|
try {
|
|
androidVideoSurface.setTransparent(true);
|
|
log.debug("compositing enabled (setTransparent(true) sent)");
|
|
} catch (err) {
|
|
log.warn("setTransparent(true) failed:", err);
|
|
nativeVideoActive.set(false);
|
|
}
|
|
}
|
|
|
|
/** Restore the opaque webview background. Safe to call unconditionally. */
|
|
export function disableNativeVideoCompositing(): void {
|
|
try {
|
|
bridge()?.setTransparent(false);
|
|
} catch (err) {
|
|
log.warn("setTransparent(false) failed:", err);
|
|
}
|
|
// Always clear the page layer, even if the bridge call failed, so the app is
|
|
// never left rendering over a transparent window.
|
|
nativeVideoActive.set(false);
|
|
}
|