Add vidstack video player

thrimbletrimmer-solid
ElementalAlchemist 2 weeks ago
parent 5ee213455d
commit d0640bf5c8

@ -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"
}
}

@ -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<StreamVideoInfo>;
setStreamVideoInfo: Setter<StreamVideoInfo>;
showTimeRangeLink: boolean;
errorList: Accessor<string[]>;
setErrorList: Setter<string[]>;
}
export const StreamTimeSettings: Component<StreamTimeSettingsProps> = (props) => {
const [timeType, setTimeType] = createSignal<TimeType>(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<KeyboardShortcutProps> = (
</details>
);
};
export interface VideoPlayerProps {
videoURL: string;
errorList: Accessor<string[]>;
setErrorList: Setter<string[]>;
videoPlayer: Accessor<Hls>;
}
export const VideoPlayer: Component<VideoPlayerProps> = (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 <video ref={videoElement} controls={true}></video>;
};

@ -1,8 +1,3 @@
.hidden {
display: none;
}
video {
max-width: 100%;
max-height: 50vh;
}

@ -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<RestreamerDefaultProps> = (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<RestreamerDefaultProps> = (props) => {
return url;
};
videoPlayer().loadSource(videoURL());
return (
<>
<StreamTimeSettings
@ -121,12 +118,10 @@ const RestreamerWithDefaults: Component<RestreamerDefaultProps> = (props) => {
setStreamVideoInfo={setStreamVideoInfo}
showTimeRangeLink={false}
/>
<VideoPlayer
videoURL={videoURL()}
errorList={props.errorList}
setErrorList={props.setErrorList}
videoPlayer={videoPlayer}
/>
<media-player src={videoURL()} preload="auto">
<media-provider />
<media-video-layout />
</media-player>
</>
);
};

@ -7,7 +7,7 @@
"esModuleInterop": true,
"jsx": "preserve",
"jsxImportSource": "solid-js",
"types": ["vite/client"],
"types": ["vidstack/solid", "vite/client"],
"noEmit": true,
"isolatedModules": true
}

Loading…
Cancel
Save