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

149 lines
5.3 KiB
TypeScript

'use client';
import { useEffect, useState, forwardRef, useContext, Ref } from 'react';
import { IVod } from '@/lib/vods';
import { useAuth } from '@/components/auth';
import { getVodTitle } from './vod-page';
import { VideoSourceSelector } from '@/components/video-source-selector'
import { buildIpfsUrl } from '@/lib/ipfs';
import { strapiUrl } from '@/lib/constants';
import MuxPlayer from '@mux/mux-player-react/lazy';
import { VideoContext } from './video-context';
import MuxPlayerElement from '@mux/mux-player';
import VideoApiElement from "@mux/mux-player/dist/types/video-api";
interface IPlayerProps {
vod: IVod;
setIsPlayerReady: Function;
}
interface ITokens {
playbackToken: string;
storyboardToken: string;
thumbnailToken: string;
}
async function getMuxPlaybackTokens(playbackId: string, jwt: string): Promise<ITokens> {
const res = await fetch(`${strapiUrl}/api/mux-asset/secure?id=${playbackId}`, {
headers: {
'Authorization': `Bearer ${jwt}`
}
})
const json = await res.json()
return {
playbackToken: json.playbackToken,
storyboardToken: json.storyboardToken,
thumbnailToken: json.thumbnailToken
}
}
function hexToRgba(hex: string, alpha: number) {
const r = parseInt(hex.slice(1, 3), 16);
const g = parseInt(hex.slice(3, 5), 16);
const b = parseInt(hex.slice(5, 7), 16);
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
}
export const VideoPlayer = forwardRef(function VideoPlayer( props: IPlayerProps, ref: Ref<MuxPlayerElement> ): React.JSX.Element {
const { vod, setIsPlayerReady } = props
const title: string = getVodTitle(vod);
const { authData } = useAuth();
const [selectedVideoSource, setSelectedVideoSource] = useState('');
const [isEntitledToCDN, setIsEntitledToCDN] = useState(false);
const [hlsSource, setHlsSource] = useState<string>('');
const [isClient, setIsClient] = useState(false);
const [playbackId, setPlaybackId] = useState('');
const [src, setSrc] = useState('');
const [tokens, setTokens] = useState({});
const { setTimeStamp } = useContext(VideoContext);
useEffect(() => {
setIsClient(true);
const token = authData?.accessToken;
const playbackId = vod?.attributes.muxAsset?.data?.attributes?.playbackId;
if (token) setIsEntitledToCDN(true);
if (selectedVideoSource === 'Mux') {
if (!!token && !!playbackId) {
try {
getMuxPlaybackTokens(vod.attributes.muxAsset.data.attributes.playbackId, token)
.then((tokens) => {
setTokens({
playback: tokens.playbackToken,
storyboard: tokens.storyboardToken,
thumbnail: tokens.thumbnailToken
})
setHlsSource(vod.attributes.muxAsset.data.attributes.playbackId)
setPlaybackId(vod.attributes.muxAsset.data.attributes.playbackId)
});
}
catch (e) {
console.error(e)
}
}
} else if (selectedVideoSource === 'B2') {
if (!vod.attributes.videoSrcB2) return; // This shouldn't happen because videoSourceSelector won't choose B2 if there is no b2. This return is only for satisfying TS
setHlsSource(vod.attributes.videoSrcB2.data.attributes.cdnUrl);
setPlaybackId('');
setSrc(vod.attributes.videoSrcB2.data.attributes.cdnUrl);
} else if (selectedVideoSource === 'IPFSSource') {
setHlsSource('');
setPlaybackId('');
setSrc(buildIpfsUrl(vod.attributes.videoSrcHash))
} else if (selectedVideoSource === 'IPFS240') {
setHlsSource('');
setPlaybackId('');
setSrc(buildIpfsUrl(vod.attributes.video240Hash))
}
}, [selectedVideoSource, authData, vod, setHlsSource]);
if (!isClient) return <></>
return (
<>
<MuxPlayer
onCanPlay={() => {
setIsPlayerReady(true)}
}
ref={ref}
preload="auto"
crossOrigin="*"
loading="viewport"
playbackId={playbackId}
src={src}
tokens={tokens}
primaryColor="#FFFFFF"
metadata={{
video_title: getVodTitle(vod)
}}
streamType="on-demand"
onTimeUpdate={(evt) => {
const muxPlayer = evt.target as VideoApiElement
const { currentTime } = muxPlayer;
setTimeStamp(currentTime)
}}
muted
></MuxPlayer>
<VideoSourceSelector
isMux={!!vod?.attributes.muxAsset?.data?.attributes?.playbackId}
isB2={!!vod?.attributes.videoSrcB2?.data?.attributes?.cdnUrl}
isIPFSSource={!!vod?.attributes.videoSrcHash}
isIPFS240={!!vod?.attributes.video240Hash}
isEntitledToCDN={isEntitledToCDN}
selectedVideoSource={selectedVideoSource}
setSelectedVideoSource={setSelectedVideoSource}
></VideoSourceSelector>
</>
)
})