Files
amqtrain/src/routes/anime/[annId]/+page.svelte

174 lines
5.0 KiB
Svelte
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script lang="ts">
import { onMount } from "svelte";
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 { trackFromSongRow } from "$lib/player/types";
import { seasonName } from "$lib/utils/amq";
import type { PageData } from "./$types";
let { data }: { data: PageData } = $props();
// If SSR returned null (because client DB wasn't available),
// re-run load on the client once the DB is ready by invalidating.
onMount(() => {
if (data.animeWithSongs) return;
// Invalid route param -> nothing to hydrate
if (!data.annId) return;
if (clientDb) {
void invalidate("clientdb:songs");
return;
}
});
function playableTracks() {
const rows = data.animeWithSongs?.songs ?? [];
return rows
.map((s) =>
trackFromSongRow({
annSongId: s.annSongId,
animeName: data.animeWithSongs?.anime.mainName ?? "Unknown Anime",
type: s.type,
number: s.number,
songName: s.songName,
artistName: s.artistName,
fileName: s.fileName ?? null,
}),
)
.filter((t) => t !== null);
}
function queueAll() {
addAllToQueue(playableTracks());
}
function playAllNextFromAnime() {
playAllNext(playableTracks());
}
</script>
{#if !clientDb}
<p class="mt-3 text-sm text-muted-foreground">Loading DB...</p>
{/if}
{#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).
</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>
<p class="text-sm text-muted-foreground">
{data.animeWithSongs.anime.year}
{seasonName(Number(data.animeWithSongs.anime.seasonId))}
</p>
<div class="flex flex-wrap items-center gap-2 text-sm">
<a
class="underline underline-offset-4 hover:no-underline text-muted-foreground hover:text-foreground"
href={`https://www.animenewsnetwork.com/encyclopedia/anime.php?id=${data.animeWithSongs.anime.annId}`}
target="_blank"
rel="noreferrer"
>
ANN
</a>
{#if data.animeWithSongs.anime.malId != null}
<a
class="underline underline-offset-4 hover:no-underline text-muted-foreground hover:text-foreground"
href={`https://myanimelist.net/anime/${data.animeWithSongs.anime.malId}/`}
target="_blank"
rel="noreferrer"
>
MAL
</a>
{:else}
<span
class="cursor-not-allowed select-none text-muted-foreground/60 underline underline-offset-4 decoration-dotted"
title="MAL link unavailable (missing MAL id)"
aria-disabled="true"
>
MAL
</span>
{/if}
{#if data.animeWithSongs.anime.aniListId != null}
<a
class="underline underline-offset-4 hover:no-underline text-muted-foreground hover:text-foreground"
href={`https://anilist.co/anime/${data.animeWithSongs.anime.aniListId}`}
target="_blank"
rel="noreferrer"
>
AniList
</a>
{:else}
<span
class="cursor-not-allowed select-none text-muted-foreground/60 underline underline-offset-4 decoration-dotted"
title="AniList link unavailable (missing AniList id)"
aria-disabled="true"
>
AniList
</span>
{/if}
</div>
</header>
<section class="mt-6">
<div class="flex flex-wrap items-center justify-between gap-3">
<h2 class="text-lg font-semibold">Songs</h2>
<div class="flex flex-wrap items-center gap-2">
<button
type="button"
class="inline-flex h-9 items-center justify-center rounded-md border px-3 text-sm font-medium hover:bg-accent hover:text-accent-foreground disabled:opacity-50"
onclick={queueAll}
disabled={data.animeWithSongs.songs.length === 0}
title="Add all playable songs from this anime to the end of the queue"
>
Queue all
</button>
<button
type="button"
class="inline-flex h-9 items-center justify-center rounded-md border px-3 text-sm font-medium hover:bg-accent hover:text-accent-foreground disabled:opacity-50"
onclick={playAllNextFromAnime}
disabled={data.animeWithSongs.songs.length === 0}
title="Insert all playable songs from this anime right after the current track"
>
Play all next
</button>
</div>
</div>
{#if data.animeWithSongs.songs.length === 0}
<p class="mt-2 text-sm text-muted-foreground">
No linked songs found for this anime.
</p>
{:else}
<ul class="mt-3 space-y-3">
{#each data.animeWithSongs.songs as s (s.annSongId)}
<li>
<SongEntry
annSongId={s.annSongId}
animeName={data.animeWithSongs.anime.mainName}
type={s.type}
number={s.number}
songName={s.songName}
artistName={s.artistName}
fileName={s.fileName}
globalPercent={s.globalPercent}
/>
</li>
{/each}
</ul>
{/if}
</section>
{/if}