From 4d419b77c0695e15bed1e511e949c6d91af444b0 Mon Sep 17 00:00:00 2001 From: Alexandre Tuleu Date: Thu, 21 Sep 2023 15:10:49 +0200 Subject: [PATCH] Implements Sonarr callbacks --- package-lock.json | 6 ++ package.json | 1 + src/lib/components/CircularProgressBar.svelte | 2 - src/lib/humanize.ts | 8 ++ src/lib/services/pihole/+content.svelte | 1 - src/lib/services/sonarr/+content.svelte | 28 +++++++ src/lib/services/sonarr/+service.ts | 84 ++++++++++++++++++- 7 files changed, 125 insertions(+), 5 deletions(-) create mode 100644 src/lib/humanize.ts create mode 100644 src/lib/services/sonarr/+content.svelte diff --git a/package-lock.json b/package-lock.json index 54c29e6..744e5ea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@fortawesome/fontawesome-free": "^6.4.2", "dotenv": "^16.3.1", + "humanize-duration-ts": "^2.1.1", "ip-range-check": "^0.2.0", "js-yaml": "^4.1.0" }, @@ -2521,6 +2522,11 @@ "node": ">=8" } }, + "node_modules/humanize-duration-ts": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/humanize-duration-ts/-/humanize-duration-ts-2.1.1.tgz", + "integrity": "sha512-TibNF2/fkypjAfHdGpWL/dmWUS0G6Qi+3mKyiB6LDCowbMy+PtzbgPTnFMNTOVAJXDau01jYrJ3tFoz5AJSqhA==" + }, "node_modules/ignore": { "version": "5.2.4", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", diff --git a/package.json b/package.json index 5d87425..1a9cc6f 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "dependencies": { "@fortawesome/fontawesome-free": "^6.4.2", "dotenv": "^16.3.1", + "humanize-duration-ts": "^2.1.1", "ip-range-check": "^0.2.0", "js-yaml": "^4.1.0" } diff --git a/src/lib/components/CircularProgressBar.svelte b/src/lib/components/CircularProgressBar.svelte index 6ff54ce..30c78ae 100644 --- a/src/lib/components/CircularProgressBar.svelte +++ b/src/lib/components/CircularProgressBar.svelte @@ -4,8 +4,6 @@ const radius = 40; $: circumference = 2.0 * radius * Math.PI; $: offset = (1.0 - Math.min(Math.max(ratio, 0.0), 1.0)) * circumference; - $: console.log(offset); - $: console.log(circumference); diff --git a/src/lib/services/sonarr/+content.svelte b/src/lib/services/sonarr/+content.svelte new file mode 100644 index 0000000..b7895de --- /dev/null +++ b/src/lib/services/sonarr/+content.svelte @@ -0,0 +1,28 @@ + + +
+ {#if data.health?.errors > 0} + + : {data.health?.errors} + + {/if} + {#if data.health?.warnings > 0} + + : {data.health?.warnings} + + {/if} + {#if data.queue?.total > 0} + + : {data.queue?.total} + + {/if} + + {#if data.queue?.nextDate} + in ~{humanizeRelativeTime(data.queue?.nextDate)} + {/if} +
diff --git a/src/lib/services/sonarr/+service.ts b/src/lib/services/sonarr/+service.ts index 4b7e16a..7f93155 100644 --- a/src/lib/services/sonarr/+service.ts +++ b/src/lib/services/sonarr/+service.ts @@ -1,5 +1,85 @@ +import type { ServiceConfig } from '$lib/config'; import type { ServiceHandler } from '../service'; -export const handle: ServiceHandler = () => { - return { logo: 'https://cdn.rawgit.com/Sonarr/Sonarr/develop/Logo/Sonarr.svg' }; +interface Status { + warnings: number; + errors: number; +} + +function buildStatus(statuses: any[]): Status { + let warnings = 0; + let errors = 0; + for (const status of statuses) { + switch (status.type) { + case 'warning': + warnings += 1; + break; + case 'error': + errors += 1; + break; + } + } + return { warnings, errors }; +} + +interface Queue { + nextDate?: Date; + total: number; +} +function recordEstimatedCompletionTime(record: any): number { + if (record.estimatedCompletionTime == undefined) { + return Infinity; + } + return new Date(record.estimatedCompletionTime).getTime(); +} + +function buildQueue(queue: any): Queue { + if (queue?.records?.length === 0) { + return { total: 0 }; + } + let nextTime: number = recordEstimatedCompletionTime(queue.records[0]); + for (let i = 1; i < queue.records.length; i++) { + const recordTime = recordEstimatedCompletionTime(queue.records[i]); + if (recordTime < nextTime) { + nextTime = recordTime; + } + } + + return { + total: queue.records.length, + nextDate: nextTime != Infinity ? new Date(nextTime) : undefined + }; +} + +export const handle: ServiceHandler = async (config: ServiceConfig) => { + const res = { + logo: 'https://cdn.rawgit.com/Sonarr/Sonarr/develop/Logo/Sonarr.svg', + subtitle: 'TV Show tracker' + }; + + const params = '?apikey=' + config.api_key; + + const requests = [ + fetch(config.url + '/api/v3/health' + params), + fetch(config.url + '/api/v3/queue' + params + '&includeUnknownSeriesItems=true') + ]; + + const [health, queue] = await Promise.allSettled(requests); + res.status = 'online'; + + if (health.status != 'fulfilled') { + console.warn("Could not fetch '" + config.url + "' status: " + health.value); + res.status = 'offline'; + } else if (health.value.ok == true) { + res.health = buildStatus(await health.value.json()); + } + + if (queue.status != 'fulfilled') { + console.warn("Could not fetch '" + config.url + "' queue: " + queue.value); + res.status = 'offline'; + } else if (queue.value.ok == true) { + res.queue = buildQueue(await queue.value.json()); + } + + return res; };