teidesu-scripts/utils/navidrome.ts
2025-05-09 06:46:50 +00:00

64 lines
1.7 KiB
TypeScript

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<typeof NavidromeSong>
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)
}
}