fix(frontend): wire hardware media-key next/previous track buttons

The player never registered Media Session action handlers, so the OS/keyboard next-track and previous-track keys had nothing to call — only play/pause worked via the browser's built-in audio-element heuristic. Now previoustrack/nexttrack are wired to the player, and metadata/playback state are kept in sync for the lock-screen/OS media widget.
This commit is contained in:
2026-07-16 17:20:49 +02:00
parent 03b1166307
commit 82152beb93
+31
View File
@@ -137,10 +137,12 @@ class PlayerState {
audio.addEventListener('play', () => {
this.playing = true;
this.#startProgressLoop();
this.#syncMediaSessionPlaybackState();
});
audio.addEventListener('pause', () => {
this.playing = false;
this.#stopProgressLoop();
this.#syncMediaSessionPlaybackState();
});
audio.addEventListener('ended', () => {
this.#stopProgressLoop();
@@ -162,9 +164,37 @@ class PlayerState {
this.error = 'No se pudo reproducir la pista.';
});
this.#audio = audio;
this.#setupMediaSession(audio);
return audio;
}
#setupMediaSession(audio: HTMLAudioElement) {
if (!('mediaSession' in navigator)) return;
navigator.mediaSession.setActionHandler('play', () => audio.play().catch(() => {}));
navigator.mediaSession.setActionHandler('pause', () => audio.pause());
navigator.mediaSession.setActionHandler('previoustrack', () => this.previous());
navigator.mediaSession.setActionHandler('nexttrack', () => this.next());
}
#syncMediaSessionPlaybackState() {
if (!browser || !('mediaSession' in navigator)) return;
navigator.mediaSession.playbackState = this.playing ? 'playing' : 'paused';
}
#syncMediaSessionMetadata(track: PlayerTrack) {
if (!browser || !('mediaSession' in navigator)) return;
if (!track.id) {
navigator.mediaSession.metadata = null;
return;
}
const artwork = track.coverUrl ?? `/api/tracks/${track.id}/cover?size=small`;
navigator.mediaSession.metadata = new MediaMetadata({
title: track.title,
artist: track.artist,
artwork: [{ src: artwork, sizes: '256x256' }]
});
}
#tickProgress = () => {
const audio = this.#audio;
if (!audio) return;
@@ -229,6 +259,7 @@ class PlayerState {
const id = this.currentId;
const track = this.tracks.find((t) => t.id === id);
this.#applyAccent(id, track?.coverUrl);
if (track) this.#syncMediaSessionMetadata(track);
const token = ++this.#loadToken;
this.loading = true;
this.error = '';