diff --git a/apps/nextjs/src/components/board/sections/content.tsx b/apps/nextjs/src/components/board/sections/content.tsx
index cbf344828..bb6b7174e 100644
--- a/apps/nextjs/src/components/board/sections/content.tsx
+++ b/apps/nextjs/src/components/board/sections/content.tsx
@@ -98,7 +98,7 @@ const BoardItem = ({ item, ...dimensions }: ItemProps) => {
return (
<>
-
+
{
pos="absolute"
top={offset}
right={offset}
+ style={{ zIndex: 1 }}
>
diff --git a/packages/definitions/src/widget.ts b/packages/definitions/src/widget.ts
index 9a0ce8b0c..72f71fad4 100644
--- a/packages/definitions/src/widget.ts
+++ b/packages/definitions/src/widget.ts
@@ -1,2 +1,2 @@
-export const widgetKinds = ["clock", "weather", "app"] as const;
+export const widgetKinds = ["clock", "weather", "app", "video"] as const;
export type WidgetKind = (typeof widgetKinds)[number];
diff --git a/packages/translation/src/lang/en.ts b/packages/translation/src/lang/en.ts
index 7766b1646..ba09f37df 100644
--- a/packages/translation/src/lang/en.ts
+++ b/packages/translation/src/lang/en.ts
@@ -202,6 +202,7 @@ export default {
confirm: "Confirm",
previous: "Previous",
next: "Next",
+ checkoutDocs: "Check out the documentation",
},
multiSelect: {
placeholder: "Pick one or more values",
@@ -435,6 +436,30 @@ export default {
},
},
},
+ video: {
+ name: "Video Stream",
+ description: "Embed a video stream or video from a camera or a website",
+ option: {
+ feedUrl: {
+ label: "Feed URL",
+ },
+ hasAutoPlay: {
+ label: "Autoplay",
+ description:
+ "Autoplay only works when muted because of browser restrictions",
+ },
+ isMuted: {
+ label: "Muted",
+ },
+ hasControls: {
+ label: "Show controls",
+ },
+ },
+ error: {
+ noUrl: "No Video URL provided",
+ forYoutubeUseIframe: "For YouTube videos use the iframe option",
+ },
+ },
},
widgetPreview: {
toggle: {
diff --git a/packages/widgets/package.json b/packages/widgets/package.json
index da4a512bd..5ae8e29f3 100644
--- a/packages/widgets/package.json
+++ b/packages/widgets/package.json
@@ -23,6 +23,7 @@
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
+ "@types/video.js": "^7.3.57",
"eslint": "^8.57.0",
"typescript": "^5.4.5"
},
@@ -43,6 +44,7 @@
"@homarr/redis": "workspace:^0.1.0",
"@homarr/translation": "workspace:^0.1.0",
"@homarr/ui": "workspace:^0.1.0",
- "@homarr/validation": "workspace:^0.1.0"
+ "@homarr/validation": "workspace:^0.1.0",
+ "video.js": "^8.10.0"
}
}
diff --git a/packages/widgets/src/index.tsx b/packages/widgets/src/index.tsx
index b12edfa77..6fece044d 100644
--- a/packages/widgets/src/index.tsx
+++ b/packages/widgets/src/index.tsx
@@ -9,6 +9,7 @@ import * as app from "./app";
import * as clock from "./clock";
import type { WidgetComponentProps } from "./definition";
import type { WidgetImportRecord } from "./import";
+import * as video from "./video";
import * as weather from "./weather";
export { reduceWidgetOptionsWithDefaultValues } from "./options";
@@ -21,6 +22,7 @@ export const widgetImports = {
clock,
weather,
app,
+ video,
} satisfies WidgetImportRecord;
export type WidgetImports = typeof widgetImports;
diff --git a/packages/widgets/src/video/component.module.css b/packages/widgets/src/video/component.module.css
new file mode 100644
index 000000000..e2e3bfbe1
--- /dev/null
+++ b/packages/widgets/src/video/component.module.css
@@ -0,0 +1,6 @@
+.video {
+ height: 100%;
+ width: 100%;
+ border-radius: var(--mantine-radius-md);
+ overflow: hidden;
+}
diff --git a/packages/widgets/src/video/component.tsx b/packages/widgets/src/video/component.tsx
new file mode 100644
index 000000000..689b66ba3
--- /dev/null
+++ b/packages/widgets/src/video/component.tsx
@@ -0,0 +1,98 @@
+"use client";
+
+import { useEffect, useRef } from "react";
+import combineClasses from "clsx";
+import videojs from "video.js";
+
+import { useI18n } from "@homarr/translation/client";
+import {
+ Anchor,
+ Center,
+ Group,
+ IconBrandYoutube,
+ IconDeviceCctvOff,
+ Stack,
+ Title,
+} from "@homarr/ui";
+
+import type { WidgetComponentProps } from "../definition";
+import classes from "./component.module.css";
+
+import "video.js/dist/video-js.css";
+
+export default function VideoWidget({
+ options,
+}: WidgetComponentProps<"video">) {
+ if (options.feedUrl.trim() === "") {
+ return ;
+ }
+
+ if (options.feedUrl.trim().startsWith("https://www.youtube.com/watch")) {
+ return ;
+ }
+
+ return ;
+}
+
+const NoUrl = () => {
+ const t = useI18n();
+
+ return (
+
+
+
+ {t("widget.video.error.noUrl")}
+
+
+ );
+};
+
+const ForYoutubeUseIframe = () => {
+ const t = useI18n();
+
+ return (
+
+
+
+ {t("widget.video.error.forYoutubeUseIframe")}
+
+ {t("common.action.checkoutDocs")}
+
+
+
+ );
+};
+
+const Feed = ({ options }: Pick, "options">) => {
+ const videoRef = useRef(null);
+
+ useEffect(() => {
+ if (!videoRef.current) {
+ return;
+ }
+
+ // Initialize Video.js player if it's not already initialized
+ if (!("player" in videoRef.current)) {
+ videojs(
+ videoRef.current,
+ {
+ autoplay: options.hasAutoPlay,
+ muted: options.isMuted,
+ controls: options.hasControls,
+ },
+ () => undefined,
+ );
+ }
+ }, [videoRef]);
+
+ return (
+
+
+
+ );
+};
diff --git a/packages/widgets/src/video/index.ts b/packages/widgets/src/video/index.ts
new file mode 100644
index 000000000..c95278bf1
--- /dev/null
+++ b/packages/widgets/src/video/index.ts
@@ -0,0 +1,20 @@
+import { IconDeviceCctv } from "@homarr/ui";
+
+import { createWidgetDefinition } from "../definition";
+import { optionsBuilder } from "../options";
+
+export const { definition, componentLoader } = createWidgetDefinition("video", {
+ icon: IconDeviceCctv,
+ options: optionsBuilder.from((factory) => ({
+ feedUrl: factory.text({
+ defaultValue: "",
+ }),
+ hasAutoPlay: factory.switch({
+ withDescription: true,
+ }),
+ isMuted: factory.switch({
+ defaultValue: true,
+ }),
+ hasControls: factory.switch(),
+ })),
+}).withDynamicImport(() => import("./component"));
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 79a8b78fa..5a303233d 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -816,6 +816,9 @@ importers:
'@homarr/validation':
specifier: workspace:^0.1.0
version: link:../validation
+ video.js:
+ specifier: ^8.10.0
+ version: 8.10.0
devDependencies:
'@homarr/eslint-config':
specifier: workspace:^0.2.0
@@ -826,6 +829,9 @@ importers:
'@homarr/tsconfig':
specifier: workspace:^0.1.0
version: link:../../tooling/typescript
+ '@types/video.js':
+ specifier: ^7.3.57
+ version: 7.3.57
eslint:
specifier: ^8.57.0
version: 8.57.0
@@ -3253,6 +3259,10 @@ packages:
resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==}
dev: false
+ /@types/video.js@7.3.57:
+ resolution: {integrity: sha512-CKAaJ9p/myadqT/FAnlzVvHMVj35ynl86eGgqPM9paTCWbFtq9JUZATUzOir+bLjRyXqrjA10e5KgHc7dRR38g==}
+ dev: true
+
/@types/ws@8.5.10:
resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==}
dependencies:
@@ -3394,6 +3404,48 @@ packages:
/@ungap/structured-clone@1.2.0:
resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
+ /@videojs/http-streaming@3.10.0(video.js@8.10.0):
+ resolution: {integrity: sha512-Lf1rmhTalV4Gw0bJqHmH4lfk/FlepUDs9smuMtorblAYnqDlE2tbUOb7sBXVYoXGdbWbdTW8jH2cnS+6HWYJ4Q==}
+ engines: {node: '>=8', npm: '>=5'}
+ peerDependencies:
+ video.js: ^7 || ^8
+ dependencies:
+ '@babel/runtime': 7.23.9
+ '@videojs/vhs-utils': 4.0.0
+ aes-decrypter: 4.0.1
+ global: 4.4.0
+ m3u8-parser: 7.1.0
+ mpd-parser: 1.3.0
+ mux.js: 7.0.2
+ video.js: 8.10.0
+ dev: false
+
+ /@videojs/vhs-utils@3.0.5:
+ resolution: {integrity: sha512-PKVgdo8/GReqdx512F+ombhS+Bzogiofy1LgAj4tN8PfdBx3HSS7V5WfJotKTqtOWGwVfSWsrYN/t09/DSryrw==}
+ engines: {node: '>=8', npm: '>=5'}
+ dependencies:
+ '@babel/runtime': 7.23.9
+ global: 4.4.0
+ url-toolkit: 2.2.5
+ dev: false
+
+ /@videojs/vhs-utils@4.0.0:
+ resolution: {integrity: sha512-xJp7Yd4jMLwje2vHCUmi8MOUU76nxiwII3z4Eg3Ucb+6rrkFVGosrXlMgGnaLjq724j3wzNElRZ71D/CKrTtxg==}
+ engines: {node: '>=8', npm: '>=5'}
+ dependencies:
+ '@babel/runtime': 7.23.9
+ global: 4.4.0
+ url-toolkit: 2.2.5
+ dev: false
+
+ /@videojs/xhr@2.6.0:
+ resolution: {integrity: sha512-7J361GiN1tXpm+gd0xz2QWr3xNWBE+rytvo8J3KuggFaLg+U37gZQ2BuPLcnkfGffy2e+ozY70RHC8jt7zjA6Q==}
+ dependencies:
+ '@babel/runtime': 7.23.9
+ global: 4.4.0
+ is-function: 1.0.2
+ dev: false
+
/@vitejs/plugin-react@4.2.1(vite@5.2.6):
resolution: {integrity: sha512-oojO9IDc4nCUUi8qIR11KoQm0XFFLIwsRBwHRR4d/88IWghn1y6ckz/bJ8GHDCsYEJee8mDzqtJxh15/cisJNQ==}
engines: {node: ^14.18.0 || >=16.0.0}
@@ -3487,6 +3539,11 @@ packages:
pretty-format: 29.7.0
dev: true
+ /@xmldom/xmldom@0.8.10:
+ resolution: {integrity: sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==}
+ engines: {node: '>=10.0.0'}
+ dev: false
+
/@xterm/addon-canvas@0.7.0(@xterm/xterm@5.5.0):
resolution: {integrity: sha512-LF5LYcfvefJuJ7QotNRdRSPc9YASAVDeoT5uyXS/nZshZXjYplGXRECBGiznwvhNL2I8bq1Lf5MzRwstsYQ2Iw==}
peerDependencies:
@@ -3528,6 +3585,15 @@ packages:
engines: {node: '>=0.4.0'}
hasBin: true
+ /aes-decrypter@4.0.1:
+ resolution: {integrity: sha512-H1nh/P9VZXUf17AA5NQfJML88CFjVBDuGkp5zDHa7oEhYN9TTpNLJknRY1ie0iSKWlDf6JRnJKaZVDSQdPy6Cg==}
+ dependencies:
+ '@babel/runtime': 7.23.9
+ '@videojs/vhs-utils': 3.0.5
+ global: 4.4.0
+ pkcs7: 1.0.4
+ dev: false
+
/agent-base@6.0.2:
resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==}
engines: {node: '>= 6.0.0'}
@@ -4465,6 +4531,10 @@ packages:
csstype: 3.1.3
dev: false
+ /dom-walk@0.1.2:
+ resolution: {integrity: sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==}
+ dev: false
+
/dot-case@2.1.1:
resolution: {integrity: sha512-HnM6ZlFqcajLsyudHq7LeeLDr2rFAVYtDv/hV5qchQEidSck8j9OPUsXY9KwJv/lHMtYlX4DjRQqwFYa+0r8Ug==}
dependencies:
@@ -5518,6 +5588,13 @@ packages:
once: 1.4.0
dev: false
+ /global@4.4.0:
+ resolution: {integrity: sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==}
+ dependencies:
+ min-document: 2.19.0
+ process: 0.11.10
+ dev: false
+
/globals@11.12.0:
resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
engines: {node: '>=4'}
@@ -5752,6 +5829,10 @@ packages:
engines: {node: '>=8'}
dev: true
+ /individual@2.0.0:
+ resolution: {integrity: sha512-pWt8hBCqJsUWI/HtcfWod7+N9SgAqyPEaF7JQjwzjn5vGrpg6aQ5qeAFQ7dx//UH4J1O+7xqew+gCeeFt6xN/g==}
+ dev: false
+
/inflight@1.0.6:
resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
dependencies:
@@ -5926,6 +6007,10 @@ packages:
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
engines: {node: '>=8'}
+ /is-function@1.0.2:
+ resolution: {integrity: sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==}
+ dev: false
+
/is-generator-function@1.0.10:
resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==}
engines: {node: '>= 0.4'}
@@ -6278,6 +6363,10 @@ packages:
object.values: 1.1.7
dev: false
+ /keycode@2.2.0:
+ resolution: {integrity: sha512-ps3I9jAdNtRpJrbBvQjpzyFbss/skHqzS+eu4RxKLaEAtFqkjZaB6TZMSivPbLxf4K7VI4SjR0P5mRCX5+Q25A==}
+ dev: false
+
/keygrip@1.1.0:
resolution: {integrity: sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==}
engines: {node: '>= 0.6'}
@@ -6448,6 +6537,14 @@ packages:
es5-ext: 0.10.62
dev: false
+ /m3u8-parser@7.1.0:
+ resolution: {integrity: sha512-7N+pk79EH4oLKPEYdgRXgAsKDyA/VCo0qCHlUwacttQA0WqsjZQYmNfywMvjlY9MpEBVZEt0jKFd73Kv15EBYQ==}
+ dependencies:
+ '@babel/runtime': 7.23.9
+ '@videojs/vhs-utils': 3.0.5
+ global: 4.4.0
+ dev: false
+
/magic-string@0.30.7:
resolution: {integrity: sha512-8vBuFF/I/+OSLRmdf2wwFCJCz+nSn0m6DPvGH1fS/KiQoSaR+sETbov0eIk9KhEKy8CYqIkIAnbohxT/4H0kuA==}
engines: {node: '>=12'}
@@ -6577,6 +6674,12 @@ packages:
engines: {node: '>=10'}
dev: false
+ /min-document@2.19.0:
+ resolution: {integrity: sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ==}
+ dependencies:
+ dom-walk: 0.1.2
+ dev: false
+
/minimatch@3.1.2:
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
dependencies:
@@ -6656,6 +6759,16 @@ packages:
ufo: 1.4.0
dev: true
+ /mpd-parser@1.3.0:
+ resolution: {integrity: sha512-WgeIwxAqkmb9uTn4ClicXpEQYCEduDqRKfmUdp4X8vmghKfBNXZLYpREn9eqrDx/Tf5LhzRcJLSpi4ohfV742Q==}
+ hasBin: true
+ dependencies:
+ '@babel/runtime': 7.23.9
+ '@videojs/vhs-utils': 4.0.0
+ '@xmldom/xmldom': 0.8.10
+ global: 4.4.0
+ dev: false
+
/mrmime@2.0.0:
resolution: {integrity: sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==}
engines: {node: '>=10'}
@@ -6672,6 +6785,24 @@ packages:
resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==}
dev: true
+ /mux.js@7.0.2:
+ resolution: {integrity: sha512-CM6+QuyDbc0qW1OfEjkd2+jVKzTXF+z5VOKH0eZxtZtnrG/ilkW/U7l7IXGtBNLASF9sKZMcK1u669cq50Qq0A==}
+ engines: {node: '>=8', npm: '>=5'}
+ hasBin: true
+ dependencies:
+ '@babel/runtime': 7.23.9
+ global: 4.4.0
+ dev: false
+
+ /mux.js@7.0.3:
+ resolution: {integrity: sha512-gzlzJVEGFYPtl2vvEiJneSWAWD4nfYRHD5XgxmB2gWvXraMPOYk+sxfvexmNfjQUFpmk6hwLR5C6iSFmuwCHdQ==}
+ engines: {node: '>=8', npm: '>=5'}
+ hasBin: true
+ dependencies:
+ '@babel/runtime': 7.23.9
+ global: 4.4.0
+ dev: false
+
/mysql2@3.9.2:
resolution: {integrity: sha512-3Cwg/UuRkAv/wm6RhtPE5L7JlPB877vwSF6gfLAS68H+zhH+u5oa3AieqEd0D0/kC3W7qIhYbH419f7O9i/5nw==}
engines: {node: '>= 8.0'}
@@ -7170,6 +7301,13 @@ packages:
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
engines: {node: '>=8.6'}
+ /pkcs7@1.0.4:
+ resolution: {integrity: sha512-afRERtHn54AlwaF2/+LFszyAANTCggGilmcmILUzEjvs3XgFZT+xE6+QWQcAGmu4xajy+Xtj7acLOPdx5/eXWQ==}
+ hasBin: true
+ dependencies:
+ '@babel/runtime': 7.23.9
+ dev: false
+
/pkg-types@1.0.3:
resolution: {integrity: sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==}
dependencies:
@@ -7309,6 +7447,11 @@ packages:
resolution: {integrity: sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==}
dev: false
+ /process@0.11.10:
+ resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==}
+ engines: {node: '>= 0.6.0'}
+ dev: false
+
/prop-types@15.8.1:
resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
dependencies:
@@ -7817,6 +7960,12 @@ packages:
dependencies:
queue-microtask: 1.2.3
+ /rust-result@1.0.0:
+ resolution: {integrity: sha512-6cJzSBU+J/RJCF063onnQf0cDUOHs9uZI1oroSGnHOph+CQTIJ5Pp2hK5kEQq1+7yE/EEWfulSNXAQ2jikPthA==}
+ dependencies:
+ individual: 2.0.0
+ dev: false
+
/rxjs@6.6.7:
resolution: {integrity: sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==}
engines: {npm: '>=2.0.0'}
@@ -7843,6 +7992,12 @@ packages:
/safe-buffer@5.2.1:
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
+ /safe-json-parse@4.0.0:
+ resolution: {integrity: sha512-RjZPPHugjK0TOzFrLZ8inw44s9bKox99/0AZW9o/BEQVrJfhI+fIHMErnPyRa89/yRXUUr93q+tiN6zhoVV4wQ==}
+ dependencies:
+ rust-result: 1.0.0
+ dev: false
+
/safe-regex-test@1.0.3:
resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==}
engines: {node: '>= 0.4'}
@@ -8723,6 +8878,10 @@ packages:
requires-port: 1.0.0
dev: true
+ /url-toolkit@2.2.5:
+ resolution: {integrity: sha512-mtN6xk+Nac+oyJ/PrI7tzfmomRVNFIWKUbG8jdYFt52hxbiReFAXIjYskvu64/dvuW71IcB7lV8l0HvZMac6Jg==}
+ dev: false
+
/use-callback-ref@1.3.1(@types/react@18.2.76)(react@18.2.0):
resolution: {integrity: sha512-Lg4Vx1XZQauB42Hw3kK7JM6yjVjgFmFC5/Ab797s79aARomD2nEErc4mCgM8EZrARLmmbWpi5DGCadmK50DcAQ==}
engines: {node: '>=10'}
@@ -8819,6 +8978,45 @@ packages:
builtins: 5.0.1
dev: true
+ /video.js@8.10.0:
+ resolution: {integrity: sha512-7UeG/flj/pp8tNGW8WKPP1VJb3x2FgLoqUWzpZqkoq5YIyf6MNzmIrKtxprl438T5RVkcj+OzV8IX4jYSAn4Sw==}
+ dependencies:
+ '@babel/runtime': 7.23.9
+ '@videojs/http-streaming': 3.10.0(video.js@8.10.0)
+ '@videojs/vhs-utils': 4.0.0
+ '@videojs/xhr': 2.6.0
+ aes-decrypter: 4.0.1
+ global: 4.4.0
+ keycode: 2.2.0
+ m3u8-parser: 7.1.0
+ mpd-parser: 1.3.0
+ mux.js: 7.0.3
+ safe-json-parse: 4.0.0
+ videojs-contrib-quality-levels: 4.0.0(video.js@8.10.0)
+ videojs-font: 4.1.0
+ videojs-vtt.js: 0.15.5
+ dev: false
+
+ /videojs-contrib-quality-levels@4.0.0(video.js@8.10.0):
+ resolution: {integrity: sha512-u5rmd8BjLwANp7XwuQ0Q/me34bMe6zg9PQdHfTS7aXgiVRbNTb4djcmfG7aeSrkpZjg+XCLezFNenlJaCjBHKw==}
+ engines: {node: '>=14', npm: '>=6'}
+ peerDependencies:
+ video.js: ^8
+ dependencies:
+ global: 4.4.0
+ video.js: 8.10.0
+ dev: false
+
+ /videojs-font@4.1.0:
+ resolution: {integrity: sha512-X1LuPfLZPisPLrANIAKCknZbZu5obVM/ylfd1CN+SsCmPZQ3UMDPcvLTpPBJxcBuTpHQq2MO1QCFt7p8spnZ/w==}
+ dev: false
+
+ /videojs-vtt.js@0.15.5:
+ resolution: {integrity: sha512-yZbBxvA7QMYn15Lr/ZfhhLPrNpI/RmCSCqgIff57GC2gIrV5YfyzLfLyZMj0NnZSAz8syB4N0nHXpZg9MyrMOQ==}
+ dependencies:
+ global: 4.4.0
+ dev: false
+
/vite-node@1.5.0(@types/node@20.12.7):
resolution: {integrity: sha512-tV8h6gMj6vPzVCa7l+VGq9lwoJjW8Y79vst8QZZGiuRAfijU+EEWuc0kFpmndQrWhMMhet1jdSF+40KSZUqIIw==}
engines: {node: ^18.0.0 || >=20.0.0}