WIP: global player refactor pt. 1

This commit is contained in:
2026-02-09 23:19:17 -08:00
parent 9126e34f38
commit aea41df214
20 changed files with 1045 additions and 2740 deletions

View File

@@ -2,7 +2,7 @@
import { resolve } from "$app/paths";
import "./layout.css";
import favicon from "$lib/assets/favicon.svg";
import GlobalPlayer from "$lib/components/GlobalPlayer.svelte";
import PlayerRoot from "$lib/components/player/PlayerRoot.svelte";
import ClientOnly from "$lib/components/util/ClientOnly.svelte";
let { children } = $props();
@@ -23,8 +23,12 @@
<header
class="sticky top-0 z-40 border-b bg-background/80 backdrop-blur lg:col-span-2"
>
<div class="mx-auto flex h-14 max-w-6xl items-center justify-between px-4">
<a href={resolve("/")} class="font-semibold tracking-tight">AMQ Train</a>
<div
class="mx-auto flex h-14 max-w-6xl items-center justify-between px-4"
>
<a href={resolve("/")} class="font-semibold tracking-tight"
>AMQ Train</a
>
<nav class="flex items-center gap-2 text-sm">
<a href={resolve("/")}>Anime</a>
@@ -42,7 +46,7 @@
<aside class="hidden lg:block">
<ClientOnly showFallback={false}>
{#snippet children()}
<GlobalPlayer />
<PlayerRoot />
{/snippet}
</ClientOnly>
</aside>

View File

@@ -9,7 +9,7 @@
getClientDb,
searchAnimeByName,
} from "$lib/db/client-db";
import { addAllToQueue, playAllNext } from "$lib/player/player.svelte";
import { player } from "$lib/player/store.svelte";
import { trackFromSongRow } from "$lib/player/types";
import { AmqBrowseSearchSchema } from "$lib/types/search/amq-browse";
import { seasonName } from "$lib/utils/amq";
@@ -78,7 +78,7 @@
)
.filter((t) => t !== null);
addAllToQueue(tracks);
player.addAll(tracks);
}
async function playAllNextForAnime(a: AnimeItem) {
@@ -102,7 +102,7 @@
)
.filter((t) => t !== null);
playAllNext(tracks);
player.playAllNext(tracks);
}
onMount(() => {
@@ -157,7 +157,8 @@
class="rounded border px-3 py-2 text-sm"
placeholder="Type to search by name…"
value={params.q}
oninput={(e) => (params.q = (e.currentTarget as HTMLInputElement).value)}
oninput={(e) =>
(params.q = (e.currentTarget as HTMLInputElement).value)}
autocomplete="off"
spellcheck={false}
/>
@@ -175,7 +176,10 @@
<li class="rounded border px-3 py-2">
<div class="flex flex-wrap items-center justify-between gap-3">
<div class="min-w-0">
<a class="font-medium hover:underline" href={animeHref(a.annId)}>
<a
class="font-medium hover:underline"
href={animeHref(a.annId)}
>
{a.mainName}
</a>
<div class="text-sm text-muted-foreground">
@@ -192,7 +196,8 @@
type="button"
class="btn-icon"
onclick={() => void playAllNextForAnime(a)}
disabled={a.opCount + a.edCount + a.insertCount === 0}
disabled={a.opCount + a.edCount + a.insertCount ===
0}
title="Play all next"
aria-label="Play all next"
>
@@ -203,7 +208,8 @@
type="button"
class="btn-icon"
onclick={() => void queueAllForAnime(a)}
disabled={a.opCount + a.edCount + a.insertCount === 0}
disabled={a.opCount + a.edCount + a.insertCount ===
0}
title="Queue all"
aria-label="Queue all"
>

View File

@@ -3,7 +3,7 @@
import { invalidate } from "$app/navigation";
import SongEntry from "$lib/components/SongEntry.svelte";
import { db as clientDb } from "$lib/db/client-db";
import { addAllToQueue, playAllNext } from "$lib/player/player.svelte";
import { player } from "$lib/player/store.svelte";
import { trackFromSongRow } from "$lib/player/types";
import { seasonName } from "$lib/utils/amq";
import type { PageData } from "./$types";
@@ -42,11 +42,11 @@
}
function queueAll() {
addAllToQueue(playableTracks());
player.addAll(playableTracks());
}
function playAllNextFromAnime() {
playAllNext(playableTracks());
player.playAllNext(playableTracks());
}
</script>
@@ -57,14 +57,16 @@
{#if !data.annId}
<h1 class="text-2xl font-semibold">Anime not found</h1>
<p class="mt-2 text-sm text-muted-foreground">
The requested anime entry doesnt exist (or the route param wasnt a valid
ANN id).
The requested anime entry doesnt exist (or the route param wasnt a
valid ANN id).
</p>
{:else if !data.animeWithSongs}
<p class="mt-3 text-sm text-muted-foreground">Loading anime…</p>
{:else}
<header class="mt-2 space-y-2">
<h1 class="text-2xl font-semibold">{data.animeWithSongs.anime.mainName}</h1>
<h1 class="text-2xl font-semibold">
{data.animeWithSongs.anime.mainName}
</h1>
<p class="text-sm text-muted-foreground">
{data.animeWithSongs.anime.year}

View File

@@ -6,7 +6,7 @@
import { invalidate } from "$app/navigation";
import SongEntry from "$lib/components/SongEntry.svelte";
import { db as clientDb } from "$lib/db/client-db";
import { addAllToQueue, playAllNext } from "$lib/player/player.svelte";
import { player } from "$lib/player/store.svelte";
import { trackFromSongRow } from "$lib/player/types";
import {
MalAnimeListQuerySchema,
@@ -139,7 +139,7 @@
<button
type="button"
class="rounded border px-3 py-2 text-sm"
onclick={() => addAllToQueue(tracksFromResults)}
onclick={() => player.addAll(tracksFromResults)}
disabled={tracksFromResults.length === 0}
>
Add all to queue
@@ -148,7 +148,7 @@
<button
type="button"
class="rounded border px-3 py-2 text-sm"
onclick={() => playAllNext(tracksFromResults)}
onclick={() => player.playAllNext(tracksFromResults)}
disabled={tracksFromResults.length === 0}
>
Play all next
@@ -184,8 +184,8 @@
{#if (formMal ?? "").trim() && data.username && (data.malResponse?.data.length ?? 0) > 0 && data.songRows.length === 0}
<p class="mt-4 text-sm text-muted-foreground">
No songs matched in the local database. This likely means none of the MAL
anime IDs exist in the AMQ DB.
No songs matched in the local database. This likely means none of the
MAL anime IDs exist in the AMQ DB.
</p>
{/if}

View File

@@ -9,7 +9,7 @@
import { Input } from "$lib/components/ui/input";
import { Label } from "$lib/components/ui/label";
import { db as clientDb } from "$lib/db/client-db";
import { addAllToQueue, playAllNext } from "$lib/player/player.svelte";
import { player } from "$lib/player/store.svelte";
import { trackFromSongRow } from "$lib/player/types";
import { AmqSongLinkTypeMap } from "$lib/types/amq";
import type { PageData } from "./$types";
@@ -124,7 +124,9 @@
label="Song Type"
items={Object.keys(AmqSongLinkTypeMap).map((type) => ({
label: type,
value: AmqSongLinkTypeMap[type as keyof typeof AmqSongLinkTypeMap],
value: AmqSongLinkTypeMap[
type as keyof typeof AmqSongLinkTypeMap
],
}))}
bind:value={params.type}
/>
@@ -149,7 +151,7 @@
<Button
variant="outline"
class="cursor-pointer"
onclick={() => addAllToQueue(tracksFromResults)}
onclick={() => player.addAll(tracksFromResults)}
disabled={tracksFromResults.length === 0}
>
Add all to queue
@@ -158,7 +160,7 @@
<Button
variant="outline"
class="cursor-pointer"
onclick={() => playAllNext(tracksFromResults)}
onclick={() => player.playAllNext(tracksFromResults)}
disabled={tracksFromResults.length === 0}
>
Play all next