fp/packages/next/app/components/video-interactive.tsx

135 lines
4.6 KiB
TypeScript
Raw Normal View History

2024-01-20 16:16:14 +00:00
'use client';
2024-07-10 22:11:18 +00:00
import { IVod } from "@/app/lib/vods";
2024-01-20 16:16:14 +00:00
import { useRef, useState, useEffect, useCallback } from "react";
import { VideoPlayer } from "./video-player";
import { Tagger } from './tagger';
2024-07-10 22:11:18 +00:00
import { ITimestamp, getTimestampsForVod } from "@/app/lib/timestamps";
2024-01-20 16:16:14 +00:00
import { TimestampsList } from "./timestamps-list";
2024-07-10 22:11:18 +00:00
import { ITagVodRelation } from "@/app/lib/tag-vod-relations";
2024-01-20 16:16:14 +00:00
import { VideoContext } from "./video-context";
import { getVodTitle } from "./vod-page";
import { useSearchParams } from 'next/navigation';
2024-07-10 22:11:18 +00:00
import type VideoApiElement from "@mux/mux-player";
import { parseUrlTimestamp } from "@/app/lib/dates";
2024-01-20 16:16:14 +00:00
import { faTags, faNoteSticky, faClock } from "@fortawesome/free-solid-svg-icons";
import { Tag } from './tag';
import VodNav from './vod-nav';
import LinkableHeading from "./linkable-heading";
export interface IVideoInteractiveProps {
vod: IVod;
}
function secondsToHumanReadable(timestampInSeconds: number): string {
const hours = Math.floor(timestampInSeconds / 3600);
const minutes = Math.floor((timestampInSeconds % 3600) / 60);
const seconds = timestampInSeconds % 60;
return `${hours}h${minutes}m${seconds}s`;
}
function humanReadableTimestampToSeconds(timestamp: string): number | null {
const parts = timestamp.split(':');
if (parts.length !== 3) {
// Invalid format, return null or throw an error as appropriate
return null;
}
const hours = parseInt(parts[0], 10);
const minutes = parseInt(parts[1], 10);
const seconds = parseInt(parts[2], 10);
if (isNaN(hours) || isNaN(minutes) || isNaN(seconds)) {
// Invalid numeric values, return null or throw an error as appropriate
return null;
}
const totalSeconds = hours * 3600 + minutes * 60 + seconds;
return totalSeconds;
}
export function VideoInteractive({ vod }: IVideoInteractiveProps): React.JSX.Element {
const [timeStamp, setTimeStamp] = useState(0);
const [tvrs, setTvrs] = useState([]);
const [isPlayerReady, setIsPlayerReady] = useState(false);
const [timestamps, setTimestamps] = useState<ITimestamp[]>([]);
const [currentTsPage, setCurrentTsPage] = useState(1);
const getTimestampPage = useCallback(async (page: number) => {
const timestamps = await getTimestampsForVod(vod.id, page);
setTimestamps(timestamps);
}, [vod.id, setTimestamps]); // IGNORE TS LINTER! DO NOT PUT timestamps HERE! IT CAUSES SELF-DDOS!
const ref = useRef(null);
const searchParams = useSearchParams();
const t = searchParams.get('t');
useEffect(() => {
getTimestampPage(currentTsPage);
}, [vod.id, getTimestampPage, currentTsPage]);
useEffect(() => {
if (!t) return;
if (!ref?.current) return;
const videoRef = ref.current as VideoApiElement;
const seconds = parseUrlTimestamp(t)
if (seconds === null) return;
videoRef.currentTime = seconds;
}, [t, isPlayerReady, ref])
return (
<VideoContext.Provider value={{
timeStamp,
setTimeStamp,
tvrs,
setTvrs
}}>
2024-04-08 18:24:54 +00:00
2024-01-20 16:16:14 +00:00
<VideoPlayer
vod={vod}
ref={ref}
setIsPlayerReady={setIsPlayerReady}
></VideoPlayer>
<h3 className="subtitle is-3">
{getVodTitle(vod)}
</h3>
<VodNav vod={vod}></VodNav>
<div className='mb-3 fp-vod-data'>
{vod.attributes.note && (
<>
<LinkableHeading text="Notes" slug="notes" icon={faNoteSticky}></LinkableHeading>
<div className='notification'>{vod.attributes.note}</div>
</>
)}
<LinkableHeading text="Tags" slug="tags" icon={faTags}></LinkableHeading>
<div className="tags has-addons mb-5">
2024-02-02 23:50:24 +00:00
{vod.attributes.tagVodRelations.data.length === 0 && <div className="ml-5 mr-2"><p><i>This vod has no tags</i></p></div>}
2024-01-20 16:16:14 +00:00
{vod.attributes.tagVodRelations.data.map((tvr: ITagVodRelation) => (
<Tag key={tvr.id} tvr={tvr}></Tag>
))}
<Tagger vod={vod} setTimestamps={setTimestamps}></Tagger>
</div>
<LinkableHeading text="Timestamps" slug="timestamps" icon={faClock}></LinkableHeading>
<TimestampsList timestamps={timestamps} setTimestamps={setTimestamps} vod={vod}></TimestampsList>
</div>
</VideoContext.Provider>
)
}