From 4590164b134668fa2f33a59bd963dbc1ee8e8108 Mon Sep 17 00:00:00 2001 From: Janne Koschinski <janne@kuschku.de> Date: Fri, 15 Jan 2021 15:41:19 +0100 Subject: [PATCH] Cleaned up some code --- src/routes/player/Player.tsx | 145 +++--------------- src/routes/player/SeekBar.tsx | 13 +- src/routes/player/video/AudioSelection.tsx | 30 ++++ src/routes/player/video/EpisodeSelection.tsx | 24 +++ src/routes/player/video/PlayerControls.tsx | 77 ++++++++++ src/routes/player/video/SubtitleSelection.tsx | 30 ++++ 6 files changed, 191 insertions(+), 128 deletions(-) create mode 100644 src/routes/player/video/AudioSelection.tsx create mode 100644 src/routes/player/video/EpisodeSelection.tsx create mode 100644 src/routes/player/video/PlayerControls.tsx create mode 100644 src/routes/player/video/SubtitleSelection.tsx diff --git a/src/routes/player/Player.tsx b/src/routes/player/Player.tsx index b6936f0..211dc2c 100644 --- a/src/routes/player/Player.tsx +++ b/src/routes/player/Player.tsx @@ -1,25 +1,23 @@ -import {ChangeEvent, Fragment, useCallback, useState} from "react"; +import {Fragment, useState} from "react"; import {createUseStyles} from "react-jss"; import {Link} from "react-router-dom"; import {ContentMeta} from "../../api/models/dto/ContentMeta"; import {Media} from "../../api/models/Media"; import {getLocalizedDescription, getLocalizedName, getLocalizedRating} from "../../api/models/Content"; -import {formatDuration} from "../../util/formatDuration"; import {useLocale} from "../../util/locale/LocalizedContext"; import {SeekBar} from "./SeekBar"; import {VideoElement} from "./video/VideoElement"; import {PlayerApi} from "./video/PlayerApi"; -import {useAudioTracks} from "../../util/media/useAudioTracks"; -import {useDebugInfo} from "../../util/media/useDebugInfo"; import {Subtitle} from "../../api/models/Subtitle"; import {MousePosition} from "../../util/mouse/MousePosition"; import {VideoProvider} from "./video/VideoContext"; import {SubtitleRenderer} from "./subtitles/SubtitleRenderer"; import {useShowEpisodes} from "../../api/ApiHooks"; -import {MediaEpisode} from "../main/MediaEpisode"; -import {usePaused} from "../../util/media/usePaused"; -import {useCurrentTime} from "../../util/media/useCurrentTime"; import {useDuration} from "../../util/media/useDuration"; +import {AudioSelection} from "./video/AudioSelection"; +import {SubtitleSelection} from "./video/SubtitleSelection"; +import {EpisodeSelection} from "./video/EpisodeSelection"; +import {PlayerControls} from "./video/PlayerControls"; interface Props { meta: ContentMeta, @@ -47,42 +45,7 @@ export function Player( const [playerApi, setPlayerApi] = useState<PlayerApi | null>(null); const [mousePosition, setMousePosition] = useState<MousePosition | null>(null); - const [volume, setVolume] = useState<number>(1); - const paused = usePaused(playerApi); - const currentTime = useCurrentTime(playerApi) const duration = useDuration(playerApi); - const [audioTracks, currentTrack, setCurrentTrack] = useAudioTracks(playerApi); - - useDebugInfo("player", playerApi); - useDebugInfo("content", content); - useDebugInfo("subtitle", subtitle); - useDebugInfo("currentTime", currentTime); - useDebugInfo("duration", duration); - useDebugInfo("audioTracks", audioTracks); - - const onPause = useCallback(() => { - if (playerApi) { - playerApi.pause() - } - }, [playerApi]); - - const onPlay = useCallback(() => { - if (playerApi) { - playerApi.play() - } - }, [playerApi]); - - const onFastForward = useCallback(() => { - if (playerApi) { - playerApi.setCurrentTime(playerApi.getCurrentTime() + 10) - } - }, [playerApi]); - - const onRewind = useCallback(() => { - if (playerApi) { - playerApi.setCurrentTime(playerApi.getCurrentTime() - 10) - } - }, [playerApi]); return ( <VideoProvider value={playerApi?.getVideoElement() || null}> @@ -98,20 +61,15 @@ export function Player( {relatedEpisodesError ? ( <p>{"" + relatedEpisodesError}</p> ) : ( - <ul> - {relatedEpisodes.map(episode => - <MediaEpisode - key={episode.content.ids.uuid} - item={episode} - disabled={episode.content.ids.uuid === content.ids.uuid} - /> - )} - </ul> + <EpisodeSelection + content={content} + episodes={relatedEpisodes} + /> )} </Fragment> )} - <div className={classes.player}> - <div className={classes.playerCanvas}> + <div className={classes.videoContainer}> + <div className={classes.videoCanvas}> <VideoElement className={classes.video} media={media} @@ -126,83 +84,20 @@ export function Player( /> </div> </div> - <p style={{fontVariant: "tabular-nums"}}> - {formatDuration(mousePosition ? (mousePosition.relative * duration) : currentTime)} / {formatDuration(duration)} - </p> - <button - onClick={onPlay} - disabled={!paused} - > - Play - </button> - <button - onClick={onPause} - disabled={paused} - > - Pause - </button> - <button - onClick={onRewind} - > - Rewind - </button> - <button - onClick={onFastForward} - > - Fast Forward - </button> - <p> - <input - type="range" - min="0" - max="100" - value={volume * 100} - onChange={(event: ChangeEvent<HTMLInputElement>) => { - setVolume(event.target.valueAsNumber / 100); - playerApi?.setVolume(event.target.valueAsNumber / 100) - }} - /> - </p> + <PlayerControls playerApi={playerApi} /> <h3>Audio</h3> - <ul> - {audioTracks.map(track => ( - <li key={track.index}> - <strong>{track.lang}</strong> - {track.labels} - - <button - disabled={currentTrack === track} - onClick={() => setCurrentTrack(track)} - > - Choose - </button> - </li> - ))} - </ul> + <AudioSelection playerApi={playerApi} /> <h3>Subtitles</h3> - <ul> - {[null, ...content.subtitles].map(track => ( - <li key={track?.src || "none"}> - <strong>{track?.language || "none"}</strong> - {track?.specifier} - {track?.format} - - <button - disabled={subtitle === track} - onClick={() => setSubtitle(track)} - > - Choose - </button> - </li> - ))} - </ul> + <SubtitleSelection + subtitles={[null, ...content.subtitles]} + subtitle={subtitle} + setSubtitle={setSubtitle} + /> <SeekBar videoApi={playerApi} previewSrc={content.preview} mousePosition={mousePosition} setMousePosition={setMousePosition} - duration={duration} - position={currentTime} /> </div> </VideoProvider> @@ -210,7 +105,7 @@ export function Player( } const useStyles = createUseStyles({ - player: { + videoContainer: { width: "40rem", height: "30rem", background: "#dc5", @@ -219,7 +114,7 @@ const useStyles = createUseStyles({ justifyContent: "center", alignItems: "center", }, - playerCanvas: { + videoCanvas: { position: "relative", display: "flex", }, diff --git a/src/routes/player/SeekBar.tsx b/src/routes/player/SeekBar.tsx index 315d531..8454069 100644 --- a/src/routes/player/SeekBar.tsx +++ b/src/routes/player/SeekBar.tsx @@ -6,17 +6,18 @@ import {useOffsetAbsolute} from "../../util/offset/useOffsetAbsolute"; import {useOffsetRelative} from "../../util/offset/useOffsetRelative"; import {PreviewBar} from "./PreviewBar"; import {PlayerApi} from "./video/PlayerApi"; +import {formatDuration} from "../../util/formatDuration"; +import {useCurrentTime} from "../../util/media/useCurrentTime"; +import {useDuration} from "../../util/media/useDuration"; interface Props { videoApi: PlayerApi | null, previewSrc: string | null, mousePosition: MousePosition | null, setMousePosition: (position: MousePosition | null) => void, - duration: number, - position: number, } -export function SeekBar({videoApi, previewSrc, mousePosition, setMousePosition, duration, position}: Props) { +export function SeekBar({videoApi, previewSrc, mousePosition, setMousePosition}: Props) { const classes = useStyles(); const isVisible = mousePosition !== null; @@ -25,6 +26,9 @@ export function SeekBar({videoApi, previewSrc, mousePosition, setMousePosition, const [seekHeadRef, setSeekHeadRef] = useState<HTMLDivElement | null>(null); const [playHeadRef, setPlayHeadRef] = useState<HTMLDivElement | null>(null); + const position = useCurrentTime(videoApi) + const duration = useDuration(videoApi); + const seekHeadOffset = useOffsetAbsolute(seekBarRef, seekHeadRef, mousePosition?.absolute || 0); const playHeadOffset = useOffsetRelative(seekBarRef, playHeadRef, position / duration); @@ -84,6 +88,9 @@ export function SeekBar({videoApi, previewSrc, mousePosition, setMousePosition, return ( <Fragment> + <p style={{fontVariant: "tabular-nums"}}> + {formatDuration(mousePosition ? (mousePosition.relative * duration) : position)} / {formatDuration(duration)} + </p> <PreviewBar previewSrc={previewSrc} duration={duration} diff --git a/src/routes/player/video/AudioSelection.tsx b/src/routes/player/video/AudioSelection.tsx new file mode 100644 index 0000000..d70f245 --- /dev/null +++ b/src/routes/player/video/AudioSelection.tsx @@ -0,0 +1,30 @@ +import {PlayerApi} from "./PlayerApi"; +import {useAudioTracks} from "../../../util/media/useAudioTracks"; + +interface Props { + playerApi: PlayerApi | null +} + +export function AudioSelection( + {playerApi}: Props +) { + const [audioTracks, currentTrack, setCurrentTrack] = useAudioTracks(playerApi); + + return ( + <ul> + {audioTracks.map(track => ( + <li key={track.index}> + <strong>{track.lang}</strong> + {track.labels} + + <button + disabled={currentTrack === track} + onClick={() => setCurrentTrack(track)} + > + Choose + </button> + </li> + ))} + </ul> + ); +} diff --git a/src/routes/player/video/EpisodeSelection.tsx b/src/routes/player/video/EpisodeSelection.tsx new file mode 100644 index 0000000..13bb585 --- /dev/null +++ b/src/routes/player/video/EpisodeSelection.tsx @@ -0,0 +1,24 @@ +import {MediaEpisode} from "../../main/MediaEpisode"; +import {Instalment} from "../../../api/models/Instalment"; +import {Content} from "../../../api/models/Content"; + +interface Props { + content: Content, + episodes: Instalment[], +} + +export function EpisodeSelection( + {episodes, content}: Props +) { + return ( + <ul> + {episodes.map(episode => + <MediaEpisode + key={episode.content.ids.uuid} + item={episode} + disabled={episode.content.ids.uuid === content.ids.uuid} + /> + )} + </ul> + ); +} diff --git a/src/routes/player/video/PlayerControls.tsx b/src/routes/player/video/PlayerControls.tsx new file mode 100644 index 0000000..8548d3b --- /dev/null +++ b/src/routes/player/video/PlayerControls.tsx @@ -0,0 +1,77 @@ +import {ChangeEvent, Fragment, useCallback, useState} from "react"; +import {PlayerApi} from "./PlayerApi"; +import {usePaused} from "../../../util/media/usePaused"; + +interface Props { + playerApi: PlayerApi | null, +} + +export function PlayerControls( + {playerApi}: Props +) { + const [volume, setVolume] = useState<number>(1); + const paused = usePaused(playerApi); + + const onPause = useCallback(() => { + if (playerApi) { + playerApi.pause() + } + }, [playerApi]); + + const onPlay = useCallback(() => { + if (playerApi) { + playerApi.play() + } + }, [playerApi]); + + const onFastForward = useCallback(() => { + if (playerApi) { + playerApi.setCurrentTime(playerApi.getCurrentTime() + 10) + } + }, [playerApi]); + + const onRewind = useCallback(() => { + if (playerApi) { + playerApi.setCurrentTime(playerApi.getCurrentTime() - 10) + } + }, [playerApi]); + + return ( + <Fragment> + <button + onClick={onPlay} + disabled={!paused} + > + Play + </button> + <button + onClick={onPause} + disabled={paused} + > + Pause + </button> + <button + onClick={onRewind} + > + Rewind + </button> + <button + onClick={onFastForward} + > + Fast Forward + </button> + <p> + <input + type="range" + min="0" + max="100" + value={volume * 100} + onChange={(event: ChangeEvent<HTMLInputElement>) => { + setVolume(event.target.valueAsNumber / 100); + playerApi?.setVolume(event.target.valueAsNumber / 100) + }} + /> + </p> + </Fragment> + ); +} diff --git a/src/routes/player/video/SubtitleSelection.tsx b/src/routes/player/video/SubtitleSelection.tsx new file mode 100644 index 0000000..86f96a8 --- /dev/null +++ b/src/routes/player/video/SubtitleSelection.tsx @@ -0,0 +1,30 @@ +import {Subtitle} from "../../../api/models/Subtitle"; + +interface Props { + subtitles: (Subtitle | null)[], + subtitle: Subtitle | null, + setSubtitle: (subtitle: Subtitle | null) => void, +} + +export function SubtitleSelection( + {subtitles, subtitle, setSubtitle}: Props +) { + return ( + <ul> + {subtitles.map(track => ( + <li key={track?.src || "none"}> + <strong>{track?.language || "none"}</strong> + {track?.specifier} + {track?.format} + + <button + disabled={subtitle === track} + onClick={() => setSubtitle(track)} + > + Choose + </button> + </li> + ))} + </ul> + ); +} -- GitLab