From 89ff17564e923d8b5099227a5c521a840170872a Mon Sep 17 00:00:00 2001 From: ElementalAlchemist Date: Sun, 10 Nov 2024 23:49:34 -0600 Subject: [PATCH] Basic loading of videos --- thrimbletrimmer/src/common/video.module.scss | 2 +- thrimbletrimmer/src/common/video.tsx | 30 +++++++++- thrimbletrimmer/src/globalStyle.scss | 5 ++ thrimbletrimmer/src/restreamer/Restreamer.tsx | 55 +++++++++++++++++-- .../src/thumbnails/ThumbnailManager.tsx | 4 +- 5 files changed, 89 insertions(+), 7 deletions(-) diff --git a/thrimbletrimmer/src/common/video.module.scss b/thrimbletrimmer/src/common/video.module.scss index f40dd5c..f543f05 100644 --- a/thrimbletrimmer/src/common/video.module.scss +++ b/thrimbletrimmer/src/common/video.module.scss @@ -8,4 +8,4 @@ .streamTimeSettingLabel { margin-right: 3px; -} \ No newline at end of file +} diff --git a/thrimbletrimmer/src/common/video.tsx b/thrimbletrimmer/src/common/video.tsx index 86aa053..64c7614 100644 --- a/thrimbletrimmer/src/common/video.tsx +++ b/thrimbletrimmer/src/common/video.tsx @@ -1,4 +1,5 @@ -import { Accessor, Component, createSignal, Setter, Show } from "solid-js"; +import { Accessor, Component, createEffect, createSignal, Setter, Show } from "solid-js"; +import Hls from "hls.js"; import { DateTime } from "luxon"; import { TimeType, @@ -8,6 +9,8 @@ import { } 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]; @@ -177,3 +180,28 @@ 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 35998a3..c95dc87 100644 --- a/thrimbletrimmer/src/globalStyle.scss +++ b/thrimbletrimmer/src/globalStyle.scss @@ -1,3 +1,8 @@ .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 ca31f94..f12525e 100644 --- a/thrimbletrimmer/src/restreamer/Restreamer.tsx +++ b/thrimbletrimmer/src/restreamer/Restreamer.tsx @@ -1,8 +1,23 @@ -import { Accessor, Component, createResource, createSignal, For, Show, Suspense } from "solid-js"; +import { + Accessor, + Component, + createResource, + createSignal, + For, + Setter, + Show, + Suspense, +} from "solid-js"; +import Hls from "hls.js"; import { DateTime } from "luxon"; import styles from "./Restreamer.module.scss"; -import { dateTimeFromWubloaderTime } from "../common/convertTime"; -import { KeyboardShortcuts, StreamTimeSettings, StreamVideoInfo } from "../common/video"; +import { dateTimeFromWubloaderTime, wubloaderTimeFromDateTime } from "../common/convertTime"; +import { + KeyboardShortcuts, + StreamTimeSettings, + StreamVideoInfo, + VideoPlayer, +} from "../common/video"; export interface DefaultsData { video_channel: string; @@ -51,7 +66,11 @@ export const Restreamer: Component = () => { - + @@ -60,6 +79,8 @@ export const Restreamer: Component = () => { interface RestreamerDefaultProps { defaults: DefaultsData; + errorList: Accessor; + setErrorList: Setter; } const RestreamerWithDefaults: Component = (props) => { @@ -72,6 +93,26 @@ const RestreamerWithDefaults: Component = (props) => { streamEndTime: null, }); + const [videoPlayer, setVideoPlayer] = createSignal(new Hls()); + + const videoURL = () => { + const streamInfo = streamVideoInfo(); + const startTime = wubloaderTimeFromDateTime(streamInfo.streamStartTime); + const query = new URLSearchParams({ start: startTime }); + if (streamInfo.streamEndTime) { + const endTime = wubloaderTimeFromDateTime(streamInfo.streamEndTime); + query.append("end", endTime); + } + const queryString = query.toString(); + let url = `/playlist/${streamInfo.streamName}.m3u8`; + if (queryString !== "") { + url += `?${queryString}`; + } + return url; + }; + + videoPlayer().loadSource(videoURL()); + return ( <> = (props) => { setStreamVideoInfo={setStreamVideoInfo} showTimeRangeLink={false} /> + ); }; diff --git a/thrimbletrimmer/src/thumbnails/ThumbnailManager.tsx b/thrimbletrimmer/src/thumbnails/ThumbnailManager.tsx index 1a96ed6..bb970a4 100644 --- a/thrimbletrimmer/src/thumbnails/ThumbnailManager.tsx +++ b/thrimbletrimmer/src/thumbnails/ThumbnailManager.tsx @@ -334,7 +334,9 @@ const ThumbnailManager: Component = () => {
- +
    {(error: string, index: Accessor) =>
  • {error}
  • }