flesh out anime page
This commit is contained in:
@@ -30,6 +30,7 @@ export async function getAnimeList(db: ClientDb, limit = DEFAULT_LIST_LIMIT) {
|
|||||||
year: anime.year,
|
year: anime.year,
|
||||||
seasonId: anime.seasonId,
|
seasonId: anime.seasonId,
|
||||||
malId: anime.malId,
|
malId: anime.malId,
|
||||||
|
aniListId: anime.aniListId,
|
||||||
})
|
})
|
||||||
.from(anime)
|
.from(anime)
|
||||||
.orderBy(desc(anime.year), desc(anime.seasonId), desc(anime.annId))
|
.orderBy(desc(anime.year), desc(anime.seasonId), desc(anime.annId))
|
||||||
@@ -60,6 +61,7 @@ export async function searchAnimeByName(
|
|||||||
year: anime.year,
|
year: anime.year,
|
||||||
seasonId: anime.seasonId,
|
seasonId: anime.seasonId,
|
||||||
malId: anime.malId,
|
malId: anime.malId,
|
||||||
|
aniListId: anime.aniListId,
|
||||||
})
|
})
|
||||||
.from(anime)
|
.from(anime)
|
||||||
.where(like(anime.mainName, pattern))
|
.where(like(anime.mainName, pattern))
|
||||||
@@ -84,6 +86,7 @@ export async function getAnimeWithSongsByAnnId(db: ClientDb, annId: number) {
|
|||||||
year: anime.year,
|
year: anime.year,
|
||||||
seasonId: anime.seasonId,
|
seasonId: anime.seasonId,
|
||||||
malId: anime.malId,
|
malId: anime.malId,
|
||||||
|
aniListId: anime.aniListId,
|
||||||
})
|
})
|
||||||
.from(anime)
|
.from(anime)
|
||||||
.where(eq(anime.annId, annId))
|
.where(eq(anime.annId, annId))
|
||||||
@@ -142,6 +145,7 @@ export async function getSongsForMalAnimeIds(
|
|||||||
.select({
|
.select({
|
||||||
annId: anime.annId,
|
annId: anime.annId,
|
||||||
malId: anime.malId,
|
malId: anime.malId,
|
||||||
|
aniListId: anime.aniListId,
|
||||||
animeName: anime.mainName,
|
animeName: anime.mainName,
|
||||||
year: anime.year,
|
year: anime.year,
|
||||||
seasonId: anime.seasonId,
|
seasonId: anime.seasonId,
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
import { invalidate } from "$app/navigation";
|
import { invalidate } from "$app/navigation";
|
||||||
import SongEntry from "$lib/components/SongEntry.svelte";
|
import SongEntry from "$lib/components/SongEntry.svelte";
|
||||||
import { db as clientDb } from "$lib/db/client-db";
|
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 { seasonName } from "$lib/utils/amq";
|
||||||
import type { PageData } from "./$types";
|
import type { PageData } from "./$types";
|
||||||
|
|
||||||
@@ -21,6 +23,31 @@
|
|||||||
return;
|
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>
|
</script>
|
||||||
|
|
||||||
{#if !clientDb}
|
{#if !clientDb}
|
||||||
@@ -36,18 +63,89 @@
|
|||||||
{:else if !data.animeWithSongs}
|
{:else if !data.animeWithSongs}
|
||||||
<p class="mt-3 text-sm text-muted-foreground">Loading anime…</p>
|
<p class="mt-3 text-sm text-muted-foreground">Loading anime…</p>
|
||||||
{:else}
|
{: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>
|
<h1 class="text-2xl font-semibold">{data.animeWithSongs.anime.mainName}</h1>
|
||||||
|
|
||||||
<p class="text-sm text-muted-foreground">
|
<p class="text-sm text-muted-foreground">
|
||||||
{data.animeWithSongs.anime.year}{seasonName(
|
{data.animeWithSongs.anime.year}
|
||||||
Number(data.animeWithSongs.anime.seasonId),
|
{seasonName(Number(data.animeWithSongs.anime.seasonId))}
|
||||||
)} • ANN {data.animeWithSongs.anime.annId} • MAL {data.animeWithSongs
|
|
||||||
.anime.malId}
|
|
||||||
</p>
|
</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>
|
</header>
|
||||||
|
|
||||||
<section class="mt-6">
|
<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}
|
{#if data.animeWithSongs.songs.length === 0}
|
||||||
<p class="mt-2 text-sm text-muted-foreground">
|
<p class="mt-2 text-sm text-muted-foreground">
|
||||||
|
|||||||
Reference in New Issue
Block a user