Files
homarr/packages/api/src/router/widgets/audio-stats.ts
T
Thomas Camlong c9f003dadd feat: add Uptime Kuma, Audiobookshelf, Paperless-ngx, and Navidrome integrations
Add four new integrations with full widget support:

- Uptime Kuma: dashboard monitors, uptime percentage ring, status counts
- Audiobookshelf: library stats, audiobooks/podcasts counts, listening time
- Paperless-ngx: document stats, inbox ratio ring, correspondents/tags
- Navidrome: artist/album/song counts, now playing with track list

Also adds a unified "Audio Stats" widget that merges Navidrome and
Audiobookshelf into a single configurable widget with backend-specific
option visibility.

Includes Vitest specs for pagination, status mapping, and shared utils.
2026-06-09 18:09:22 +02:00

41 lines
1.8 KiB
TypeScript

import type { AudiobookshelfDashboardData, NavidromeDashboardData } from "@homarr/integrations/types";
import { audiobookshelfRequestHandler } from "@homarr/request-handler/audiobookshelf";
import { navidromeRequestHandler } from "@homarr/request-handler/navidrome";
import { createOneIntegrationMiddleware } from "../../middlewares/integration";
import { createTRPCRouter, publicProcedure } from "../../trpc";
const requestHandlerByKind = {
navidrome: navidromeRequestHandler,
audiobookshelf: audiobookshelfRequestHandler,
} as const;
export const audioStatsRouter = createTRPCRouter({
getStats: publicProcedure
.concat(createOneIntegrationMiddleware("query", "navidrome", "audiobookshelf"))
.query(async ({ ctx }) => {
const fetchStatsByKind = {
navidrome: async () => {
const navidromeIntegration = { ...ctx.integration, kind: "navidrome" as const };
const innerHandler = requestHandlerByKind.navidrome.handler(navidromeIntegration, {});
const data = await innerHandler.getCachedOrUpdatedDataAsync({ forceUpdate: false });
return {
kind: "navidrome" as const,
data: data.data as NavidromeDashboardData,
};
},
audiobookshelf: async () => {
const audiobookshelfIntegration = { ...ctx.integration, kind: "audiobookshelf" as const };
const innerHandler = requestHandlerByKind.audiobookshelf.handler(audiobookshelfIntegration, {});
const data = await innerHandler.getCachedOrUpdatedDataAsync({ forceUpdate: false });
return {
kind: "audiobookshelf" as const,
data: data.data as AudiobookshelfDashboardData,
};
},
} as const;
return fetchStatsByKind[ctx.integration.kind]();
}),
});