flesh out main page
This commit is contained in:
@@ -31,6 +31,10 @@ export async function getAnimeList(db: ClientDb, limit = DEFAULT_LIST_LIMIT) {
|
|||||||
seasonId: anime.seasonId,
|
seasonId: anime.seasonId,
|
||||||
malId: anime.malId,
|
malId: anime.malId,
|
||||||
aniListId: anime.aniListId,
|
aniListId: anime.aniListId,
|
||||||
|
|
||||||
|
opCount: anime.opCount,
|
||||||
|
edCount: anime.edCount,
|
||||||
|
insertCount: anime.insertCount,
|
||||||
})
|
})
|
||||||
.from(anime)
|
.from(anime)
|
||||||
.orderBy(desc(anime.year), desc(anime.seasonId), desc(anime.annId))
|
.orderBy(desc(anime.year), desc(anime.seasonId), desc(anime.annId))
|
||||||
@@ -62,6 +66,10 @@ export async function searchAnimeByName(
|
|||||||
seasonId: anime.seasonId,
|
seasonId: anime.seasonId,
|
||||||
malId: anime.malId,
|
malId: anime.malId,
|
||||||
aniListId: anime.aniListId,
|
aniListId: anime.aniListId,
|
||||||
|
|
||||||
|
opCount: anime.opCount,
|
||||||
|
edCount: anime.edCount,
|
||||||
|
insertCount: anime.insertCount,
|
||||||
})
|
})
|
||||||
.from(anime)
|
.from(anime)
|
||||||
.where(like(anime.mainName, pattern))
|
.where(like(anime.mainName, pattern))
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { ListPlus, SkipForward } from "@lucide/svelte";
|
||||||
import { Debounced } from "runed";
|
import { Debounced } from "runed";
|
||||||
import { useSearchParams } from "runed/kit";
|
import { useSearchParams } from "runed/kit";
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
@@ -8,6 +9,8 @@
|
|||||||
getClientDb,
|
getClientDb,
|
||||||
searchAnimeByName,
|
searchAnimeByName,
|
||||||
} from "$lib/db/client-db";
|
} from "$lib/db/client-db";
|
||||||
|
import { addAllToQueue, playAllNext } from "$lib/player/player.svelte";
|
||||||
|
import { trackFromSongRow } from "$lib/player/types";
|
||||||
import { AmqBrowseSearchSchema } from "$lib/types/search/amq-browse";
|
import { AmqBrowseSearchSchema } from "$lib/types/search/amq-browse";
|
||||||
import { seasonName } from "$lib/utils/amq";
|
import { seasonName } from "$lib/utils/amq";
|
||||||
|
|
||||||
@@ -49,6 +52,63 @@
|
|||||||
return `/anime/${annId}`;
|
return `/anime/${annId}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function queueAllForAnime(a: AnimeItem) {
|
||||||
|
// NOTE: This expects your list query to include `opCount`, `edCount`, and `insertCount`.
|
||||||
|
// If the DB query doesn't select those fields yet, TypeScript will complain until you update it.
|
||||||
|
const { db } = getClientDb();
|
||||||
|
|
||||||
|
// Fetch all songs for this anime and add playable ones to the queue.
|
||||||
|
// We reuse the existing `getAnimeWithSongsByAnnId` helper if it exists in `$lib/db/client-db`.
|
||||||
|
// If your barrel export doesn't include it, you'll need to export it there (outside this file).
|
||||||
|
const { getAnimeWithSongsByAnnId } = await import("$lib/db/client-db");
|
||||||
|
const res = await getAnimeWithSongsByAnnId(db, a.annId);
|
||||||
|
if (!res) return;
|
||||||
|
|
||||||
|
const tracks = res.songs
|
||||||
|
.map((s) =>
|
||||||
|
trackFromSongRow({
|
||||||
|
annSongId: s.annSongId,
|
||||||
|
animeName: res.anime.mainName,
|
||||||
|
type: s.type,
|
||||||
|
number: s.number,
|
||||||
|
songName: s.songName,
|
||||||
|
artistName: s.artistName,
|
||||||
|
fileName: s.fileName ?? null,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.filter((t) => t !== null);
|
||||||
|
|
||||||
|
addAllToQueue(tracks);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function playAllNextForAnime(a: AnimeItem) {
|
||||||
|
const { db } = getClientDb();
|
||||||
|
|
||||||
|
const { getAnimeWithSongsByAnnId } = await import("$lib/db/client-db");
|
||||||
|
const res = await getAnimeWithSongsByAnnId(db, a.annId);
|
||||||
|
if (!res) return;
|
||||||
|
|
||||||
|
const tracks = res.songs
|
||||||
|
.map((s) =>
|
||||||
|
trackFromSongRow({
|
||||||
|
annSongId: s.annSongId,
|
||||||
|
animeName: res.anime.mainName,
|
||||||
|
type: s.type,
|
||||||
|
number: s.number,
|
||||||
|
songName: s.songName,
|
||||||
|
artistName: s.artistName,
|
||||||
|
fileName: s.fileName ?? null,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.filter((t) => t !== null);
|
||||||
|
|
||||||
|
playAllNext(tracks);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
void loadInitial();
|
||||||
|
});
|
||||||
|
|
||||||
async function loadInitial() {
|
async function loadInitial() {
|
||||||
status = "loading";
|
status = "loading";
|
||||||
error = null;
|
error = null;
|
||||||
@@ -63,10 +123,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
void loadInitial();
|
|
||||||
});
|
|
||||||
|
|
||||||
// React to debounced query changes (URL updates + user typing)
|
// React to debounced query changes (URL updates + user typing)
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (status !== "ready") return;
|
if (status !== "ready") return;
|
||||||
@@ -117,12 +173,43 @@
|
|||||||
<ul class="mt-4 space-y-2">
|
<ul class="mt-4 space-y-2">
|
||||||
{#each anime as a (a.annId)}
|
{#each anime as a (a.annId)}
|
||||||
<li class="rounded border px-3 py-2">
|
<li class="rounded border px-3 py-2">
|
||||||
<a class="font-medium hover:underline" href={animeHref(a.annId)}>
|
<div class="flex flex-wrap items-center justify-between gap-3">
|
||||||
{a.mainName}
|
<div class="min-w-0">
|
||||||
</a>
|
<a class="font-medium hover:underline" href={animeHref(a.annId)}>
|
||||||
<div class="text-sm text-muted-foreground">
|
{a.mainName}
|
||||||
{a.year}
|
</a>
|
||||||
{seasonName(a.seasonId)} • ANN {a.annId} • MAL {a.malId}
|
<div class="text-sm text-muted-foreground">
|
||||||
|
{a.year}
|
||||||
|
{seasonName(a.seasonId)} • ANN {a.annId} • MAL {a.malId}
|
||||||
|
</div>
|
||||||
|
<div class="mt-1 text-sm text-muted-foreground">
|
||||||
|
OP {a.opCount} • ED {a.edCount} • INS {a.insertCount}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex shrink-0 items-center gap-1">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn-icon"
|
||||||
|
onclick={() => void playAllNextForAnime(a)}
|
||||||
|
disabled={a.opCount + a.edCount + a.insertCount === 0}
|
||||||
|
title="Play all next"
|
||||||
|
aria-label="Play all next"
|
||||||
|
>
|
||||||
|
<SkipForward class="icon-btn" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn-icon"
|
||||||
|
onclick={() => void queueAllForAnime(a)}
|
||||||
|
disabled={a.opCount + a.edCount + a.insertCount === 0}
|
||||||
|
title="Queue all"
|
||||||
|
aria-label="Queue all"
|
||||||
|
>
|
||||||
|
<ListPlus class="icon-btn" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
{/each}
|
{/each}
|
||||||
|
|||||||
Reference in New Issue
Block a user