"use client"; import { useEffect, useRef } from "react"; import { Anchor, Box, Center, Group, Stack, Title } from "@mantine/core"; import { IconBrandYoutube, IconDeviceCctvOff } from "@tabler/icons-react"; import videojs from "video.js"; import { useI18n } from "@homarr/translation/client"; import type { WidgetComponentProps } from "../definition"; import classes from "./component.module.css"; import "video.js/dist/video-js.css"; import type Player from "video.js/dist/types/player"; import { createDocumentationLink } from "@homarr/definitions"; export default function VideoWidget({ options, isEditMode }: WidgetComponentProps<"video">) { if (options.feedUrl.trim() === "") { return ; } if (isYouTubeUrl(options.feedUrl)) { return ; } return ; } export const isYouTubeUrl = (value: string): boolean => { try { const url = new URL(value); return url.hostname === "youtu.be" || url.hostname === "youtube.com" || url.hostname.endsWith(".youtube.com"); } catch { return false; } }; 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, isEditMode }: Pick, "options"> & { isEditMode: boolean }) => { const videoRef = useRef(null); const playerRef = useRef(null); useEffect(() => { if (playerRef.current) 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); playerRef.current = videojs(videoElement, { autoplay: options.hasAutoPlay, muted: options.isMuted, controls: options.hasControls, sources: [ { src: options.feedUrl, }, ], }); // All other properties are updated with other useEffect }, [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(!isEditMode && options.hasControls); }, [isEditMode, options.hasControls]); useEffect(() => { const player = playerRef.current; return () => { if (player && !player.isDisposed()) { player.dispose(); playerRef.current = null; } }; }, [playerRef]); return ( ); };