fix(android): keep lockscreen/media controls in sync with playback
The lockscreen controls drifted out of sync, especially while casting, and couldn't control remote playback. Two media sessions were competing (a Media3 MediaSession driving transport vs a MediaSessionCompat driving the notification), position was only pushed on play/pause so the scrubber froze mid-track, and remote mode showed stale local metadata with dead buttons. - Make MediaSessionCompat the single source of truth; route all transport commands (both the Compat callback and the Media3 wrappedPlayer) through Rust via nativeOnMediaCommand instead of touching ExoPlayer directly. - Push position on every 250ms tick via a lightweight updatePlaybackPosition, and report 0.0 playback speed when paused so Android stops extrapolating. - Mirror the remote session's now-playing onto the lockscreen from the native session poller (works while the screen is locked, unlike WebView timers) via a new player::update_lockscreen_metadata JNI bridge. - Make MediaSessionHandler mode-aware: in remote mode forward play/pause/next/ prev/seek to the remote Jellyfin session; Stop while casting emits RemoteDisconnectRequested, which the frontend handles by transferring to local. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -10,7 +10,7 @@
|
||||
*/
|
||||
|
||||
import { writable, get, derived } from "svelte/store";
|
||||
import { commands } from "$lib/api/bindings";
|
||||
import { commands, events } from "$lib/api/bindings";
|
||||
import { sessions, selectedSession } from "./sessions";
|
||||
import { auth } from "./auth";
|
||||
import { ticksToSeconds } from "$lib/utils/playbackUnits";
|
||||
@@ -275,6 +275,22 @@ function createPlaybackModeStore() {
|
||||
let consecutiveMisses = 0;
|
||||
const DISCONNECT_THRESHOLD = 3; // ~6s at 2s polling interval
|
||||
|
||||
// The lockscreen Stop button (while casting) asks the native side to
|
||||
// disconnect from the remote session and resume locally. The remote->local
|
||||
// transfer must be driven here because it reloads the media item locally,
|
||||
// which only the frontend can currently do.
|
||||
events.playerStatusEvent.listen((event) => {
|
||||
if (event.payload.type === "remote_disconnect_requested") {
|
||||
const currentState = get({ subscribe });
|
||||
if (currentState.mode === "remote") {
|
||||
console.log("[PlaybackMode] Lockscreen requested disconnect; transferring to local");
|
||||
transferToLocal().catch((e) =>
|
||||
console.error("[PlaybackMode] Lockscreen-triggered transfer failed:", e),
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
selectedSession.subscribe((session) => {
|
||||
const currentState = get({ subscribe });
|
||||
|
||||
|
||||
Reference in New Issue
Block a user