From 8e266ef383d600facda3de567a9aa385a5aec14b Mon Sep 17 00:00:00 2001 From: James Hawkins Date: Sun, 1 May 2022 15:02:57 +0100 Subject: [PATCH] remove /src2 --- .env | 1 - src2/App.js | 19 -- src2/assets/down-arrow.svg | 1 - src2/components/Arrow.css | 7 - src2/components/Arrow.js | 15 -- src2/components/Card.css | 31 --- src2/components/Card.js | 28 -- src2/components/EpisodeSelector.css | 3 - src2/components/EpisodeSelector.js | 50 ---- src2/components/ErrorBanner.css | 11 - src2/components/ErrorBanner.js | 10 - src2/components/InputBox.css | 95 ------- src2/components/InputBox.js | 29 --- src2/components/MovieRow.css | 97 ------- src2/components/MovieRow.js | 59 ----- src2/components/NumberSelector.css | 55 ---- src2/components/NumberSelector.js | 27 -- src2/components/PercentageOverlay.css | 12 - src2/components/PercentageOverlay.js | 13 - src2/components/Progress.css | 43 ---- src2/components/Progress.js | 21 -- src2/components/SelectBox.css | 111 -------- src2/components/SelectBox.js | 91 ------- src2/components/Title.css | 52 ---- src2/components/Title.js | 41 --- src2/components/TypeSelector.css | 65 ----- src2/components/TypeSelector.js | 36 --- src2/components/VideoElement.css | 10 - src2/components/VideoElement.js | 63 ----- src2/components/VideoPlaceholder.css | 23 -- src2/components/VideoPlaceholder.js | 12 - src2/hooks/useMovie.js | 30 --- src2/hooks/useWindowSize.js | 28 -- src2/index.css | 70 ----- src2/index.js | 14 - src2/lib/index.js | 55 ---- src2/lib/scraper/gdriveplayer.js.disabled | 111 -------- src2/lib/scraper/gomostream.js.disabled | 91 ------- src2/lib/scraper/lookmovie.js | 164 ------------ src2/lib/scraper/theflix.js | 120 --------- src2/lib/scraper/vidzstore.js | 41 --- src2/lib/scraper/vmovee.js.disabled | 84 ------ src2/lib/scraper/xemovie.js | 121 --------- src2/lib/storage/VideoProgress.js | 43 ---- src2/lib/storage/base.js | 230 ----------------- src2/lib/util/unpacker.js | 53 ---- src2/views/Movie.css | 6 - src2/views/Movie.js | 156 ----------- src2/views/Search.css | 76 ------ src2/views/Search.js | 299 ---------------------- 50 files changed, 2923 deletions(-) delete mode 100644 .env delete mode 100644 src2/App.js delete mode 100644 src2/assets/down-arrow.svg delete mode 100644 src2/components/Arrow.css delete mode 100644 src2/components/Arrow.js delete mode 100644 src2/components/Card.css delete mode 100644 src2/components/Card.js delete mode 100644 src2/components/EpisodeSelector.css delete mode 100644 src2/components/EpisodeSelector.js delete mode 100644 src2/components/ErrorBanner.css delete mode 100644 src2/components/ErrorBanner.js delete mode 100644 src2/components/InputBox.css delete mode 100644 src2/components/InputBox.js delete mode 100644 src2/components/MovieRow.css delete mode 100644 src2/components/MovieRow.js delete mode 100644 src2/components/NumberSelector.css delete mode 100644 src2/components/NumberSelector.js delete mode 100644 src2/components/PercentageOverlay.css delete mode 100644 src2/components/PercentageOverlay.js delete mode 100644 src2/components/Progress.css delete mode 100644 src2/components/Progress.js delete mode 100644 src2/components/SelectBox.css delete mode 100644 src2/components/SelectBox.js delete mode 100644 src2/components/Title.css delete mode 100644 src2/components/Title.js delete mode 100644 src2/components/TypeSelector.css delete mode 100644 src2/components/TypeSelector.js delete mode 100644 src2/components/VideoElement.css delete mode 100644 src2/components/VideoElement.js delete mode 100644 src2/components/VideoPlaceholder.css delete mode 100644 src2/components/VideoPlaceholder.js delete mode 100644 src2/hooks/useMovie.js delete mode 100644 src2/hooks/useWindowSize.js delete mode 100644 src2/index.css delete mode 100644 src2/index.js delete mode 100644 src2/lib/index.js delete mode 100644 src2/lib/scraper/gdriveplayer.js.disabled delete mode 100644 src2/lib/scraper/gomostream.js.disabled delete mode 100644 src2/lib/scraper/lookmovie.js delete mode 100644 src2/lib/scraper/theflix.js delete mode 100644 src2/lib/scraper/vidzstore.js delete mode 100644 src2/lib/scraper/vmovee.js.disabled delete mode 100644 src2/lib/scraper/xemovie.js delete mode 100644 src2/lib/storage/VideoProgress.js delete mode 100644 src2/lib/storage/base.js delete mode 100644 src2/lib/util/unpacker.js delete mode 100644 src2/views/Movie.css delete mode 100644 src2/views/Movie.js delete mode 100644 src2/views/Search.css delete mode 100644 src2/views/Search.js diff --git a/.env b/.env deleted file mode 100644 index c4a4738f..00000000 --- a/.env +++ /dev/null @@ -1 +0,0 @@ -REACT_APP_CORS_PROXY_URL=https://proxy-1.movie-web.workers.dev/?destination= diff --git a/src2/App.js b/src2/App.js deleted file mode 100644 index 8e248937..00000000 --- a/src2/App.js +++ /dev/null @@ -1,19 +0,0 @@ -import { SearchView } from './views/Search'; -import { MovieView } from './views/Movie'; -import { useMovie, MovieProvider } from './hooks/useMovie'; -import './index.css'; - -function Router() { - const { streamData } = useMovie(); - return streamData ? : ; -} - -function App() { - return ( - - - - ); -} - -export default App; diff --git a/src2/assets/down-arrow.svg b/src2/assets/down-arrow.svg deleted file mode 100644 index 619f62aa..00000000 --- a/src2/assets/down-arrow.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src2/components/Arrow.css b/src2/components/Arrow.css deleted file mode 100644 index 0578ea6d..00000000 --- a/src2/components/Arrow.css +++ /dev/null @@ -1,7 +0,0 @@ -.feather.left { - transform: rotate(180deg); -} - -.arrow { - display: inline-block; -} diff --git a/src2/components/Arrow.js b/src2/components/Arrow.js deleted file mode 100644 index c1128532..00000000 --- a/src2/components/Arrow.js +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react' -import './Arrow.css' - -// left?: boolean -export function Arrow(props) { - return ( - - - - - `}}> - - ) -} diff --git a/src2/components/Card.css b/src2/components/Card.css deleted file mode 100644 index d0c6a631..00000000 --- a/src2/components/Card.css +++ /dev/null @@ -1,31 +0,0 @@ -.card { - background-color: var(--card); - padding: 3rem 4rem; - margin: 0 3rem; - margin-bottom: 6rem; - border-radius: 10px; - box-sizing: border-box; - transition: height 500ms ease-in-out; -} - -.card-wrapper.full { - width: 81rem; -} - -.card-wrapper { - transition: height 500ms ease-in-out; - width: 45rem; - max-width: 100%; -} - -.card-wrapper.overflow-hidden { - overflow: hidden; -} - -@media screen and (max-width: 700px) { - .card { - margin: 0; - margin-bottom: 6rem; - padding: 3rem 2rem; - } -} \ No newline at end of file diff --git a/src2/components/Card.js b/src2/components/Card.js deleted file mode 100644 index ba623d38..00000000 --- a/src2/components/Card.js +++ /dev/null @@ -1,28 +0,0 @@ -import React from 'react' -import './Card.css' - -// fullWidth: boolean -// show: boolean -// doTransition: boolean -export function Card(props) { - - const [showing, setShowing] = React.useState(false); - const measureRef = React.useRef(null) - const [height, setHeight] = React.useState(0); - - React.useEffect(() => { - if (!measureRef?.current) return; - setShowing(props.show); - setHeight(measureRef.current.clientHeight) - }, [props.show, measureRef]) - - return ( -
-
- {props.children} -
-
- ) -} diff --git a/src2/components/EpisodeSelector.css b/src2/components/EpisodeSelector.css deleted file mode 100644 index eb060543..00000000 --- a/src2/components/EpisodeSelector.css +++ /dev/null @@ -1,3 +0,0 @@ -.episodeSelector { - margin-top: 20px; -} \ No newline at end of file diff --git a/src2/components/EpisodeSelector.js b/src2/components/EpisodeSelector.js deleted file mode 100644 index b7c62094..00000000 --- a/src2/components/EpisodeSelector.js +++ /dev/null @@ -1,50 +0,0 @@ -import React from 'react'; -import { TypeSelector } from './TypeSelector'; -import { NumberSelector } from './NumberSelector'; -import { VideoProgressStore } from '../lib/storage/VideoProgress' -import { SelectBox } from '../components/SelectBox'; -import './EpisodeSelector.css' -import { useWindowSize } from '../hooks/useWindowSize'; - -export function EpisodeSelector({ setSelectedSeason, selectedSeason, setEpisode, seasons, episodes, currentSeason, currentEpisode, streamData }) { - const choices = episodes ? episodes.map(v => { - const progressData = VideoProgressStore.get(); - - let currentlyAt = 0; - let totalDuration = 0; - - const progress = progressData?.[streamData.source]?.[streamData.type]?.[streamData.slug]?.[`${selectedSeason}-${v}`] - - if (progress) { - currentlyAt = progress.currentlyAt - totalDuration = progress.totalDuration - } - - const percentage = Math.round((currentlyAt / totalDuration) * 100) - - return { - value: v.toString(), - label: v, - percentage - } - }) : []; - - const windowSize = useWindowSize() - - return ( -
- { - (seasons.length > 0 && (windowSize.width <= 768 || seasons.length > 4)) ? - ( - setSelectedSeason(seasons[index])} selectedItem={seasons.findIndex(s => s === selectedSeason)} options={seasons.map(season => { return {id: season, name: `Season ${season}` }})}/> - ) - : - ( - ({ value: v.toString(), label: `Season ${v}`}))} /> - ) - } -

- setEpisode({episode: e, season: selectedSeason})} choices={choices} selected={(selectedSeason.toString() === currentSeason) ? currentEpisode : null} /> -
- ) -} diff --git a/src2/components/ErrorBanner.css b/src2/components/ErrorBanner.css deleted file mode 100644 index 8c9666a1..00000000 --- a/src2/components/ErrorBanner.css +++ /dev/null @@ -1,11 +0,0 @@ -.errorBanner { - 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); -} \ No newline at end of file diff --git a/src2/components/ErrorBanner.js b/src2/components/ErrorBanner.js deleted file mode 100644 index 90a8f285..00000000 --- a/src2/components/ErrorBanner.js +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react'; -import './ErrorBanner.css'; - -export function ErrorBanner({children}) { - return ( -
- {children} -
- ) -} \ No newline at end of file diff --git a/src2/components/InputBox.css b/src2/components/InputBox.css deleted file mode 100644 index 1a5944e3..00000000 --- a/src2/components/InputBox.css +++ /dev/null @@ -1,95 +0,0 @@ -.inputBar { - width: 100%; - display: flex; - height: 3rem; -} - -.inputBar > *:first-child{ - border-radius: 0 !important; - border-top-left-radius: 10px !important; - border-bottom-left-radius: 10px !important; -} - -.inputBar > *:last-child { - border-radius: 0 !important; - border-top-right-radius: 10px !important; - border-bottom-right-radius: 10px !important; -} - -.inputTextBox { - border-width: 0; - outline: none; - background-color: var(--content); - color: var(--text); - padding: .7rem 1.5rem; - height: auto; - flex: 1; - box-sizing: border-box; -} - -.inputSearchButton { - background-color: var(--button); - border-width: 0; - color: var(--button-text, var(--text)); - padding: .5rem 2.1rem; - - font-weight: bold; - cursor: pointer; -} - -.inputSearchButton:hover { - background-color: var(--button-hover); -} - -.inputTextBox:hover { - background-color: var(--content-hover); -} - -.inputSearchButton .text > .arrow { - opacity: 0; - transition: opacity 0.2s ease-in-out, transform 0.2s ease-in-out; - position: absolute; - right: -0.8rem; - bottom: -0.2rem; -} - -.inputSearchButton .text { - display: flex; - position: relative; - transition: transform 0.2s ease-in-out; -} - -.inputSearchButton:hover .text > .arrow { - transform: translateX(8px); - opacity: 1; -} - -.inputSearchButton:hover .text { - transform: translateX(-10px); -} - -.inputSearchButton:active { - background-color: var(--button-active); -} - -@media screen and (max-width: 700px) { - .inputBar { - flex-direction: column; - align-items: flex-start; - height: auto; - } - - .inputBar > *:nth-child(n) { - border-radius: 10px !important; - } - - .inputSearchButton { - margin-top: .5rem; - align-self: center; - } - - .inputTextBox { - margin-top: .5rem; - width: 100%; - } -} diff --git a/src2/components/InputBox.js b/src2/components/InputBox.js deleted file mode 100644 index f1f5d2ea..00000000 --- a/src2/components/InputBox.js +++ /dev/null @@ -1,29 +0,0 @@ -import React from 'react'; -import { Arrow } from './Arrow'; -import './InputBox.css' - -// props = { onSubmit: (str) => {}, placeholder: string} -export function InputBox({ onSubmit, placeholder }) { - const [searchTerm, setSearchTerm] = React.useState(""); - - return ( -
{ - e.preventDefault(); - onSubmit(searchTerm) - return false; - }}> - setSearchTerm(e.target.value)} - required - /> - -
- ) -} diff --git a/src2/components/MovieRow.css b/src2/components/MovieRow.css deleted file mode 100644 index de87da9c..00000000 --- a/src2/components/MovieRow.css +++ /dev/null @@ -1,97 +0,0 @@ -.movieRow { - position: relative; - display: flex; - border-radius: 5px; - background-color: var(--content); - color: var(--text); - padding: .8rem 1.5rem; - margin-top: .5rem; - cursor: pointer; - transition: transform 50ms ease-in-out; - user-select: none; - overflow: hidden; -} - -.movieRow p { - margin: 0; -} - -.movieRow .left { - flex: 1; - display: flex; - flex-flow: row wrap; - align-items: flex-start; - margin-right: 0.5rem; -} - -.movieRow .left .titleWrapper { - height: 100%; - display: flex; - align-items: center; - justify-content: center; -} - -.movieRow .left .seasonEpisodeSubtitle, -.movieRow .left .year { - color: var(--text-secondary); -} - -.movieRow .watch { - color: var(--theme-color-text); - display: flex; - align-items: center; -} - -.movieRow .watch .arrow { - margin-left: .5rem; - transition: transform 50ms ease-in-out; - transform: translateY(.1rem); -} - -.movieRow:active { - transform: scale(1.02); -} - -.movieRow:hover { - background-color: var(--content-hover); -} - -.movieRow:hover .watch .arrow { - transform: translateX(.3rem) translateY(.1rem); -} - -.movieRow:focus-visible { - border: 1px solid #fff; - background-color: var(--content-hover); -} - -.movieRow:focus-visible .watch .arrow { - transform: translateX(.3rem) translateY(.1rem); -} - -.attribute { - color: var(--text); - background-color: var(--theme-color); - font-size: .75rem; - padding: .25rem; - border-radius: 10px; - margin-right: 10px; -} - -.subtitleIcon { - width: 30px; - display: flex; - justify-content: center; - align-items: center; - margin-right: 10px; -} - -@media screen and (max-width: 400px) { - .movieRow { - flex-direction: column; - } - - .movieRow .watch { - margin-top: .5rem; - } -} diff --git a/src2/components/MovieRow.js b/src2/components/MovieRow.js deleted file mode 100644 index 8a600da3..00000000 --- a/src2/components/MovieRow.js +++ /dev/null @@ -1,59 +0,0 @@ -import React from 'react' -import { Arrow } from './Arrow' -import { PercentageOverlay } from './PercentageOverlay' -import { VideoProgressStore } from '../lib/storage/VideoProgress' -import './MovieRow.css' - -// title: string -// onClick: () => void -export function MovieRow(props) { - const progressData = VideoProgressStore.get(); - let progress; - let percentage = null; - - if (props.type === "movie") { - progress = progressData?.[props.source]?.movie?.[props.slug]?.full - - if (progress) { - percentage = Math.floor((progress.currentlyAt / progress.totalDuration) * 100) - } - } - - function handleKeyPress(event){ - if ((event.code === 'Enter' || event.code === 'Space') && props.onClick){ - props.onClick(); - } - } - - return ( -
props.onClick && props.onClick()}> - - { (props.source === "lookmovie" || props.source === "xemovie") && ( -
- - - -
- ) } - -
- {/* */} -
-
- {props.title} -   - ({props.year}) - {props.place ? ` - S${props.place.season}:E${props.place.episode}` : ''} -
-
-
- -
-

Watch {props.type}

- -
- - -
- ) -} diff --git a/src2/components/NumberSelector.css b/src2/components/NumberSelector.css deleted file mode 100644 index 5ec06fc7..00000000 --- a/src2/components/NumberSelector.css +++ /dev/null @@ -1,55 +0,0 @@ -.numberSelector { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(2.5rem, 1fr)); - gap: 5px; - position: relative; - margin-bottom: 1.5rem; -} - -.numberSelector .choiceWrapper { - position: relative; - border-radius: 10%; - overflow: hidden; -} - -.numberSelector .choiceWrapper::before { - content: ''; - display: block; - width: 100%; - padding-bottom: 100%; -} - -.numberSelector .choice { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background-color: var(--choice); - margin-right: 5px; - padding: .2rem; - display: flex; - justify-content: center; - align-items: center; - text-align: center; - color: var(--text); - font-weight: bold; - cursor: pointer; - user-select: none; - box-sizing: border-box; -} - -.numberSelector .choice:hover, -.numberSelector .choiceWrapper:focus-visible .choice { - background-color: var(--choice-hover); -} - -.numberSelector .choiceWrapper:focus-visible { - border: 1px solid #fff; -} - -.numberSelector .choice.selected { - color: var(--choice-active-text, var(--text)); - background-color: var(--choice-active); -} - diff --git a/src2/components/NumberSelector.js b/src2/components/NumberSelector.js deleted file mode 100644 index eb41530c..00000000 --- a/src2/components/NumberSelector.js +++ /dev/null @@ -1,27 +0,0 @@ -import React from 'react'; -// import { Arrow } from './Arrow'; -import './NumberSelector.css' -import { PercentageOverlay } from './PercentageOverlay'; - -// setType: (txt: string) => void -// choices: { label: string, value: string }[] -// selected: string -export function NumberSelector({ setType, choices, selected }) { - const handleKeyPress = choice => event => { - if (event.code === 'Space' || event.code === 'Enter'){ - setType(choice); - } - } - return ( -
- {choices.map(v=>( -
-
setType(v.value)}> - {v.label} - -
-
- ))} -
- ) -} diff --git a/src2/components/PercentageOverlay.css b/src2/components/PercentageOverlay.css deleted file mode 100644 index 259f25dd..00000000 --- a/src2/components/PercentageOverlay.css +++ /dev/null @@ -1,12 +0,0 @@ -.progressBar { - position: absolute; - bottom: 0; - left: 0; - width: 100%; - height: 100%; - opacity: 0.2; -} -.progressBarInner { - background: var(--theme-color); - height: 100%; -} \ No newline at end of file diff --git a/src2/components/PercentageOverlay.js b/src2/components/PercentageOverlay.js deleted file mode 100644 index 0415774e..00000000 --- a/src2/components/PercentageOverlay.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react' -import './PercentageOverlay.css' - -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 diff --git a/src2/components/Progress.css b/src2/components/Progress.css deleted file mode 100644 index eb7e9d73..00000000 --- a/src2/components/Progress.css +++ /dev/null @@ -1,43 +0,0 @@ -.progress { - text-align: center; - color: var(--text-secondary); - display: flex; - align-items: center; - justify-content: center; - flex-direction: column; - height: 5rem; - margin-top: 1rem; - transition: height 800ms ease-in-out, opacity 800ms ease-in-out; - opacity: 1; -} - -.progress.hide { - opacity: 0; - height: 0rem; -} - -.progress p { - margin: 0; - margin-bottom: 1rem; -} - -.progress .bar { - width: 13rem; - max-width: 100%; - background-color: var(--content); - border-radius: 10px; - height: 7px; - display: inline-block; -} - -.progress .bar .bar-inner { - transition: width 400ms ease-in-out, background-color 100ms ease-in-out; - background-color: var(--theme-color); - border-radius: 10px; - height: 100%; - width: 0%; -} - -.progress.failed .bar .bar-inner { - background-color: var(--failed); -} diff --git a/src2/components/Progress.js b/src2/components/Progress.js deleted file mode 100644 index 38e7d6c1..00000000 --- a/src2/components/Progress.js +++ /dev/null @@ -1,21 +0,0 @@ -import React from 'react' -import './Progress.css' - -// show: boolean -// progress: number -// steps: number -// text: string -// failed: boolean -export function Progress(props) { - return ( -
- { props.text && props.text.length > 0 ? ( -

{props.text}

) : null} -
-
-
-
- ) -} \ No newline at end of file diff --git a/src2/components/SelectBox.css b/src2/components/SelectBox.css deleted file mode 100644 index 1ca3e8b3..00000000 --- a/src2/components/SelectBox.css +++ /dev/null @@ -1,111 +0,0 @@ -@import url('https://fonts.googleapis.com/css?family=Open+Sans:300,300i,400,400i,600,600i,700,700i,800,800i&display=swap'); - -/* select box styling */ -.select-box { - display: flex; - width: 200px; - flex-direction: column; - position: relative; -} - -.select-box:focus-visible .selected { - border: 1px solid #fff; -} - -.select-box > * { - box-sizing: border-box; -} - -.select-box .options-container { - max-height: 0; - width: calc( 100% - 12px); - opacity: 0; - transition: all 0.2s ease-in-out; - overflow: hidden; - border-radius: 5px; - background-color: var(--choice); - order: 1; - position: absolute; - z-index: 1; - top: 50px; -} - -.select-box .selected { - margin-bottom: 8px; - position: relative; - width: 188px; - height: 45px; - border-radius: 5px; - display: flex; - align-items: center; - background-color: var(--choice); - color: white; - order: 0; -} - -.select-box .selected::after { - content: ""; - width: 1.2rem; - height: 1.2rem; - background: url(../assets/down-arrow.svg); - position: absolute; - right: 15px; - top: 50%; - transition: transform 150ms; - transform: translateY(-50%); - background-size: contain; - background-position: center; -} - - -.select-box .option .item { - color: var(--text); - font-weight: bold; -} - -.select-box .options-container.active { - max-height: 240px; - opacity: 1; - overflow-y: scroll; -} - -.select-box .options-container.active + .selected::after { - transform: translateY(-50%) rotateX(180deg); -} - -.select-box .options-container::-webkit-scrollbar { - width: 8px; - background: #0d141f; - background: #81878f; - background: #f1f2f3; - border-radius: 0 5px 5px 0; -} - -.select-box .options-container::-webkit-scrollbar-thumb { - background: #525861; - background: #81878f; - border-radius: 0 5px 5px 0; -} -.select-box .option { - padding: 12px 15px; -} - -.select-box .option, -.selected { - cursor: pointer; -} - -.select-box .options-container .option:hover { - background: var(--choice-hover); -} -.select-box .options-container .option:hover .item { - color: var(--choice-active-text, var(--text)); -} - -.select-box label { - cursor: pointer; -} - -.select-box .option .radio { - display: none; -} diff --git a/src2/components/SelectBox.js b/src2/components/SelectBox.js deleted file mode 100644 index 743c559d..00000000 --- a/src2/components/SelectBox.js +++ /dev/null @@ -1,91 +0,0 @@ -import { useRef, useState, useEffect } from "react"; -import "./SelectBox.css"; - -function Option({ option, ...props }) { - return ( -
- - -
- ); -} - -export function SelectBox({ options, selectedItem, setSelectedItem }) { - if (!Array.isArray(options)) { - throw new Error("Items must be an array!"); - } - - const [active, setActive] = useState(false); - - const containerRef = useRef(); - - const handleClick = (e) => { - if (containerRef.current.contains(e.target)) { - // inside click - return; - } - // outside click - closeDropdown(); - }; - - const closeDropdown = () => { - setActive(false); - }; - - useEffect(() => { - // add when mounted - document.addEventListener("mousedown", handleClick); - // return function to be called when unmounted - return () => { - document.removeEventListener("mousedown", handleClick); - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - const onOptionClick = (e, option, i) => { - e.stopPropagation(); - setSelectedItem(i); - closeDropdown(); - }; - - const handleSelectedKeyPress = (event) => { - if (event.code === "Enter" || event.code === "Space") { - setActive((a) => !a); - } - }; - - const handleOptionKeyPress = (option, i) => (event) => { - if (event.code === "Enter" || event.code === "Space") { - onOptionClick(event, option, i); - } - }; - - return ( -
setActive((a) => !a)} - > -
- {options ?
-
- {options.map((opt, i) => ( -
-
- ); -} diff --git a/src2/components/Title.css b/src2/components/Title.css deleted file mode 100644 index d789f69f..00000000 --- a/src2/components/Title.css +++ /dev/null @@ -1,52 +0,0 @@ -.title { - font-size: 2rem; - color: var(--text); - /* max-width: 20rem; */ - margin: 0; - padding: 0; - margin-bottom: 3.5rem; -} - -.title-size-medium { - font-size: 1.5rem; -} -.title-size-small { - font-size: 1.1rem; - color: var(--text-secondary); -} - -.title-accent { - color: var(--theme-color); - font-weight: 600; - margin: 0; - padding: 0; - margin-bottom: 0.5rem; - margin-top: 1rem; - display: inline-block; -} - -.title-accent.title-accent-link { - cursor: pointer; -} - -.title.accent.title-accent-link:focus-visible { - border: 1px solid #ffffff; -} - -.title.accent.title-accent-link:focus-visible .arrow { - transform: translateY(.1rem) translateX(-.5rem); -} - - -.title-accent.title-accent-link .arrow { - transition: transform 100ms ease-in-out; - transform: translateY(.1rem); - margin-right: .2rem; -} - -.title-accent.title-accent-link:hover .arrow { - transform: translateY(.1rem) translateX(-.5rem); -} - - - diff --git a/src2/components/Title.js b/src2/components/Title.js deleted file mode 100644 index 19ecf844..00000000 --- a/src2/components/Title.js +++ /dev/null @@ -1,41 +0,0 @@ -import React from 'react'; -import { useHistory } from 'react-router-dom'; -import { useMovie } from '../hooks/useMovie' -import { Arrow } from '../components/Arrow' -import './Title.css' - -// size: "big" | "medium" | "small" | null -// accent: string | null -// accentLink: string | null -export function Title(props) { - const { streamData, resetStreamData } = useMovie(); - const history = useHistory(); - const size = props.size || "big"; - - const accentLink = props.accentLink || ""; - const accent = props.accent || ""; - - function handleAccentClick(){ - if (accentLink.length > 0) { - history.push(`/${streamData.type}`); - resetStreamData(); - } - } - - function handleKeyPress(event){ - if (event.code === 'Enter' || event.code === 'Space'){ - handleAccentClick(); - } - } - - return ( -
- {accent.length > 0 ? ( -

0 ? 'title-accent-link' : ''}`} tabIndex={accentLink.length > 0 ? 0 : undefined} onKeyPress={handleKeyPress}> - {accentLink.length > 0 ? () : null}{accent} -

- ) : null} -

{props.children}

-
- ) -} diff --git a/src2/components/TypeSelector.css b/src2/components/TypeSelector.css deleted file mode 100644 index 82ada085..00000000 --- a/src2/components/TypeSelector.css +++ /dev/null @@ -1,65 +0,0 @@ - -/* TODO better responsiveness, use dropdown if more than 5 options */ -.typeSelector { - display: inline-flex; - position: relative; - margin-bottom: 1.5rem; - max-width: 100%; -} -.typeSelector:not(.nowrap) { - flex-wrap: wrap; -} - -.typeSelector::before { - content: ""; - position: absolute; - width: 100%; - bottom: 0; - background-color: var(--content); - height: 4px; - border-radius: 2px; -} - -.typeSelector .choice { - width: 7rem; - height: 3rem; - padding: .3rem .2rem; - display: flex; - justify-content: center; - align-items: center; - text-align: center; - box-sizing: border-box; - color: var(--text-tertiary); - font-weight: bold; - cursor: pointer; - user-select: none; -} - -.typeSelector .choice:hover { - color: var(--text-secondary); -} - -.typeSelector .choice:focus-visible { - border: 1px solid #fff; - color: var(--text-secondary); -} - -.typeSelector .choice.selected { - color: var(--text); -} - -.typeSelector .selectedBar { - position: absolute; - height: 4px; - width: 7rem; - background-color: var(--theme-color); - border-radius: 2px; - bottom: 0; - transition: transform 150ms ease-in-out; -} - -@media screen and (max-width: 700px) { - .typeSelector:not(.nowrap) { - display: block; - } -} diff --git a/src2/components/TypeSelector.js b/src2/components/TypeSelector.js deleted file mode 100644 index 3102c4d8..00000000 --- a/src2/components/TypeSelector.js +++ /dev/null @@ -1,36 +0,0 @@ -import React from 'react'; -import './TypeSelector.css'; - -// setType: (txt: string) => void -// choices: { label: string, value: string }[] -// selected: string -export function TypeSelector({ setType, choices, selected, noWrap = false }) { - const selectedIndex = choices.findIndex(v => v.value === selected); - const transformStyles = { - opacity: selectedIndex !== -1 ? 1 : 0, - transform: `translateX(${selectedIndex !== -1 ? selectedIndex * 7 : 0}rem)`, - }; - - const handleKeyPress = choice => event => { - if (event.code === 'Enter' || event.code === 'Space') { - setType(choice); - } - }; - - return ( -
- {choices.map(v => ( -
setType(v.value)} - onKeyPress={handleKeyPress(v.value)} - tabIndex={0} - > - {v.label} -
- ))} -
-
- ); -} diff --git a/src2/components/VideoElement.css b/src2/components/VideoElement.css deleted file mode 100644 index f252de55..00000000 --- a/src2/components/VideoElement.css +++ /dev/null @@ -1,10 +0,0 @@ -.videoElement { - width: 100%; - background-color: black; - border-radius: 5px; -} - -.videoElementText { - color: var(--text); - margin: 0; -} diff --git a/src2/components/VideoElement.js b/src2/components/VideoElement.js deleted file mode 100644 index 65c024f2..00000000 --- a/src2/components/VideoElement.js +++ /dev/null @@ -1,63 +0,0 @@ -import React from 'react' -import Hls from 'hls.js' -import { VideoPlaceholder } from './VideoPlaceholder' - -import './VideoElement.css' - -// streamUrl: string -// loading: boolean -// setProgress: (event: NativeEvent) => void -// videoRef: useRef -// startTime: number -export function VideoElement({ streamUrl, loading, setProgress, videoRef, startTime, streamData }) { - const [error, setError] = React.useState(false); - - function onLoad() { - if (startTime) - videoRef.current.currentTime = startTime; - } - - React.useEffect(() => { - if (!streamUrl.includes('.mp4')) { - setError(false) - if (!videoRef || !videoRef.current || !streamUrl || streamUrl.length === 0 || loading) return; - - const hls = new Hls(); - - if (!Hls.isSupported() && videoRef.current.canPlayType('application/vnd.apple.mpegurl')) { - videoRef.current.src = streamUrl; - return; - } else if (!Hls.isSupported()) { - setError(true) - return; - } - - hls.attachMedia(videoRef.current); - hls.loadSource(streamUrl); - } - }, [videoRef, streamUrl, loading]); - - if (error) - return (Your browser is not supported) - - if (loading) - return Loading episode... - - if (!streamUrl || streamUrl.length === 0) - return No video selected - - if (!streamUrl.includes('.mp4')) { - return ( - - ) - } else { - return ( - - ) - } -} diff --git a/src2/components/VideoPlaceholder.css b/src2/components/VideoPlaceholder.css deleted file mode 100644 index 20dc70f7..00000000 --- a/src2/components/VideoPlaceholder.css +++ /dev/null @@ -1,23 +0,0 @@ -.videoPlaceholder { - width: 100%; - position: relative; -} -.videoPlaceholder::before { - content: ''; - display: block; - width: 100%; - padding-bottom: 56.25%; -} -.videoPlaceholderBox { - display: flex; - justify-content: center; - align-items: center; - width: 100%; - height: 100%; - top: 0; - left: 0; - position: absolute; - background: var(--choice); - border-radius: 6px; - color: var(--text); -} \ No newline at end of file diff --git a/src2/components/VideoPlaceholder.js b/src2/components/VideoPlaceholder.js deleted file mode 100644 index 193ba3e8..00000000 --- a/src2/components/VideoPlaceholder.js +++ /dev/null @@ -1,12 +0,0 @@ -import React from 'react' -import './VideoPlaceholder.css' - -export function VideoPlaceholder(props) { - return ( -
-
-

{props.children}

-
-
- ) -} \ No newline at end of file diff --git a/src2/hooks/useMovie.js b/src2/hooks/useMovie.js deleted file mode 100644 index 5441444d..00000000 --- a/src2/hooks/useMovie.js +++ /dev/null @@ -1,30 +0,0 @@ -import React from 'react' -const MovieContext = React.createContext(null) - -export function MovieProvider(props) { - const [page, setPage] = React.useState("search"); - const [stream, setStream] = React.useState(""); - const [streamData, setStreamData] = React.useState(null); //{ title: "", slug: "", type: "", episodes: [], seasons: [] }) - - return ( - ({...p,...d})) - }, - resetStreamData() { setStreamData(null) } - }}> - {props.children} - - ) -} - -export function useMovie(props) { - return React.useContext(MovieContext); -} diff --git a/src2/hooks/useWindowSize.js b/src2/hooks/useWindowSize.js deleted file mode 100644 index 87427a62..00000000 --- a/src2/hooks/useWindowSize.js +++ /dev/null @@ -1,28 +0,0 @@ -import { useEffect, useState } from "react"; - -// https://usehooks.com/useWindowSize/ -export function useWindowSize() { - // Initialize state with undefined width/height so server and client renders match - // Learn more here: https://joshwcomeau.com/react/the-perils-of-rehydration/ - const [windowSize, setWindowSize] = useState({ - width: undefined, - height: undefined, - }); - useEffect(() => { - // Handler to call on window resize - function handleResize() { - // Set window width/height to state - setWindowSize({ - width: window.innerWidth, - height: window.innerHeight, - }); - } - // Add event listener - window.addEventListener("resize", handleResize); - // Call handler right away so state gets updated with initial window size - handleResize(); - // Remove event listener on cleanup - return () => window.removeEventListener("resize", handleResize); - }, []); // Empty array ensures that effect is only run on mount - return windowSize; - } \ No newline at end of file diff --git a/src2/index.css b/src2/index.css deleted file mode 100644 index 53c6a1a5..00000000 --- a/src2/index.css +++ /dev/null @@ -1,70 +0,0 @@ -:root { - --theme-color: #E880C5; - --theme-color-text: var(--theme-color); - - --failed: #d85b66; - - --body: #16171D; - --card: #22232A; - - --text: white; - --text-secondary: #BCBECB; - --text-tertiary: #585A67; - - --content: #36363e; - --content-hover: #3C3D44; - - --button: #A73B83; - --button-hover: #9C3179; - --button-active: #8b286a; - --button-text: var(--text); - - --choice: #2E2F37; - --choice-hover: #45464D; - --choice-active: #45464D; - - --source-headings: #5b5c63; -} -/* @media (prefers-color-scheme: light) { - :root { - --theme-color: #457461; - - --body: white; - --card: #f8f9fa; - - --content: #eee; - --content-hover: #e7e7e7; - - --text: #333; - --text-secondary: #616161; - --text-tertiary: #aaa; - - --button: #457461; - --button-hover: #4e836e; - --button-active: #437a64; - --button-text: white; - - --choice: var(--content); - --choice-hover: var(--content-hover); - --choice-active: var(--content-hover); - } -} */ - -body, html { - margin: 0; - background-color: var(--body); - min-height: 100vh; -} - -body, html, input, button { - font-family: 'Segoe UI', 'Roboto', 'Oxygen', - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', - sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - font-size: 1rem; -} - -*:focus { - outline: none; -} \ No newline at end of file diff --git a/src2/index.js b/src2/index.js deleted file mode 100644 index ef47def1..00000000 --- a/src2/index.js +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import { HashRouter } from 'react-router-dom'; -import './index.css'; -import App from './App'; - -ReactDOM.render( - - - - - , - document.getElementById('root') -); diff --git a/src2/lib/index.js b/src2/lib/index.js deleted file mode 100644 index eff47a71..00000000 --- a/src2/lib/index.js +++ /dev/null @@ -1,55 +0,0 @@ -import lookmovie from './scraper/lookmovie'; -import xemovie from './scraper/xemovie'; -import theflix from './scraper/theflix'; -import vidzstore from './scraper/vidzstore'; - -async function findContent(searchTerm, type) { - const results = { options: []}; - const content = await Promise.all([ - // lookmovie.findContent(searchTerm, type), - xemovie.findContent(searchTerm, type), - theflix.findContent(searchTerm, type), - vidzstore.findContent(searchTerm, type) - ]); - - content.forEach((o) => { - if (!o || !o.options) return; - - o.options.forEach((i) => { - if (!i) return; - results.options.push(i) - }) - }); - - return results; -} - -async function getStreamUrl(slug, type, source, season, episode) { - switch (source) { - case 'lookmovie': - return await lookmovie.getStreamUrl(slug, type, season, episode); - case 'theflix': - return await theflix.getStreamUrl(slug, type, season, episode); - case 'vidzstore': - return await vidzstore.getStreamUrl(slug); - case 'xemovie': - return await xemovie.getStreamUrl(slug, type, season, episode); - default: - return; - } -} - -async function getEpisodes(slug, source) { - switch (source) { - case 'lookmovie': - return await lookmovie.getEpisodes(slug); - case 'theflix': - return await theflix.getEpisodes(slug); - case 'xemovie': - return await xemovie.getEpisodes(slug); - default: - return; - } -} - -export { findContent, getStreamUrl, getEpisodes } diff --git a/src2/lib/scraper/gdriveplayer.js.disabled b/src2/lib/scraper/gdriveplayer.js.disabled deleted file mode 100644 index 0c206844..00000000 --- a/src2/lib/scraper/gdriveplayer.js.disabled +++ /dev/null @@ -1,111 +0,0 @@ -// THIS SCRAPER DOES NOT CURRENTLY WORK AND IS NOT IN USE - -const BASE_URL = `${process.env.REACT_APP_CORS_PROXY_URL}https://database.gdriveplayer.us`; -const MOVIE_URL = `${process.env.REACT_APP_CORS_PROXY_URL}https://database.gdriveplayer.us/player.php`; -const SHOW_URL = `${process.env.REACT_APP_CORS_PROXY_URL}https://series.databasegdriveplayer.co/player.php`; - -async function findContent(searchTerm, type) { - try { - if (type !== 'movie') return; - - const term = searchTerm.toLowerCase() - const tmdbRes = await fetch(`${process.env.REACT_APP_CORS_PROXY_URL}https://www.themoviedb.org/search?query=${term}`).then(d => d.text()); - - const doc = new DOMParser().parseFromString(tmdbRes, 'text/html'); - const nodes = Array.from(doc.querySelectorAll('div.results > div > div.wrapper')); - const results = nodes.slice(0, 10).map((node) => { - let type = node.querySelector('div.details > div.wrapper > div.title > div > a').getAttribute('data-media-type'); - switch (type) { - case 'movie': - type = 'movie'; - break; - case 'tv': - type = 'show'; - // eslint-disable-next-line array-callback-return - return; - case 'collection': - // eslint-disable-next-line array-callback-return - return; - default: - break; - } - - return { - type: type, - title: node.querySelector('div.details > div.wrapper > div.title > div > a').textContent, - year: node.querySelector('div.details > div.wrapper > div.title > span').textContent.trim().split(' ')[2], - slug: node.querySelector('div.details > div.wrapper > div.title > div > a').href.split('/')[4], - source: 'gdriveplayer' - } - }); - - if (results.length > 1) { - return { options: results }; - } else { - return { options: [ results[0] ] } - } - } catch (err) { - console.error(err); - throw new Error(err) - } -} - -async function getStreamUrl(slug, type, season, episode) { - if (type !== 'movie') return; - - // const tmdbRes = await fetch(`${process.env.REACT_APP_CORS_PROXY_URL}https://www.themoviedb.org/search?query=${term}`).then(d => d.text()); - - console.log(`${MOVIE_URL}?tmdb=${slug}`) - const res = await fetch(`${MOVIE_URL}?tmdb=${slug}`).then(d => d.text()); - - const embed = Array.from(new DOMParser().parseFromString(res, 'text/html').querySelectorAll('.list-server-items a')) - .find((e) => e.textContent.includes("Mirror")) - - if (embed && embed.getAttribute('href')) { - let href = embed.getAttribute('href'); - if (href.startsWith('//')) href = `https:${href}`; - - const res1 = await fetch(`${process.env.REACT_APP_CORS_PROXY_URL}${href}`.replace('streaming.php', 'download')).then(d => d.text()); - const sb = Array.from(new DOMParser().parseFromString(res1, 'text/html').querySelectorAll('a')) - .find((a) => a.textContent.includes("StreamSB")); - - console.log(sb); - - if (sb && sb.getAttribute('href')) { - console.log(sb.getAttribute('href')) - const src = await sbPlayGetLink(sb.getAttribute('href')); - if (src) return { url: src }; - } - } - - return { url: '' } -} - -async function sbPlayGetLink(href) { - if (href.startsWith("//")) href = `https:${href}`; - - const res = await fetch(`${process.env.REACT_APP_CORS_PROXY_URL}${href}`).then(d => d.text()); - const a = new DOMParser().parseFromString(res, 'text/html').querySelector('table tbody tr a'); - - if (a && a.getAttribute('onclick')) { - let match = a.getAttribute("onclick").match(/'([^']+)'/gm); - console.log(a.getAttribute("onclick")); - - if (match) { - let [code, mode, hash] = match; - - const url = `https://sbplay2.com/dl?op=download_orig&id=${code.replace(/'/gm, "")}&mode=${mode.replace(/'/gm, "")}&hash=${hash.replace(/'/gm, "")}`; - - // https://sbplay2.com/dl?op=download_orig&id=glr78kyk21kd&mode=n&hash=1890245-0-0-1640889479-95e144cdfdbe0e9104a67b8e3eee0c2d - // https://sbplay2.com/dl?op=download_orig&id=0hh6mxf5qqn0&mode=h&hash=2473604-78-149-1640889782-797bc207a16b2934c21ea6fdb1e97352 - // https://proxy-1.movie-web.workers.dev/?destination=https://sbplay2.com/dl?op=download_orig&id=glr78kyk21kd&mode=n&hash=1890245-0-0-1640889479-95e144cdfdbe0e9104a67b8e3eee0c2d - - const text = await fetch(url).then((e) => e.text()); - const a = new DOMParser().parseFromString(text, 'text/html').querySelector(".contentbox span a"); - if (a && a.getAttribute("href")) return a.getAttribute("href"); - } - } -} - -const gdriveplayer = { findContent, getStreamUrl } -export default gdriveplayer; \ No newline at end of file diff --git a/src2/lib/scraper/gomostream.js.disabled b/src2/lib/scraper/gomostream.js.disabled deleted file mode 100644 index 6be4ec04..00000000 --- a/src2/lib/scraper/gomostream.js.disabled +++ /dev/null @@ -1,91 +0,0 @@ -// THIS SCRAPER DOES NOT CURRENTLY WORK AND IS NOT IN USE - -import { unpack } from '../util/unpacker'; - -const BASE_URL = `${process.env.REACT_APP_CORS_PROXY_URL}https://gomo.to`; -const MOVIE_URL = `${BASE_URL}/movie` -const DECODING_URL = `${BASE_URL}/decoding_v3.php` - -async function findContent(searchTerm, type) { - try { - if (type !== 'movie') return; - - const term = searchTerm.toLowerCase() - const imdbRes = await fetch(`${process.env.REACT_APP_CORS_PROXY_URL}https://v2.sg.media-imdb.com/suggestion/${term.slice(0, 1)}/${term}.json`).then(d => d.json()) - - const results = []; - imdbRes.d.forEach((e) => { - if (!e.id.startsWith('tt')) return; - - // Block tv shows - if (e.q === "TV series") return; - if (e.q === "TV mini-series") return; - if (e.q === "video game") return; - if (e.q === "TV movie") return; - if (e.q === "TV special") return; - - results.push({ - title: e.l, - slug: e.id, - type: 'movie', - year: e.y, - source: 'gomostream' - }) - }); - - if (results.length > 1) { - return { options: results }; - } else { - return { options: [ results[0] ] } - } - } catch (err) { - console.error(err); - throw new Error(err) - } -} - -async function getStreamUrl(slug, type, season, episode) { - if (type !== 'movie') return; - - // Get stream to go with IMDB ID - const site1 = await fetch(`${MOVIE_URL}/${slug}`).then((d) => d.text()); - - if (site1 === "Movie not available.") - return { url: '' }; - - const tc = site1.match(/var tc = '(.+)';/)?.[1] - const _token = site1.match(/"_token": "(.+)",/)?.[1] - - const fd = new FormData() - fd.append('tokenCode', tc) - fd.append('_token', _token) - - const src = await fetch(DECODING_URL, { - method: "POST", - body: fd, - headers: { - 'x-token': tc.slice(5, 13).split("").reverse().join("") + "13574199" - } - }).then((d) => d.json()); - - const embedUrl = src.find(url => url.includes('gomo.to')); - const site2 = await fetch(`${process.env.REACT_APP_CORS_PROXY_URL}${embedUrl}`).then((d) => d.text()); - - const parser = new DOMParser(); - const site2Dom = parser.parseFromString(site2, "text/html"); - - if (site2Dom.body.innerText === "File was deleted") - return { url: '' } - - const script = site2Dom.querySelectorAll("script")[8].innerHTML; - - let unpacked = unpack(script).split(''); - unpacked.splice(0, 43); - let index = unpacked.findIndex((e) => e === '"'); - const url = unpacked.slice(0, index).join(''); - - return { url } -} - -const gomostream = { findContent, getStreamUrl } -export default gomostream; \ No newline at end of file diff --git a/src2/lib/scraper/lookmovie.js b/src2/lib/scraper/lookmovie.js deleted file mode 100644 index d7fe7981..00000000 --- a/src2/lib/scraper/lookmovie.js +++ /dev/null @@ -1,164 +0,0 @@ -import Fuse from 'fuse.js' -import JSON5 from 'json5' - -const BASE_URL = `https://lookmovie.io`; -const API_URL = `${process.env.REACT_APP_CORS_PROXY_URL}https://lookmovie125.xyz`; -const CORS_URL = `${process.env.REACT_APP_CORS_PROXY_URL}${BASE_URL}`; -let phpsessid; - -async function findContent(searchTerm, type) { - try { - const searchUrl = `${CORS_URL}/${type}s/search/?q=${encodeURIComponent(searchTerm)}`; - const searchRes = await fetch(searchUrl).then((d) => d.text()); - - // Parse DOM to find search results on full search page - const parser = new DOMParser(); - const doc = parser.parseFromString(searchRes, "text/html"); - const nodes = Array.from(doc.querySelectorAll('.movie-item-style-1')); - const results = nodes.map(node => { - return { - type, - title: node.querySelector('h6 a').innerText.trim(), - year: node.querySelector('.year').innerText.trim(), - slug: node.querySelector('a').href.split('/').pop(), - } - }); - - const fuse = new Fuse(results, { threshold: 0.3, distance: 200, keys: ["title"] }); - const matchedResults = fuse - .search(searchTerm.toString()) - .map((result) => result.item); - - if (matchedResults.length === 0) { - return { options: [] } - } - - if (matchedResults.length > 1) { - const res = { options: [] }; - - matchedResults.forEach((r) => res.options.push({ - title: r.title, - slug: r.slug, - type: r.type, - year: r.year, - source: 'lookmovie' - })); - - return res; - } else { - const { title, slug, type, year } = matchedResults[0]; - - return { - options: [{ title, slug, type, year, source: 'lookmovie' }] - } - } - } catch (e) { - return { options: [] } - } -} -async function getVideoUrl(config) { - let url = ''; - - if (config.type === 'movie') { - url = `${API_URL}/api/v1/security/movie-access?id_movie=${config.id}&token=1&sk=&step=1`; - } else if (config.type === 'show') { - url = `${API_URL}/api/v1/security/episode-access?id_episode=${config.id}`; - } - - const data = await fetch(url, { - headers: { phpsessid }, - }).then((d) => d.json()); - - const subs = data?.subtitles.filter((sub) => { - if (typeof sub.file === 'object') return false; - return true; - }) - - // Find video URL and return it (with a check for a full url if needed) - const opts = ["1080p", "1080", "720p", "720", "480p", "480", "auto"]; - - let videoUrl = ""; - for (let res of opts) { - if (data.streams[res] && !data.streams[res].includes('dummy') && !data.streams[res].includes('earth-1984') && !videoUrl) { - videoUrl = data.streams[res] - } - } - - return { - videoUrl: videoUrl.startsWith("/") ? `${BASE_URL}${videoUrl}` : videoUrl, - subs: subs, - }; -} - -async function getEpisodes(slug) { - const url = `${CORS_URL}/shows/view/${slug}`; - const pageReq = await fetch(url, { - headers: { phpsessid }, - }).then((d) => d.text()); - - const data = JSON5.parse("{" + - pageReq - .slice(pageReq.indexOf(`show_storage`)) - .split("};")[0] - .split("= {")[1] - .trim() + - "}" - ); - - let seasons = []; - let episodes = []; - data.seasons.forEach((e) => { - if (!seasons.includes(e.season)) - seasons.push(e.season); - - if (!episodes[e.season]) - episodes[e.season] = [] - episodes[e.season].push(e.episode) - }) - - return { seasons, episodes } -} - -async function getStreamUrl(slug, type, season, episode) { - const url = `${CORS_URL}/${type}s/view/${slug}`; - const pageRes = await fetch(url); - if (pageRes.headers.get('phpsessid')) phpsessid = pageRes.headers.get('phpsessid'); - const pageResText = await pageRes.text(); - - const data = JSON5.parse("{" + - pageResText - .slice(pageResText.indexOf(`${type}_storage`)) - .split("};")[0] - .split("= {")[1] - .trim() + - "}" - ); - - let id = ''; - - if (type === "movie") { - id = data.id_movie; - } else if (type === "show") { - const episodeObj = data.seasons.find((v) => { return v.season === season && v.episode === episode; }); - - if (episodeObj) { - id = episodeObj.id_episode; - } - } - - if (id === '') { - return { url: '' } - } - - const videoUrl = await getVideoUrl({ - slug: slug, - id: id, - type: type, - }); - - return { url: videoUrl.videoUrl, subtitles: videoUrl.subs }; -} - - -const lookMovie = { findContent, getStreamUrl, getEpisodes }; -export default lookMovie; diff --git a/src2/lib/scraper/theflix.js b/src2/lib/scraper/theflix.js deleted file mode 100644 index f8291ff4..00000000 --- a/src2/lib/scraper/theflix.js +++ /dev/null @@ -1,120 +0,0 @@ -const BASE_URL = `${process.env.REACT_APP_CORS_PROXY_URL}https://theflix.to`; - -async function findContent(searchTerm, type) { - try { - const term = searchTerm.toLowerCase() - const tmdbRes = await fetch(`${process.env.REACT_APP_CORS_PROXY_URL}https://www.themoviedb.org/search/${type === 'show' ? 'tv' : type}?query=${term}`).then(d => d.text()); - - const doc = new DOMParser().parseFromString(tmdbRes, 'text/html'); - const nodes = Array.from(doc.querySelectorAll('div.results > div > div.wrapper')); - const results = nodes.slice(0, 10).map((node) => { - let type = node.querySelector('div.details > div.wrapper > div.title > div > a').getAttribute('data-media-type'); - type = type === 'tv' ? 'show' : type; - - let title; - let year; - let slug; - - if (type === 'movie') { - try { - title = node.querySelector('div.details > div.wrapper > div.title > div > a').textContent; - year = node.querySelector('div.details > div.wrapper > div.title > span').textContent.trim().split(' ')[2]; - slug = node.querySelector('div.details > div.wrapper > div.title > div > a').getAttribute('href').split('/')[2]; - } catch (e) { - // eslint-disable-next-line array-callback-return - return; - } - } else if (type === 'show') { - try { - title = node.querySelector('div.details > div.wrapper > div.title > div > a > h2').textContent; - year = node.querySelector('div.details > div.wrapper > div.title > span').textContent.trim().split(' ')[2]; - slug = node.querySelector('div.details > div.wrapper > div.title > div > a').getAttribute('href').split('/')[2]; - } catch (e) { - // eslint-disable-next-line array-callback-return - return; - } - } - - return { - type: type, - title: title, - year: year, - slug: slug + '-' + title.replace(/[^a-z0-9]+|\s+/gmi, " ").replace(/\s+/g, '-').toLowerCase(), - source: 'theflix' - } - }); - - if (results.length > 1) { - return { options: results }; - } else { - return { options: [ results[0] ] } - } - } catch (err) { - console.error(err); - throw new Error(err) - } -} - -async function getEpisodes(slug) { - let tmdbRes; - - try { - tmdbRes = await fetch(`${process.env.REACT_APP_CORS_PROXY_URL}https://www.themoviedb.org/tv/${slug}/seasons`).then(d => d.text()); - } catch (err) { - tmdbRes = await fetch(`${process.env.REACT_APP_CORS_PROXY_URL}https://www.themoviedb.org/tv/${slug.split('-')[0]}/seasons`).then(d => d.text()); - - if (tmdbRes) - slug = slug.split('-')[0]; - } - - const sNodes = Array.from(new DOMParser().parseFromString(tmdbRes, 'text/html').querySelectorAll('div.column_wrapper > div.flex > div')); - - let seasons = []; - let episodes = []; - - for (let s of sNodes) { - const text = s.querySelector('div > section > div > div > div > h2 > a').textContent; - if (!text.includes('Season')) continue; - - const season = text.split(' ')[1]; - - if (!seasons.includes(season)) { - seasons.push(season); - } - - if (!episodes[season]) { - episodes[season] = []; - } - - const epRes = await fetch(`${process.env.REACT_APP_CORS_PROXY_URL}https://www.themoviedb.org/tv/${slug}/season/${season}`).then(d => d.text()); - const epNodes = Array.from(new DOMParser().parseFromString(epRes, 'text/html').querySelectorAll('div.episode_list > div.card')); - epNodes.forEach((e, i) => episodes[season].push(++i)); - } - - return { seasons, episodes }; -} - -async function getStreamUrl(slug, type, season, episode) { - let url; - - if (type === 'show') { - url = `${BASE_URL}/tv-show/${slug}/season-${season}/episode-${episode}`; - } else { - url = `${BASE_URL}/movie/${slug}?movieInfo=${slug}`; - } - - const res = await fetch(url).then(d => d.text()); - - const scripts = Array.from(new DOMParser().parseFromString(res, "text/html").querySelectorAll('script')); - const prop = scripts.find((e) => e.textContent.includes("theflixvd.b-cdn")); - - if (prop) { - const data = JSON.parse(prop.textContent); - return { url: data.props.pageProps.videoUrl }; - } - - return { url: '' } -} - -const theflix = { findContent, getStreamUrl, getEpisodes } -export default theflix; diff --git a/src2/lib/scraper/vidzstore.js b/src2/lib/scraper/vidzstore.js deleted file mode 100644 index ec008568..00000000 --- a/src2/lib/scraper/vidzstore.js +++ /dev/null @@ -1,41 +0,0 @@ -const BASE_URL = `${process.env.REACT_APP_CORS_PROXY_URL}https://stream.vidzstore.com`; - -async function findContent(searchTerm, type) { - if (type === 'show') return { options: [] }; - try { - const searchUrl = `${BASE_URL}/search.php?sd=${searchTerm.replace(/ /g, "_")}`; - const searchRes = await fetch(searchUrl).then((d) => d.text()); - - const parser = new DOMParser(); - const doc = parser.parseFromString(searchRes, "text/html"); - const nodes = [...doc.querySelectorAll(".post")]; - const results = nodes.map(node => { - const title = node.querySelector("a").title.replace(/-/g, " ").trim(); - const titleArray = title.split(" "); - titleArray.splice(-2); - return { - type, - title: titleArray.join(" "), - year: node.querySelector(".post-meta").innerText.split(" ").pop().split("-").shift(), - slug: encodeURIComponent(node.querySelector("a").href.split('/').pop()), - source: "vidzstore", - } - }); - - return { options: results }; - } catch { - return { options: [] }; - } -} - -async function getStreamUrl(slug) { - const url = `${BASE_URL}/${decodeURIComponent(slug)}`; - - const res = await fetch(url).then(d => d.text()); - const DOM = new DOMParser().parseFromString(res, "text/html"); - - return { url: DOM.querySelector("source").src }; -} - -const vidzstore = { findContent, getStreamUrl } -export default vidzstore; \ No newline at end of file diff --git a/src2/lib/scraper/vmovee.js.disabled b/src2/lib/scraper/vmovee.js.disabled deleted file mode 100644 index 41039ea6..00000000 --- a/src2/lib/scraper/vmovee.js.disabled +++ /dev/null @@ -1,84 +0,0 @@ -// THIS SCRAPER DOES NOT CURRENTLY WORK AND IS NOT IN USE - -import { unpack } from '../util/unpacker'; - -const BASE_URL = `https://www.vmovee.watch`; -const CORS_URL = `${process.env.REACT_APP_CORS_PROXY_URL}${BASE_URL}`; -const SHOW_URL = `${CORS_URL}/series` -const MOVIE_URL = `${CORS_URL}/movies` -const MOVIE_URL_NO_CORS = `${BASE_URL}/movies` - -async function findContent(searchTerm, type) { - try { - if (type !== 'movie') return; - - const searchUrl = `${CORS_URL}/?s=${encodeURIComponent(searchTerm)}`; - const searchRes = await fetch(searchUrl).then((d) => d.text()); - - const parser = new DOMParser(); - const doc = parser.parseFromString(searchRes, "text/html"); - const nodes = Array.from(doc.querySelectorAll('div.search-page > div.result-item > article')); - const results = nodes.map(node => { - const imgHolder = node.querySelector('div.image > div.thumbnail > a'); - const titleHolder = node.querySelector('div.title > a'); - - return { - type: imgHolder.querySelector('span').textContent === 'TV' ? 'show' : 'movie', - title: titleHolder.textContent, - year: node.querySelector('div.details > div.meta > span.year').textContent, - slug: titleHolder.href.split('/')[4], - source: 'vmovee' - } - }); - - if (results.length > 1) { - return { options: results }; - } else { - return { options: [ results[0] ] } - } - } catch (err) { - throw new Error(err) - } -} - -async function getStreamUrl(slug, type, season, episode) { - let url = ''; - - if (type === 'movie') { - url = `${MOVIE_URL}/${slug}`; - } else if (type === 'show') { - url = `${SHOW_URL}/${slug}`; - } - - const res1 = await fetch(url, { headers: new Headers().append('referer', `${BASE_URL}/dashboard/admin-ajax.php`) }); - const id = res1.headers.get('link').split('>')[0].split('?p=')[1]; - - const res2Headers = new Headers().append('referer', `${BASE_URL}/dashboard/admin-ajax.php`); - const form = new FormData(); - form.append('action', 'doo_player_ajax') - form.append('post', id) - form.append('nume', '2') - form.append('type', type) - - const res2 = await fetch(`${CORS_URL}/dashboard/admin-ajax.php`, { - method: 'POST', - headers: res2Headers, - body: form - }).then((res) => res.json()); - let realUrl = res2.embed_url; - - console.log(res2) - - if (realUrl.startsWith('//')) { - realUrl = `https:${realUrl}`; - } - - const res3 = await fetch(`${process.env.REACT_APP_CORS_PROXY_URL}${realUrl}`); - res3.headers.forEach(console.log) - - return { url: '' } - -} - -const vmovee = { findContent, getStreamUrl } -export default vmovee; \ No newline at end of file diff --git a/src2/lib/scraper/xemovie.js b/src2/lib/scraper/xemovie.js deleted file mode 100644 index 6f7066db..00000000 --- a/src2/lib/scraper/xemovie.js +++ /dev/null @@ -1,121 +0,0 @@ -import Fuse from 'fuse.js' - -const BASE_URL = `${process.env.REACT_APP_CORS_PROXY_URL}https://xemovie.co`; - -async function findContent(searchTerm, type) { - try { - let results; - - const searchUrl = `${BASE_URL}/search?q=${encodeURIComponent(searchTerm)}`; - const searchRes = await fetch(searchUrl).then((d) => d.text()); - - const parser = new DOMParser(); - const doc = parser.parseFromString(searchRes, "text/html"); - switch (type) { - case 'show': - // const showContainer = doc.querySelectorAll(".py-10")[1].querySelector(".grid"); - // const showNodes = [...showContainer.querySelectorAll("a")].filter(link => !link.className); - // results = showNodes.map(node => { - // node = node.parentElement - // return { - // type, - // title: [...new Set(node.innerText.split("\n"))][1].split("(")[0].trim(), - // year: [...new Set(node.innerText.split("\n"))][3], - // slug: node.querySelector("a").href.split('/').pop(), - // source: "xemovie" - // } - // }) - // break; - return { options: [] }; - case 'movie': - const movieContainer = doc.querySelectorAll(".py-10")[0].querySelector(".grid"); - const movieNodes = [...movieContainer.querySelectorAll("a")].filter(link => !link.className); - results = movieNodes.map(node => { - node = node.parentElement - return { - type, - title: [...new Set(node.innerText.split("\n"))][1].split("(")[0].trim(), - year: [...new Set(node.innerText.split("\n"))][3], - slug: node.querySelector("a").href.split('/').pop(), - source: "xemovie" - } - }) - break; - default: - results = []; - break; - } - - const fuse = new Fuse(results, { threshold: 0.3, keys: ["title"] }); - const matchedResults = fuse - .search(searchTerm) - .map(result => result.item); - - if (matchedResults.length === 0) { - return { options: [] }; - } - - if (matchedResults.length > 1) { - const res = { options: [] }; - - matchedResults.forEach((r) => res.options.push({ - title: r.title, - slug: r.slug, - type: r.type, - year: r.year, - source: 'xemovie' - })); - - return res; - } else { - const { title, slug, type, year } = matchedResults[0]; - - return { - options: [{ title, slug, type, year, source: 'xemovie' }] - } - } - } catch { - return { options: [] }; - } -} - -async function getStreamUrl(slug, type, season, episode) { - let url; - - if (type === "show") { - - } else { - url = `${BASE_URL}/movies/${slug}/watch`; - } - - let mediaUrl = ""; - let subtitles = []; - - const res = await fetch(url).then(d => d.text()); - const DOM = new DOMParser().parseFromString(res, "text/html"); - - for (const script of DOM.scripts) { - if (script.textContent.match(/https:\/\/s[0-9]\.xemovie\.com/)) { - // eslint-disable-next-line - let data = JSON.parse(JSON.stringify(eval(`(${script.textContent.replace("const data = ", "").split("};")[0]}})`))); - // eslint-disable-next-line - mediaUrl = data.playlist[0].file; - // eslint-disable-next-line - for (const subtitleTrack of data.playlist[0].tracks) { - const subtitleBlob = URL.createObjectURL(await fetch(`${process.env.REACT_APP_CORS_PROXY_URL}${subtitleTrack.file}`).then(res => res.blob())); // do this so no need for CORS errors - subtitles.push({ - file: subtitleBlob, - language: subtitleTrack.label - }) - } - } - } - return { url: mediaUrl, subtitles: subtitles } -} - -async function getEpisodes(slug) { - -} - -const xemovie = { findContent, getStreamUrl, getEpisodes } -export default xemovie; \ No newline at end of file diff --git a/src2/lib/storage/VideoProgress.js b/src2/lib/storage/VideoProgress.js deleted file mode 100644 index c968bc2b..00000000 --- a/src2/lib/storage/VideoProgress.js +++ /dev/null @@ -1,43 +0,0 @@ -import { versionedStoreBuilder } from './base.js'; - -/* -version 0 -{ - [{scraperid}]: { - movie: { - [{movie-id}]: { - full: { - currentlyAt: number, - totalDuration: number, - updatedAt: number, // unix timestamp in ms - meta: FullMetaObject, // no idea whats in here - } - } - }, - show: { - [{show-id}]: { - [{season}-{episode}]: { - currentlyAt: number, - totalDuration: number, - updatedAt: number, // unix timestamp in ms - show: { - episode: string, - season: string, - }, - meta: FullMetaObject, // no idea whats in here - } - } - } - } -} -*/ - -export const VideoProgressStore = versionedStoreBuilder() - .setKey('video-progress') - .addVersion({ - version: 0, - create() { - return {} - } - }) - .build() diff --git a/src2/lib/storage/base.js b/src2/lib/storage/base.js deleted file mode 100644 index 83b75ade..00000000 --- a/src2/lib/storage/base.js +++ /dev/null @@ -1,230 +0,0 @@ -function buildStoreObject(d) { - const data = { - versions: d.versions, - currentVersion: d.maxVersion, - id: d.storageString, - } - - function update(obj) { - if (!obj) - throw new Error("object to update is not an object"); - - // repeat until object fully updated - if (obj["--version"] === undefined) - obj["--version"] = 0; - while (obj["--version"] !== this.currentVersion) { - // get version - let version = obj["--version"] || 0; - if (version.constructor !== Number || version < 0) - version = -42; // invalid on purpose so it will reset - else { - version = (version+1).toString() - } - - // check if version exists - if (!this.versions[version]) { - console.error(`Version not found for storage item in store ${this.id}, resetting`); - obj = null; - break; - } - - // update object - obj = this.versions[version].update(obj); - } - - // if resulting obj is null, use latest version as init object - if (obj === null) { - console.error(`Storage item for store ${this.id} has been reset due to faulty updates`); - return this.versions[this.currentVersion.toString()].init(); - } - - // updates succesful, return - return obj; - } - - function get() { - // get from storage api - const store = this; - let data = localStorage.getItem(this.id); - - // parse json if item exists - if (data) { - try { - data = JSON.parse(data); - if (!data.constructor) { - console.error(`Storage item for store ${this.id} has not constructor`) - throw new Error("storage item has no constructor") - } - if (data.constructor !== Object) { - console.error(`Storage item for store ${this.id} is not an object`) - throw new Error("storage item is not an object") - } - } catch (_) { - // if errored, set to null so it generates new one, see below - console.error(`Failed to parse storage item for store ${this.id}`) - data = null; - } - } - - // if item doesnt exist, generate from version init - if (!data) { - data = this.versions[this.currentVersion.toString()].init(); - } - - // update the data if needed - data = this.update(data); - - // add a save object to return value - data.save = function save() { - localStorage.setItem(store.id, JSON.stringify(data)); - } - - // add instance helpers - Object.entries(d.instanceHelpers).forEach(([name, helper]) => { - if (data[name] !== undefined) - throw new Error(`helper name: ${name} on instance of store ${this.id} is reserved`) - data[name] = helper.bind(data); - }) - - // return data - return data; - } - - // add functions to store - data.get = get.bind(data); - data.update = update.bind(data); - - // add static helpers - Object.entries(d.staticHelpers).forEach(([name, helper]) => { - if (data[name] !== undefined) - throw new Error(`helper name: ${name} on store ${data.id} is reserved`) - data[name] = helper.bind({}); - }) - - return data; -} - -/* - * Builds a versioned store - * - * manages versioning of localstorage items -*/ -export function versionedStoreBuilder() { - return { - _data: { - versionList: [], - maxVersion: 0, - versions: {}, - storageString: null, - instanceHelpers: {}, - staticHelpers: {}, - }, - - /* - * set key of localstorage item, must be unique - */ - setKey(str) { - this._data.storageString = str; - return this; - }, - - /* - * add a version to the store - * - * version: version number - * migrate: function to update from previous version to this version - * create: function to return an empty storage item from this version (in correct syntax) - */ - addVersion({ version, migrate, create }) { - // input checking - if (version < 0) - throw new Error("Cannot add version below 0 in store"); - if (version > 0 && !migrate) - throw new Error(`Missing migration on version ${version} (needed for any version above 0)`); - - // update max version list - if (version > this._data.maxVersion) - this._data.maxVersion = version; - // add to version list - this._data.versionList.push(version); - - - // register version - this._data.versions[version.toString()] = { - version: version, // version number - update: migrate ? (data) => { // update function, and increment version - migrate(data); - data["--version"] = version; - return data; - } : null, - init: create ? () => { // return an initial object - const data = create(); - data["--version"] = version; - return data; - } : null - } - return this; - }, - - /* - * register a instance or static helper to the store - * - * name: name of the helper function - * helper: function to execute, the 'this' context is the current storage item (type is instance) - * type: "instance" or "static". instance is put on the storage item when you store.get() it, static is on the store - */ - registerHelper({ name, helper, type }) { - // type - if (!type) - type = "instance" - - // input checking - if (!name || name.constructor !== String) { - throw new Error("helper name is not a string") - } - if (!helper || helper.constructor !== Function) { - throw new Error("helper function is not a function") - } - if (!["instance", "static"].includes(type)) { - throw new Error("helper type must be either 'instance' or 'static'") - } - - // register helper - if (type === "instance") - this._data.instanceHelpers[name] = helper - else if (type === "static") - this._data.staticHelpers[name] = helper - - return this; - }, - - /* - * returns function store based on what has been set - */ - build() { - // check if version list doesnt skip versions - const versionListSorted = this._data.versionList.sort((a,b)=>a-b); - versionListSorted.forEach((v, i, arr) => { - if (i === 0) - return; - if (v !== arr[i-1]+1) - throw new Error("Version list of store is not incremental"); - }) - - // version zero must exist - if (versionListSorted[0] !== 0) - throw new Error("Version 0 doesn't exist in version list of store"); - - // max version must have init function - if (!this._data.versions[this._data.maxVersion.toString()].init) - throw new Error(`Missing create function on version ${this._data.maxVersion} (needed for latest version of store)`); - - // check storage string - if (!this._data.storageString) - throw new Error("storage key not set in store"); - - // build versioned store - return buildStoreObject(this._data); - } - } -} diff --git a/src2/lib/util/unpacker.js b/src2/lib/util/unpacker.js deleted file mode 100644 index da7e1e51..00000000 --- a/src2/lib/util/unpacker.js +++ /dev/null @@ -1,53 +0,0 @@ -const alphabet = { - 62: "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", - 95: '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~' -}; - -function _filterargs(str) { - var juicers = [ - /}\('([\s\S]*)', *(\d+), *(\d+), *'([\s\S]*)'\.split\('\|'\), *(\d+), *([\s\S]*)\)\)/, - /}\('([\s\S]*)', *(\d+), *(\d+), *'([\s\S]*)'\.split\('\|'\)/ - ]; - - for (var c = 0; c < juicers.length; ++c) { - var m, juicer = juicers[c]; - - // eslint-disable-next-line no-cond-assign - if (m = juicer.exec(str)) { - return [m[1], m[4].split('|'), parseInt(m[2]), parseInt(m[3])]; - } - } - - throw new Error("Could not make sense of p.a.c.k.e.r data (unexpected code structure)"); -} - -function _unbaser(base) { - if (2 <= base <= 36) return (str) => parseInt(str, base); - - const dictionary = {}; - var alpha = alphabet[base]; - if (!alpha) throw new Error("Unsupported encoding"); - - for (let c = 0; c < alpha.length; ++alpha) { - dictionary[alpha[c]] = c; - } - - return (str) => str.split("").reverse().reduce((cipher, ind) => Math.pow(base, ind) * dictionary[cipher]); -} - -function unpack(str) { - var params = _filterargs(str); - var payload = params[0], symtab = params[1], radix = params[2], count = params[3]; - - if (count !== symtab.length) { - throw new Error("Malformed p.a.c.k.e.r. symtab. (" + count + " != " + symtab.length + ")"); - } - - var unbase = _unbaser(radix); - var lookup = (word) => symtab[unbase(word)] || word; - var source = payload.replace(/\b\w+\b/g, lookup); - - return source; -} - -export { unpack }; \ No newline at end of file diff --git a/src2/views/Movie.css b/src2/views/Movie.css deleted file mode 100644 index f70a8fa8..00000000 --- a/src2/views/Movie.css +++ /dev/null @@ -1,6 +0,0 @@ -.showType-show .title-size-big { - margin-bottom: 10px; -} -.showType-show .title-size-small, .showType-movie .title-size-big { - margin-bottom: 30px; -} \ No newline at end of file diff --git a/src2/views/Movie.js b/src2/views/Movie.js deleted file mode 100644 index 6f6561b9..00000000 --- a/src2/views/Movie.js +++ /dev/null @@ -1,156 +0,0 @@ -import React from 'react' -import { useRouteMatch, useHistory } from 'react-router-dom' -import { Helmet } from 'react-helmet'; -import { Title } from '../components/Title' -import { Card } from '../components/Card' -import { useMovie } from '../hooks/useMovie' -import { VideoElement } from '../components/VideoElement' -import { EpisodeSelector } from '../components/EpisodeSelector' -import { getStreamUrl } from '../lib/index' -import { VideoProgressStore } from '../lib/storage/VideoProgress' - -import './Movie.css' - -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 [ selectedSeason, setSelectedSeason ] = React.useState("1"); - const [ startTime, setStartTime ] = React.useState(0); - const videoRef = React.useRef(null); - let isVideoTimeSet = React.useRef(false); - - const season = showRouteMatch?.params.season || "1"; - const episode = showRouteMatch?.params.episode || "1"; - - // eslint-disable-next-line react-hooks/exhaustive-deps - function setEpisode({ season, episode }) { - history.push(`${baseRouteMatch.url}/season/${season}/episode/${episode}`); - isVideoTimeSet.current = false; - } - - React.useEffect(() => { - 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(() => { - let cancel = false; - - if (streamData.type !== "show") return () => { - cancel = true; - }; - - if (!episode) { - setLoading(false); - setStreamUrl(''); - return; - } - - setLoading(true); - getStreamUrl(streamData.slug, streamData.type, streamData.source, season, episode) - .then(({ url, subtitles }) => { - if (cancel) return; - streamData.subtitles = subtitles; - setStreamUrl(url) - setLoading(false); - }) - .catch((e) => { - if (cancel) return; - console.error(e) - }) - - return () => { - cancel = true; - } - }, [episode, streamData, setStreamUrl, season]); - - React.useEffect(() => { - if (streamData.type === "show") { - setSeasonList(streamData.seasons); - setEpisodeList(streamData.episodes[selectedSeason]); - } - }, [streamData.seasons, streamData.episodes, streamData.type, selectedSeason]) - - React.useEffect(() => { - const progressData = VideoProgressStore.get(); - let key = streamData.type === "show" ? `${season}-${episode}` : "full" - let time = progressData?.[streamData.source]?.[streamData.type]?.[streamData.slug]?.[key]?.currentlyAt; - setStartTime(time); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [baseRouteMatch, showRouteMatch]); - - const setProgress = (evt) => { - let progressSave = VideoProgressStore.get(); - - if (!progressSave[streamData.source]) - progressSave[streamData.source] = {} - if (!progressSave[streamData.source][streamData.type]) - progressSave[streamData.source][streamData.type] = {} - if (!progressSave[streamData.source][streamData.type][streamData.slug]) - progressSave[streamData.source][streamData.type][streamData.slug] = {} - - // Store real data - let key = streamData.type === "show" ? `${season}-${episode}` : "full" - progressSave[streamData.source][streamData.type][streamData.slug][key] = { - currentlyAt: Math.floor(evt.currentTarget.currentTime), - totalDuration: Math.floor(evt.currentTarget.duration), - updatedAt: Date.now(), - meta: streamData - } - - if(streamData.type === "show") { - progressSave[streamData.source][streamData.type][streamData.slug][key].show = { - season, - episode - } - } - - progressSave.save(); - } - - return ( -
- - {streamData.title}{streamData.type === 'show' ? ` | S${season}E${episode}` : ''} | movie-web - - - - - {streamData.title} - - {streamData.type === "show" ? - Season {season}: Episode {episode} - : undefined} - - - - {streamData.type === "show" ? - - : ''} - -
- ) -} diff --git a/src2/views/Search.css b/src2/views/Search.css deleted file mode 100644 index d3ce7d38..00000000 --- a/src2/views/Search.css +++ /dev/null @@ -1,76 +0,0 @@ -.cardView { - display: flex; - min-height: 100vh; - justify-content: center; - align-items: center; - flex-direction: column; - padding: 1rem; - box-sizing: border-box; -} - -.cardView nav { - width: 100%; - max-width: 624px; -} -.cardView nav span { - padding: 8px 16px; - margin-right: 10px; - border-radius: 4px; - color: var(--text); -} - -.cardView nav span:focus-visible { - border: 1px solid #fff; -} - -.cardView nav span:not(.selected-link) { - cursor: pointer; -} -.cardView nav span.selected-link { - background: var(--card); - color: var(--button-text); - font-weight: bold; -} - -.cardView > * { - margin-top: 2rem; -} - -.cardView > *:first-child { - margin-top: 38px; -} - -.topRightCredits { - position: absolute; - right: 1rem; - top: 1rem; - margin-top: 0 !important; - text-align: right; -} - -.topRightCredits a, .topRightCredits a:visited { - color: var(--theme-color); - text-decoration: none; - margin: 0; -} - -.topRightCredits a:hover, .topRightCredits a:active { - color: var(--theme-color); - text-decoration: none; -} - -.topRightCredits a .arrow { - transform: translateY(.1rem); -} - -.topRightCredits a:hover .arrow { - transform: translateY(.1rem) translateX(.2rem); -} - -p.source { - text-transform: uppercase; - font-weight: bold; - color: var(--source-headings); - font-size: 0.8em; - margin-top: 2rem; -} diff --git a/src2/views/Search.js b/src2/views/Search.js deleted file mode 100644 index 775de439..00000000 --- a/src2/views/Search.js +++ /dev/null @@ -1,299 +0,0 @@ -import React from 'react'; -import { Helmet } from 'react-helmet'; -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 { Progress } from '../components/Progress'; -import { Title } from '../components/Title'; -import { TypeSelector } from '../components/TypeSelector'; -import { useMovie } from '../hooks/useMovie'; -import { findContent, getEpisodes, getStreamUrl } from '../lib/index'; -import { VideoProgressStore } from '../lib/storage/VideoProgress' - -import './Search.css'; - -export function SearchView() { - const { navigate, setStreamUrl, setStreamData } = useMovie(); - - const history = useHistory(); - const routeMatch = useRouteMatch('/:type'); - const type = routeMatch?.params?.type; - const streamRouteMatch = useRouteMatch('/:type/:source/:title/:slug'); - - const maxSteps = 4; - const [options, setOptions] = React.useState([]); - const [progress, setProgress] = React.useState(0); - const [text, setText] = React.useState(""); - 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); - setText(str) - setFailed(true) - } - - async function getStream(title, slug, type, source, year) { - setStreamUrl(""); - - try { - setProgress(2); - setText(`Getting stream for "${title}"`); - - let seasons = []; - let episodes = []; - if (type === "show") { - const data = await getEpisodes(slug, source); - seasons = data.seasons; - episodes = data.episodes; - } - - let realUrl = ''; - let subtitles = [] - - if (type === "movie") { - const { url, subtitles: subs } = await getStreamUrl(slug, type, source); - - if (url === '') { - return fail(`Not found: ${title}`) - } - - realUrl = url; - subtitles = subs - } - - setProgress(maxSteps); - setStreamUrl(realUrl); - setStreamData({ - title, - type, - seasons, - episodes, - slug, - source, - year, - subtitles - }) - setText(`Streaming...`) - navigate("movie") - } catch (err) { - console.error(err); - fail("Failed to get stream") - } - } - - async function searchMovie(query, contentType) { - setFailed(false); - setText(`Searching for ${contentType} "${query}"`); - setProgress(1) - setShowingOptions(false) - - try { - const { options } = await findContent(query, contentType); - - if (options.length === 0) { - return fail(`Could not find that ${contentType}`) - } else if (options.length > 1) { - setProgress(2); - setText(`Choose your ${contentType}`); - setOptions(options); - setShowingOptions(true); - return; - } - - const { title, slug, type, source, year } = options[0]; - history.push(`${routeMatch.url}/${source}/${title}/${slug}`); - getStream(title, slug, type, source, year); - } catch (err) { - console.error(err); - fail(`Failed to watch ${contentType}`) - } - } - - React.useEffect(() => { - async function fetchHealth() { - await fetch(process.env.REACT_APP_CORS_PROXY_URL).catch(() => { - // Request failed; source likely offline - setErrorStatus(`Our content provider is currently offline, apologies.`) - }) - } - fetchHealth() - }, []); - - React.useEffect(() => { - if (streamRouteMatch) { - if (streamRouteMatch?.params.type === 'movie' || streamRouteMatch.params.type === 'show') getStream(streamRouteMatch.params.title, streamRouteMatch.params.slug, streamRouteMatch.params.type, streamRouteMatch.params.source); - else return setErrorStatus("Failed to find movie. Please try searching below."); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - React.useEffect(() => { - const progressData = VideoProgressStore.get(); - 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 - } - - // 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 - } else { - // we can't do next episode for movies! - 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; - 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]) { - 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 { - 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); - } - } - } - - newContinueWatching = newContinueWatching.sort((a, b) => { - return b.data.updatedAt - a.data.updatedAt - }); - - setContinueWatching(newContinueWatching) - }) - }, []); - - if (!type || (type !== 'movie' && type !== 'show')) { - return - } - - const handleKeyPress = page => event => { - if (event.code === 'Enter' || event.code === 'Space'){ - setPage(page); - } - } - - return ( -
- - {type === 'movie' ? 'movies' : 'shows'} | movie-web - - - {/* 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 - 0} failed={failed} progress={progress} steps={maxSteps} text={text} /> - {continueWatching?.map((v, i) => ( - { - if (v.type === 'show') { - history.push(`/show/${v.source}/${v.data.meta.title}/${v.slug}/season/${v.data.show.season}/episode/${v.data.show.episode}`) - } else { - history.push(`/movie/${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) - }} /> - ))} - : } - - -
- ) -}