import { z } from 'zod' import { ffetch as ffetchBase } from './fetch.ts' import { getEnv } from './misc.ts' let _cachedFfetch: typeof ffetchBase | undefined export async function getNavidromeFfetch() { if (_cachedFfetch) return _cachedFfetch const baseUrl = getEnv('NAVIDROME_ENDPOINT') const authRes = await ffetchBase.post('/auth/login', { baseUrl, json: { username: getEnv('NAVIDROME_USERNAME'), password: getEnv('NAVIDROME_PASSWORD'), }, }).parsedJson(z.object({ token: z.string() })) _cachedFfetch = ffetchBase.extend({ baseUrl, headers: { 'x-nd-authorization': `Bearer ${authRes.token}`, }, }) return _cachedFfetch } export const NavidromeSong = z.object({ id: z.string(), title: z.string(), album: z.string(), albumArtist: z.string(), artist: z.string(), path: z.string(), libraryPath: z.string(), duration: z.number(), size: z.number(), }) export type NavidromeSong = z.infer export async function fetchSongs(offset: number, pageSize: number) { const api = await getNavidromeFfetch() return api('/api/song', { query: { _start: offset, _end: offset + pageSize, _order: 'ASC', _sort: 'title', }, }).parsedJson(z.array(NavidromeSong)) } export async function* fetchSongsIter(params?: { chunkSize?: number onChunkProcessed?: (page: number, items: number) => void }) { const { chunkSize = 1000, onChunkProcessed } = params ?? {} for (let offset = 0; ; offset += chunkSize) { const songs = await fetchSongs(offset, chunkSize) if (songs.length === 0) return yield * songs onChunkProcessed?.(Math.floor(offset / chunkSize), songs.length) } }