import { createQuery } from '@tanstack/svelte-query'; import { api } from './client'; import { qk } from './queries'; import type { AlbumRef, Page } from './types'; export const BROWSE_PAGE_SIZE = 50; // Genres are the raw ID3 strings, split on [;,] server-side but otherwise // untouched — no case folding, no synonym mapping. So "Rock" and "rock" can // both appear, as can "Rock/Pop" beside "Rock" and "Pop". Deliberate for v1: // the raw spread has to be visible before anyone can judge whether it needs // normalising. export type GenreCount = { genre: string; track_count: number }; export type YearCount = { year: number; album_count: number }; export async function listGenres(): Promise { return api.get('/api/library/genres'); } export async function listAlbumYears(): Promise { return api.get('/api/library/years'); } // The indexes use svelte-query: they're fetched once per page mount, so static // options are enough, and the cache means bouncing between browse and a // drill-down doesn't refetch. The drill-down lists below deliberately do NOT — // see the note on listAlbumsByGenre. export function createGenresQuery() { return createQuery({ queryKey: qk.genres(), queryFn: listGenres, // Genres only change when the library is rescanned. staleTime: 5 * 60_000 }); } export function createAlbumYearsQuery() { return createQuery({ queryKey: qk.albumYears(), queryFn: listAlbumYears, staleTime: 5 * 60_000 }); } // Genre travels as a QUERY parameter, never a path segment. Raw ID3 genres // contain slashes ("Rock/Pop" is a real tag), which a path segment cannot // carry — the server would see two segments, and a hard reload of such a URL // would not survive the SPA fallback either. // // Called directly rather than through createInfiniteQuery because the selected // genre comes from the URL and changes without remounting the page. This // codebase has no reactive-query-options pattern, and introducing one here // would be a larger change than the feature warrants. export async function listAlbumsByGenre( genre: string, limit: number, offset: number ): Promise> { const params = new URLSearchParams({ genre, limit: String(limit), offset: String(offset) }); return api.get>(`/api/library/albums?${params}`); } // Single-year drill-down. The server takes an inclusive range, so one year is // expressed as its own degenerate range rather than needing a separate shape. export async function listAlbumsByYear( year: number, limit: number, offset: number ): Promise> { const params = new URLSearchParams({ year_from: String(year), year_to: String(year), limit: String(limit), offset: String(offset) }); return api.get>(`/api/library/albums?${params}`); }