48 lines
1.6 KiB
TypeScript
48 lines
1.6 KiB
TypeScript
import crypto from 'crypto'
|
|
|
|
const NAVIDROME_URL = process.env.NAVIDROME_URL || 'https://music.jefflix.lol'
|
|
const NAVIDROME_USER = process.env.NAVIDROME_USER || ''
|
|
const NAVIDROME_PASS = process.env.NAVIDROME_PASS || ''
|
|
|
|
function subsonicAuth() {
|
|
const salt = crypto.randomBytes(6).toString('hex')
|
|
const token = crypto.createHash('md5').update(NAVIDROME_PASS + salt).digest('hex')
|
|
return { u: NAVIDROME_USER, t: token, s: salt, v: '1.16.1', c: 'jefflix', f: 'json' }
|
|
}
|
|
|
|
function buildUrl(path: string, params: Record<string, string> = {}) {
|
|
const auth = subsonicAuth()
|
|
const url = new URL(`/rest/${path}`, NAVIDROME_URL)
|
|
for (const [k, v] of Object.entries({ ...auth, ...params })) {
|
|
url.searchParams.set(k, v)
|
|
}
|
|
return url.toString()
|
|
}
|
|
|
|
/** JSON response from Subsonic API (parsed) */
|
|
export async function navidromeGet<T = Record<string, unknown>>(
|
|
path: string,
|
|
params: Record<string, string> = {}
|
|
): Promise<T> {
|
|
const url = buildUrl(path, params)
|
|
const res = await fetch(url, { cache: 'no-store' })
|
|
if (!res.ok) throw new Error(`Navidrome ${path} returned ${res.status}`)
|
|
const json = await res.json()
|
|
const sub = json['subsonic-response']
|
|
if (sub?.status !== 'ok') {
|
|
throw new Error(sub?.error?.message || `Navidrome error on ${path}`)
|
|
}
|
|
return sub as T
|
|
}
|
|
|
|
/** Raw binary response (for streaming audio, cover art) */
|
|
export async function navidromeFetch(
|
|
path: string,
|
|
params: Record<string, string> = {}
|
|
): Promise<Response> {
|
|
const url = buildUrl(path, params)
|
|
const res = await fetch(url, { cache: 'no-store' })
|
|
if (!res.ok) throw new Error(`Navidrome ${path} returned ${res.status}`)
|
|
return res
|
|
}
|