diff --git a/src/App.test.tsx b/src/App.test.tsx index 4db7ebc25c2d066cd254805af5dda1ed1d2bc819..9a9be2e13d7860a0bd6413e4af7f2e3df931888e 100644 --- a/src/App.test.tsx +++ b/src/App.test.tsx @@ -1,4 +1,3 @@ -import React from 'react'; import { render } from '@testing-library/react'; import App from './App'; diff --git a/src/App.tsx b/src/App.tsx index 46aa8836c1af8c35000d66468bf1fc1b10bbb046..be09b0342d61fd05e7ec915fef50a0177d2982c0 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,4 +1,3 @@ -import React from 'react'; import {BrowserRouter, Route} from 'react-router-dom'; import {MainPage} from "./routes/main/MainPage"; import {PlayerPage} from "./routes/player/PlayerPage"; diff --git a/src/index.tsx b/src/index.tsx index e24554152f57b5162ee953c3ae4129d019de6880..33c5e0bdac55be15eac06d0d52e16d14d31b809e 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,5 +1,5 @@ import {produce} from 'immer' -import React from 'react'; +import { StrictMode } from 'react'; import ReactDOM from 'react-dom'; import {defaults} from 'react-sweet-state'; @@ -15,9 +15,9 @@ defaults.devtools = process.env.NODE_ENV !== "production"; defaults.mutator = (currentState, producer) => produce(currentState, producer) ReactDOM.render( - <React.StrictMode> + <StrictMode> <App/> - </React.StrictMode>, + </StrictMode>, document.getElementById('root') ); diff --git a/src/routes/ContentRoute.tsx b/src/routes/ContentRoute.tsx index be5f46f02f2900b9d363576f96a273f785148b1f..5d5b36ab03f679888973958a7d7f06edb1cf8cb0 100644 --- a/src/routes/ContentRoute.tsx +++ b/src/routes/ContentRoute.tsx @@ -1,4 +1,4 @@ -import React, {PropsWithChildren, useEffect, useState} from "react"; +import { PropsWithChildren, useEffect, useState } from "react"; import {useParams} from "react-router"; import {useApiClient} from "../api/ApiClientContext"; import {ContentMeta} from "../api/models/dto/ContentMeta"; diff --git a/src/routes/main/MainPage.tsx b/src/routes/main/MainPage.tsx index 3501acea5b187114d0cb1b8cb3eae30540371aea..03d69bc1fcc0cbbf087d9881d700194b131136b6 100644 --- a/src/routes/main/MainPage.tsx +++ b/src/routes/main/MainPage.tsx @@ -1,4 +1,4 @@ -import React, {useEffect, useState} from "react"; +import { useEffect, useState } from "react"; import {createUseStyles} from "react-jss"; import {Link} from "react-router-dom"; import {useApiClient} from "../../api/ApiClientContext"; diff --git a/src/routes/player/Player.tsx b/src/routes/player/Player.tsx index 25d15d1e212c1d20c7c2fe3a9be1a915abde164c..c812b3ee4dd5ac86d01c1a3b4f8d2384bb45921a 100644 --- a/src/routes/player/Player.tsx +++ b/src/routes/player/Player.tsx @@ -1,4 +1,4 @@ -import React, {useState} from "react"; +import {useState} from "react"; import {createUseStyles} from "react-jss"; import {Link} from "react-router-dom"; import {ContentMeta} from "../../api/models/dto/ContentMeta"; @@ -54,7 +54,10 @@ export function Player( return ( <div> <Link to="/">Back</Link> - <p>{name?.name}</p> + <h2>{name?.name}</h2> + <p>{rating?.certification}</p> + <strong>{description?.tagline}</strong> + <p>{description?.overview}</p> <p>{instalment?.content && getLocalizedName(instalment?.content, locale)?.name}</p> <div className={classes.player}> <div className={classes.playerCanvas}> @@ -91,30 +94,28 @@ export function Player( <p style={{fontVariant: "tabular-nums"}}>{formatDuration(position)} / {formatDuration(duration)}</p> <button onClick={videoElement?.play}>Play</button> <button onClick={videoElement?.pause}>Pause</button> - <p> - Audio - <ul> - {audioTracks.map(track => ( - <li key={track.index}> - <strong>{track.lang}</strong> - {track.labels} - <button onClick={() => videoElement?.setAudioTrack(track)}>Choose</button> - </li> - ))} - </ul> - </p> - <p> - Subtitles - <ul> - {content.subtitles.map(track => ( - <li key={track.src}> - <strong>{track.language}</strong> - {track.specifier} - <button onClick={() => setSubtitle(track)}>Choose</button> - </li> - ))} - </ul> - </p> + <h3>Audio</h3> + <ul> + {audioTracks.map(track => ( + <li key={track.index}> + <strong>{track.lang}</strong> + {track.labels} + + <button onClick={() => videoElement?.setAudioTrack(track)}>Choose</button> + </li> + ))} + </ul> + <h3>Subtitles</h3> + <ul> + {content.subtitles.map(track => ( + <li key={track.src}> + <strong>{track.language}</strong> + {track.specifier} + + <button onClick={() => setSubtitle(track)}>Choose</button> + </li> + ))} + </ul> <SeekBar video={videoElement} previewTrack={previewTrack} diff --git a/src/routes/player/PlayerError.tsx b/src/routes/player/PlayerError.tsx index 6cec388bdbee0c4977fdf562bb636dcdc98e5d49..d6669c2c216b523eb9c435f98086d7528a0fcba4 100644 --- a/src/routes/player/PlayerError.tsx +++ b/src/routes/player/PlayerError.tsx @@ -1,5 +1,3 @@ -import React from "react"; - export function PlayerError() { return ( <div>Error</div> diff --git a/src/routes/player/PlayerLoading.tsx b/src/routes/player/PlayerLoading.tsx index ae71f1fe7a240e2ad4d1776ce6f92d9f6d9a528a..b11833e31f24146ca0dd1a4e0fd6f21f6e3f6470 100644 --- a/src/routes/player/PlayerLoading.tsx +++ b/src/routes/player/PlayerLoading.tsx @@ -1,5 +1,3 @@ -import React from "react"; - export function PlayerLoading() { return ( <div>Loading</div> diff --git a/src/routes/player/PlayerPage.tsx b/src/routes/player/PlayerPage.tsx index eb96af03ce4b4f8b0e1e8d64a2a9832cdaf1a174..d65c9eb569a12ba6240c949dfaea02aadec609e4 100644 --- a/src/routes/player/PlayerPage.tsx +++ b/src/routes/player/PlayerPage.tsx @@ -1,4 +1,3 @@ -import React from "react"; import {getPlayableMedia} from "../../api/models/Content"; import {usePlayabilityRating} from "../../util/mime/usePlayabilityRating"; import {useCurrentContent} from "../../util/CurrentContentContext"; diff --git a/src/routes/player/PreviewBar.tsx b/src/routes/player/PreviewBar.tsx index 27a6b62a7c692e032b43f854aecc44c8a84f7f55..9c5a7feca7f1b24585d79058453c75bc79beeced 100644 --- a/src/routes/player/PreviewBar.tsx +++ b/src/routes/player/PreviewBar.tsx @@ -1,4 +1,4 @@ -import React, {useMemo} from "react"; +import { useMemo } from "react"; import {createUseStyles} from "react-jss"; import {MousePosition} from "../../util/mouse/MousePosition"; import {useOffsetAbsoluteRef} from "../../util/offset/useOffsetAbsoluteRef"; diff --git a/src/routes/player/PreviewViewer.tsx b/src/routes/player/PreviewViewer.tsx index 9928f522a7e867a9c82bca12f60b6fbb6fca7617..4649bb42be22116f6824ff831e09ab2e5a06cf66 100644 --- a/src/routes/player/PreviewViewer.tsx +++ b/src/routes/player/PreviewViewer.tsx @@ -1,4 +1,4 @@ -import React, {useMemo} from "react"; +import { Fragment, useMemo } from "react"; import {createUseStyles} from "react-jss"; import {HeadPortal} from "../../util/head/HeadPortal"; import {useImage} from "../../util/media/useImage"; @@ -26,14 +26,14 @@ export function PreviewViewer({previewTrack, position}: Props) { return url.toString(); }))), [cues, previewTrack]); return useMemo(() => ( - <React.Fragment> + <Fragment> <HeadPortal> {sources?.map(it => ( <link key={it} rel="preload" href={it} as="image" crossOrigin="anonymous"/> ))} </HeadPortal> <img alt="" className={classes.preview} src={sprite || undefined}/> - </React.Fragment> + </Fragment> ), [classes.preview, sources, sprite]); } diff --git a/src/routes/player/SeekBar.tsx b/src/routes/player/SeekBar.tsx index 90bcbda6b8fef0ee3a6af7a9fea0025bcd3adf9c..b77d4de7dca7279c1aa2df3015a9073c96a81895 100644 --- a/src/routes/player/SeekBar.tsx +++ b/src/routes/player/SeekBar.tsx @@ -1,4 +1,4 @@ -import React, {MouseEvent, useCallback, useMemo, useState} from "react"; +import { Fragment, MouseEvent, useCallback, useMemo, useState } from "react"; import {createUseStyles} from "react-jss"; import {getMousePosition} from "../../util/mouse/getMousePosition"; import {MousePosition} from "../../util/mouse/MousePosition"; @@ -79,7 +79,7 @@ export function SeekBar({video, previewTrack, duration, position}: Props) { ), [classes.seekBar, onClick, onMouseLeave, onMouseMove, playHead, seekHead]); return ( - <React.Fragment> + <Fragment> <PreviewBar previewTrack={previewTrack} duration={duration} @@ -87,8 +87,8 @@ export function SeekBar({video, previewTrack, duration, position}: Props) { hidden={mousePosition === null} /> {seekBar} - </React.Fragment> - ) + </Fragment> + ); } const useStyles = createUseStyles({ diff --git a/src/routes/player/subtitles/TtmlRenderer.tsx b/src/routes/player/subtitles/TtmlRenderer.tsx index 5f5b151fb583f7e7d6b3cff885f6ca02cf6f47f2..bd599e6369a9efd4c1ac6878b319785e5ec65f89 100644 --- a/src/routes/player/subtitles/TtmlRenderer.tsx +++ b/src/routes/player/subtitles/TtmlRenderer.tsx @@ -1,4 +1,4 @@ -import React, {useEffect, useState} from "react"; +import { useEffect, useState } from "react"; import TtmlHelper from "./TtmlHelper"; interface Props { diff --git a/src/routes/player/video/DashVideoElement.tsx b/src/routes/player/video/DashVideoElement.tsx index 84a0b3bb6e314bd30b25ebc528d07d907869c3c4..c6698ea62b77c9e15cc2f38383415b256a64be91 100644 --- a/src/routes/player/video/DashVideoElement.tsx +++ b/src/routes/player/video/DashVideoElement.tsx @@ -1,4 +1,4 @@ -import React, {PropsWithChildren, useEffect, useImperativeHandle, useMemo, useState} from "react"; +import {forwardRef, PropsWithChildren, useEffect, useImperativeHandle, useMemo, useState,} from "react"; import {Media} from "../../../api/models/Media"; import dashjs, {MediaInfo} from "dashjs"; import {VideoApi} from "./VideoApi"; @@ -9,7 +9,7 @@ interface Props { className?: string, } -export const DashVideoElement = React.forwardRef<VideoApi, PropsWithChildren<Props>>(function ( +export const DashVideoElement = forwardRef<VideoApi, PropsWithChildren<Props>>(function ( {media, autoPlay, className, children}, ref ) { @@ -19,34 +19,64 @@ export const DashVideoElement = React.forwardRef<VideoApi, PropsWithChildren<Pro useImperativeHandle(ref, () => ({ METADATA_EVENT: dashjs.MediaPlayer.events.PLAYBACK_METADATA_LOADED, TIMECHANGE_EVENT: dashjs.MediaPlayer.events.PLAYBACK_TIME_UPDATED, - play: () => { + play() { + if (!player.isReady()) { + return; + } player.play(); }, - pause: () => { + pause() { + if (!player.isReady()) { + return; + } player.pause(); }, - isPaused: () => { + isPaused() { + if (!player.isReady()) { + return true; + } return player.isPaused(); }, - setPlaybackRate: (value: number) => { + setPlaybackRate(value: number) { + if (!player.isReady()) { + return; + } player.setPlaybackRate(value); }, - getPlaybackRate: () => { + getPlaybackRate(): number { + if (!player.isReady()) { + return 1; + } return player.getPlaybackRate(); }, setCurrentTime(value: number) { + if (!player.isReady()) { + return; + } player.seek(value); }, getCurrentTime(): number { + if (!player.isReady()) { + return 0; + } return player.time(); }, getDuration(): number { + if (!player.isReady()) { + return 0; + } return player.duration(); }, canPlay(): boolean { + if (!player.isReady()) { + return false; + } return !Number.isNaN(player.duration()); }, setAudioTrack(track: MediaInfo) { + if (!player.isReady()) { + return; + } player.setQualityFor("audio", 0); player.setCurrentTrack(track); }, @@ -57,6 +87,9 @@ export const DashVideoElement = React.forwardRef<VideoApi, PropsWithChildren<Pro player.off(event, listener); }, getAudioTracks(): MediaInfo[] { + if (!player.isReady()) { + return []; + } return player.getTracksFor("audio").map((info: MediaInfo) => { return info; }) diff --git a/src/routes/player/video/RawVideoElement.tsx b/src/routes/player/video/RawVideoElement.tsx index 4dba70eddb33f5e64a1191a9c205a5411fbc6c28..b957682b8fc292e610be1da010236c15b863a0d3 100644 --- a/src/routes/player/video/RawVideoElement.tsx +++ b/src/routes/player/video/RawVideoElement.tsx @@ -1,4 +1,4 @@ -import React, {PropsWithChildren, useImperativeHandle, useState} from "react"; +import { forwardRef, PropsWithChildren, useImperativeHandle, useState } from "react"; import {Media} from "../../../api/models/Media"; import {VideoApi} from "./VideoApi"; import {MediaInfo} from "dashjs"; @@ -9,7 +9,7 @@ interface Props { className?: string, } -export const RawVideoElement = React.forwardRef<VideoApi, PropsWithChildren<Props>>(function ( +export const RawVideoElement = forwardRef<VideoApi, PropsWithChildren<Props>>(function ( {media, autoPlay, className, children}, ref ) { diff --git a/src/routes/player/video/VideoElement.tsx b/src/routes/player/video/VideoElement.tsx index 99c9c48b47b2a6ddc91a9a293d5a5fd275293b6b..526b40a5cdedbe760395738798e93416812e0641 100644 --- a/src/routes/player/video/VideoElement.tsx +++ b/src/routes/player/video/VideoElement.tsx @@ -1,4 +1,4 @@ -import React, {PropsWithChildren} from "react"; +import { forwardRef, PropsWithChildren } from "react"; import {Media} from "../../../api/models/Media"; import {DashVideoElement} from "./DashVideoElement"; import {RawVideoElement} from "./RawVideoElement"; @@ -10,7 +10,7 @@ interface Props { className?: string, } -export const VideoElement = React.forwardRef<VideoApi, PropsWithChildren<Props>>(function ( +export const VideoElement = forwardRef<VideoApi, PropsWithChildren<Props>>(function ( props: Props, ref ) { switch (props.media.mime) { diff --git a/src/util/head/HeadPortal.tsx b/src/util/head/HeadPortal.tsx index 8b70c222a6f90ca5f194416506f27ed19b48c551..f35b2ef5167becc433da6de5cba3e9e37d3db13a 100644 --- a/src/util/head/HeadPortal.tsx +++ b/src/util/head/HeadPortal.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import * as React from "react"; import ReactDOM from "react-dom"; export function HeadPortal({children}: React.PropsWithChildren<{}>) { diff --git a/src/util/media/useTextTrackCues.ts b/src/util/media/useTextTrackCues.ts index 557a77431b3e0642b4b429f49dcbeffbe1087707..b4e1d6dd25fab1580c889ec19e23fede238ace71 100644 --- a/src/util/media/useTextTrackCues.ts +++ b/src/util/media/useTextTrackCues.ts @@ -1,17 +1,17 @@ import {useEffect, useState} from "react"; export const useTextTrackCues = (track: HTMLTrackElement | null) => { - const [cues, setCues] = useState<TextTrackCue[]>([]); + const [cues, setCues] = useState<VTTCue[]>([]); useEffect(() => { if (track !== null) { track.track.mode = "hidden"; if (track.readyState >= 1) { - setCues(Array.from(track.track.cues || [])) + setCues(Array.from(track.track.cues || []) as VTTCue[]) } else { let animationFrame: number | null = null; const listener = () => { animationFrame = window.requestAnimationFrame(() => { - setCues(Array.from(track.track.cues || [])) + setCues(Array.from(track.track.cues || []) as VTTCue[]) }) }; track.addEventListener("load", listener) diff --git a/tsconfig.json b/tsconfig.json index f2850b71613ed26b0fd526160a1a452b1f06f5bc..e18c413ebfc941981152c0cf8228faabb4aa748b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -17,7 +17,8 @@ "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, - "jsx": "react" + "jsx": "react-jsx", + "noFallthroughCasesInSwitch": true }, "include": [ "src"