Add URL routing to show episode selector

This commit is contained in:
Josh Heng 2021-08-02 14:08:55 +01:00
parent 6a8af72f81
commit a48c77a1cb
2 changed files with 44 additions and 22 deletions

View File

@ -3,10 +3,8 @@ import { TypeSelector } from './TypeSelector';
import { NumberSelector } from './NumberSelector'; import { NumberSelector } from './NumberSelector';
import './EpisodeSelector.css' import './EpisodeSelector.css'
export function EpisodeSelector({ setSeason, setEpisode, seasons, season, episodes, currentSeason, currentEpisode, slug, source }) { export function EpisodeSelector({ setSelectedSeason, setEpisode, seasons, selectedSeason, season, episodes, currentSeason, currentEpisode, source }) {
const choices = episodes ? episodes.map(v => {
const choices = episodes.map(v => {
let progressData = JSON.parse(localStorage.getItem('video-progress') || "{}") let progressData = JSON.parse(localStorage.getItem('video-progress') || "{}")
let currentlyAt = 0; let currentlyAt = 0;
@ -25,12 +23,12 @@ export function EpisodeSelector({ setSeason, setEpisode, seasons, season, episod
label: v, label: v,
percentage percentage
} }
}) }) : [];
return ( return (
<div className="episodeSelector"> <div className="episodeSelector">
<TypeSelector setType={setSeason} choices={seasons.map(v=>({ value: v.toString(), label: `Season ${v}`}))} selected={currentSeason}/><br></br> <TypeSelector setType={setSelectedSeason} choices={seasons.map(v=>({ value: v.toString(), label: `Season ${v}`}))} selected={selectedSeason}/><br></br>
<NumberSelector setType={(e) => setEpisode({episode: e, season: currentSeason})} choices={choices} selected={currentEpisode.season === currentSeason?currentEpisode.episode:null}/> <NumberSelector setType={(e) => setEpisode({episode: e, season: selectedSeason})} choices={choices} selected={(selectedSeason.toString() === currentSeason) ? currentEpisode : null} />
</div> </div>
) )
} }

View File

@ -1,4 +1,5 @@
import React from 'react' import React from 'react'
import { useRouteMatch, useHistory } from 'react-router-dom'
import { Title } from '../components/Title' import { Title } from '../components/Title'
import { Card } from '../components/Card' import { Card } from '../components/Card'
import { useMovie } from '../hooks/useMovie' import { useMovie } from '../hooks/useMovie'
@ -9,41 +10,57 @@ import { getStreamUrl } from '../lib/index'
import './Movie.css' import './Movie.css'
export function MovieView(props) { export function MovieView(props) {
const baseRouteMatch = useRouteMatch('/:type/:source/:title/:slug');
const showRouteMatch = useRouteMatch('/:type/:source/:title/:slug/season/:season/episode/:episode');
const history = useHistory();
const { streamUrl, streamData, setStreamUrl } = useMovie(); const { streamUrl, streamData, setStreamUrl } = useMovie();
const [season, setSeason] = React.useState("1");
const [seasonList, setSeasonList] = React.useState([]); const [seasonList, setSeasonList] = React.useState([]);
const [episodeLists, setEpisodeList] = React.useState([]); const [episodeLists, setEpisodeList] = React.useState([]);
const [episode, setEpisode] = React.useState({ episode: null, season: null });
const [loading, setLoading] = React.useState(false); const [loading, setLoading] = React.useState(false);
const [ selectedSeason, setSelectedSeason ] = React.useState("1");
const season = showRouteMatch?.params.season || "1";
const episode = showRouteMatch?.params.episode || "1";
React.useEffect(() => { React.useEffect(() => {
setEpisodeList(streamData.episodes[season]); setEpisodeList(streamData.episodes[selectedSeason]);
}, [season, streamData.episodes]) }, [selectedSeason, streamData.episodes])
React.useEffect(() => { React.useEffect(() => {
if (streamData.type === "show") { if (streamData.type === "show") {
setSeasonList(streamData.seasons); setSeasonList(streamData.seasons);
setSeason(streamData.seasons[0])
// TODO load from localstorage last watched // TODO load from localstorage last watched
setEpisode({ episode: streamData.episodes[streamData.seasons[0]][0], season: streamData.seasons[0] })
setEpisodeList(streamData.episodes[streamData.seasons[0]]); setEpisodeList(streamData.episodes[streamData.seasons[0]]);
} }
}, [streamData]) }, [streamData])
React.useEffect(() => {
if (streamData.type === "show" && !showRouteMatch) history.replace(`${baseRouteMatch.url}/season/1/episode/1`);
}, [streamData, showRouteMatch, history, baseRouteMatch.url]);
React.useEffect(() => {
if (streamData.type === "show" && !showRouteMatch) history.replace(`${baseRouteMatch.url}/season/1/episode/1`);
}, [streamData, showRouteMatch, history, baseRouteMatch.url]);
React.useEffect(() => {
if (streamData.type === "show" && showRouteMatch) setSelectedSeason(showRouteMatch.params.season.toString());
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
React.useEffect(() => { React.useEffect(() => {
let cancel = false; let cancel = false;
// ignore if not a show // ignore if not a show
if (streamData.type !== "show") return () => { if (streamData.type !== "show") return () => {
cancel = true; cancel = true;
}; };
if (!episode.episode) { if (!episode) {
setLoading(false); setLoading(false);
setStreamUrl(''); setStreamUrl('');
return; return;
} }
setLoading(true); setLoading(true);
getStreamUrl(streamData.slug, streamData.type, streamData.source, episode.season, episode.episode) getStreamUrl(streamData.slug, streamData.type, streamData.source, season, episode)
.then(({url}) => { .then(({url}) => {
if (cancel) return; if (cancel) return;
setStreamUrl(url) setStreamUrl(url)
@ -56,7 +73,11 @@ export function MovieView(props) {
return () => { return () => {
cancel = true; cancel = true;
} }
}, [episode, streamData, setStreamUrl]) }, [episode, streamData, setStreamUrl, season]);
function setEpisode({ season, episode }) {
history.push(`${baseRouteMatch.url}/season/${season}/episode/${episode}`);
}
const setProgress = (evt) => { const setProgress = (evt) => {
let ls = JSON.parse(localStorage.getItem("video-progress") || "{}") let ls = JSON.parse(localStorage.getItem("video-progress") || "{}")
@ -69,7 +90,7 @@ export function MovieView(props) {
} }
// Store real data // Store real data
let key = streamData.type === "show" ? `${season}-${episode.episode}` : "full" let key = streamData.type === "show" ? `${season}-${episode}` : "full"
ls[streamData.source][streamData.type][streamData.slug][key] = { ls[streamData.source][streamData.type][streamData.slug][key] = {
currentlyAt: Math.floor(evt.currentTarget.currentTime), currentlyAt: Math.floor(evt.currentTarget.currentTime),
totalDuration: Math.floor(evt.currentTarget.duration), totalDuration: Math.floor(evt.currentTarget.duration),
@ -79,7 +100,7 @@ export function MovieView(props) {
if(streamData.type === "show") { if(streamData.type === "show") {
ls[streamData.source][streamData.type][streamData.slug][key].show = { ls[streamData.source][streamData.type][streamData.slug][key].show = {
season, season,
episode: episode.episode episode: episode
} }
} }
@ -93,19 +114,22 @@ export function MovieView(props) {
{streamData.title} {streamData.title}
</Title> </Title>
{streamData.type === "show" ? <Title size="small"> {streamData.type === "show" ? <Title size="small">
Season {episode.season}: Episode {episode.episode} Season {season}: Episode {episode}
</Title> : undefined} </Title> : undefined}
<VideoElement streamUrl={streamUrl} loading={loading} setProgress={setProgress} /> <VideoElement streamUrl={streamUrl} loading={loading} setProgress={setProgress} />
{streamData.type === "show" ? {streamData.type === "show" ?
<EpisodeSelector <EpisodeSelector
setSeason={setSeason} setSelectedSeason={setSelectedSeason}
selectedSeason={selectedSeason}
setEpisode={setEpisode} setEpisode={setEpisode}
season={season}
seasons={seasonList} seasons={seasonList}
episodes={episodeLists} episodes={episodeLists}
slug={streamData.slug}
currentSeason={season} currentSeason={season}
currentEpisode={episode} currentEpisode={episode}
source={streamData.source} source={streamData.source}
/> />
: ''} : ''}