From 0aed412ccb1b2def54447a22f2438b99fd3335f0 Mon Sep 17 00:00:00 2001 From: ElementalAlchemist Date: Mon, 11 Nov 2024 18:40:10 -0600 Subject: [PATCH] Add vidstack video player --- thrimbletrimmer/package.json | 3 +- thrimbletrimmer/src/common/video.tsx | 75 ++++++++++++------- thrimbletrimmer/src/globalStyle.scss | 5 -- thrimbletrimmer/src/restreamer/Restreamer.tsx | 29 +++---- thrimbletrimmer/tsconfig.json | 2 +- 5 files changed, 61 insertions(+), 53 deletions(-) diff --git a/thrimbletrimmer/package.json b/thrimbletrimmer/package.json index 034fe75..2a9a0f2 100644 --- a/thrimbletrimmer/package.json +++ b/thrimbletrimmer/package.json @@ -22,6 +22,7 @@ "dependencies": { "hls.js": "1.5.17", "luxon": "3.4.4", - "solid-js": "1.9.3" + "solid-js": "1.9.3", + "vidstack": "1.12.12" } } diff --git a/thrimbletrimmer/src/common/video.tsx b/thrimbletrimmer/src/common/video.tsx index 64c7614..73086b2 100644 --- a/thrimbletrimmer/src/common/video.tsx +++ b/thrimbletrimmer/src/common/video.tsx @@ -1,16 +1,16 @@ import { Accessor, Component, createEffect, createSignal, Setter, Show } from "solid-js"; -import Hls from "hls.js"; import { DateTime } from "luxon"; import { TimeType, wubloaderTimeFromDateTime, busTimeFromDateTime, timeAgoFromDateTime, + dateTimeFromWubloaderTime, + dateTimeFromBusTime, + dateTimeFromTimeAgo, } from "./convertTime"; import styles from "./video.module.scss"; -Hls.DefaultConfig.maxBufferHole = 600; - export const VIDEO_FRAMES_PER_SECOND = 30; export const PLAYBACK_RATES = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2, 4, 8]; @@ -26,16 +26,58 @@ export interface StreamTimeSettingsProps { streamVideoInfo: Accessor; setStreamVideoInfo: Setter; showTimeRangeLink: boolean; + errorList: Accessor; + setErrorList: Setter; } export const StreamTimeSettings: Component = (props) => { const [timeType, setTimeType] = createSignal(TimeType.UTC); const submitHandler = (event: SubmitEvent) => { + event.preventDefault(); + const form = event.currentTarget as HTMLFormElement; const formData = new FormData(form); - // TODO + const streamName = formData.get("stream") as string; + const startTimeEntered = formData.get("start-time") as string; + const endTimeEntered = formData.get("end-time") as string; + const timeType = +formData.get("time-type") as TimeType; + + let startTime: DateTime | null = null; + let endTime: DateTime | null = null; + switch (timeType) { + case TimeType.UTC: + startTime = dateTimeFromWubloaderTime(startTimeEntered); + if (endTimeEntered !== "") { + endTime = dateTimeFromWubloaderTime(endTimeEntered); + } + break; + case TimeType.BusTime: + startTime = dateTimeFromBusTime(props.busStartTime(), startTimeEntered); + if (endTimeEntered !== "") { + endTime = dateTimeFromBusTime(props.busStartTime(), endTimeEntered); + } + break; + case TimeType.TimeAgo: + startTime = dateTimeFromTimeAgo(startTimeEntered); + if (endTimeEntered !== "") { + endTime = dateTimeFromTimeAgo(endTimeEntered); + } + break; + } + + if (startTime === null || (endTimeEntered !== "" && endTime === null)) { + const error = "A load boundary time could not be parsed. Check the format of your times."; + props.setErrorList([...props.errorList(), error]); + return; + } + + props.setStreamVideoInfo({ + streamName: streamName, + streamStartTime: startTime, + streamEndTime: endTime, + }); }; const startTimeDisplay = () => { @@ -180,28 +222,3 @@ export const KeyboardShortcuts: Component = ( ); }; - -export interface VideoPlayerProps { - videoURL: string; - errorList: Accessor; - setErrorList: Setter; - videoPlayer: Accessor; -} - -export const VideoPlayer: Component = (props) => { - if (!Hls.isSupported()) { - const newError = - "Your browser doesn't support MediaSource extensions. Video playback and editing won't work."; - props.setErrorList([...props.errorList(), newError]); - return <>; - } - - let videoElement; - createEffect(() => { - if (videoElement) { - props.videoPlayer().attachMedia(videoElement); - } - }); - - return ; -}; diff --git a/thrimbletrimmer/src/globalStyle.scss b/thrimbletrimmer/src/globalStyle.scss index c95dc87..35998a3 100644 --- a/thrimbletrimmer/src/globalStyle.scss +++ b/thrimbletrimmer/src/globalStyle.scss @@ -1,8 +1,3 @@ .hidden { display: none; } - -video { - max-width: 100%; - max-height: 50vh; -} \ No newline at end of file diff --git a/thrimbletrimmer/src/restreamer/Restreamer.tsx b/thrimbletrimmer/src/restreamer/Restreamer.tsx index f12525e..1eccaca 100644 --- a/thrimbletrimmer/src/restreamer/Restreamer.tsx +++ b/thrimbletrimmer/src/restreamer/Restreamer.tsx @@ -8,16 +8,17 @@ import { Show, Suspense, } from "solid-js"; -import Hls from "hls.js"; import { DateTime } from "luxon"; import styles from "./Restreamer.module.scss"; import { dateTimeFromWubloaderTime, wubloaderTimeFromDateTime } from "../common/convertTime"; -import { - KeyboardShortcuts, - StreamTimeSettings, - StreamVideoInfo, - VideoPlayer, -} from "../common/video"; +import { KeyboardShortcuts, StreamTimeSettings, StreamVideoInfo } from "../common/video"; + +import "vidstack/player/styles/default/theme.css"; +import "vidstack/player/styles/default/layouts/video.css"; +import "vidstack/player"; +import "vidstack/player/layouts/default"; +import "vidstack/player/ui"; +import { MediaPlayerElement } from "vidstack/elements"; export interface DefaultsData { video_channel: string; @@ -93,8 +94,6 @@ const RestreamerWithDefaults: Component = (props) => { streamEndTime: null, }); - const [videoPlayer, setVideoPlayer] = createSignal(new Hls()); - const videoURL = () => { const streamInfo = streamVideoInfo(); const startTime = wubloaderTimeFromDateTime(streamInfo.streamStartTime); @@ -111,8 +110,6 @@ const RestreamerWithDefaults: Component = (props) => { return url; }; - videoPlayer().loadSource(videoURL()); - return ( <> = (props) => { setStreamVideoInfo={setStreamVideoInfo} showTimeRangeLink={false} /> - + + + + ); }; diff --git a/thrimbletrimmer/tsconfig.json b/thrimbletrimmer/tsconfig.json index 25838eb..47dd96e 100644 --- a/thrimbletrimmer/tsconfig.json +++ b/thrimbletrimmer/tsconfig.json @@ -7,7 +7,7 @@ "esModuleInterop": true, "jsx": "preserve", "jsxImportSource": "solid-js", - "types": ["vite/client"], + "types": ["vidstack/solid", "vite/client"], "noEmit": true, "isolatedModules": true }