149 lines
5.3 KiB
TypeScript
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>
|
||
|
</>
|
||
|
)
|
||
|
})
|