146 lines
3.3 KiB
Svelte
146 lines
3.3 KiB
Svelte
<script lang="ts">
|
|
import { onMount, setContext } from "svelte";
|
|
import { player } from "$lib/player/store.svelte";
|
|
import { AudioContext, setAudioContext } from "./ctx.svelte";
|
|
import PlayerDesktop from "./PlayerDesktop.svelte";
|
|
import PlayerMobile from "./PlayerMobile.svelte";
|
|
|
|
// Initialize context
|
|
const audioCtx = new AudioContext();
|
|
setContext("amqtrain:player:audio-ctx", audioCtx);
|
|
|
|
let audioEl: HTMLAudioElement;
|
|
|
|
import { loadState, saveState } from "$lib/player/persist";
|
|
|
|
// ... existing imports ...
|
|
|
|
onMount(() => {
|
|
audioCtx.setElement(audioEl);
|
|
|
|
// Load state
|
|
const saved = loadState();
|
|
if (saved) {
|
|
player.init(saved);
|
|
}
|
|
|
|
// Setup MediaSession
|
|
if ("mediaSession" in navigator) {
|
|
navigator.mediaSession.setActionHandler("play", () => {
|
|
audioCtx.paused = false;
|
|
});
|
|
navigator.mediaSession.setActionHandler("pause", () => {
|
|
audioCtx.paused = true;
|
|
});
|
|
navigator.mediaSession.setActionHandler("previoustrack", () =>
|
|
player.prev(),
|
|
);
|
|
navigator.mediaSession.setActionHandler("nexttrack", () => player.next());
|
|
navigator.mediaSession.setActionHandler("stop", () => {
|
|
audioCtx.paused = true;
|
|
audioCtx.currentTime = 0;
|
|
});
|
|
}
|
|
});
|
|
|
|
// Persist state changes
|
|
$effect(() => {
|
|
// Create a dependency on all persisted fields
|
|
const state = {
|
|
queue: player.queue,
|
|
currentId: player.currentId,
|
|
volume: player.volume,
|
|
isMuted: player.isMuted,
|
|
minimized: !player.uiOpen,
|
|
};
|
|
saveState(state);
|
|
});
|
|
|
|
// Update MediaSession metadata
|
|
$effect(() => {
|
|
const track = player.currentTrack;
|
|
if (track && "mediaSession" in navigator) {
|
|
navigator.mediaSession.metadata = new MediaMetadata({
|
|
title: track.title,
|
|
artist: track.artist,
|
|
album: track.album || track.animeName,
|
|
artwork: [
|
|
// We could add artwork here if available in track
|
|
// { src: track.artworkUrl, sizes: '512x512', type: 'image/png' }
|
|
],
|
|
});
|
|
}
|
|
});
|
|
|
|
// ... existing effect for playback ...
|
|
$effect(() => {
|
|
const track = player.currentTrack;
|
|
if (audioEl) {
|
|
if (track) {
|
|
const newSrc = track.src;
|
|
if (audioEl.src !== newSrc) {
|
|
audioEl.src = newSrc;
|
|
audioEl.play().catch((e) => {
|
|
console.warn("Autoplay blocked or failed", e);
|
|
});
|
|
}
|
|
} else {
|
|
audioEl.removeAttribute("src");
|
|
}
|
|
|
|
// Update MediaSession playback state
|
|
if ("mediaSession" in navigator) {
|
|
navigator.mediaSession.playbackState = audioEl.paused
|
|
? "paused"
|
|
: "playing";
|
|
}
|
|
}
|
|
});
|
|
|
|
// ... existing callbacks ...
|
|
|
|
// Bindings and Event Listeners
|
|
function onTimeUpdate() {
|
|
audioCtx.currentTime = audioEl.currentTime;
|
|
}
|
|
|
|
function onDurationChange() {
|
|
audioCtx.duration = audioEl.duration;
|
|
}
|
|
|
|
function onPlay() {
|
|
audioCtx.paused = false;
|
|
if ("mediaSession" in navigator)
|
|
navigator.mediaSession.playbackState = "playing";
|
|
}
|
|
|
|
function onPause() {
|
|
audioCtx.paused = true;
|
|
if ("mediaSession" in navigator)
|
|
navigator.mediaSession.playbackState = "paused";
|
|
}
|
|
|
|
function onEnded() {
|
|
player.next();
|
|
}
|
|
</script>
|
|
|
|
<audio
|
|
bind:this={audioEl}
|
|
ontimeupdate={onTimeUpdate}
|
|
ondurationchange={onDurationChange}
|
|
onplay={onPlay}
|
|
onpause={onPause}
|
|
onended={onEnded}
|
|
class="hidden"
|
|
></audio>
|
|
|
|
<div class="contents">
|
|
<div class="lg:hidden">
|
|
<PlayerMobile />
|
|
</div>
|
|
<div class="hidden lg:block h-full">
|
|
<PlayerDesktop />
|
|
</div>
|
|
</div>
|