songs page init

This commit is contained in:
2026-02-06 08:20:42 -08:00
parent 323c7c7f77
commit 0531a1f5c0
5 changed files with 520 additions and 1 deletions

View File

@@ -1,4 +1,14 @@
import { desc, eq, inArray, like } from "drizzle-orm";
import {
and,
between,
desc,
eq,
gte,
inArray,
like,
lte,
or,
} from "drizzle-orm";
import { anime, animeSongLinks, artists, groups, songs } from "$lib/db/schema";
import type { ClientDb } from "./index";
@@ -183,6 +193,113 @@ export async function getSongsForMalAnimeIds(
);
}
// Define interfaces for filters
export interface SongFilters {
songName?: string;
artistName?: string;
animeName?: string; // Searches mainName, mainNameEn, mainNameJa
songTypes?: number[]; // 1: OP, 2: ED, 3: INS
globalPercentMin?: number; // 0-100
globalPercentMax?: number; // 0-100
category?: number; // 0: none, 1: instrumental, 2: chanting, 3: character, 4: standard
}
export async function getSongsWithFilters(
db: ClientDb,
filters: SongFilters,
limit = DEFAULT_LIST_LIMIT,
) {
const safeLimit = clampLimit(limit);
const {
songName,
artistName,
animeName,
songTypes,
globalPercentMin,
globalPercentMax,
category,
} = filters;
const query = db
.select({
annSongId: songs.annSongId,
songName: songs.name,
fileName: songs.fileName,
globalPercent: songs.globalPercent,
category: songs.category,
type: animeSongLinks.type,
number: animeSongLinks.number,
animeAnnId: anime.annId,
animeMainName: anime.mainName,
animeMainNameEn: anime.mainNameEn,
animeMainNameJa: anime.mainNameJa,
artistName: artists.name,
groupName: groups.name,
})
.from(songs)
.leftJoin(artists, eq(artists.songArtistId, songs.songArtistId))
.leftJoin(groups, eq(groups.songGroupId, songs.songGroupId))
.innerJoin(animeSongLinks, eq(animeSongLinks.annSongId, songs.annSongId))
.innerJoin(anime, eq(anime.annId, animeSongLinks.annId))
.limit(safeLimit);
const conditions = [];
if (songName) {
conditions.push(like(songs.name, `%${songName}%`));
}
if (artistName) {
// Search artistName OR groupName
const artistPattern = `%${artistName}%`;
conditions.push(
or(like(artists.name, artistPattern), like(groups.name, artistPattern)),
);
}
if (animeName) {
// Search mainName, mainNameEn, or mainNameJa
const animePattern = `%${animeName}%`;
conditions.push(
or(
like(anime.mainName, animePattern),
like(anime.mainNameEn, animePattern),
like(anime.mainNameJa, animePattern),
),
);
}
if (songTypes && songTypes.length > 0) {
conditions.push(inArray(animeSongLinks.type, songTypes));
}
if (globalPercentMin !== undefined && globalPercentMax !== undefined) {
conditions.push(
between(songs.globalPercent, globalPercentMin, globalPercentMax),
);
} else if (globalPercentMin !== undefined) {
conditions.push(gte(songs.globalPercent, globalPercentMin));
} else if (globalPercentMax !== undefined) {
conditions.push(lte(songs.globalPercent, globalPercentMax));
}
if (category !== undefined) {
conditions.push(eq(songs.category, category));
}
if (conditions.length > 0) {
query.where(and(...conditions));
}
// Order by song name for now, can add more sophisticated sorting later
query.orderBy(songs.name);
return query.execute();
}
function clampLimit(limit: number) {
const n = Number(limit);
if (!Number.isFinite(n)) return DEFAULT_LIST_LIMIT;