mirror of
https://github.com/movie-web/movie-web.git
synced 2024-12-24 04:31:51 +01:00
continue watching
there are so many bugs
This commit is contained in:
parent
32ed5e1340
commit
ae0698ef80
@ -24,6 +24,7 @@
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.movieRow .left .seasonEpisodeSubtitle,
|
||||
.movieRow .left .year {
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
@ -20,14 +20,14 @@ export function MovieRow(props) {
|
||||
return (
|
||||
<div className="movieRow" onClick={() => props.onClick && props.onClick()}>
|
||||
<div className="left">
|
||||
{props.title}
|
||||
{props.title} <span className="seasonEpisodeSubtitle">{props.place ? ` — Season ${props.place.season}: episode ${props.place.episode}` : ''}</span>
|
||||
<span className="year">({props.year})</span>
|
||||
</div>
|
||||
<div className="watch">
|
||||
<p>Watch {props.type}</p>
|
||||
<Arrow/>
|
||||
</div>
|
||||
<PercentageOverlay percentage={percentage} />
|
||||
<PercentageOverlay percentage={props.percentage || percentage} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import React, { useCallback } from 'react'
|
||||
import { useRouteMatch, useHistory } from 'react-router-dom'
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { Title } from '../components/Title'
|
||||
@ -14,15 +14,47 @@ 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 [seasonList, setSeasonList] = React.useState([]);
|
||||
const [episodeLists, setEpisodeList] = React.useState([]);
|
||||
const [loading, setLoading] = React.useState(false);
|
||||
const [ seasonList, setSeasonList ] = React.useState([]);
|
||||
const [ episodeLists, setEpisodeList ] = React.useState([]);
|
||||
const [ loading, setLoading ] = React.useState(false);
|
||||
const [ selectedSeason, setSelectedSeason ] = React.useState("1");
|
||||
|
||||
const season = showRouteMatch?.params.season || "1";
|
||||
const episode = showRouteMatch?.params.episode || "1";
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
function setEpisode({ season, episode }) {
|
||||
// getStream(title, slug, type, source, year);
|
||||
// console.log(season, episode)
|
||||
let tmpSeason = showRouteMatch.params.season;
|
||||
let tmpEpisode = showRouteMatch.params.episode;
|
||||
if (tmpSeason != season && tmpEpisode != episode)
|
||||
history.replace(`${baseRouteMatch.url}/season/${season}/episode/${episode}`);
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
// Cache streamData continue watching on home page
|
||||
let movieCache = JSON.parse(localStorage.getItem("movie-cache") || "{}");
|
||||
|
||||
if (!movieCache[streamData.source]) movieCache[streamData.source] = {}
|
||||
movieCache[streamData.source][streamData.slug] = {
|
||||
cachedAt: Date.now()
|
||||
}
|
||||
|
||||
localStorage.setItem("movie-cache", JSON.stringify(movieCache));
|
||||
|
||||
// Set season and episode list for GUI
|
||||
if (streamData.type === "show") {
|
||||
setSeasonList(streamData.seasons);
|
||||
setSelectedSeason(streamData.seasons[0])
|
||||
// TODO load from localstorage last watched
|
||||
setEpisode({ episode: streamData.episodes[streamData.seasons[0]][0], season: streamData.seasons[0] })
|
||||
setEpisodeList(streamData.episodes[streamData.seasons[0]]);
|
||||
}
|
||||
}, [streamData, setEpisode])
|
||||
|
||||
React.useEffect(() => {
|
||||
setEpisodeList(streamData.episodes[selectedSeason]);
|
||||
}, [selectedSeason, streamData.episodes])
|
||||
@ -39,14 +71,9 @@ export function MovieView(props) {
|
||||
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
|
||||
}, []);
|
||||
}, [showRouteMatch, streamData]);
|
||||
|
||||
React.useEffect(() => {
|
||||
let cancel = false;
|
||||
@ -71,19 +98,38 @@ export function MovieView(props) {
|
||||
if (cancel) return;
|
||||
console.error(e)
|
||||
})
|
||||
|
||||
return () => {
|
||||
cancel = true;
|
||||
}
|
||||
}, [episode, streamData, setStreamUrl, season]);
|
||||
|
||||
function setEpisode({ season, episode }) {
|
||||
history.push(`${baseRouteMatch.url}/season/${season}/episode/${episode}`);
|
||||
}
|
||||
React.useEffect(() => {
|
||||
// Cache streamData continue watching on home page
|
||||
let movieCache = JSON.parse(localStorage.getItem("movie-cache") || "{}");
|
||||
|
||||
if(!movieCache[streamData.source]) movieCache[streamData.source] = {}
|
||||
movieCache[streamData.source][streamData.slug] = {
|
||||
cachedAt: Date.now()
|
||||
}
|
||||
|
||||
localStorage.setItem("movie-cache", JSON.stringify(movieCache));
|
||||
|
||||
// Set season and episode list for GUI
|
||||
if (streamData.type === "show") {
|
||||
setSeasonList(streamData.seasons);
|
||||
setSelectedSeason(streamData.seasons[0])
|
||||
// TODO load from localstorage last watched
|
||||
setEpisode({ episode: streamData.episodes[streamData.seasons[0]][0], season: streamData.seasons[0] })
|
||||
setEpisodeList(streamData.episodes[streamData.seasons[0]]);
|
||||
}
|
||||
}, [streamData, setEpisode])
|
||||
|
||||
const setProgress = (evt) => {
|
||||
let ls = JSON.parse(localStorage.getItem("video-progress") || "{}")
|
||||
|
||||
// We're just checking lookmovie for now since there is only one scraper
|
||||
console.log(streamData);
|
||||
|
||||
if(!ls[streamData.source]) ls[streamData.source] = {}
|
||||
if(!ls[streamData.source][streamData.type]) ls[streamData.source][streamData.type] = {}
|
||||
if(!ls[streamData.source][streamData.type][streamData.slug]) {
|
||||
@ -91,17 +137,18 @@ export function MovieView(props) {
|
||||
}
|
||||
|
||||
// Store real data
|
||||
let key = streamData.type === "show" ? `${season}-${episode}` : "full"
|
||||
let key = streamData.type === "show" ? `${season}-${episode.episode}` : "full"
|
||||
ls[streamData.source][streamData.type][streamData.slug][key] = {
|
||||
currentlyAt: Math.floor(evt.currentTarget.currentTime),
|
||||
totalDuration: Math.floor(evt.currentTarget.duration),
|
||||
updatedAt: Date.now()
|
||||
updatedAt: Date.now(),
|
||||
meta: streamData
|
||||
}
|
||||
|
||||
if(streamData.type === "show") {
|
||||
ls[streamData.source][streamData.type][streamData.slug][key].show = {
|
||||
season,
|
||||
episode: episode
|
||||
episode: episode.episode
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,11 +8,30 @@
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.cardView > div {
|
||||
.cardView nav {
|
||||
width: 100%;
|
||||
max-width: 624px;
|
||||
}
|
||||
.cardView nav a {
|
||||
padding: 8px 16px;
|
||||
margin-right: 10px;
|
||||
border-radius: 4px;
|
||||
color: var(--text);
|
||||
}
|
||||
.cardView nav a:not(.selected-link) {
|
||||
cursor: pointer;
|
||||
}
|
||||
.cardView nav a.selected-link {
|
||||
background: var(--button);
|
||||
color: var(--button-text);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.cardView > * {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.cardView > div:first-child {
|
||||
.cardView > *:first-child {
|
||||
margin-top: 38px;
|
||||
}
|
||||
|
||||
|
@ -30,6 +30,8 @@ export function SearchView() {
|
||||
const [failed, setFailed] = React.useState(false);
|
||||
const [showingOptions, setShowingOptions] = React.useState(false);
|
||||
const [errorStatus, setErrorStatus] = React.useState(false);
|
||||
const [page, setPage] = React.useState('search');
|
||||
const [continueWatching, setContinueWatching] = React.useState([])
|
||||
|
||||
const fail = (str) => {
|
||||
setProgress(maxSteps);
|
||||
@ -37,7 +39,7 @@ export function SearchView() {
|
||||
setFailed(true)
|
||||
}
|
||||
|
||||
async function getStream(title, slug, type, source) {
|
||||
async function getStream(title, slug, type, source, year) {
|
||||
setStreamUrl("");
|
||||
|
||||
try {
|
||||
@ -71,7 +73,8 @@ export function SearchView() {
|
||||
seasons,
|
||||
episodes,
|
||||
slug,
|
||||
source
|
||||
source,
|
||||
year
|
||||
})
|
||||
setText(`Streaming...`)
|
||||
navigate("movie")
|
||||
@ -100,9 +103,9 @@ export function SearchView() {
|
||||
return;
|
||||
}
|
||||
|
||||
const { title, slug, type, source } = options[0];
|
||||
const { title, slug, type, source, year } = options[0];
|
||||
history.push(`${routeMatch.url}/${source}/${title}/${slug}`);
|
||||
getStream(title, slug, type, source);
|
||||
getStream(title, slug, type, source, year);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
fail(`Failed to watch ${contentType}`)
|
||||
@ -128,7 +131,38 @@ export function SearchView() {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
if (!type || (type !== 'movie' && type !== 'show')) return <Redirect to="/movie" />
|
||||
React.useEffect(() => {
|
||||
const progressData = JSON.parse(localStorage.getItem('video-progress') || "{}")
|
||||
let newContinueWatching = []
|
||||
Object.keys(progressData).forEach(source => {
|
||||
const all = [
|
||||
...Object.entries(progressData[source]?.show ?? {}),
|
||||
...Object.entries(progressData[source]?.movie ?? {})
|
||||
]
|
||||
for (const [slug, data] of all) {
|
||||
for (let subselection of Object.values(data)) {
|
||||
let entry = {
|
||||
slug,
|
||||
data: subselection,
|
||||
type: subselection.show ? 'show' : 'movie',
|
||||
percentageDone: Math.floor((subselection.currentlyAt / subselection.totalDuration) * 100),
|
||||
source
|
||||
}
|
||||
if (entry.percentageDone < 90) {
|
||||
newContinueWatching.push(entry)
|
||||
}
|
||||
}
|
||||
}
|
||||
newContinueWatching = newContinueWatching.sort((a, b) => {
|
||||
return b.data.updatedAt - a.data.updatedAt
|
||||
})
|
||||
setContinueWatching(newContinueWatching)
|
||||
})
|
||||
}, []);
|
||||
|
||||
if (!type || (type !== 'movie' && type !== 'show')) {
|
||||
return <Redirect to="/movie" />
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="cardView">
|
||||
@ -136,47 +170,73 @@ export function SearchView() {
|
||||
<title>{type === 'movie' ? 'Movies' : 'TV Shows'} | movie-web</title>
|
||||
</Helmet>
|
||||
|
||||
<Card>
|
||||
<DiscordBanner />
|
||||
{errorStatus ? <ErrorBanner>{errorStatus}</ErrorBanner> : ''}
|
||||
<Title accent="Because watching content legally is boring">
|
||||
What do you wanna watch?
|
||||
</Title>
|
||||
<TypeSelector
|
||||
setType={(type) => history.push(`/${type}`)}
|
||||
choices={[
|
||||
{ label: "Movie", value: "movie" },
|
||||
{ label: "TV Show", value: "show" }
|
||||
]}
|
||||
noWrap={true}
|
||||
selected={type}
|
||||
/>
|
||||
<InputBox placeholder={type === "movie" ? "Hamilton" : "Atypical"} onSubmit={(str) => searchMovie(str, type)} />
|
||||
<Progress show={progress > 0} failed={failed} progress={progress} steps={maxSteps} text={text} />
|
||||
</Card>
|
||||
{/* Nav */}
|
||||
<nav>
|
||||
<a className={page === 'search' ? 'selected-link' : ''} onClick={() => setPage('search')} href>Search</a>
|
||||
{continueWatching.length > 0 ?
|
||||
<a className={page === 'watching' ? 'selected-link' : ''} onClick={() => setPage('watching')} href>Continue watching</a>
|
||||
: ''}
|
||||
</nav>
|
||||
|
||||
{/* Search */}
|
||||
{page === 'search' ?
|
||||
<React.Fragment>
|
||||
<Card>
|
||||
<DiscordBanner />
|
||||
{errorStatus ? <ErrorBanner>{errorStatus}</ErrorBanner> : ''}
|
||||
<Title accent="Because watching content legally is boring">
|
||||
What do you wanna watch?
|
||||
</Title>
|
||||
<TypeSelector
|
||||
setType={(type) => history.push(`/${type}`)}
|
||||
choices={[
|
||||
{ label: "Movie", value: "movie" },
|
||||
{ label: "TV Show", value: "show" }
|
||||
]}
|
||||
noWrap={true}
|
||||
selected={type}
|
||||
/>
|
||||
<InputBox placeholder={type === "movie" ? "Hamilton" : "Atypical"} onSubmit={(str) => searchMovie(str, type)} />
|
||||
<Progress show={progress > 0} failed={failed} progress={progress} steps={maxSteps} text={text} />
|
||||
</Card>
|
||||
|
||||
<Card show={showingOptions} doTransition>
|
||||
<Title size="medium">
|
||||
Whoops, there are a few {type}s like that
|
||||
</Title>
|
||||
{Object.entries(options.reduce((a, v) => {
|
||||
if (!a[v.source]) a[v.source] = []
|
||||
a[v.source].push(v)
|
||||
return a;
|
||||
}, {})).map(v => (
|
||||
<div key={v[0]}>
|
||||
<p className="source">{v[0]}</p>
|
||||
{v[1].map((v, i) => (
|
||||
<MovieRow key={i} title={v.title} slug={v.slug} type={v.type} year={v.year} source={v.source} onClick={() => {
|
||||
history.push(`${routeMatch.url}/${v.source}/${v.title}/${v.slug}`);
|
||||
setShowingOptions(false)
|
||||
getStream(v.title, v.slug, v.type, v.source, v.year)
|
||||
}} />
|
||||
))}
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</Card>
|
||||
</React.Fragment> : <React.Fragment />}
|
||||
|
||||
{/* Continue watching */}
|
||||
{continueWatching.length > 0 && page === 'watching' ? <Card>
|
||||
<Title>Continue watching</Title>
|
||||
{console.log(continueWatching)}
|
||||
{continueWatching?.map((v, i) => (
|
||||
<MovieRow key={i} title={v.data.meta.title} slug={v.data.meta.slug} type={v.type} year={v.data.meta.year} source={v.source} place={v.data.show} percentage={v.percentageDone} onClick={() => {
|
||||
history.push(`${routeMatch.url}/${v.source}/${v.data.meta.title}/${v.slug}`)
|
||||
setShowingOptions(false)
|
||||
getStream(v.data.meta.title, v.data.meta.slug, v.type, v.source, v.year)
|
||||
}} />
|
||||
))}
|
||||
</Card> : <React.Fragment></React.Fragment>}
|
||||
|
||||
<Card show={showingOptions} doTransition>
|
||||
<Title size="medium">
|
||||
Whoops, there are a few {type}s like that
|
||||
</Title>
|
||||
{ Object.entries(options.reduce((a, v) => {
|
||||
if (!a[v.source]) a[v.source] = []
|
||||
a[v.source].push(v)
|
||||
return a;
|
||||
}, {})).map(v => (
|
||||
<div key={v[0]}>
|
||||
<p className="source">{v[0]}</p>
|
||||
{v[1].map((v, i) => (
|
||||
<MovieRow key={i} title={v.title} slug={v.slug} type={v.type} year={v.year} source={v.source} onClick={() => {
|
||||
history.push(`${routeMatch.url}/${v.source}/${v.title}/${v.slug}`);
|
||||
setShowingOptions(false)
|
||||
getStream(v.title, v.slug, v.type, v.source)
|
||||
}} />
|
||||
))}
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</Card>
|
||||
<div className="topRightCredits">
|
||||
<a href="https://github.com/JamesHawkinss/movie-web" target="_blank" rel="noreferrer">Check it out on GitHub <Arrow /></a>
|
||||
<br />
|
||||
|
Loading…
Reference in New Issue
Block a user