list page pt. 10 song entry component
This commit is contained in:
87
src/lib/components/SongEntry.svelte
Normal file
87
src/lib/components/SongEntry.svelte
Normal 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>
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { invalidate } from "$app/navigation";
|
import { invalidate } from "$app/navigation";
|
||||||
|
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 {
|
import {
|
||||||
MalAnimeListQuerySchema,
|
MalAnimeListQuerySchema,
|
||||||
@@ -146,32 +147,16 @@
|
|||||||
|
|
||||||
<ul class="mt-3 space-y-2">
|
<ul class="mt-3 space-y-2">
|
||||||
{#each data.songRows as r (String(r.annId) + ":" + String(r.annSongId))}
|
{#each data.songRows as r (String(r.annId) + ":" + String(r.annSongId))}
|
||||||
<li class="rounded border px-3 py-2">
|
<li>
|
||||||
<div class="font-medium">
|
<SongEntry
|
||||||
{r.songName}
|
animeName={r.animeName}
|
||||||
{#if songArtistLabel(r)}
|
type={r.type}
|
||||||
<span class="text-sm text-muted-foreground">
|
number={r.number}
|
||||||
— {songArtistLabel(r)}</span
|
songName={r.songName}
|
||||||
>
|
artistName={songArtistLabel(r)}
|
||||||
{/if}
|
fileName={r.fileName}
|
||||||
</div>
|
showPlayer={true}
|
||||||
|
/>
|
||||||
<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>
|
</li>
|
||||||
{/each}
|
{/each}
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
Reference in New Issue
Block a user