From 28e39e46ae949f0fb57872575bdbeee9aba8f40e Mon Sep 17 00:00:00 2001 From: Matteo Bossi Date: Fri, 2 Jun 2023 14:57:27 +0200 Subject: [PATCH 1/4] Add queue list --- src/components/services/widget/block-list.jsx | 31 ++++++++ src/components/services/widget/container.jsx | 6 +- src/widgets/radarr/component.jsx | 79 +++++++++++++++---- src/widgets/radarr/widget.js | 33 +++++++- src/widgets/sonarr/component.jsx | 75 +++++++++++++++--- src/widgets/sonarr/widget.js | 39 ++++++++- tailwind.config.js | 3 + 7 files changed, 232 insertions(+), 34 deletions(-) create mode 100644 src/components/services/widget/block-list.jsx diff --git a/src/components/services/widget/block-list.jsx b/src/components/services/widget/block-list.jsx new file mode 100644 index 00000000..138576bc --- /dev/null +++ b/src/components/services/widget/block-list.jsx @@ -0,0 +1,31 @@ +import { useTranslation } from "next-i18next"; +import { useCallback, useState } from 'react'; +import classNames from "classnames"; + +import ResolvedIcon from '../../resolvedicon'; + + +export default function BlockList({ label, children, childHeight }) { + const { t } = useTranslation(); + const [isOpen, setOpen] = useState(false); + + const changeState = useCallback(() => setOpen(!isOpen), [isOpen, setOpen]); + + return ( +
+ +
+ {children} +
+
+ ); +} diff --git a/src/components/services/widget/container.jsx b/src/components/services/widget/container.jsx index f4d8c13e..4b8a06ca 100644 --- a/src/components/services/widget/container.jsx +++ b/src/components/services/widget/container.jsx @@ -15,7 +15,9 @@ export default function Container({ error = false, children, service }) { return } - let visibleChildren = children; + const childrenArray = Array.isArray(children) ? children : [children]; + + let visibleChildren = childrenArray; const fields = service?.widget?.fields; const type = service?.widget?.type; if (fields && type) { @@ -24,7 +26,7 @@ export default function Container({ error = false, children, service }) { // fields: [ "resources.cpu", "resources.mem", "field"] // or even // fields: [ "resources.cpu", "widget_type.field" ] - visibleChildren = children?.filter(child => fields.some(field => { + visibleChildren = childrenArray?.filter(child => fields.some(field => { let fullField = field; if (!field.includes(".")) { fullField = `${type}.${field}`; diff --git a/src/widgets/radarr/component.jsx b/src/widgets/radarr/component.jsx index f8a932ea..4e53ef9a 100644 --- a/src/widgets/radarr/component.jsx +++ b/src/widgets/radarr/component.jsx @@ -1,7 +1,10 @@ import { useTranslation } from "next-i18next"; +import { useCallback } from 'react'; +import classNames from 'classnames'; import Container from "components/services/widget/container"; import Block from "components/services/widget/block"; +import BlockList from "components/services/widget/block-list"; import useWidgetAPI from "utils/proxy/use-widget-api"; export default function Component({ service }) { @@ -10,29 +13,75 @@ export default function Component({ service }) { const { data: moviesData, error: moviesError } = useWidgetAPI(widget, "movie"); const { data: queuedData, error: queuedError } = useWidgetAPI(widget, "queue/status"); + const { data: queueDetailsData, error: queueDetailsError } = useWidgetAPI(widget, "queue/details"); - if (moviesError || queuedError) { - const finalError = moviesError ?? queuedError; + // information taken from the Radarr docs: https://radarr.video/docs/api/ + const formatDownloadState = useCallback((downloadState) => { + switch (downloadState) { + case "importPending": + return "import pending"; + case "failedPending": + return "failed pending"; + default: + return downloadState; + } + }, []); + + if (moviesError || queuedError || queueDetailsError) { + const finalError = moviesError ?? queuedError ?? queueDetailsError; return ; } - if (!moviesData || !queuedData) { + if (!moviesData || !queuedData || !queueDetailsData) { return ( - - - - - - + <> + + + + + + + + + + ); } return ( - - - - - - + <> + + + + + + + + + {Array.isArray(queueDetailsData) ? queueDetailsData.map((queueEntry) => ( +
+
+
{moviesData.all.find((entry) => entry.id === queueEntry.movieId)?.title}
+
{formatDownloadState(queueEntry.trackedDownloadState)}
+
+
+
+
+
+
{queueEntry.timeLeft}
+
+
+ )) : undefined} + + + ); } diff --git a/src/widgets/radarr/widget.js b/src/widgets/radarr/widget.js index 78054219..0f53ab14 100644 --- a/src/widgets/radarr/widget.js +++ b/src/widgets/radarr/widget.js @@ -1,5 +1,5 @@ import genericProxyHandler from "utils/proxy/handlers/generic"; -import { jsonArrayFilter } from "utils/proxy/api-helpers"; +import { asJson, jsonArrayFilter } from "utils/proxy/api-helpers"; const widget = { api: "{url}/api/v3/{endpoint}?apikey={key}", @@ -12,6 +12,7 @@ const widget = { wanted: jsonArrayFilter(data, (item) => item.monitored && !item.hasFile && item.isAvailable).length, have: jsonArrayFilter(data, (item) => item.hasFile).length, missing: jsonArrayFilter(data, (item) => item.monitored && !item.hasFile).length, + all: asJson(data), }), }, "queue/status": { @@ -20,6 +21,36 @@ const widget = { "totalCount" ] }, + "queue/details": { + endpoint: "queue/details", + map: (data) => asJson(data).map((entry) => ({ + trackedDownloadState: entry.trackedDownloadState, + trackedDownloadStatus: entry.trackedDownloadStatus, + timeLeft: entry.timeleft, + size: entry.size, + sizeLeft: entry.sizeleft, + movieId: entry.movieId + })).sort((a, b) => { + const downloadingA = a.trackedDownloadState === "downloading" + const downloadingB = b.trackedDownloadState === "downloading" + if (downloadingA && !downloadingB) { + return -1; + } + if (downloadingB && !downloadingA) { + return 1; + } + + const percentA = a.sizeLeft / a.size; + const percentB = b.sizeLeft / b.size; + if (percentA < percentB) { + return -1; + } + if (percentA > percentB) { + return 1; + } + return 0; + }) + }, }, }; diff --git a/src/widgets/sonarr/component.jsx b/src/widgets/sonarr/component.jsx index adbb8c30..9a0f98ae 100644 --- a/src/widgets/sonarr/component.jsx +++ b/src/widgets/sonarr/component.jsx @@ -1,8 +1,11 @@ import { useTranslation } from "next-i18next"; +import classNames from 'classnames'; +import { useCallback } from 'react'; import Container from "components/services/widget/container"; import Block from "components/services/widget/block"; import useWidgetAPI from "utils/proxy/use-widget-api"; +import BlockList from 'components/services/widget/block-list'; export default function Component({ service }) { const { t } = useTranslation(); @@ -11,27 +14,73 @@ export default function Component({ service }) { const { data: wantedData, error: wantedError } = useWidgetAPI(widget, "wanted/missing"); const { data: queuedData, error: queuedError } = useWidgetAPI(widget, "queue"); const { data: seriesData, error: seriesError } = useWidgetAPI(widget, "series"); + const { data: queueDetailsData, error: queueDetailsError } = useWidgetAPI(widget, "queue/details"); - if (wantedError || queuedError || seriesError) { - const finalError = wantedError ?? queuedError ?? seriesError; + // information taken from the Sonarr docs: https://sonarr.tv/docs/api/ + const formatDownloadState = useCallback((downloadState) => { + switch (downloadState) { + case "importPending": + return "import pending"; + case "failedPending": + return "failed pending"; + default: + return downloadState; + } + }, []); + + if (wantedError || queuedError || seriesError || queueDetailsError) { + const finalError = wantedError ?? queuedError ?? seriesError ?? queueDetailsError; return ; } - if (!wantedData || !queuedData || !seriesData) { + if (!wantedData || !queuedData || !seriesData || !queueDetailsData) { return ( - - - - - + <> + + + + + + + + + ); } return ( - - - - - + <> + + + + + + + + {Array.isArray(queueDetailsData) ? queueDetailsData.map((queueEntry) => ( +
+
+
{seriesData.find((entry) => entry.id === queueEntry.seriesId).title} • {queueEntry.episodeTitle}
+
{formatDownloadState(queueEntry.trackedDownloadState)}
+
+
+
+
+
+
{queueEntry.timeLeft}
+
+
+ )) : undefined} + + + ); } diff --git a/src/widgets/sonarr/widget.js b/src/widgets/sonarr/widget.js index c1413975..80afdb99 100644 --- a/src/widgets/sonarr/widget.js +++ b/src/widgets/sonarr/widget.js @@ -8,9 +8,10 @@ const widget = { mappings: { series: { endpoint: "series", - map: (data) => ({ - total: asJson(data).length, - }) + map: (data) => asJson(data).map((entry) => ({ + title: entry.title, + id: entry.id + })) }, queue: { endpoint: "queue", @@ -24,6 +25,38 @@ const widget = { "totalRecords" ] }, + "queue/details": { + endpoint: "queue/details", + map: (data) => asJson(data).map((entry) => ({ + trackedDownloadState: entry.trackedDownloadState, + trackedDownloadStatus: entry.trackedDownloadStatus, + timeLeft: entry.timeleft, + size: entry.size, + sizeLeft: entry.sizeleft, + seriesId: entry.seriesId, + episodeTitle: entry.episode?.title, + episodeId: entry.episodeId + })).sort((a, b) => { + const downloadingA = a.trackedDownloadState === "downloading" + const downloadingB = b.trackedDownloadState === "downloading" + if (downloadingA && !downloadingB) { + return -1; + } + if (downloadingB && !downloadingA) { + return 1; + } + + const percentA = a.sizeLeft / a.size; + const percentB = b.sizeLeft / b.size; + if (percentA < percentB) { + return -1; + } + if (percentA > percentB) { + return 1; + } + return 0; + }) + } }, }; diff --git a/tailwind.config.js b/tailwind.config.js index b981051b..96c9e641 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -29,6 +29,9 @@ module.exports = { '3xl': '1800px', // => @media (min-width: 1800px) { ... } }, + transitionProperty: { + 'height': 'height' + }, }, }, plugins: [tailwindForms, tailwindScrollbars], From 0eab4e79437e61d3f0a12cbd2bab415d5f856df0 Mon Sep 17 00:00:00 2001 From: Matteo Bossi Date: Fri, 2 Jun 2023 15:46:43 +0200 Subject: [PATCH 2/4] Fix Mobile view --- src/widgets/radarr/component.jsx | 4 +++- src/widgets/sonarr/component.jsx | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/widgets/radarr/component.jsx b/src/widgets/radarr/component.jsx index 4e53ef9a..2e58bf9d 100644 --- a/src/widgets/radarr/component.jsx +++ b/src/widgets/radarr/component.jsx @@ -61,7 +61,9 @@ export default function Component({ service }) { {Array.isArray(queueDetailsData) ? queueDetailsData.map((queueEntry) => (
-
{moviesData.all.find((entry) => entry.id === queueEntry.movieId)?.title}
+
+
{moviesData.all.find((entry) => entry.id === queueEntry.movieId)?.title}
+
{formatDownloadState(queueEntry.trackedDownloadState)}
diff --git a/src/widgets/sonarr/component.jsx b/src/widgets/sonarr/component.jsx index 9a0f98ae..ee548b58 100644 --- a/src/widgets/sonarr/component.jsx +++ b/src/widgets/sonarr/component.jsx @@ -60,7 +60,9 @@ export default function Component({ service }) { {Array.isArray(queueDetailsData) ? queueDetailsData.map((queueEntry) => (
-
{seriesData.find((entry) => entry.id === queueEntry.seriesId).title} • {queueEntry.episodeTitle}
+
+
{seriesData.find((entry) => entry.id === queueEntry.seriesId).title} • {queueEntry.episodeTitle}
+
{formatDownloadState(queueEntry.trackedDownloadState)}
From 5b3d1cc6e06ef71d2d9bf8fe4d396c7383c57370 Mon Sep 17 00:00:00 2001 From: Matteo Bossi Date: Tue, 6 Jun 2023 01:14:10 +0200 Subject: [PATCH 3/4] Make styling more consistent and add toggle to opt-in instead of opting out --- public/locales/en/common.json | 6 ++- src/components/widgets/queue/queueEntry.jsx | 28 ++++++++++ src/utils/config/service-helpers.js | 8 ++- src/widgets/radarr/component.jsx | 57 +++++++++------------ src/widgets/radarr/widget.js | 3 +- src/widgets/sonarr/component.jsx | 57 +++++++++------------ src/widgets/sonarr/widget.js | 3 +- 7 files changed, 92 insertions(+), 70 deletions(-) create mode 100644 src/components/widgets/queue/queueEntry.jsx diff --git a/public/locales/en/common.json b/public/locales/en/common.json index 7f1a86de..a3d74aee 100755 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -194,13 +194,15 @@ "sonarr": { "wanted": "Wanted", "queued": "Queued", - "series": "Series" + "series": "Series", + "queue": "Queue" }, "radarr": { "wanted": "Wanted", "missing": "Missing", "queued": "Queued", - "movies": "Movies" + "movies": "Movies", + "queue": "Queue" }, "lidarr": { "wanted": "Wanted", diff --git a/src/components/widgets/queue/queueEntry.jsx b/src/components/widgets/queue/queueEntry.jsx new file mode 100644 index 00000000..0d0625e9 --- /dev/null +++ b/src/components/widgets/queue/queueEntry.jsx @@ -0,0 +1,28 @@ +import {BsFillPlayFill, BsPauseFill} from "react-icons/bs"; + +export default function QueueEntry({ status, title, activity, timeLeft, progress}) { + return ( +
+
+
+ {status === "paused" && ( + + )} + {status !== "paused" && ( + + )} +
+
+
{title}
+
+
+ {timeLeft ? `${activity} - ${timeLeft}` : activity} +
+
+ ); +} diff --git a/src/utils/config/service-helpers.js b/src/utils/config/service-helpers.js index 7f9d45e4..41fe263a 100644 --- a/src/utils/config/service-helpers.js +++ b/src/utils/config/service-helpers.js @@ -168,7 +168,7 @@ export async function servicesFromKubernetes() { .filter((ingress) => ingress.metadata.annotations && ingress.metadata.annotations[`${ANNOTATION_BASE}/href`]) ingressList.items.push(...traefikServices); } - + if (!ingressList) { return []; } @@ -276,7 +276,8 @@ export function cleanServiceGroups(groups) { wan, // opnsense widget, pfsense widget enableBlocks, // emby/jellyfin enableNowPlaying, - volume, // diskstation widget + volume, // diskstation widget, + enableQueue, // sonarr/radarr } = cleanedService.widget; const fieldsList = typeof fields === 'string' ? JSON.parse(fields) : fields; @@ -312,6 +313,9 @@ export function cleanServiceGroups(groups) { if (enableBlocks !== undefined) cleanedService.widget.enableBlocks = JSON.parse(enableBlocks); if (enableNowPlaying !== undefined) cleanedService.widget.enableNowPlaying = JSON.parse(enableNowPlaying); } + if (["sonarr", "radarr"].includes(type)) { + if (enableQueue !== undefined) cleanedService.widget.enableQueue = JSON.parse(enableQueue); + } if (["diskstation", "qnap"].includes(type)) { if (volume) cleanedService.widget.volume = volume; } diff --git a/src/widgets/radarr/component.jsx b/src/widgets/radarr/component.jsx index 2e58bf9d..0212eaa7 100644 --- a/src/widgets/radarr/component.jsx +++ b/src/widgets/radarr/component.jsx @@ -1,6 +1,7 @@ import { useTranslation } from "next-i18next"; import { useCallback } from 'react'; -import classNames from 'classnames'; + +import QueueEntry from "../../components/widgets/queue/queueEntry"; import Container from "components/services/widget/container"; import Block from "components/services/widget/block"; @@ -32,6 +33,8 @@ export default function Component({ service }) { return ; } + const enableQueue = widget?.enableQueue; + if (!moviesData || !queuedData || !queueDetailsData) { return ( <> @@ -41,9 +44,11 @@ export default function Component({ service }) { - - - + { enableQueue && + + + + } ); } @@ -56,34 +61,22 @@ export default function Component({ service }) { - - - {Array.isArray(queueDetailsData) ? queueDetailsData.map((queueEntry) => ( -
-
-
-
{moviesData.all.find((entry) => entry.id === queueEntry.movieId)?.title}
-
-
{formatDownloadState(queueEntry.trackedDownloadState)}
-
-
-
-
-
-
{queueEntry.timeLeft}
-
-
- )) : undefined} - - + { enableQueue && + + + {Array.isArray(queueDetailsData) ? queueDetailsData.map((queueEntry) => ( + entry.id === queueEntry.movieId)?.title} + activity={formatDownloadState(queueEntry.trackedDownloadState)} + key={queueEntry.movieId} + /> + )) : undefined} + + + } ); } diff --git a/src/widgets/radarr/widget.js b/src/widgets/radarr/widget.js index 0f53ab14..8d70192f 100644 --- a/src/widgets/radarr/widget.js +++ b/src/widgets/radarr/widget.js @@ -29,7 +29,8 @@ const widget = { timeLeft: entry.timeleft, size: entry.size, sizeLeft: entry.sizeleft, - movieId: entry.movieId + movieId: entry.movieId, + status: entry.status })).sort((a, b) => { const downloadingA = a.trackedDownloadState === "downloading" const downloadingB = b.trackedDownloadState === "downloading" diff --git a/src/widgets/sonarr/component.jsx b/src/widgets/sonarr/component.jsx index ee548b58..0f87b975 100644 --- a/src/widgets/sonarr/component.jsx +++ b/src/widgets/sonarr/component.jsx @@ -1,7 +1,8 @@ import { useTranslation } from "next-i18next"; -import classNames from 'classnames'; import { useCallback } from 'react'; +import QueueEntry from "../../components/widgets/queue/queueEntry"; + import Container from "components/services/widget/container"; import Block from "components/services/widget/block"; import useWidgetAPI from "utils/proxy/use-widget-api"; @@ -33,6 +34,8 @@ export default function Component({ service }) { return ; } + const enableQueue = widget?.enableQueue; + if (!wantedData || !queuedData || !seriesData || !queueDetailsData) { return ( <> @@ -41,9 +44,11 @@ export default function Component({ service }) { - - - + { enableQueue && + + + + } ); } @@ -55,34 +60,22 @@ export default function Component({ service }) { - - - {Array.isArray(queueDetailsData) ? queueDetailsData.map((queueEntry) => ( -
-
-
-
{seriesData.find((entry) => entry.id === queueEntry.seriesId).title} • {queueEntry.episodeTitle}
-
-
{formatDownloadState(queueEntry.trackedDownloadState)}
-
-
-
-
-
-
{queueEntry.timeLeft}
-
-
- )) : undefined} - - + { enableQueue && + + + {Array.isArray(queueDetailsData) ? queueDetailsData.map((queueEntry) => ( + entry.id === queueEntry.seriesId)?.title } • ${ queueEntry.episodeTitle}`} + activity={formatDownloadState(queueEntry.trackedDownloadState)} + key={queueEntry.episodeId} + /> + )) : undefined} + + + } ); } diff --git a/src/widgets/sonarr/widget.js b/src/widgets/sonarr/widget.js index 80afdb99..c0fae806 100644 --- a/src/widgets/sonarr/widget.js +++ b/src/widgets/sonarr/widget.js @@ -35,7 +35,8 @@ const widget = { sizeLeft: entry.sizeleft, seriesId: entry.seriesId, episodeTitle: entry.episode?.title, - episodeId: entry.episodeId + episodeId: entry.episodeId, + status: entry.status })).sort((a, b) => { const downloadingA = a.trackedDownloadState === "downloading" const downloadingB = b.trackedDownloadState === "downloading" From dd4ee443029ce79e6a8ac600ca8debdcbb1fae38 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Mon, 5 Jun 2023 22:21:26 -0700 Subject: [PATCH 4/4] Simplify sonarr / radarr queues, better handle some errors --- public/locales/en/common.json | 6 +- src/components/services/widget/block-list.jsx | 31 ---------- src/components/widgets/queue/queueEntry.jsx | 16 +---- src/widgets/radarr/component.jsx | 54 +++++++--------- src/widgets/radarr/widget.js | 2 +- src/widgets/sonarr/component.jsx | 62 +++++++++---------- src/widgets/sonarr/widget.js | 6 +- tailwind.config.js | 3 - 8 files changed, 64 insertions(+), 116 deletions(-) delete mode 100644 src/components/services/widget/block-list.jsx diff --git a/public/locales/en/common.json b/public/locales/en/common.json index a3d74aee..e20e1908 100755 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -195,14 +195,16 @@ "wanted": "Wanted", "queued": "Queued", "series": "Series", - "queue": "Queue" + "queue": "Queue", + "unknown": "Unknown" }, "radarr": { "wanted": "Wanted", "missing": "Missing", "queued": "Queued", "movies": "Movies", - "queue": "Queue" + "queue": "Queue", + "unknown": "Unknown" }, "lidarr": { "wanted": "Wanted", diff --git a/src/components/services/widget/block-list.jsx b/src/components/services/widget/block-list.jsx deleted file mode 100644 index 138576bc..00000000 --- a/src/components/services/widget/block-list.jsx +++ /dev/null @@ -1,31 +0,0 @@ -import { useTranslation } from "next-i18next"; -import { useCallback, useState } from 'react'; -import classNames from "classnames"; - -import ResolvedIcon from '../../resolvedicon'; - - -export default function BlockList({ label, children, childHeight }) { - const { t } = useTranslation(); - const [isOpen, setOpen] = useState(false); - - const changeState = useCallback(() => setOpen(!isOpen), [isOpen, setOpen]); - - return ( -
- -
- {children} -
-
- ); -} diff --git a/src/components/widgets/queue/queueEntry.jsx b/src/components/widgets/queue/queueEntry.jsx index 0d0625e9..adea45ad 100644 --- a/src/components/widgets/queue/queueEntry.jsx +++ b/src/components/widgets/queue/queueEntry.jsx @@ -1,22 +1,12 @@ -import {BsFillPlayFill, BsPauseFill} from "react-icons/bs"; - -export default function QueueEntry({ status, title, activity, timeLeft, progress}) { +export default function QueueEntry({ title, activity, timeLeft, progress}) { return ( -
+
-
- {status === "paused" && ( - - )} - {status !== "paused" && ( - - )} -
{title}
diff --git a/src/widgets/radarr/component.jsx b/src/widgets/radarr/component.jsx index 0212eaa7..6ce2f599 100644 --- a/src/widgets/radarr/component.jsx +++ b/src/widgets/radarr/component.jsx @@ -5,9 +5,12 @@ import QueueEntry from "../../components/widgets/queue/queueEntry"; import Container from "components/services/widget/container"; import Block from "components/services/widget/block"; -import BlockList from "components/services/widget/block-list"; import useWidgetAPI from "utils/proxy/use-widget-api"; +function getProgress(sizeLeft, size) { + return sizeLeft === 0 ? 100 : (1 - sizeLeft / size) * 100 +} + export default function Component({ service }) { const { t } = useTranslation(); const { widget } = service; @@ -16,7 +19,6 @@ export default function Component({ service }) { const { data: queuedData, error: queuedError } = useWidgetAPI(widget, "queue/status"); const { data: queueDetailsData, error: queueDetailsError } = useWidgetAPI(widget, "queue/details"); - // information taken from the Radarr docs: https://radarr.video/docs/api/ const formatDownloadState = useCallback((downloadState) => { switch (downloadState) { case "importPending": @@ -33,26 +35,19 @@ export default function Component({ service }) { return ; } - const enableQueue = widget?.enableQueue; - if (!moviesData || !queuedData || !queueDetailsData) { return ( - <> - - - - - - - { enableQueue && - - - - } - + + + + + + ); } + const enableQueue = widget?.enableQueue && Array.isArray(queueDetailsData) && queueDetailsData.length > 0; + return ( <> @@ -61,21 +56,16 @@ export default function Component({ service }) { - { enableQueue && - - - {Array.isArray(queueDetailsData) ? queueDetailsData.map((queueEntry) => ( - entry.id === queueEntry.movieId)?.title} - activity={formatDownloadState(queueEntry.trackedDownloadState)} - key={queueEntry.movieId} - /> - )) : undefined} - - + {enableQueue && + queueDetailsData.map((queueEntry) => ( + entry.id === queueEntry.movieId)?.title ?? t("radarr.unknown")} + activity={formatDownloadState(queueEntry.trackedDownloadState)} + key={`${queueEntry.movieId}-${queueEntry.sizeLeft}`} + /> + )) } ); diff --git a/src/widgets/radarr/widget.js b/src/widgets/radarr/widget.js index 8d70192f..3373975e 100644 --- a/src/widgets/radarr/widget.js +++ b/src/widgets/radarr/widget.js @@ -29,7 +29,7 @@ const widget = { timeLeft: entry.timeleft, size: entry.size, sizeLeft: entry.sizeleft, - movieId: entry.movieId, + movieId: entry.movieId ?? entry.id, status: entry.status })).sort((a, b) => { const downloadingA = a.trackedDownloadState === "downloading" diff --git a/src/widgets/sonarr/component.jsx b/src/widgets/sonarr/component.jsx index 0f87b975..27b1ab03 100644 --- a/src/widgets/sonarr/component.jsx +++ b/src/widgets/sonarr/component.jsx @@ -6,7 +6,20 @@ import QueueEntry from "../../components/widgets/queue/queueEntry"; import Container from "components/services/widget/container"; import Block from "components/services/widget/block"; import useWidgetAPI from "utils/proxy/use-widget-api"; -import BlockList from 'components/services/widget/block-list'; + +function getProgress(sizeLeft, size) { + return sizeLeft === 0 ? 100 : (1 - sizeLeft / size) * 100 +} + +function getTitle(queueEntry, seriesData) { + let title = '' + const seriesTitle = seriesData.find((entry) => entry.id === queueEntry.seriesId)?.title; + if (seriesTitle) title += `${seriesTitle}: `; + const { episodeTitle } = queueEntry; + if (episodeTitle) title += episodeTitle; + if (title === '') return null; + return title; +} export default function Component({ service }) { const { t } = useTranslation(); @@ -17,7 +30,6 @@ export default function Component({ service }) { const { data: seriesData, error: seriesError } = useWidgetAPI(widget, "series"); const { data: queueDetailsData, error: queueDetailsError } = useWidgetAPI(widget, "queue/details"); - // information taken from the Sonarr docs: https://sonarr.tv/docs/api/ const formatDownloadState = useCallback((downloadState) => { switch (downloadState) { case "importPending": @@ -34,25 +46,18 @@ export default function Component({ service }) { return ; } - const enableQueue = widget?.enableQueue; - if (!wantedData || !queuedData || !seriesData || !queueDetailsData) { return ( - <> - - - - - - { enableQueue && - - - - } - + + + + + ); } + const enableQueue = widget?.enableQueue && Array.isArray(queueDetailsData) && queueDetailsData.length > 0; + return ( <> @@ -60,21 +65,16 @@ export default function Component({ service }) { - { enableQueue && - - - {Array.isArray(queueDetailsData) ? queueDetailsData.map((queueEntry) => ( - entry.id === queueEntry.seriesId)?.title } • ${ queueEntry.episodeTitle}`} - activity={formatDownloadState(queueEntry.trackedDownloadState)} - key={queueEntry.episodeId} - /> - )) : undefined} - - + {enableQueue && + queueDetailsData.map((queueEntry) => ( + + )) } ); diff --git a/src/widgets/sonarr/widget.js b/src/widgets/sonarr/widget.js index c0fae806..7f658eb1 100644 --- a/src/widgets/sonarr/widget.js +++ b/src/widgets/sonarr/widget.js @@ -34,9 +34,9 @@ const widget = { size: entry.size, sizeLeft: entry.sizeleft, seriesId: entry.seriesId, - episodeTitle: entry.episode?.title, - episodeId: entry.episodeId, - status: entry.status + episodeTitle: entry.episode?.title ?? entry.title, + episodeId: entry.episodeId ?? entry.id, + status: entry.status, })).sort((a, b) => { const downloadingA = a.trackedDownloadState === "downloading" const downloadingB = b.trackedDownloadState === "downloading" diff --git a/tailwind.config.js b/tailwind.config.js index 96c9e641..b981051b 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -29,9 +29,6 @@ module.exports = { '3xl': '1800px', // => @media (min-width: 1800px) { ... } }, - transitionProperty: { - 'height': 'height' - }, }, }, plugins: [tailwindForms, tailwindScrollbars],