fix: Tdarr widget empty statistics tab (#2883)

Co-authored-by: Manuel <30572287+manuel-rw@users.noreply.github.com>
This commit is contained in:
rezstje
2025-04-16 20:34:06 +02:00
committed by GitHub
parent 00053bda7b
commit 2d12c1e641
4 changed files with 60 additions and 78 deletions

View File

@@ -4,6 +4,7 @@ export interface TdarrPieSegment {
} }
export interface TdarrStatistics { export interface TdarrStatistics {
libraryName: string;
totalFileCount: number; totalFileCount: number;
totalTranscodeCount: number; totalTranscodeCount: number;
totalHealthCheckCount: number; totalHealthCheckCount: number;
@@ -11,19 +12,12 @@ export interface TdarrStatistics {
failedHealthCheckCount: number; failedHealthCheckCount: number;
stagedTranscodeCount: number; stagedTranscodeCount: number;
stagedHealthCheckCount: number; stagedHealthCheckCount: number;
pies: { totalSavedSpace: number;
libraryName: string; transcodeStatus: TdarrPieSegment[];
libraryId: string; healthCheckStatus: TdarrPieSegment[];
totalFiles: number; videoCodecs: TdarrPieSegment[];
totalTranscodes: number; videoContainers: TdarrPieSegment[];
savedSpace: number; videoResolutions: TdarrPieSegment[];
totalHealthChecks: number; audioCodecs: TdarrPieSegment[];
transcodeStatus: TdarrPieSegment[]; audioContainers: TdarrPieSegment[];
healthCheckStatus: TdarrPieSegment[];
videoCodecs: TdarrPieSegment[];
videoContainers: TdarrPieSegment[];
videoResolutions: TdarrPieSegment[];
audioCodecs: TdarrPieSegment[];
audioContainers: TdarrPieSegment[];
}[];
} }

View File

@@ -20,15 +20,13 @@ export class TdarrIntegration extends Integration {
} }
public async getStatisticsAsync(): Promise<TdarrStatistics> { public async getStatisticsAsync(): Promise<TdarrStatistics> {
const url = this.url("/api/v2/cruddb"); const url = this.url("/api/v2/stats/get-pies");
const response = await fetchWithTrustedCertificatesAsync(url, { const response = await fetchWithTrustedCertificatesAsync(url, {
method: "POST", method: "POST",
headers: { "content-type": "application/json" }, headers: { accept: "application/json", "Content-Type": "application/json" },
body: JSON.stringify({ body: JSON.stringify({
data: { data: {
collection: "StatisticsJSONDB", libraryId: "", // empty string to get all libraries
mode: "getById",
docID: "statistics",
}, },
}), }),
}); });
@@ -36,28 +34,29 @@ export class TdarrIntegration extends Integration {
const statisticsData = await getStatisticsSchema.parseAsync(await response.json()); const statisticsData = await getStatisticsSchema.parseAsync(await response.json());
return { return {
totalFileCount: statisticsData.totalFileCount, libraryName: "All",
totalTranscodeCount: statisticsData.totalTranscodeCount, totalFileCount: statisticsData.pieStats.totalFiles,
totalHealthCheckCount: statisticsData.totalHealthCheckCount, totalTranscodeCount: statisticsData.pieStats.totalTranscodeCount,
failedTranscodeCount: statisticsData.table3Count, totalHealthCheckCount: statisticsData.pieStats.totalHealthCheckCount,
failedHealthCheckCount: statisticsData.table6Count, // The Tdarr API only returns a category if there is at least one item in it
stagedTranscodeCount: statisticsData.table1Count, failedTranscodeCount:
stagedHealthCheckCount: statisticsData.table4Count, statisticsData.pieStats.status.transcode.find((transcode) => transcode.name === "Transcode error")?.value ?? 0,
pies: statisticsData.pies.map((pie) => ({ failedHealthCheckCount:
libraryName: pie[0], statisticsData.pieStats.status.healthcheck.find((healthcheck) => healthcheck.name === "Error")?.value ?? 0,
libraryId: pie[1], stagedTranscodeCount:
totalFiles: pie[2], statisticsData.pieStats.status.transcode.find((transcode) => transcode.name === "Transcode success")?.value ??
totalTranscodes: pie[3], 0,
savedSpace: pie[4] * 1_000_000_000, // file_size is in GB, convert to bytes, stagedHealthCheckCount:
totalHealthChecks: pie[5], statisticsData.pieStats.status.healthcheck.find((healthcheck) => healthcheck.name === "Queued")?.value ?? 0,
transcodeStatus: pie[6],
healthCheckStatus: pie[7], totalSavedSpace: statisticsData.pieStats.sizeDiff * 1_000_000_000, // sizeDiff is in GB, convert to bytes
videoCodecs: pie[8], transcodeStatus: statisticsData.pieStats.status.transcode,
videoContainers: pie[9], healthCheckStatus: statisticsData.pieStats.status.healthcheck,
videoResolutions: pie[10], videoCodecs: statisticsData.pieStats.video.codecs,
audioCodecs: pie[11], videoContainers: statisticsData.pieStats.video.containers,
audioContainers: pie[12], videoResolutions: statisticsData.pieStats.video.resolutions,
})), audioCodecs: statisticsData.pieStats.audio.codecs,
audioContainers: statisticsData.pieStats.audio.containers,
}; };
} }
@@ -124,7 +123,7 @@ export class TdarrIntegration extends Integration {
healthCheck: item.HealthCheck, healthCheck: item.HealthCheck,
transcode: item.TranscodeDecisionMaker, transcode: item.TranscodeDecisionMaker,
filePath: item.file, filePath: item.file,
fileSize: item.file_size * 1_000_000, // file_size is in MB, convert to bytes fileSize: Math.floor(item.file_size * 1_000_000), // file_size is in MB, convert to bytes, floor because it returns as float
container: item.container, container: item.container,
codec: item.video_codec_name, codec: item.video_codec_name,
resolution: item.video_resolution, resolution: item.video_resolution,
@@ -162,7 +161,7 @@ export class TdarrIntegration extends Integration {
healthCheck: item.HealthCheck, healthCheck: item.HealthCheck,
transcode: item.TranscodeDecisionMaker, transcode: item.TranscodeDecisionMaker,
filePath: item.file, filePath: item.file,
fileSize: item.file_size * 1_000_000, // file_size is in MB, convert to bytes fileSize: Math.floor(item.file_size * 1_000_000), // file_size is in MB, convert to bytes, floor because it returns as float
container: item.container, container: item.container,
codec: item.video_codec_name, codec: item.video_codec_name,
resolution: item.video_resolution, resolution: item.video_resolution,

View File

@@ -1,72 +1,60 @@
import { z } from "zod"; import { z } from "zod";
export const getStatisticsSchema = z.object({ export const getStatisticsSchema = z.object({
totalFileCount: z.number(), pieStats: z.object({
totalTranscodeCount: z.number(), totalFiles: z.number(),
totalHealthCheckCount: z.number(), totalTranscodeCount: z.number(),
table3Count: z.number(), sizeDiff: z.number(),
table6Count: z.number(), totalHealthCheckCount: z.number(),
table1Count: z.number(), status: z.object({
table4Count: z.number(), transcode: z.array(
pies: z.array(
z.tuple([
z.string(), // Library Name
z.string(), // Library ID
z.number(), // File count
z.number(), // Number of transcodes
z.number(), // Space saved (in GB)
z.number(), // Number of health checks
z.array(
z.object({ z.object({
// Transcode Status (Pie segments)
name: z.string(), name: z.string(),
value: z.number(), value: z.number(),
}), }),
), ),
z.array( healthcheck: z.array(
z.object({ z.object({
// Health Status (Pie segments)
name: z.string(), name: z.string(),
value: z.number(), value: z.number(),
}), }),
), ),
z.array( }),
video: z.object({
codecs: z.array(
z.object({ z.object({
// Video files - Codecs (Pie segments)
name: z.string(), name: z.string(),
value: z.number(), value: z.number(),
}), }),
), ),
z.array( containers: z.array(
z.object({ z.object({
// Video files - Containers (Pie segments)
name: z.string(), name: z.string(),
value: z.number(), value: z.number(),
}), }),
), ),
z.array( resolutions: z.array(
z.object({ z.object({
// Video files - Resolutions (Pie segments)
name: z.string(), name: z.string(),
value: z.number(), value: z.number(),
}), }),
), ),
z.array( }),
audio: z.object({
codecs: z.array(
z.object({ z.object({
// Audio files - Codecs (Pie segments)
name: z.string(), name: z.string(),
value: z.number(), value: z.number(),
}), }),
), ),
z.array( containers: z.array(
z.object({ z.object({
// Audio files - Containers (Pie segments)
name: z.string(), name: z.string(),
value: z.number(), value: z.number(),
}), }),
), ),
]), }),
), }),
}); });
export const getNodesResponseSchema = z.record( export const getNodesResponseSchema = z.record(

View File

@@ -17,9 +17,10 @@ interface StatisticsPanelProps {
export function StatisticsPanel(props: StatisticsPanelProps) { export function StatisticsPanel(props: StatisticsPanelProps) {
const t = useI18n("widget.mediaTranscoding.panel.statistics"); const t = useI18n("widget.mediaTranscoding.panel.statistics");
const allLibs = props.statistics.pies.find((pie) => pie.libraryName === "All"); const allLibs = props.statistics;
if (!allLibs) { // Check if Tdarr hs any Files
if (!(allLibs.totalFileCount > 0)) {
return ( return (
<Center style={{ flex: "1" }}> <Center style={{ flex: "1" }}>
<Title order={6}>{t("empty")}</Title> <Title order={6}>{t("empty")}</Title>
@@ -40,7 +41,7 @@ export function StatisticsPanel(props: StatisticsPanelProps) {
<StatisticItem <StatisticItem
icon={IconDatabaseHeart} icon={IconDatabaseHeart}
label={t("savedSpace")} label={t("savedSpace")}
value={humanFileSize(Math.floor(allLibs.savedSpace))} value={humanFileSize(Math.floor(allLibs.totalSavedSpace))}
/> />
</Group> </Group>
<Group justify="center" wrap="wrap" grow> <Group justify="center" wrap="wrap" grow>