fix(video-widget): hls videos not working (#4015)
This commit is contained in:
@@ -42,9 +42,11 @@ const nextConfig: NextConfig = {
|
|||||||
headers: [
|
headers: [
|
||||||
{
|
{
|
||||||
key: "Content-Security-Policy",
|
key: "Content-Security-Policy",
|
||||||
|
// worker-src / media-src with blob: is necessary for video.js, see https://github.com/homarr-labs/homarr/issues/3912 and https://stackoverflow.com/questions/65792855/problem-with-video-js-and-content-security-policy-csp
|
||||||
value: `
|
value: `
|
||||||
default-src 'self';
|
default-src 'self';
|
||||||
script-src * 'unsafe-inline' 'unsafe-eval';
|
script-src * 'unsafe-inline' 'unsafe-eval';
|
||||||
|
worker-src * blob:;
|
||||||
base-uri 'self';
|
base-uri 'self';
|
||||||
connect-src *;
|
connect-src *;
|
||||||
style-src * 'unsafe-inline';
|
style-src * 'unsafe-inline';
|
||||||
@@ -53,7 +55,7 @@ const nextConfig: NextConfig = {
|
|||||||
form-action 'self';
|
form-action 'self';
|
||||||
img-src * data:;
|
img-src * data:;
|
||||||
font-src * data:;
|
font-src * data:;
|
||||||
media-src * data:;
|
media-src * data: blob:;
|
||||||
`
|
`
|
||||||
.replace(/\s{2,}/g, " ")
|
.replace(/\s{2,}/g, " ")
|
||||||
.trim(),
|
.trim(),
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useEffect, useRef } from "react";
|
import { useEffect, useRef } from "react";
|
||||||
import { Anchor, Center, Group, Stack, Title } from "@mantine/core";
|
import { Anchor, Box, Center, Group, Stack, Title } from "@mantine/core";
|
||||||
import { IconBrandYoutube, IconDeviceCctvOff } from "@tabler/icons-react";
|
import { IconBrandYoutube, IconDeviceCctvOff } from "@tabler/icons-react";
|
||||||
import combineClasses from "clsx";
|
|
||||||
import videojs from "video.js";
|
import videojs from "video.js";
|
||||||
|
|
||||||
import { useI18n } from "@homarr/translation/client";
|
import { useI18n } from "@homarr/translation/client";
|
||||||
@@ -13,6 +12,8 @@ import classes from "./component.module.css";
|
|||||||
|
|
||||||
import "video.js/dist/video-js.css";
|
import "video.js/dist/video-js.css";
|
||||||
|
|
||||||
|
import type Player from "video.js/dist/types/player";
|
||||||
|
|
||||||
import { createDocumentationLink } from "@homarr/definitions";
|
import { createDocumentationLink } from "@homarr/definitions";
|
||||||
|
|
||||||
export default function VideoWidget({ options }: WidgetComponentProps<"video">) {
|
export default function VideoWidget({ options }: WidgetComponentProps<"video">) {
|
||||||
@@ -55,32 +56,66 @@ const ForYoutubeUseIframe = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const Feed = ({ options }: Pick<WidgetComponentProps<"video">, "options">) => {
|
const Feed = ({ options }: Pick<WidgetComponentProps<"video">, "options">) => {
|
||||||
const videoRef = useRef<HTMLVideoElement>(null);
|
const videoRef = useRef<HTMLDivElement>(null);
|
||||||
|
const playerRef = useRef<Player>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!videoRef.current) {
|
if (playerRef.current) return;
|
||||||
return;
|
const videoElement = document.createElement("video-js");
|
||||||
|
videoElement.classList.add("vjs-big-play-centered");
|
||||||
|
if (classes.video) {
|
||||||
|
videoElement.classList.add(classes.video);
|
||||||
}
|
}
|
||||||
|
videoRef.current?.appendChild(videoElement);
|
||||||
|
|
||||||
// Initialize Video.js player if it's not already initialized
|
playerRef.current = videojs(videoElement, {
|
||||||
if (!("player" in videoRef.current)) {
|
autoplay: options.hasAutoPlay,
|
||||||
videojs(
|
muted: options.isMuted,
|
||||||
videoRef.current,
|
controls: options.hasControls,
|
||||||
|
sources: [
|
||||||
{
|
{
|
||||||
autoplay: options.hasAutoPlay,
|
src: options.feedUrl,
|
||||||
muted: options.isMuted,
|
|
||||||
controls: options.hasControls,
|
|
||||||
},
|
},
|
||||||
() => undefined,
|
],
|
||||||
);
|
});
|
||||||
}
|
// All other properties are updated with other useEffect
|
||||||
}, [options.hasAutoPlay, options.hasControls, options.isMuted, videoRef]);
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [videoRef]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!playerRef.current) return;
|
||||||
|
playerRef.current.src(options.feedUrl);
|
||||||
|
}, [options.feedUrl]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!playerRef.current) return;
|
||||||
|
playerRef.current.autoplay(options.hasAutoPlay);
|
||||||
|
}, [options.hasAutoPlay]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!playerRef.current) return;
|
||||||
|
playerRef.current.muted(options.isMuted);
|
||||||
|
}, [options.isMuted]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!playerRef.current) return;
|
||||||
|
playerRef.current.controls(options.hasControls);
|
||||||
|
}, [options.hasControls]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const player = playerRef.current;
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (player && !player.isDisposed()) {
|
||||||
|
player.dispose();
|
||||||
|
playerRef.current = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, [playerRef]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Group justify="center" w="100%" h="100%" pos="relative">
|
<Group justify="center" w="100%" h="100%" pos="relative">
|
||||||
<video className={combineClasses("video-js", classes.video)} ref={videoRef}>
|
<Box w="100%" h="100%" ref={videoRef} />
|
||||||
<source src={options.feedUrl} />
|
|
||||||
</video>
|
|
||||||
</Group>
|
</Group>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user