syco.me Homelab Dashboard
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
import { Router } from 'express'
|
||||
import axios from 'axios'
|
||||
|
||||
const router = Router()
|
||||
|
||||
router.get('/calendar', async (req, res) => {
|
||||
const today = new Date()
|
||||
const defaultStart = new Date(today.getFullYear(), today.getMonth(), 1).toISOString().slice(0, 10)
|
||||
const defaultEnd = new Date(today.getFullYear(), today.getMonth() + 1, 0).toISOString().slice(0, 10)
|
||||
|
||||
const start = (req.query.start as string) || defaultStart
|
||||
const end = (req.query.end as string) || defaultEnd
|
||||
const calParams = { start, end, unmonitored: false }
|
||||
|
||||
const sonarrHeaders = process.env.SONARR_API_KEY ? { 'X-Api-Key': process.env.SONARR_API_KEY } : null
|
||||
|
||||
const results = await Promise.allSettled([
|
||||
process.env.RADARR_HOST && process.env.RADARR_API_KEY
|
||||
? axios.get(`${process.env.RADARR_HOST}/api/v3/calendar`, {
|
||||
headers: { 'X-Api-Key': process.env.RADARR_API_KEY },
|
||||
params: calParams,
|
||||
})
|
||||
: Promise.resolve(null),
|
||||
|
||||
process.env.SONARR_HOST && sonarrHeaders
|
||||
? axios.get(`${process.env.SONARR_HOST}/api/v3/calendar`, {
|
||||
headers: sonarrHeaders,
|
||||
params: calParams,
|
||||
})
|
||||
: Promise.resolve(null),
|
||||
|
||||
process.env.SONARR_HOST && sonarrHeaders
|
||||
? axios.get(`${process.env.SONARR_HOST}/api/v3/series`, {
|
||||
headers: sonarrHeaders,
|
||||
})
|
||||
: Promise.resolve(null),
|
||||
|
||||
process.env.LIDARR_HOST && process.env.LIDARR_API_KEY
|
||||
? axios.get(`${process.env.LIDARR_HOST}/api/v1/calendar`, {
|
||||
headers: { 'X-Api-Key': process.env.LIDARR_API_KEY },
|
||||
params: calParams,
|
||||
})
|
||||
: Promise.resolve(null),
|
||||
])
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const radarrData: any[] = results[0].status === 'fulfilled' && results[0].value?.data ? results[0].value.data : []
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const sonarrData: any[] = results[1].status === 'fulfilled' && results[1].value?.data ? results[1].value.data : []
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const seriesList: any[] = results[2].status === 'fulfilled' && results[2].value?.data ? results[2].value.data : []
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const lidarrData: any[] = results[3].status === 'fulfilled' && results[3].value?.data ? results[3].value.data : []
|
||||
|
||||
const seriesById: Record<number, string> = {}
|
||||
for (const s of seriesList) seriesById[s.id] = s.title
|
||||
|
||||
const items = [
|
||||
...radarrData.map(m => ({
|
||||
date: (m.digitalRelease ?? m.physicalRelease ?? m.inCinemas ?? '').slice(0, 10),
|
||||
title: m.title,
|
||||
subtitle: m.year ? String(m.year) : '',
|
||||
type: 'movie' as const,
|
||||
downloaded: m.hasFile ?? false,
|
||||
})).filter(m => m.date),
|
||||
|
||||
...sonarrData.map(e => ({
|
||||
date: (e.airDate ?? '').slice(0, 10),
|
||||
title: seriesById[e.seriesId] ?? e.title,
|
||||
subtitle: `S${String(e.seasonNumber).padStart(2,'0')}E${String(e.episodeNumber).padStart(2,'0')}${e.title && e.title !== 'TBA' ? ` · ${e.title}` : ''}`,
|
||||
type: 'episode' as const,
|
||||
downloaded: e.hasFile ?? false,
|
||||
})).filter(e => e.date),
|
||||
|
||||
...lidarrData.map(a => ({
|
||||
date: (a.releaseDate ?? '').slice(0, 10),
|
||||
title: a.artist?.artistName ?? '',
|
||||
subtitle: a.title,
|
||||
type: 'album' as const,
|
||||
downloaded: a.statistics?.trackFileCount > 0,
|
||||
})).filter(a => a.date),
|
||||
].sort((a, b) => a.date.localeCompare(b.date))
|
||||
|
||||
res.json({ items })
|
||||
})
|
||||
|
||||
router.get('/stats', async (_req, res) => {
|
||||
const results = await Promise.allSettled([
|
||||
process.env.RADARR_HOST && process.env.RADARR_API_KEY ? Promise.all([
|
||||
axios.get(`${process.env.RADARR_HOST}/api/v3/movie`, { headers: { 'X-Api-Key': process.env.RADARR_API_KEY } }),
|
||||
axios.get(`${process.env.RADARR_HOST}/api/v3/queue/status`, { headers: { 'X-Api-Key': process.env.RADARR_API_KEY } }),
|
||||
]) : Promise.resolve(null),
|
||||
|
||||
process.env.SONARR_HOST && process.env.SONARR_API_KEY ? Promise.all([
|
||||
axios.get(`${process.env.SONARR_HOST}/api/v3/series`, { headers: { 'X-Api-Key': process.env.SONARR_API_KEY } }),
|
||||
axios.get(`${process.env.SONARR_HOST}/api/v3/wanted/missing?pageSize=1`, { headers: { 'X-Api-Key': process.env.SONARR_API_KEY } }),
|
||||
axios.get(`${process.env.SONARR_HOST}/api/v3/queue/status`, { headers: { 'X-Api-Key': process.env.SONARR_API_KEY } }),
|
||||
]) : Promise.resolve(null),
|
||||
|
||||
process.env.LIDARR_HOST && process.env.LIDARR_API_KEY ? Promise.all([
|
||||
axios.get(`${process.env.LIDARR_HOST}/api/v1/artist`, { headers: { 'X-Api-Key': process.env.LIDARR_API_KEY } }),
|
||||
axios.get(`${process.env.LIDARR_HOST}/api/v1/wanted/missing?pageSize=1`, { headers: { 'X-Api-Key': process.env.LIDARR_API_KEY } }),
|
||||
axios.get(`${process.env.LIDARR_HOST}/api/v1/queue/status`, { headers: { 'X-Api-Key': process.env.LIDARR_API_KEY } }),
|
||||
]) : Promise.resolve(null),
|
||||
])
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const radarr = results[0].status === 'fulfilled' && results[0].value ? results[0].value as any[] : null
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const sonarr = results[1].status === 'fulfilled' && results[1].value ? results[1].value as any[] : null
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const lidarr = results[2].status === 'fulfilled' && results[2].value ? results[2].value as any[] : null
|
||||
|
||||
res.json({
|
||||
radarr: radarr ? {
|
||||
movies: radarr[0].data.length,
|
||||
missing: radarr[0].data.filter((m: { hasFile: boolean; monitored: boolean }) => !m.hasFile && m.monitored).length,
|
||||
queue: radarr[1].data.totalCount ?? 0,
|
||||
} : null,
|
||||
sonarr: sonarr ? {
|
||||
series: sonarr[0].data.length,
|
||||
missing: sonarr[1].data.totalRecords ?? 0,
|
||||
queue: sonarr[2].data.totalCount ?? 0,
|
||||
} : null,
|
||||
lidarr: lidarr ? {
|
||||
artists: lidarr[0].data.length,
|
||||
missing: lidarr[1].data.totalRecords ?? 0,
|
||||
queue: lidarr[2].data.totalCount ?? 0,
|
||||
} : null,
|
||||
})
|
||||
})
|
||||
|
||||
export default router
|
||||
Reference in New Issue
Block a user