mirror of
https://github.com/movie-web/movie-web.git
synced 2024-12-27 07:21:49 +01:00
HLS support and some styling fixes for context menus
Co-authored-by: Jip Frijlink <JipFr@users.noreply.github.com>
This commit is contained in:
parent
8f8bbf28c1
commit
f2266bff6b
@ -11,6 +11,7 @@ import { useOverlayRouter } from "@/hooks/useOverlayRouter";
|
|||||||
import { usePlayerStore } from "@/stores/player/store";
|
import { usePlayerStore } from "@/stores/player/store";
|
||||||
import {
|
import {
|
||||||
SourceQuality,
|
SourceQuality,
|
||||||
|
allQualities,
|
||||||
qualityToString,
|
qualityToString,
|
||||||
} from "@/stores/player/utils/qualities";
|
} from "@/stores/player/utils/qualities";
|
||||||
|
|
||||||
@ -26,7 +27,7 @@ function QualityOption(props: {
|
|||||||
textClasses = "text-video-context-type-main text-opacity-40";
|
textClasses = "text-video-context-type-main text-opacity-40";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Context.Link noHover={props.disabled} onClick={props.onClick}>
|
<Context.Link onClick={props.disabled ? undefined : props.onClick}>
|
||||||
<Context.LinkTitle textClass={textClasses}>
|
<Context.LinkTitle textClass={textClasses}>
|
||||||
{props.children}
|
{props.children}
|
||||||
</Context.LinkTitle>
|
</Context.LinkTitle>
|
||||||
@ -54,23 +55,28 @@ function QualityView({ id }: { id: string }) {
|
|||||||
[router, switchQuality]
|
[router, switchQuality]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const allVisibleQualities = allQualities.filter((t) => t !== "unknown");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Context.BackLink onClick={() => router.navigate("/")}>
|
<Context.BackLink onClick={() => router.navigate("/")}>
|
||||||
Quality
|
Quality
|
||||||
</Context.BackLink>
|
</Context.BackLink>
|
||||||
<Context.Section>
|
<Context.Section>
|
||||||
{availableQualities.map((v) => (
|
{allVisibleQualities.map((v) => (
|
||||||
<QualityOption
|
<QualityOption
|
||||||
key={v}
|
key={v}
|
||||||
selected={v === currentQuality}
|
selected={v === currentQuality}
|
||||||
onClick={() => change(v)}
|
onClick={
|
||||||
|
availableQualities.includes(v) ? () => change(v) : undefined
|
||||||
|
}
|
||||||
|
disabled={!availableQualities.includes(v)}
|
||||||
>
|
>
|
||||||
{qualityToString(v)}
|
{qualityToString(v)}
|
||||||
</QualityOption>
|
</QualityOption>
|
||||||
))}
|
))}
|
||||||
<Context.Divider />
|
<Context.Divider />
|
||||||
<Context.Link noHover onClick={() => router.navigate("/")}>
|
<Context.Link>
|
||||||
<Context.LinkTitle>Automatic quality</Context.LinkTitle>
|
<Context.LinkTitle>Automatic quality</Context.LinkTitle>
|
||||||
<span>Toggle</span>
|
<span>Toggle</span>
|
||||||
</Context.Link>
|
</Context.Link>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import fscreen from "fscreen";
|
import fscreen from "fscreen";
|
||||||
|
import Hls from "hls.js";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
DisplayInterface,
|
DisplayInterface,
|
||||||
@ -17,15 +18,38 @@ import { makeEmitter } from "@/utils/events";
|
|||||||
export function makeVideoElementDisplayInterface(): DisplayInterface {
|
export function makeVideoElementDisplayInterface(): DisplayInterface {
|
||||||
const { emit, on, off } = makeEmitter<DisplayInterfaceEvents>();
|
const { emit, on, off } = makeEmitter<DisplayInterfaceEvents>();
|
||||||
let source: LoadableSource | null = null;
|
let source: LoadableSource | null = null;
|
||||||
|
let hls: Hls | null = null;
|
||||||
let videoElement: HTMLVideoElement | null = null;
|
let videoElement: HTMLVideoElement | null = null;
|
||||||
let containerElement: HTMLElement | null = null;
|
let containerElement: HTMLElement | null = null;
|
||||||
let isFullscreen = false;
|
let isFullscreen = false;
|
||||||
let isPausedBeforeSeeking = false;
|
let isPausedBeforeSeeking = false;
|
||||||
let isSeeking = false;
|
let isSeeking = false;
|
||||||
|
|
||||||
|
function setupSource(vid: HTMLVideoElement, src: LoadableSource) {
|
||||||
|
if (src.type === "hls") {
|
||||||
|
if (!Hls.isSupported()) throw new Error("HLS not supported");
|
||||||
|
|
||||||
|
hls = new Hls({ enableWorker: false });
|
||||||
|
hls.on(Hls.Events.ERROR, (event, data) => {
|
||||||
|
console.error("HLS error", data);
|
||||||
|
if (data.fatal) {
|
||||||
|
throw new Error(
|
||||||
|
`HLS ERROR:${data.error?.message ?? "Something went wrong"}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
hls.attachMedia(vid);
|
||||||
|
hls.loadSource(src.url);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
vid.src = src.url;
|
||||||
|
}
|
||||||
|
|
||||||
function setSource() {
|
function setSource() {
|
||||||
if (!videoElement || !source) return;
|
if (!videoElement || !source) return;
|
||||||
videoElement.src = source.url;
|
setupSource(videoElement, source);
|
||||||
|
|
||||||
videoElement.addEventListener("play", () => {
|
videoElement.addEventListener("play", () => {
|
||||||
emit("play", undefined);
|
emit("play", undefined);
|
||||||
@ -64,6 +88,7 @@ export function makeVideoElementDisplayInterface(): DisplayInterface {
|
|||||||
on,
|
on,
|
||||||
off,
|
off,
|
||||||
destroy: () => {
|
destroy: () => {
|
||||||
|
if (hls) hls.destroy();
|
||||||
if (videoElement) {
|
if (videoElement) {
|
||||||
videoElement.src = "";
|
videoElement.src = "";
|
||||||
videoElement.remove();
|
videoElement.remove();
|
||||||
|
@ -31,21 +31,29 @@ function Section(props: { children: React.ReactNode }) {
|
|||||||
return <div className="my-5">{props.children}</div>;
|
return <div className="my-5">{props.children}</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function Link(props: {
|
function Link(props: { onClick?: () => void; children: React.ReactNode }) {
|
||||||
onClick?: () => void;
|
const classes = classNames(
|
||||||
children: React.ReactNode;
|
"flex justify-between items-center py-2 pl-3 pr-3 -ml-3 rounded w-full",
|
||||||
noHover?: boolean;
|
{
|
||||||
}) {
|
"cursor-default": !props.onClick,
|
||||||
|
"hover:bg-video-context-border hover:bg-opacity-10": !!props.onClick,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const styles = { width: "calc(100% + 1.5rem)" };
|
||||||
|
|
||||||
|
if (!props.onClick) {
|
||||||
|
return (
|
||||||
|
<div className={classes} style={styles}>
|
||||||
|
{props.children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className={classNames([
|
className={classes}
|
||||||
"flex justify-between items-center py-2 pl-3 pr-3 -ml-3 rounded w-full",
|
style={styles}
|
||||||
props.noHover
|
|
||||||
? "cursor-default"
|
|
||||||
: "hover:bg-video-context-border hover:bg-opacity-10",
|
|
||||||
])}
|
|
||||||
style={{ width: "calc(100% + 1.5rem)" }}
|
|
||||||
onClick={props.onClick}
|
onClick={props.onClick}
|
||||||
>
|
>
|
||||||
{props.children}
|
{props.children}
|
||||||
@ -59,11 +67,11 @@ function BackLink(props: {
|
|||||||
rightSide?: React.ReactNode;
|
rightSide?: React.ReactNode;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<h3 className="font-bold text-video-context-type-main pb-4 pt-5 border-b border-opacity-25 border-video-context-border mb-6 flex justify-between items-center">
|
<h3 className="font-bold text-video-context-type-main pb-3 pt-5 border-b border-opacity-25 border-video-context-border mb-6 flex justify-between items-center">
|
||||||
<div className="flex items-center space-x-3">
|
<div className="flex items-center space-x-3">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="-ml-1 p-1 rounded hover:bg-video-context-light hover:bg-opacity-10"
|
className="-ml-2 p-2 rounded hover:bg-video-context-light hover:bg-opacity-10"
|
||||||
onClick={props.onClick}
|
onClick={props.onClick}
|
||||||
>
|
>
|
||||||
<Icon className="text-xl" icon={Icons.ARROW_LEFT} />
|
<Icon className="text-xl" icon={Icons.ARROW_LEFT} />
|
||||||
|
@ -51,7 +51,7 @@ const qualityMap: Record<SourceQuality, string> = {
|
|||||||
unknown: "unknown",
|
unknown: "unknown",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const allQualities = Object.keys(qualityMap);
|
export const allQualities = Object.keys(qualityMap) as SourceQuality[];
|
||||||
|
|
||||||
export function qualityToString(quality: SourceQuality): string {
|
export function qualityToString(quality: SourceQuality): string {
|
||||||
return qualityMap[quality];
|
return qualityMap[quality];
|
||||||
|
Loading…
Reference in New Issue
Block a user