list page pt. 10 song entry component

This commit is contained in:
2026-02-05 23:28:35 -08:00
parent dcf426ac52
commit 7ffb71bc40
2 changed files with 98 additions and 26 deletions

View File

@@ -0,0 +1,87 @@
<script lang="ts">
type SongType = 1 | 2 | 3 | number;
type SongEntryProps = {
animeName: string;
type: SongType;
number: number;
songName: string;
artistName: string | null;
fileName?: string | null;
showPlayer?: boolean;
};
let {
animeName,
type,
number,
songName,
artistName,
fileName = null,
showPlayer = false,
}: SongEntryProps = $props();
const typeLabelMap: Record<number, string> = {
1: "OP",
2: "ED",
3: "INS",
};
const typeLabel = $derived(typeLabelMap[type] ?? `T${type}`);
const displayTypeNumber = $derived.by(() => {
const num = Number(number);
if (!Number.isFinite(num) || num === 0) return typeLabel;
return `${typeLabel}${num}`;
});
const artistDisplay = $derived.by(
() => artistName?.trim() || "Unknown Artist",
);
const mediaTitle = $derived.by(() => `${animeName}${displayTypeNumber}`);
const mediaArtist = $derived.by(() => artistDisplay);
const mediaAlbum = $derived.by(() => `${animeName} (${displayTypeNumber})`);
$effect(() => {
if (!showPlayer || !fileName) return;
if (typeof navigator === "undefined") return;
const mediaSession = navigator.mediaSession;
if (!mediaSession || typeof MediaMetadata === "undefined") return;
mediaSession.metadata = new MediaMetadata({
title: songName,
artist: mediaArtist,
album: mediaAlbum,
});
});
</script>
<div class="rounded border px-3 py-2">
<div class="flex flex-wrap items-baseline gap-x-2 gap-y-1">
<span class="text-sm text-muted-foreground">{animeName}</span>
<span class="text-sm text-muted-foreground"></span>
<span class="text-sm text-muted-foreground">{displayTypeNumber}</span>
</div>
<div class="mt-1 font-medium">
{songName}
<span class="text-sm text-muted-foreground">{artistDisplay}</span>
</div>
{#if showPlayer && fileName}
<div class="mt-2">
<audio
class="w-full"
controls
preload="metadata"
title={`${mediaTitle}${songName}${mediaArtist}`}
>
<source src={`/cdn/${fileName}`} type="audio/mpeg" />
Your browser does not support the audio element.
</audio>
</div>
{/if}
</div>

View File

@@ -3,6 +3,7 @@
import { onMount } from "svelte";
import { z } from "zod";
import { invalidate } from "$app/navigation";
import SongEntry from "$lib/components/SongEntry.svelte";
import { db as clientDb } from "$lib/db/client-db";
import {
MalAnimeListQuerySchema,
@@ -146,32 +147,16 @@
<ul class="mt-3 space-y-2">
{#each data.songRows as r (String(r.annId) + ":" + String(r.annSongId))}
<li class="rounded border px-3 py-2">
<div class="font-medium">
{r.songName}
{#if songArtistLabel(r)}
<span class="text-sm text-muted-foreground">
{songArtistLabel(r)}</span
>
{/if}
</div>
<div class="text-sm text-muted-foreground">
{r.animeName} • ANN {r.annId} • MAL {r.malId} • link type {r.type} #{r.number}
</div>
{#if r.fileName}
<div class="mt-2 text-sm">
<a
class="hover:underline"
href={"https://nawdist.animemusicquiz.com/" + r.fileName}
target="_blank"
rel="noreferrer"
>
Audio file
</a>
</div>
{/if}
<li>
<SongEntry
animeName={r.animeName}
type={r.type}
number={r.number}
songName={r.songName}
artistName={songArtistLabel(r)}
fileName={r.fileName}
showPlayer={true}
/>
</li>
{/each}
</ul>