Added a new widget for tvheadend
This commit is contained in:
parent
cb3248117f
commit
24cd955fa6
21
docs/widgets/services/tvheadend.md
Normal file
21
docs/widgets/services/tvheadend.md
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
---
|
||||||
|
title: Tvheadend
|
||||||
|
description: Tvheadend Widget Configuration
|
||||||
|
---
|
||||||
|
|
||||||
|
Shows the status of the DVR feature (upcoming, finished, failed recordings) as well as a slist of active subscriptions.
|
||||||
|
|
||||||
|
Learn more about [Tvheadend](https://tvheadend.org/).
|
||||||
|
|
||||||
|
Allowed fields: `["upcoming", "finished", "failed"]`.
|
||||||
|
|
||||||
|
Uses basic auth to connect to tvheadend API
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
widget:
|
||||||
|
type: tvheadend
|
||||||
|
url: http://tvheadend.host.or.ip
|
||||||
|
username: user
|
||||||
|
password: pass
|
||||||
|
fields: ["upcoming", "finished", "failed"]
|
||||||
|
```
|
||||||
@ -1007,5 +1007,10 @@
|
|||||||
"issues": "Issues",
|
"issues": "Issues",
|
||||||
"merges": "Merge Requests",
|
"merges": "Merge Requests",
|
||||||
"projects": "Projects"
|
"projects": "Projects"
|
||||||
|
},
|
||||||
|
"tvheadend": {
|
||||||
|
"upcoming": "Upcoming",
|
||||||
|
"finished": "Finished",
|
||||||
|
"failed": "Failed"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -127,6 +127,7 @@ const components = {
|
|||||||
transmission: dynamic(() => import("./transmission/component")),
|
transmission: dynamic(() => import("./transmission/component")),
|
||||||
tubearchivist: dynamic(() => import("./tubearchivist/component")),
|
tubearchivist: dynamic(() => import("./tubearchivist/component")),
|
||||||
truenas: dynamic(() => import("./truenas/component")),
|
truenas: dynamic(() => import("./truenas/component")),
|
||||||
|
tvheadend: dynamic(() => import("./tvheadend/component")),
|
||||||
unifi: dynamic(() => import("./unifi/component")),
|
unifi: dynamic(() => import("./unifi/component")),
|
||||||
unmanic: dynamic(() => import("./unmanic/component")),
|
unmanic: dynamic(() => import("./unmanic/component")),
|
||||||
uptimekuma: dynamic(() => import("./uptimekuma/component")),
|
uptimekuma: dynamic(() => import("./uptimekuma/component")),
|
||||||
|
|||||||
87
src/widgets/tvheadend/component.jsx
Normal file
87
src/widgets/tvheadend/component.jsx
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
import { useTranslation } from "next-i18next";
|
||||||
|
|
||||||
|
import Container from "components/services/widget/container";
|
||||||
|
import Block from "components/services/widget/block";
|
||||||
|
import useWidgetAPI from "utils/proxy/use-widget-api";
|
||||||
|
import Subscription from "widgets/tvheadend/subscription";
|
||||||
|
|
||||||
|
function timeAgo(ticks) {
|
||||||
|
const now = Date.now(); // Current time in milliseconds
|
||||||
|
const inputTime = ticks * 1000; // Convert the input ticks from seconds to milliseconds
|
||||||
|
const difference = now - inputTime; // Difference in milliseconds
|
||||||
|
|
||||||
|
const seconds = Math.floor((difference / 1000) % 60);
|
||||||
|
const minutes = Math.floor((difference / (1000 * 60)) % 60);
|
||||||
|
const hours = Math.floor((difference / (1000 * 60 * 60)) % 24);
|
||||||
|
return { hours, minutes, seconds };
|
||||||
|
}
|
||||||
|
|
||||||
|
function timeAgoToString(ticks) {
|
||||||
|
const { hours, minutes, seconds } = timeAgo(ticks);
|
||||||
|
const parts = [];
|
||||||
|
if (hours > 0) {
|
||||||
|
parts.push(hours);
|
||||||
|
}
|
||||||
|
parts.push(minutes);
|
||||||
|
parts.push(seconds);
|
||||||
|
|
||||||
|
return parts.map((part) => part.toString().padStart(2, "0")).join(":");
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Component({ service }) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { widget } = service;
|
||||||
|
|
||||||
|
const { data: dvrData, error: dvrError } = useWidgetAPI(widget, "dvr", {
|
||||||
|
refreshInterval: 60000,
|
||||||
|
});
|
||||||
|
const { data: subscriptionsData, error: subscriptionsError } = useWidgetAPI(widget, "subscriptions", {
|
||||||
|
refreshInterval: 5000,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (dvrError || subscriptionsError) {
|
||||||
|
const finalError = dvrError ?? subscriptionsError;
|
||||||
|
return <Container service={service} error={finalError} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dvrData || !subscriptionsData) {
|
||||||
|
return (
|
||||||
|
<Container service={service}>
|
||||||
|
<Block label="tvheadend.upcoming" />
|
||||||
|
<Block label="tvheadend.finished" />
|
||||||
|
<Block label="tvheadend.failed" />
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const upcomingCount = dvrData.entries.filter((entry) => entry.sched_status === "scheduled").length;
|
||||||
|
const finishedCount = dvrData.entries.filter((entry) => entry.sched_status === "completed").length;
|
||||||
|
const failedCount = dvrData.entries.filter((entry) => entry.sched_status === "failed").length;
|
||||||
|
|
||||||
|
const hasSubscriptions = Array.isArray(subscriptionsData.entries) && subscriptionsData.entries.length > 0;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Container service={service}>
|
||||||
|
<Block label="tvheadend.upcoming" value={t("common.number", { value: upcomingCount })} />
|
||||||
|
<Block label="tvheadend.finished" value={t("common.number", { value: finishedCount })} />
|
||||||
|
<Block label="tvheadend.failed" value={t("common.number", { value: failedCount })} />
|
||||||
|
</Container>
|
||||||
|
{hasSubscriptions &&
|
||||||
|
subscriptionsData.entries
|
||||||
|
.filter(
|
||||||
|
(entry) =>
|
||||||
|
entry.channel && // Only include entries with a valid channel
|
||||||
|
entry.state === "Running",
|
||||||
|
) // and being watched (Idle=downloading)
|
||||||
|
.sort((a, b) => a.channel.localeCompare(b.channel))
|
||||||
|
.map((subscription) => (
|
||||||
|
<Subscription
|
||||||
|
key={subscription.id}
|
||||||
|
channel={subscription.channel}
|
||||||
|
sinceStart={timeAgoToString(subscription.start)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
11
src/widgets/tvheadend/subscription.jsx
Normal file
11
src/widgets/tvheadend/subscription.jsx
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
export default function Subscription({ channel, sinceStart }) {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col pb-1 mx-1">
|
||||||
|
<div className="text-theme-700 dark:text-theme-200 text-xs relative h-5 w-full rounded-md bg-theme-200/50 dark:bg-theme-900/20 mt-1">
|
||||||
|
<div className="absolute left-2 text-xs mt-[2px]">{channel}</div>
|
||||||
|
<div className="grow " />
|
||||||
|
<div className="self-center text-xs flex justify-end mr-2 mt-[2px]">{sinceStart}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
19
src/widgets/tvheadend/widget.js
Normal file
19
src/widgets/tvheadend/widget.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import genericProxyHandler from "utils/proxy/handlers/generic";
|
||||||
|
|
||||||
|
const widget = {
|
||||||
|
api: "{url}/api/{endpoint}",
|
||||||
|
proxyHandler: genericProxyHandler,
|
||||||
|
|
||||||
|
mappings: {
|
||||||
|
dvr: {
|
||||||
|
endpoint: "dvr/entry/grid",
|
||||||
|
validate: ["entries"],
|
||||||
|
},
|
||||||
|
subscriptions: {
|
||||||
|
endpoint: "status/subscriptions",
|
||||||
|
validate: ["entries"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default widget;
|
||||||
@ -118,6 +118,7 @@ import traefik from "./traefik/widget";
|
|||||||
import transmission from "./transmission/widget";
|
import transmission from "./transmission/widget";
|
||||||
import tubearchivist from "./tubearchivist/widget";
|
import tubearchivist from "./tubearchivist/widget";
|
||||||
import truenas from "./truenas/widget";
|
import truenas from "./truenas/widget";
|
||||||
|
import tvheadend from "./tvheadend/widget";
|
||||||
import unifi from "./unifi/widget";
|
import unifi from "./unifi/widget";
|
||||||
import unmanic from "./unmanic/widget";
|
import unmanic from "./unmanic/widget";
|
||||||
import uptimekuma from "./uptimekuma/widget";
|
import uptimekuma from "./uptimekuma/widget";
|
||||||
@ -255,6 +256,7 @@ const widgets = {
|
|||||||
transmission,
|
transmission,
|
||||||
tubearchivist,
|
tubearchivist,
|
||||||
truenas,
|
truenas,
|
||||||
|
tvheadend,
|
||||||
unifi,
|
unifi,
|
||||||
unifi_console: unifi,
|
unifi_console: unifi,
|
||||||
unmanic,
|
unmanic,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user