89fd54b3dc
First Push to giTea
73 lines
2.7 KiB
TypeScript
73 lines
2.7 KiB
TypeScript
import { Router } from 'express'
|
|
import axios from 'axios'
|
|
import crypto from 'crypto'
|
|
|
|
const router = Router()
|
|
|
|
let cachedToken: string | null = null
|
|
let tokenExpiry = 0
|
|
let tokenPromise: Promise<string> | null = null
|
|
|
|
async function getToken(host: string, user: string, pass: string): Promise<string> {
|
|
if (cachedToken && Date.now() < tokenExpiry) return cachedToken
|
|
// Deduplicate concurrent login attempts
|
|
if (tokenPromise) return tokenPromise
|
|
tokenPromise = axios.post(`${host}/auth/login`, { username: user, password: pass })
|
|
.then(res => {
|
|
cachedToken = res.data.token
|
|
tokenExpiry = Date.now() + 55 * 60 * 1000
|
|
tokenPromise = null
|
|
return cachedToken!
|
|
})
|
|
.catch(err => {
|
|
tokenPromise = null
|
|
throw err
|
|
})
|
|
return tokenPromise
|
|
}
|
|
|
|
router.get('/status', async (_req, res) => {
|
|
const host = process.env.NAVIDROME_HOST
|
|
const user = process.env.NAVIDROME_USER
|
|
const pass = process.env.NAVIDROME_PASSWORD
|
|
if (!host || !user || !pass)
|
|
return res.status(503).json({ error: 'Navidrome not configured' })
|
|
|
|
try {
|
|
const jwtToken = await getToken(host, user, pass)
|
|
const headers = { 'X-ND-Authorization': `Bearer ${jwtToken}` }
|
|
|
|
// Use Subsonic API for now playing (standard endpoint)
|
|
const salt = 'sycoDash'
|
|
const md5Token = crypto.createHash('md5').update(pass + salt).digest('hex')
|
|
const subParams = { u: user, t: md5Token, s: salt, v: '1.16.1', c: 'syco-dashboard', f: 'json' }
|
|
|
|
const [artistRes, albumRes, songRes, nowPlayingRes] = await Promise.all([
|
|
axios.get(`${host}/api/artist`, { headers, params: { _start: 0, _end: 1 } }),
|
|
axios.get(`${host}/api/album`, { headers, params: { _start: 0, _end: 1 } }),
|
|
axios.get(`${host}/api/song`, { headers, params: { _start: 0, _end: 1 } }),
|
|
axios.get(`${host}/rest/getNowPlaying.view`, { params: subParams }).catch(() => null),
|
|
])
|
|
|
|
const artistCount = Number(artistRes.headers['x-total-count'] ?? 0)
|
|
const albumCount = Number(albumRes.headers['x-total-count'] ?? 0)
|
|
const songCount = Number(songRes.headers['x-total-count'] ?? 0)
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
const nowPlayingEntries: any[] = nowPlayingRes?.data?.['subsonic-response']?.nowPlaying?.entry ?? []
|
|
const entries = Array.isArray(nowPlayingEntries) ? nowPlayingEntries : [nowPlayingEntries]
|
|
const nowPlaying = entries.filter(Boolean).map((e: any) => ({
|
|
user: e.username ?? 'Unknown',
|
|
title: e.title ?? '',
|
|
artist: e.artist ?? '',
|
|
album: e.album ?? '',
|
|
}))
|
|
|
|
res.json({ artistCount, albumCount, songCount, nowPlaying })
|
|
} catch (err: any) {
|
|
res.status(502).json({ error: err.message ?? 'Navidrome error' })
|
|
}
|
|
})
|
|
|
|
export default router
|