From f0d71d11248021d503422f92b8457e5f51352e6f Mon Sep 17 00:00:00 2001 From: James Hawkins Date: Tue, 3 Aug 2021 18:54:08 +0100 Subject: [PATCH 01/22] switch over to cloudflare worker --- src/lib/scraper/gomostream.js | 2 +- src/lib/scraper/lookmovie.js | 4 ++-- src/views/Search.js | 20 ++++++++++---------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/lib/scraper/gomostream.js b/src/lib/scraper/gomostream.js index 07b067e0..a784f1f9 100644 --- a/src/lib/scraper/gomostream.js +++ b/src/lib/scraper/gomostream.js @@ -1,6 +1,6 @@ import { unpack } from '../util/unpacker'; -const CORS_URL = 'https://movie-web-proxy.herokuapp.com/'; +const CORS_URL = 'https://proxy-01.movie-web.workers.dev/?'; const BASE_URL = `${CORS_URL}https://gomo.to`; const MOVIE_URL = `${BASE_URL}/movie` const DECODING_URL = `${BASE_URL}/decoding_v3.php` diff --git a/src/lib/scraper/lookmovie.js b/src/lib/scraper/lookmovie.js index 9fafd3ce..98e4ee8d 100644 --- a/src/lib/scraper/lookmovie.js +++ b/src/lib/scraper/lookmovie.js @@ -1,8 +1,8 @@ import Fuse from 'fuse.js' import JSON5 from 'json5' -const CORS_URL = `https://movie-web-proxy.herokuapp.com`; -const BASE_URL = `${CORS_URL}/https://lookmovie.io`; +const CORS_URL = `https://proxy-01.movie-web.workers.dev/?`; +const BASE_URL = `${CORS_URL}https://lookmovie.io`; async function findContent(searchTerm, type) { const searchUrl = `${BASE_URL}/${type}s/search/?q=${encodeURIComponent(searchTerm)}`; diff --git a/src/views/Search.js b/src/views/Search.js index 836c9088..1eecdce4 100644 --- a/src/views/Search.js +++ b/src/views/Search.js @@ -109,16 +109,16 @@ export function SearchView() { } } - React.useEffect(() => { - async function fetchHealth() { - const HOME_URL = "https://movie-web-proxy.herokuapp.com" - await fetch(HOME_URL).catch(() => { - // Request failed; source likely offline - setErrorStatus(`Our content provider is currently offline, apologies.`) - }) - } - fetchHealth() - }, []); + // React.useEffect(() => { + // async function fetchHealth() { + // const HOME_URL = "https://proxy-01.movie-web.workers.dev/?" + // await fetch(HOME_URL).catch(() => { + // // Request failed; source likely offline + // setErrorStatus(`Our content provider is currently offline, apologies.`) + // }) + // } + // fetchHealth() + // }, []); React.useEffect(() => { if (streamRouteMatch) { From 0fc4e536304f6d5f694178611f72a63bb3ed6842 Mon Sep 17 00:00:00 2001 From: James Hawkins Date: Tue, 3 Aug 2021 18:54:08 +0100 Subject: [PATCH 02/22] Revert "switch over to cloudflare worker" This reverts commit f0d71d11248021d503422f92b8457e5f51352e6f. --- src/lib/scraper/gomostream.js | 2 +- src/lib/scraper/lookmovie.js | 4 ++-- src/views/Search.js | 20 ++++++++++---------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/lib/scraper/gomostream.js b/src/lib/scraper/gomostream.js index a784f1f9..07b067e0 100644 --- a/src/lib/scraper/gomostream.js +++ b/src/lib/scraper/gomostream.js @@ -1,6 +1,6 @@ import { unpack } from '../util/unpacker'; -const CORS_URL = 'https://proxy-01.movie-web.workers.dev/?'; +const CORS_URL = 'https://movie-web-proxy.herokuapp.com/'; const BASE_URL = `${CORS_URL}https://gomo.to`; const MOVIE_URL = `${BASE_URL}/movie` const DECODING_URL = `${BASE_URL}/decoding_v3.php` diff --git a/src/lib/scraper/lookmovie.js b/src/lib/scraper/lookmovie.js index 98e4ee8d..9fafd3ce 100644 --- a/src/lib/scraper/lookmovie.js +++ b/src/lib/scraper/lookmovie.js @@ -1,8 +1,8 @@ import Fuse from 'fuse.js' import JSON5 from 'json5' -const CORS_URL = `https://proxy-01.movie-web.workers.dev/?`; -const BASE_URL = `${CORS_URL}https://lookmovie.io`; +const CORS_URL = `https://movie-web-proxy.herokuapp.com`; +const BASE_URL = `${CORS_URL}/https://lookmovie.io`; async function findContent(searchTerm, type) { const searchUrl = `${BASE_URL}/${type}s/search/?q=${encodeURIComponent(searchTerm)}`; diff --git a/src/views/Search.js b/src/views/Search.js index 1eecdce4..836c9088 100644 --- a/src/views/Search.js +++ b/src/views/Search.js @@ -109,16 +109,16 @@ export function SearchView() { } } - // React.useEffect(() => { - // async function fetchHealth() { - // const HOME_URL = "https://proxy-01.movie-web.workers.dev/?" - // await fetch(HOME_URL).catch(() => { - // // Request failed; source likely offline - // setErrorStatus(`Our content provider is currently offline, apologies.`) - // }) - // } - // fetchHealth() - // }, []); + React.useEffect(() => { + async function fetchHealth() { + const HOME_URL = "https://movie-web-proxy.herokuapp.com" + await fetch(HOME_URL).catch(() => { + // Request failed; source likely offline + setErrorStatus(`Our content provider is currently offline, apologies.`) + }) + } + fetchHealth() + }, []); React.useEffect(() => { if (streamRouteMatch) { From a1c22cc618fa1d88452bf49ab78c18e6820ccec6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Aug 2021 04:42:41 +0000 Subject: [PATCH 03/22] Bump tar from 6.1.0 to 6.1.5 Bumps [tar](https://github.com/npm/node-tar) from 6.1.0 to 6.1.5. - [Release notes](https://github.com/npm/node-tar/releases) - [Changelog](https://github.com/npm/node-tar/blob/main/CHANGELOG.md) - [Commits](https://github.com/npm/node-tar/compare/v6.1.0...v6.1.5) --- updated-dependencies: - dependency-name: tar dependency-type: indirect ... Signed-off-by: dependabot[bot] --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 5db630eb..b9724b4f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10403,9 +10403,9 @@ tapable@^1.0.0, tapable@^1.1.3: integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== tar@^6.0.2: - version "6.1.0" - resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.0.tgz#d1724e9bcc04b977b18d5c573b333a2207229a83" - integrity sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA== + version "6.1.5" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.5.tgz#6e25bee1cfda94317aedc3f5d49290ae68361d73" + integrity sha512-FiK6MQyyaqd5vHuUjbg/NpO8BuEGeSXcmlH7Pt/JkugWS8s0w8nKybWjHDJiwzCAIKZ66uof4ghm4tBADjcqRA== dependencies: chownr "^2.0.0" fs-minipass "^2.0.0" From 302985dd0eea646bd28f701328fa5acc75ffe8c1 Mon Sep 17 00:00:00 2001 From: James Hawkins Date: Wed, 4 Aug 2021 13:54:34 +0100 Subject: [PATCH 04/22] class -> className --- src/components/ErrorBanner.js | 2 +- src/components/PercentageOverlay.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/ErrorBanner.js b/src/components/ErrorBanner.js index 60d8b42d..90a8f285 100644 --- a/src/components/ErrorBanner.js +++ b/src/components/ErrorBanner.js @@ -3,7 +3,7 @@ import './ErrorBanner.css'; export function ErrorBanner({children}) { return ( -
+
{children}
) diff --git a/src/components/PercentageOverlay.js b/src/components/PercentageOverlay.js index 50a23a0a..0415774e 100644 --- a/src/components/PercentageOverlay.js +++ b/src/components/PercentageOverlay.js @@ -6,8 +6,8 @@ export function PercentageOverlay({ percentage }) { if(percentage && percentage > 3) percentage = Math.max(20, percentage < 90 ? percentage : 100) return percentage > 0 ? ( -
-
+
+
) : } \ No newline at end of file From 32ed5e1340c0a60719ca1dfef928fb3c99cc463b Mon Sep 17 00:00:00 2001 From: James Hawkins Date: Wed, 4 Aug 2021 13:55:01 +0100 Subject: [PATCH 05/22] switch lookmovie.io to lookmovie.com issues reported in using lookmovie.io for asian regions --- src/lib/scraper/lookmovie.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/scraper/lookmovie.js b/src/lib/scraper/lookmovie.js index 9fafd3ce..9d31b62d 100644 --- a/src/lib/scraper/lookmovie.js +++ b/src/lib/scraper/lookmovie.js @@ -2,7 +2,7 @@ import Fuse from 'fuse.js' import JSON5 from 'json5' const CORS_URL = `https://movie-web-proxy.herokuapp.com`; -const BASE_URL = `${CORS_URL}/https://lookmovie.io`; +const BASE_URL = `${CORS_URL}/https://lookmovie.com`; async function findContent(searchTerm, type) { const searchUrl = `${BASE_URL}/${type}s/search/?q=${encodeURIComponent(searchTerm)}`; From ae0698ef8039fdf6010db913b1e1a902de3e2a67 Mon Sep 17 00:00:00 2001 From: James Hawkins Date: Fri, 6 Aug 2021 14:39:54 +0100 Subject: [PATCH 06/22] continue watching there are so many bugs --- src/components/MovieRow.css | 1 + src/components/MovieRow.js | 4 +- src/views/Movie.js | 81 +++++++++++++++---- src/views/Search.css | 23 +++++- src/views/Search.js | 150 +++++++++++++++++++++++++----------- 5 files changed, 193 insertions(+), 66 deletions(-) diff --git a/src/components/MovieRow.css b/src/components/MovieRow.css index a32463b5..4ef12f41 100644 --- a/src/components/MovieRow.css +++ b/src/components/MovieRow.css @@ -24,6 +24,7 @@ margin-right: 0.5rem; } +.movieRow .left .seasonEpisodeSubtitle, .movieRow .left .year { color: var(--text-secondary); } diff --git a/src/components/MovieRow.js b/src/components/MovieRow.js index d8ae3162..d15d35ef 100644 --- a/src/components/MovieRow.js +++ b/src/components/MovieRow.js @@ -20,14 +20,14 @@ export function MovieRow(props) { return (
props.onClick && props.onClick()}>
- {props.title}  + {props.title} {props.place ? ` — Season ${props.place.season}: episode ${props.place.episode}` : ''}  ({props.year})

Watch {props.type}

- +
) } diff --git a/src/views/Movie.js b/src/views/Movie.js index cec810e9..71c72f0c 100644 --- a/src/views/Movie.js +++ b/src/views/Movie.js @@ -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 } } diff --git a/src/views/Search.css b/src/views/Search.css index 6795e5a1..903b854c 100644 --- a/src/views/Search.css +++ b/src/views/Search.css @@ -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; } diff --git a/src/views/Search.js b/src/views/Search.js index 836c9088..715534d0 100644 --- a/src/views/Search.js +++ b/src/views/Search.js @@ -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 + 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 + } return (
@@ -136,47 +170,73 @@ export function SearchView() { {type === 'movie' ? 'Movies' : 'TV Shows'} | movie-web - - - {errorStatus ? {errorStatus} : ''} - - What do you wanna watch? - - history.push(`/${type}`)} - choices={[ - { label: "Movie", value: "movie" }, - { label: "TV Show", value: "show" } - ]} - noWrap={true} - selected={type} - /> - searchMovie(str, type)} /> - 0} failed={failed} progress={progress} steps={maxSteps} text={text} /> - + {/* Nav */} + + + {/* Search */} + {page === 'search' ? + + + + {errorStatus ? {errorStatus} : ''} + + What do you wanna watch? + + history.push(`/${type}`)} + choices={[ + { label: "Movie", value: "movie" }, + { label: "TV Show", value: "show" } + ]} + noWrap={true} + selected={type} + /> + searchMovie(str, type)} /> + 0} failed={failed} progress={progress} steps={maxSteps} text={text} /> + + + + + Whoops, there are a few {type}s like that + + {Object.entries(options.reduce((a, v) => { + if (!a[v.source]) a[v.source] = [] + a[v.source].push(v) + return a; + }, {})).map(v => ( +
+

{v[0]}

+ {v[1].map((v, i) => ( + { + history.push(`${routeMatch.url}/${v.source}/${v.title}/${v.slug}`); + setShowingOptions(false) + getStream(v.title, v.slug, v.type, v.source, v.year) + }} /> + ))} +
+ )) + } +
+
: } + + {/* Continue watching */} + {continueWatching.length > 0 && page === 'watching' ? + Continue watching + {console.log(continueWatching)} + {continueWatching?.map((v, i) => ( + { + 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) + }} /> + ))} + : } - - - Whoops, there are a few {type}s like that - - { Object.entries(options.reduce((a, v) => { - if (!a[v.source]) a[v.source] = [] - a[v.source].push(v) - return a; - }, {})).map(v => ( -
-

{v[0]}

- {v[1].map((v, i) => ( - { - history.push(`${routeMatch.url}/${v.source}/${v.title}/${v.slug}`); - setShowingOptions(false) - getStream(v.title, v.slug, v.type, v.source) - }} /> - ))} -
- )) - } -
Check it out on GitHub
From 764d67389730a3851ec9104013da8e7151cae018 Mon Sep 17 00:00:00 2001 From: James Hawkins Date: Fri, 6 Aug 2021 16:54:09 +0100 Subject: [PATCH 07/22] slightly less bugs --- src/components/EpisodeSelector.js | 6 +-- src/views/Movie.js | 78 +++++++++---------------------- 2 files changed, 24 insertions(+), 60 deletions(-) diff --git a/src/components/EpisodeSelector.js b/src/components/EpisodeSelector.js index 1b65cc4e..9ba35b2a 100644 --- a/src/components/EpisodeSelector.js +++ b/src/components/EpisodeSelector.js @@ -3,15 +3,15 @@ import { TypeSelector } from './TypeSelector'; import { NumberSelector } from './NumberSelector'; import './EpisodeSelector.css' -export function EpisodeSelector({ setSelectedSeason, setEpisode, seasons, selectedSeason, season, episodes, currentSeason, currentEpisode, source }) { +export function EpisodeSelector({ setSelectedSeason, setEpisode, seasons, selectedSeason, season, episodes, currentSeason, currentEpisode, streamData }) { const choices = episodes ? episodes.map(v => { let progressData = JSON.parse(localStorage.getItem('video-progress') || "{}") let currentlyAt = 0; let totalDuration = 0; - const progress = progressData?.[source]?.show?.slug?.[`${season}-${v}`] - if(progress) { + const progress = progressData?.[streamData.source]?.[streamData.show]?.[streamData.slug]?.[`${season}-${v}`] + if (progress) { currentlyAt = progress.currentlyAt totalDuration = progress.totalDuration } diff --git a/src/views/Movie.js b/src/views/Movie.js index 71c72f0c..ee82e4fe 100644 --- a/src/views/Movie.js +++ b/src/views/Movie.js @@ -26,47 +26,9 @@ export function MovieView(props) { // 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}`); + 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]) - - React.useEffect(() => { - if (streamData.type === "show") { - setSeasonList(streamData.seasons); - // TODO load from localstorage last watched - setEpisodeList(streamData.episodes[streamData.seasons[0]]); - } - }, [streamData]) - React.useEffect(() => { if (streamData.type === "show" && !showRouteMatch) history.replace(`${baseRouteMatch.url}/season/1/episode/1`); }, [streamData, showRouteMatch, history, baseRouteMatch.url]); @@ -77,15 +39,17 @@ export function MovieView(props) { React.useEffect(() => { let cancel = false; - // ignore if not a show + if (streamData.type !== "show") return () => { cancel = true; }; + if (!episode) { setLoading(false); setStreamUrl(''); return; } + setLoading(true); getStreamUrl(streamData.slug, streamData.type, streamData.source, season, episode) @@ -94,7 +58,7 @@ export function MovieView(props) { setStreamUrl(url) setLoading(false); }) - .catch(e => { + .catch((e) => { if (cancel) return; console.error(e) }) @@ -114,30 +78,28 @@ export function MovieView(props) { } localStorage.setItem("movie-cache", JSON.stringify(movieCache)); - - // Set season and episode list for GUI + }, [streamData]) + + React.useEffect(() => { 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]]); + setSelectedSeason(selectedSeason) + setEpisodeList(streamData.episodes[selectedSeason]); } - }, [streamData, setEpisode]) + }, [streamData, selectedSeason]) const setProgress = (evt) => { let ls = JSON.parse(localStorage.getItem("video-progress") || "{}") - 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]) { + 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]) ls[streamData.source][streamData.type][streamData.slug] = {} - } // 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] = { currentlyAt: Math.floor(evt.currentTarget.currentTime), totalDuration: Math.floor(evt.currentTarget.duration), @@ -148,7 +110,7 @@ export function MovieView(props) { if(streamData.type === "show") { ls[streamData.source][streamData.type][streamData.slug][key].show = { season, - episode: episode.episode + episode } } @@ -168,7 +130,9 @@ export function MovieView(props) { {streamData.type === "show" ? Season {season}: Episode {episode} : undefined} + + {streamData.type === "show" ? : ''} From 7a85f05c47fe545b194fd4abe8bcc25d01c8db86 Mon Sep 17 00:00:00 2001 From: James Hawkins Date: Fri, 6 Aug 2021 17:57:58 +0100 Subject: [PATCH 08/22] even more continue watching --- src/components/EpisodeSelector.js | 5 +++-- src/components/MovieRow.js | 11 +++++++---- src/views/Movie.js | 2 +- src/views/Search.js | 13 +++++++++---- 4 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/components/EpisodeSelector.js b/src/components/EpisodeSelector.js index 9ba35b2a..f0fb16fe 100644 --- a/src/components/EpisodeSelector.js +++ b/src/components/EpisodeSelector.js @@ -3,14 +3,15 @@ import { TypeSelector } from './TypeSelector'; import { NumberSelector } from './NumberSelector'; import './EpisodeSelector.css' -export function EpisodeSelector({ setSelectedSeason, setEpisode, seasons, selectedSeason, season, episodes, currentSeason, currentEpisode, streamData }) { +export function EpisodeSelector({ setSelectedSeason, selectedSeason, setEpisode, seasons, episodes, currentSeason, currentEpisode, streamData }) { const choices = episodes ? episodes.map(v => { let progressData = JSON.parse(localStorage.getItem('video-progress') || "{}") let currentlyAt = 0; let totalDuration = 0; - const progress = progressData?.[streamData.source]?.[streamData.show]?.[streamData.slug]?.[`${season}-${v}`] + const progress = progressData?.[streamData.source]?.[streamData.type]?.[streamData.slug]?.[`${selectedSeason}-${v}`] + if (progress) { currentlyAt = progress.currentlyAt totalDuration = progress.totalDuration diff --git a/src/components/MovieRow.js b/src/components/MovieRow.js index d15d35ef..f857f5fb 100644 --- a/src/components/MovieRow.js +++ b/src/components/MovieRow.js @@ -6,13 +6,14 @@ import { PercentageOverlay } from './PercentageOverlay' // title: string // onClick: () => void export function MovieRow(props) { - const progressData = JSON.parse(localStorage.getItem("video-progress") || "{}") let progress; let percentage = null; - if(props.type === "movie") { + + if (props.type === "movie") { progress = progressData?.[props.source]?.movie?.[props.slug]?.full - if(progress) { + + if (progress) { percentage = Math.floor((progress.currentlyAt / progress.totalDuration) * 100) } } @@ -20,13 +21,15 @@ export function MovieRow(props) { return (
props.onClick && props.onClick()}>
- {props.title} {props.place ? ` — Season ${props.place.season}: episode ${props.place.episode}` : ''}  + {props.title}{props.place ? ` - S${props.place.season}:E${props.place.episode}` : ''}  ({props.year})
+

Watch {props.type}

+
) diff --git a/src/views/Movie.js b/src/views/Movie.js index ee82e4fe..2e8d4ed2 100644 --- a/src/views/Movie.js +++ b/src/views/Movie.js @@ -1,4 +1,4 @@ -import React, { useCallback } from 'react' +import React from 'react' import { useRouteMatch, useHistory } from 'react-router-dom' import { Helmet } from 'react-helmet'; import { Title } from '../components/Title' diff --git a/src/views/Search.js b/src/views/Search.js index 715534d0..9bc96e5c 100644 --- a/src/views/Search.js +++ b/src/views/Search.js @@ -56,7 +56,6 @@ export function SearchView() { let realUrl = ''; if (type === "movie") { - // getStreamUrl(slug, type, source, season, episode) const { url } = await getStreamUrl(slug, type, source); if (url === '') { @@ -65,6 +64,8 @@ export function SearchView() { realUrl = url; } + console.log(year) + setProgress(maxSteps); setStreamUrl(realUrl); setStreamData({ @@ -227,12 +228,16 @@ export function SearchView() { {/* Continue watching */} {continueWatching.length > 0 && page === 'watching' ? Continue watching - {console.log(continueWatching)} {continueWatching?.map((v, i) => ( { - history.push(`${routeMatch.url}/${v.source}/${v.data.meta.title}/${v.slug}`) + if (v.type === 'show') { + history.push(`${routeMatch.url}/${v.source}/${v.data.meta.title}/${v.slug}/season/${v.data.show.season}/episode/${v.data.show.episode}`) + } else { + 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) + getStream(v.data.meta.title, v.data.meta.slug, v.type, v.source, v.data.meta.year) }} /> ))} : } From a870b91ff3d0f26be67b3e28f76b68ff1297e235 Mon Sep 17 00:00:00 2001 From: James Hawkins Date: Fri, 6 Aug 2021 18:13:56 +0100 Subject: [PATCH 09/22] small changes --- src/views/Search.js | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/views/Search.js b/src/views/Search.js index 9bc96e5c..21a73cd1 100644 --- a/src/views/Search.js +++ b/src/views/Search.js @@ -64,8 +64,6 @@ export function SearchView() { realUrl = url; } - console.log(year) - setProgress(maxSteps); setStreamUrl(realUrl); setStreamData({ @@ -135,11 +133,13 @@ export function SearchView() { React.useEffect(() => { const progressData = JSON.parse(localStorage.getItem('video-progress') || "{}") let newContinueWatching = [] - Object.keys(progressData).forEach(source => { + + 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 = { @@ -149,17 +149,20 @@ export function SearchView() { 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 @@ -168,7 +171,7 @@ export function SearchView() { return (
- {type === 'movie' ? 'Movies' : 'TV Shows'} | movie-web + {type === 'movie' ? 'movies' : 'shows'} | movie-web {/* Nav */} From b3cd011bfac6157607cb92f5392bd9cfe1e77b0d Mon Sep 17 00:00:00 2001 From: James Hawkins Date: Fri, 6 Aug 2021 18:17:43 +0100 Subject: [PATCH 10/22] remove discord banner --- src/components/DiscordBanner.css | 16 ---------------- src/components/DiscordBanner.js | 12 ------------ src/views/Search.js | 2 -- 3 files changed, 30 deletions(-) delete mode 100644 src/components/DiscordBanner.css delete mode 100644 src/components/DiscordBanner.js diff --git a/src/components/DiscordBanner.css b/src/components/DiscordBanner.css deleted file mode 100644 index 6fa3fe5d..00000000 --- a/src/components/DiscordBanner.css +++ /dev/null @@ -1,16 +0,0 @@ -.discordBanner { - margin-top: 0.5rem; - border-inline-start: none; - font-size: 16px; - font-weight: normal; - letter-spacing: -.01em; - padding: .5rem 1rem .5rem .75rem; - border-radius: .25rem; - background-color: var(--button); - color: var(--button-text); -} - -.discordBanner a { - color: var(--button-text); - /* text-decoration: none; */ -} \ No newline at end of file diff --git a/src/components/DiscordBanner.js b/src/components/DiscordBanner.js deleted file mode 100644 index 2b952104..00000000 --- a/src/components/DiscordBanner.js +++ /dev/null @@ -1,12 +0,0 @@ -import React from 'react'; -import './DiscordBanner.css'; - -export function DiscordBanner() { - return ( - - ) -} \ No newline at end of file diff --git a/src/views/Search.js b/src/views/Search.js index 21a73cd1..bc4360fd 100644 --- a/src/views/Search.js +++ b/src/views/Search.js @@ -13,7 +13,6 @@ import { useMovie } from '../hooks/useMovie'; import { TypeSelector } from '../components/TypeSelector'; import './Search.css'; -import { DiscordBanner } from '../components/DiscordBanner'; export function SearchView() { const { navigate, setStreamUrl, setStreamData } = useMovie(); @@ -186,7 +185,6 @@ export function SearchView() { {page === 'search' ? - {errorStatus ? {errorStatus} : ''} What do you wanna watch? From 2dd3a3f82ad6131fcf46309e22a8c818528b2d5d Mon Sep 17 00:00:00 2001 From: James Hawkins <jhawki2005@gmail.com> Date: Fri, 6 Aug 2021 19:08:49 +0100 Subject: [PATCH 11/22] it actually continues watching now! --- src/views/Movie.js | 24 ++++++++++++++++++++++++ src/views/Search.js | 45 +++++++++++++++++++++++++++------------------ 2 files changed, 51 insertions(+), 18 deletions(-) diff --git a/src/views/Movie.js b/src/views/Movie.js index 2e8d4ed2..936ed8ad 100644 --- a/src/views/Movie.js +++ b/src/views/Movie.js @@ -88,6 +88,30 @@ export function MovieView(props) { } }, [streamData, selectedSeason]) + React.useEffect(() => { + let cancel = false; + + if (!cancel) { + let ls = JSON.parse(localStorage.getItem("video-progress") || "{}") + let key = streamData.type === "show" ? `${season}-${episode}` : "full" + let time = ls?.[streamData.source]?.[streamData.type]?.[streamData.slug]?.[key]?.currentlyAt; + + if (time) { + const element = document.getElementsByClassName('videoElement')[0]; + + if (!element) { + return () => { cancel = false } + } + + element.currentTime = time; + } + } + + return () => { + cancel = true; + } + }) + const setProgress = (evt) => { let ls = JSON.parse(localStorage.getItem("video-progress") || "{}") diff --git a/src/views/Search.js b/src/views/Search.js index bc4360fd..adedda73 100644 --- a/src/views/Search.js +++ b/src/views/Search.js @@ -1,16 +1,16 @@ import React from 'react'; -import { Redirect, useRouteMatch, useHistory } from 'react-router-dom'; import { Helmet } from 'react-helmet'; -import { InputBox } from '../components/InputBox'; -import { Title } from '../components/Title'; +import { Redirect, useHistory, useRouteMatch } from 'react-router-dom'; +import { Arrow } from '../components/Arrow'; import { Card } from '../components/Card'; import { ErrorBanner } from '../components/ErrorBanner'; +import { InputBox } from '../components/InputBox'; import { MovieRow } from '../components/MovieRow'; -import { Arrow } from '../components/Arrow'; import { Progress } from '../components/Progress'; -import { findContent, getStreamUrl, getEpisodes } from '../lib/index'; -import { useMovie } from '../hooks/useMovie'; +import { Title } from '../components/Title'; import { TypeSelector } from '../components/TypeSelector'; +import { useMovie } from '../hooks/useMovie'; +import { findContent, getEpisodes, getStreamUrl } from '../lib/index'; import './Search.css'; @@ -161,7 +161,7 @@ export function SearchView() { setContinueWatching(newContinueWatching) }) - }); + }, []); if (!type || (type !== 'movie' && type !== 'show')) { return <Redirect to="/movie" /> @@ -229,17 +229,26 @@ export function SearchView() { {/* Continue watching */} {continueWatching.length > 0 && page === 'watching' ? <Card> <Title>Continue watching - {continueWatching?.map((v, i) => ( - { - if (v.type === 'show') { - history.push(`${routeMatch.url}/${v.source}/${v.data.meta.title}/${v.slug}/season/${v.data.show.season}/episode/${v.data.show.episode}`) - } else { - 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.data.meta.year) - }} /> + {Object.entries(continueWatching.reduce((a, v) => { + if (!a[v.source]) a[v.source] = [] + a[v.source].push(v) + return a; + }, {})).map(v => ( +
+

{v[0]}

+ {v[1].map((v, i) => ( + { + if (v.type === 'show') { + history.push(`${routeMatch.url}/${v.source}/${v.data.meta.title}/${v.slug}/season/${v.data.show.season}/episode/${v.data.show.episode}`) + } else { + 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.data.meta.year) + }} /> + ))} +
))}
: } From 7d7bf312b73b41c744f3c619469ead19cf818d2b Mon Sep 17 00:00:00 2001 From: James Hawkins Date: Fri, 6 Aug 2021 19:14:31 +0100 Subject: [PATCH 12/22] tiny change --- src/views/Search.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/views/Search.js b/src/views/Search.js index adedda73..ca8ee347 100644 --- a/src/views/Search.js +++ b/src/views/Search.js @@ -221,8 +221,7 @@ export function SearchView() { }} /> ))}
- )) - } + ))} : } From 1d4988c4dd32c2febf4931de84acdc5272825c05 Mon Sep 17 00:00:00 2001 From: James Hawkins Date: Fri, 6 Aug 2021 20:03:18 +0100 Subject: [PATCH 13/22] fix: season selector not working --- src/components/EpisodeSelector.js | 2 +- src/views/Movie.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/EpisodeSelector.js b/src/components/EpisodeSelector.js index f0fb16fe..a478873d 100644 --- a/src/components/EpisodeSelector.js +++ b/src/components/EpisodeSelector.js @@ -28,7 +28,7 @@ export function EpisodeSelector({ setSelectedSeason, selectedSeason, setEpisode, return (
- ({ value: v.toString(), label: `Season ${v}`}))} selected={selectedSeason}/>

+ ({ value: v.toString(), label: `Season ${v}`}))} />

setEpisode({episode: e, season: selectedSeason})} choices={choices} selected={(selectedSeason.toString() === currentSeason) ? currentEpisode : null} />
) diff --git a/src/views/Movie.js b/src/views/Movie.js index 936ed8ad..e7aadafc 100644 --- a/src/views/Movie.js +++ b/src/views/Movie.js @@ -35,7 +35,8 @@ export function MovieView(props) { React.useEffect(() => { if (streamData.type === "show" && showRouteMatch) setSelectedSeason(showRouteMatch.params.season.toString()); - }, [showRouteMatch, streamData]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); React.useEffect(() => { let cancel = false; @@ -83,7 +84,6 @@ export function MovieView(props) { React.useEffect(() => { if (streamData.type === "show") { setSeasonList(streamData.seasons); - setSelectedSeason(selectedSeason) setEpisodeList(streamData.episodes[selectedSeason]); } }, [streamData, selectedSeason]) From 83a8c2c071eda4a78cdd7f92d768f639e32050af Mon Sep 17 00:00:00 2001 From: James Hawkins Date: Fri, 6 Aug 2021 22:13:37 +0100 Subject: [PATCH 14/22] remove movie-cache for now not being used. --- src/views/Movie.js | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/views/Movie.js b/src/views/Movie.js index e7aadafc..d3d8d1b4 100644 --- a/src/views/Movie.js +++ b/src/views/Movie.js @@ -69,18 +69,6 @@ export function MovieView(props) { } }, [episode, streamData, setStreamUrl, season]); - 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)); - }, [streamData]) - React.useEffect(() => { if (streamData.type === "show") { setSeasonList(streamData.seasons); From f42bad8a6b8fa2bad352a816eed61881d6423361 Mon Sep 17 00:00:00 2001 From: James Hawkins Date: Sat, 7 Aug 2021 09:55:39 +0100 Subject: [PATCH 15/22] more progress --- src/components/Cross.css | 7 +++++ src/components/Cross.js | 14 ++++++++++ src/components/MovieRow.js | 4 ++- src/views/Search.css | 2 +- src/views/Search.js | 57 +++++++++++++++++++++++--------------- 5 files changed, 60 insertions(+), 24 deletions(-) create mode 100644 src/components/Cross.css create mode 100644 src/components/Cross.js diff --git a/src/components/Cross.css b/src/components/Cross.css new file mode 100644 index 00000000..d4ef70b1 --- /dev/null +++ b/src/components/Cross.css @@ -0,0 +1,7 @@ +.cross { + display: inline-block; +} + +.cross:hover { + transform: rotate(45deg); +} \ No newline at end of file diff --git a/src/components/Cross.js b/src/components/Cross.js new file mode 100644 index 00000000..eb2ae21b --- /dev/null +++ b/src/components/Cross.js @@ -0,0 +1,14 @@ +import React from 'react' +import './Cross.css' + +export function Cross(props) { + return ( + + + + `}}> + + ) +} diff --git a/src/components/MovieRow.js b/src/components/MovieRow.js index f857f5fb..a7a7adac 100644 --- a/src/components/MovieRow.js +++ b/src/components/MovieRow.js @@ -1,7 +1,8 @@ import React from 'react' import { Arrow } from './Arrow' -import './MovieRow.css' +// import { Cross } from './Crosss' import { PercentageOverlay } from './PercentageOverlay' +import './MovieRow.css' // title: string // onClick: () => void @@ -21,6 +22,7 @@ export function MovieRow(props) { return (
props.onClick && props.onClick()}>
+ {/* */} {props.title}{props.place ? ` - S${props.place.season}:E${props.place.episode}` : ''}  ({props.year})
diff --git a/src/views/Search.css b/src/views/Search.css index 903b854c..7e8cb50d 100644 --- a/src/views/Search.css +++ b/src/views/Search.css @@ -22,7 +22,7 @@ cursor: pointer; } .cardView nav a.selected-link { - background: var(--button); + background: var(--card); color: var(--button-text); font-weight: bold; } diff --git a/src/views/Search.js b/src/views/Search.js index ca8ee347..283b1d4d 100644 --- a/src/views/Search.js +++ b/src/views/Search.js @@ -151,6 +151,26 @@ export function SearchView() { if (entry.percentageDone < 90) { newContinueWatching.push(entry) + } else { + if (!subselection.show) return; + + if (subselection.meta.episodes[subselection.show.season].includes(`${parseInt(subselection.show.episode) + 1}`)) { + subselection.show = { + season: subselection.show.season, + episode: `${parseInt(subselection.show.episode) + 1}` + } + + entry.percentageDone = 0; + } else if (subselection.meta.episodes[`${parseInt(subselection.show.season) + 1}`]['1']) { + subselection.show = { + season: `${parseInt(subselection.show.season) + 1}`, + episode: '1' + } + } else { + return; + } + + newContinueWatching.push(entry); } } } @@ -178,7 +198,7 @@ export function SearchView() { setPage('search')} href>Search {continueWatching.length > 0 ? setPage('watching')} href>Continue watching - : ''} + : ''} {/* Search */} @@ -228,27 +248,20 @@ export function SearchView() { {/* Continue watching */} {continueWatching.length > 0 && page === 'watching' ? Continue watching - {Object.entries(continueWatching.reduce((a, v) => { - if (!a[v.source]) a[v.source] = [] - a[v.source].push(v) - return a; - }, {})).map(v => ( -
-

{v[0]}

- {v[1].map((v, i) => ( - { - if (v.type === 'show') { - history.push(`${routeMatch.url}/${v.source}/${v.data.meta.title}/${v.slug}/season/${v.data.show.season}/episode/${v.data.show.episode}`) - } else { - 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.data.meta.year) - }} /> - ))} -
- ))} + {continueWatching?.map((v, i) => ( + //
+ { + if (v.type === 'show') { + history.push(`${routeMatch.url}/${v.source}/${v.data.meta.title}/${v.slug}/season/${v.data.show.season}/episode/${v.data.show.episode}`) + } else { + 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.data.meta.year) + }} /> + //
+ ))}
: }
From 0d8ef938ffabf8e34539141ac292dcc3b40bc5ec Mon Sep 17 00:00:00 2001 From: James Hawkins Date: Sat, 7 Aug 2021 11:04:35 +0100 Subject: [PATCH 16/22] comments --- src/views/Search.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/views/Search.js b/src/views/Search.js index 283b1d4d..215d5aa4 100644 --- a/src/views/Search.js +++ b/src/views/Search.js @@ -151,9 +151,12 @@ export function SearchView() { if (entry.percentageDone < 90) { newContinueWatching.push(entry) + // begin next episode logic } else { + // we can't do next episode for movies! if (!subselection.show) return; + // if the current season has a next episode, load it if (subselection.meta.episodes[subselection.show.season].includes(`${parseInt(subselection.show.episode) + 1}`)) { subselection.show = { season: subselection.show.season, @@ -161,11 +164,14 @@ export function SearchView() { } entry.percentageDone = 0; - } else if (subselection.meta.episodes[`${parseInt(subselection.show.season) + 1}`]['1']) { + // if the current season does not have a next epsiode, and the next season has a first episode, load that + } else if (subselection.meta.episodes[`${parseInt(subselection.show.season) + 1}`][0]) { subselection.show = { season: `${parseInt(subselection.show.season) + 1}`, - episode: '1' + episode: subselection.meta.episodes[`${parseInt(subselection.show.season) + 1}`][0] } + + entry.percentageDone = 0; } else { return; } From 9d21124379dddd97fae27f00123ba3fa44eeadff Mon Sep 17 00:00:00 2001 From: James Hawkins Date: Sat, 7 Aug 2021 19:42:56 +0100 Subject: [PATCH 17/22] cleanup --- src/App.js | 4 ++-- src/components/Cross.css | 7 ------- src/components/Cross.js | 14 -------------- src/components/Progress.js | 2 +- src/components/Title.js | 3 ++- src/views/NotFound.js | 15 --------------- 6 files changed, 5 insertions(+), 40 deletions(-) delete mode 100644 src/components/Cross.css delete mode 100644 src/components/Cross.js delete mode 100644 src/views/NotFound.js diff --git a/src/App.js b/src/App.js index fe7edea6..c3284bde 100644 --- a/src/App.js +++ b/src/App.js @@ -1,7 +1,7 @@ -import './index.css'; import { SearchView } from './views/Search'; import { MovieView } from './views/Movie'; -import { useMovie, MovieProvider} from './hooks/useMovie'; +import { useMovie, MovieProvider } from './hooks/useMovie'; +import './index.css'; function Router() { const { streamData } = useMovie(); diff --git a/src/components/Cross.css b/src/components/Cross.css deleted file mode 100644 index d4ef70b1..00000000 --- a/src/components/Cross.css +++ /dev/null @@ -1,7 +0,0 @@ -.cross { - display: inline-block; -} - -.cross:hover { - transform: rotate(45deg); -} \ No newline at end of file diff --git a/src/components/Cross.js b/src/components/Cross.js deleted file mode 100644 index eb2ae21b..00000000 --- a/src/components/Cross.js +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react' -import './Cross.css' - -export function Cross(props) { - return ( - - - - `}}> - - ) -} diff --git a/src/components/Progress.js b/src/components/Progress.js index 05af5c4c..38e7d6c1 100644 --- a/src/components/Progress.js +++ b/src/components/Progress.js @@ -8,7 +8,7 @@ import './Progress.css' // failed: boolean export function Progress(props) { return ( -
+
{ props.text && props.text.length > 0 ? (

{props.text}

) : null}
diff --git a/src/components/Title.js b/src/components/Title.js index 6d673ac2..b03f1aa8 100644 --- a/src/components/Title.js +++ b/src/components/Title.js @@ -14,6 +14,7 @@ export function Title(props) { const accentLink = props.accentLink || ""; const accent = props.accent || ""; + return (
{accent.length > 0 ? ( @@ -26,7 +27,7 @@ export function Title(props) { {accentLink.length > 0 ? () : null}{accent}

) : null} -

{props.children}

+

{props.children}

) } diff --git a/src/views/NotFound.js b/src/views/NotFound.js deleted file mode 100644 index 5b2f4aab..00000000 --- a/src/views/NotFound.js +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react' -import { Title } from '../components/Title' -import { Card } from '../components/Card' - -export function NotFound(props) { - return ( -
- - - Oopsie doopsie - - -
- ) -} From 765749a956652db1b6dd92dce676184c95ea2011 Mon Sep 17 00:00:00 2001 From: James Hawkins Date: Sat, 7 Aug 2021 19:43:21 +0100 Subject: [PATCH 18/22] lightbulb moment! next episode logic --- src/views/Search.js | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/views/Search.js b/src/views/Search.js index 215d5aa4..7428d006 100644 --- a/src/views/Search.js +++ b/src/views/Search.js @@ -154,27 +154,31 @@ export function SearchView() { // begin next episode logic } else { // we can't do next episode for movies! - if (!subselection.show) return; + if (!subselection.show) continue; + + let newShow = {}; // if the current season has a next episode, load it if (subselection.meta.episodes[subselection.show.season].includes(`${parseInt(subselection.show.episode) + 1}`)) { - subselection.show = { - season: subselection.show.season, - episode: `${parseInt(subselection.show.episode) + 1}` - } - + newShow.season = subselection.show.season; + newShow.episode = `${parseInt(subselection.show.episode) + 1}`; entry.percentageDone = 0; // if the current season does not have a next epsiode, and the next season has a first episode, load that } else if (subselection.meta.episodes[`${parseInt(subselection.show.season) + 1}`][0]) { - subselection.show = { - season: `${parseInt(subselection.show.season) + 1}`, - episode: subselection.meta.episodes[`${parseInt(subselection.show.season) + 1}`][0] - } - + newShow.season = `${parseInt(subselection.show.season) + 1}`; + newShow.episode = subselection.meta.episodes[`${parseInt(subselection.show.season) + 1}`][0]; entry.percentageDone = 0; + // the next episode does not exist } else { - return; + continue; } + + // assign the new episode and season data + entry.data.show = { ...newShow }; + + // if the next episode exists, continue. we don't want to end up with duplicate data. + let nextEpisode = progressData?.[source]?.show?.[slug]?.[`${entry.data.show.season}-${entry.data.show.episode}`]; + if (nextEpisode) continue; newContinueWatching.push(entry); } From 0784695ae50b85143e5d4f79435bd0656bb982ae Mon Sep 17 00:00:00 2001 From: James Hawkins Date: Sat, 7 Aug 2021 20:27:22 +0100 Subject: [PATCH 19/22] commit --- src/components/VideoElement.js | 10 +++++++++- src/views/Movie.js | 22 +++++++++++----------- src/views/Search.js | 9 ++++++++- 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/src/components/VideoElement.js b/src/components/VideoElement.js index 2f3863fc..5837f0e2 100644 --- a/src/components/VideoElement.js +++ b/src/components/VideoElement.js @@ -28,7 +28,15 @@ export function VideoElement({ streamUrl, loading, setProgress }) { hls.attachMedia(videoRef.current); hls.loadSource(streamUrl); } - }, [videoRef, streamUrl, loading]) + }, [videoRef, streamUrl, loading]); + + React.useEffect(() => { + console.log('running') + const element = document.getElementsByClassName('videoElement')[0]; + if (!element) return; + + element.onProgress = setProgress; + }) if (error) return (Your browser is not supported) diff --git a/src/views/Movie.js b/src/views/Movie.js index d3d8d1b4..348e53d5 100644 --- a/src/views/Movie.js +++ b/src/views/Movie.js @@ -20,6 +20,7 @@ export function MovieView(props) { const [ episodeLists, setEpisodeList ] = React.useState([]); const [ loading, setLoading ] = React.useState(false); const [ selectedSeason, setSelectedSeason ] = React.useState("1"); + let isVideoTimeSet = React.useRef(false) const season = showRouteMatch?.params.season || "1"; const episode = showRouteMatch?.params.episode || "1"; @@ -31,12 +32,12 @@ export function MovieView(props) { React.useEffect(() => { if (streamData.type === "show" && !showRouteMatch) history.replace(`${baseRouteMatch.url}/season/1/episode/1`); - }, [streamData, showRouteMatch, history, baseRouteMatch.url]); + }, [streamData.type, 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(() => { + // if (streamData.type === "show" && showRouteMatch) setSelectedSeason(showRouteMatch.params.season.toString()); + // // eslint-disable-next-line react-hooks/exhaustive-deps + // }, []); React.useEffect(() => { let cancel = false; @@ -74,12 +75,10 @@ export function MovieView(props) { setSeasonList(streamData.seasons); setEpisodeList(streamData.episodes[selectedSeason]); } - }, [streamData, selectedSeason]) + }, [streamData.seasons, streamData.episodes, streamData.type, selectedSeason]) React.useEffect(() => { - let cancel = false; - - if (!cancel) { + if (!isVideoTimeSet.current) { let ls = JSON.parse(localStorage.getItem("video-progress") || "{}") let key = streamData.type === "show" ? `${season}-${episode}` : "full" let time = ls?.[streamData.source]?.[streamData.type]?.[streamData.slug]?.[key]?.currentlyAt; @@ -88,7 +87,7 @@ export function MovieView(props) { const element = document.getElementsByClassName('videoElement')[0]; if (!element) { - return () => { cancel = false } + return () => { isVideoTimeSet.current = false } } element.currentTime = time; @@ -96,11 +95,12 @@ export function MovieView(props) { } return () => { - cancel = true; + isVideoTimeSet.current = true; } }) const setProgress = (evt) => { + console.log('setting progress') let ls = JSON.parse(localStorage.getItem("video-progress") || "{}") if (!ls[streamData.source]) diff --git a/src/views/Search.js b/src/views/Search.js index 7428d006..3aefe420 100644 --- a/src/views/Search.js +++ b/src/views/Search.js @@ -149,6 +149,11 @@ export function SearchView() { source } + // due to a constraint with incompatible localStorage data, + // we must quit here if episode and season data is not included + // in the show's data. watching the show will resolve. + if (!subselection.meta) continue; + if (entry.percentageDone < 90) { newContinueWatching.push(entry) // begin next episode logic @@ -157,7 +162,7 @@ export function SearchView() { if (!subselection.show) continue; let newShow = {}; - + // if the current season has a next episode, load it if (subselection.meta.episodes[subselection.show.season].includes(`${parseInt(subselection.show.episode) + 1}`)) { newShow.season = subselection.show.season; @@ -189,6 +194,8 @@ export function SearchView() { return b.data.updatedAt - a.data.updatedAt }); + console.log(newContinueWatching); + setContinueWatching(newContinueWatching) }) }, []); From 0ce6efae2ef25428b48ef8fa8792664a40332315 Mon Sep 17 00:00:00 2001 From: James Hawkins Date: Sat, 7 Aug 2021 20:51:16 +0100 Subject: [PATCH 20/22] commit --- src/components/VideoElement.js | 11 +---------- src/views/Movie.js | 26 ++++++++++++-------------- src/views/Search.js | 4 +--- 3 files changed, 14 insertions(+), 27 deletions(-) diff --git a/src/components/VideoElement.js b/src/components/VideoElement.js index 5837f0e2..f5b380a4 100644 --- a/src/components/VideoElement.js +++ b/src/components/VideoElement.js @@ -6,8 +6,7 @@ import './VideoElement.css' // streamUrl: string // loading: boolean -export function VideoElement({ streamUrl, loading, setProgress }) { - const videoRef = React.useRef(null); +export function VideoElement({ streamUrl, loading, setProgress, videoRef }) { const [error, setError] = React.useState(false); React.useEffect(() => { @@ -30,14 +29,6 @@ export function VideoElement({ streamUrl, loading, setProgress }) { } }, [videoRef, streamUrl, loading]); - React.useEffect(() => { - console.log('running') - const element = document.getElementsByClassName('videoElement')[0]; - if (!element) return; - - element.onProgress = setProgress; - }) - if (error) return (Your browser is not supported) diff --git a/src/views/Movie.js b/src/views/Movie.js index 348e53d5..43a5a282 100644 --- a/src/views/Movie.js +++ b/src/views/Movie.js @@ -20,6 +20,7 @@ export function MovieView(props) { const [ episodeLists, setEpisodeList ] = React.useState([]); const [ loading, setLoading ] = React.useState(false); const [ selectedSeason, setSelectedSeason ] = React.useState("1"); + const videoRef = React.useRef(null); let isVideoTimeSet = React.useRef(false) const season = showRouteMatch?.params.season || "1"; @@ -34,10 +35,10 @@ export function MovieView(props) { if (streamData.type === "show" && !showRouteMatch) history.replace(`${baseRouteMatch.url}/season/1/episode/1`); }, [streamData.type, 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(() => { + if (streamData.type === "show" && showRouteMatch) setSelectedSeason(showRouteMatch.params.season.toString()); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); React.useEffect(() => { let cancel = false; @@ -84,23 +85,20 @@ export function MovieView(props) { let time = ls?.[streamData.source]?.[streamData.type]?.[streamData.slug]?.[key]?.currentlyAt; if (time) { - const element = document.getElementsByClassName('videoElement')[0]; - - if (!element) { - return () => { isVideoTimeSet.current = false } + if (!videoRef.current) { + isVideoTimeSet.current = false; + return; } - element.currentTime = time; + videoRef.current.currentTime = time; } } - return () => { - isVideoTimeSet.current = true; - } + isVideoTimeSet.current = true; + // eslint-disable-next-line react-hooks/exhaustive-deps }) const setProgress = (evt) => { - console.log('setting progress') let ls = JSON.parse(localStorage.getItem("video-progress") || "{}") if (!ls[streamData.source]) @@ -143,7 +141,7 @@ export function MovieView(props) { Season {season}: Episode {episode} : undefined} - + {streamData.type === "show" ? Date: Sat, 7 Aug 2021 21:19:01 +0100 Subject: [PATCH 21/22] new code --- src/components/VideoElement.js | 14 +++++++++++--- src/views/Movie.js | 30 ++++++++++-------------------- 2 files changed, 21 insertions(+), 23 deletions(-) diff --git a/src/components/VideoElement.js b/src/components/VideoElement.js index f5b380a4..13426eb0 100644 --- a/src/components/VideoElement.js +++ b/src/components/VideoElement.js @@ -6,9 +6,17 @@ import './VideoElement.css' // streamUrl: string // loading: boolean -export function VideoElement({ streamUrl, loading, setProgress, videoRef }) { +// setProgress: (event: NativeEvent) => void +// videoRef: useRef +// startTime: number +export function VideoElement({ streamUrl, loading, setProgress, videoRef, startTime }) { const [error, setError] = React.useState(false); + function onLoad() { + if (startTime) + videoRef.current.currentTime = startTime; + } + React.useEffect(() => { if (!streamUrl.endsWith('.mp4')) { setError(false) @@ -40,11 +48,11 @@ export function VideoElement({ streamUrl, loading, setProgress, videoRef }) { if (!streamUrl.endsWith('.mp4')) { return ( -