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 (
-
- )
-}
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 (
-
- )
-}
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}` : ''}
-
-
-
-
-
-
-
-
- )
-}
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 ? : null}
-
-
- {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 (
-
- )
-}
\ 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)} />
-
-
-
-
- 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
- :
}
-
-
-
- )
-}