flesh out anime page

This commit is contained in:
2026-02-06 05:02:05 -08:00
parent 82714100e4
commit 665eb31775
2 changed files with 108 additions and 6 deletions

View File

@@ -30,6 +30,7 @@ export async function getAnimeList(db: ClientDb, limit = DEFAULT_LIST_LIMIT) {
year: anime.year,
seasonId: anime.seasonId,
malId: anime.malId,
aniListId: anime.aniListId,
})
.from(anime)
.orderBy(desc(anime.year), desc(anime.seasonId), desc(anime.annId))
@@ -60,6 +61,7 @@ export async function searchAnimeByName(
year: anime.year,
seasonId: anime.seasonId,
malId: anime.malId,
aniListId: anime.aniListId,
})
.from(anime)
.where(like(anime.mainName, pattern))
@@ -84,6 +86,7 @@ export async function getAnimeWithSongsByAnnId(db: ClientDb, annId: number) {
year: anime.year,
seasonId: anime.seasonId,
malId: anime.malId,
aniListId: anime.aniListId,
})
.from(anime)
.where(eq(anime.annId, annId))
@@ -142,6 +145,7 @@ export async function getSongsForMalAnimeIds(
.select({
annId: anime.annId,
malId: anime.malId,
aniListId: anime.aniListId,
animeName: anime.mainName,
year: anime.year,
seasonId: anime.seasonId,

View File

@@ -3,6 +3,8 @@
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";
@@ -21,6 +23,31 @@
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}
@@ -36,18 +63,89 @@
{:else if !data.animeWithSongs}
<p class="mt-3 text-sm text-muted-foreground">Loading anime…</p>
{:else}
<header class="mt-2 space-y-1">
<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),
)} • ANN {data.animeWithSongs.anime.annId} • MAL {data.animeWithSongs
.anime.malId}
{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">
<h2 class="text-lg font-semibold">Songs</h2>
<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">