import * as React from 'react'; import Head from 'next/head'; import Link from 'next/link'; import { useRouter } from 'next/router'; import { Breadcrumb, Button, ButtonGroup, Col, Row, } from 'react-bootstrap'; import { useIntl } from 'react-intl'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { GetServerSideProps, InferGetServerSidePropsType } from 'next'; import { VideoEntry } from 'util/datatypes/VideoList'; import DownloadButton from 'components/DownloadButton'; import { basename } from 'path'; import withSession from 'util/session'; import { getDASHManifestURL, getHLSMasterURL } from '../../util'; import VideoPlayer from '../../components/VideoPlayer'; import CopyField from '../../components/CopyField'; import { FormattedMessage } from '../../components/localization'; import sanitizeFileName from '../../util/sanitizeFileName'; import sanitizeTitle from '../../util/sanitizeTitle'; import { notFound } from '../../util/status'; import { getDownloadURL, getIndex, getVideos, submitPreferences, } from '../../util/api'; const getProps = withSession(async (req, _res, { id, vslug }: { id: string, vslug: string }) => { if (typeof id !== 'string') { throw new Error('only expected a single id'); } if (typeof vslug !== 'string') { throw new Error('only expected a single vslug'); } // Fetch URL to thumbnails server const { ids, servers: { hls: hlsServerURL, dash: dashServerURL }, } = await getIndex(); const vodMeta = ids.find(({ id: thisID, }: { id: string }) => id === thisID); if (!vodMeta) { return { props: {} }; } // Fetch list of videos for this VOD ID const vodInfo = await getVideos(id); let videos; if (Array.isArray(vodInfo)) { videos = vodInfo; } else { videos = vodInfo.videos; } // Check if vslug is actually an index number (old app style) /* NOTE - parseInt will accept strings CONTAINING numbers. This is supposed to reject strings that are not JUST numbers. */ const vindexNum = +vslug; if (!Number.isNaN(vindexNum)) { // Check if video exists if (vindexNum < 0 || vindexNum >= videos.length) { return { props: {} }; } const video = videos[vindexNum]; return { props: { redirect: true, id, video, vslug: sanitizeTitle(video.title), }, }; } // Check if vslug is actually point to a file name const sanitizedFileName = `${sanitizeFileName(basename(vslug, '.mp4'))}.mp4`; const realVIndex = videos.findIndex( (video: VideoEntry) => video.fileName === sanitizedFileName, ); if (realVIndex >= 0) { const video = videos[realVIndex]; return { props: { redirect: true, id, video: realVIndex, vslug: sanitizeTitle(video.title), }, }; } // Check if we can find any video with matching vslug const video = videos.find(({ title }: { title: string }) => sanitizeTitle(title) === vslug); if (!video) { return { props: {} }; } // At this point we found the right video, just get more information at this point const { title } = vodMeta; // let basePath = null; // basePath = `${JSON.stringify(req.toString())}`; // TODO - detect HTTPS properly // NOTE - req.socket.encrypted fails in TypeScript due to typing const basePath = `https://${req.headers.host}`; // ask for user's volume preference let volume: number = 0.5; const volumeSessionValue = req.session.get('volume'); if (typeof volumeSessionValue === 'string') { const parsedNumber = parseFloat(volumeSessionValue); if (!Number.isNaN(parsedNumber)) { volume = parsedNumber; } } else if (typeof volumeSessionValue === 'number' && !Number.isNaN(volumeSessionValue)) { volume = volumeSessionValue; } // Pass data to the page via props return { props: { id, vslug, video, volume, title, hlsServerURL, dashServerURL, // extra: ((o) => Object // .keys(o) // .map((k) => { // if (k === undefined || k === null) { // return JSON.stringify(k); // } // if (k.toString) { // return k.toString(); // } // return ''; // }).join('\n'))(req.socket), basePath, }, }; }); export const getServerSideProps: GetServerSideProps = async ({ req, res, params, }) => getProps(req, res, params); export default function VideoPlayerPage({ id, vslug, video, volume, // extra, redirect, title, hlsServerURL, dashServerURL, basePath, }: InferGetServerSidePropsType) { if (redirect) { const router = useRouter(); React.useEffect(() => { router.push(`/${id}/${vslug}`); }); return (

You will be redirected {' '} here

); } if (!video) { return notFound(); } const intl = useIntl(); const playerRef = React.useRef(); const { fileName, title: videoTitle, sourceVideoURL, sourceVideoStart, } = video; let volumeChangeDebounceTimer: NodeJS.Timeout = null; const [currentVolume, trackCurrentVolume] = React.useState(volume); async function onVolumeChange() { const newVolume = playerRef.current.player.volume(); if (Math.abs(newVolume - currentVolume) > 0.05) { if (volumeChangeDebounceTimer !== null) { clearTimeout(volumeChangeDebounceTimer); } volumeChangeDebounceTimer = setTimeout(() => { submitPreferences({ volume: newVolume, }); trackCurrentVolume(newVolume); }, 1000); } } return (
{videoTitle} {' '} – {' '} {title} {' '} – {' '} {intl.formatMessage({ id: 'App.title', description: 'The full title of the website', defaultMessage: 'Games Done Quick Instant Archive', })} {/*
        
          {extra}
        
      
*/} {title} {videoTitle}

{title} : {' '} {videoTitle}

{/* */} { sourceVideoURL ? ( ) : ( '' ) } {[basePath, id, vslug].join('/')}
); }