mirror of
https://github.com/sussy-code/smov.git
synced 2025-01-01 16:37:39 +01:00
404 pages for all cases
This commit is contained in:
parent
356b9af4ff
commit
8a42364a95
8 changed files with 75 additions and 30 deletions
|
@ -57,13 +57,13 @@ Check out [this project's issues](https://github.com/JamesHawkinss/movie-web/iss
|
||||||
- [x] video load error, video loading (from actual video player)
|
- [x] video load error, video loading (from actual video player)
|
||||||
- [ ] Series episodes+seasons
|
- [ ] Series episodes+seasons
|
||||||
- [ ] Get rid of react warnings
|
- [ ] Get rid of react warnings
|
||||||
- [ ] Add 404 page for media (media not found, provider disabled, provider not found) & general (page not found)
|
- [x] Add 404 page for media (media not found, provider disabled, provider not found) & general (page not found)
|
||||||
- [ ] Handle disabled providers (continue watching, bookmarks & router)
|
- [x] Handle disabled providers (continue watching, bookmarks & router)
|
||||||
- [ ] Subtitles
|
- [ ] Subtitles
|
||||||
- [ ] Implement all scrapers
|
- [ ] Implement all scrapers
|
||||||
- [ ] implement sources that are not mp4
|
- [ ] implement sources that are not mp4
|
||||||
|
- [x] Bug: go back doesn't work if used directly from link
|
||||||
- [ ] Migrate old video progress
|
- [ ] Migrate old video progress
|
||||||
- [ ] Bug: go back doesn't work if used directly from link
|
|
||||||
|
|
||||||
## After all rewrite code has been written
|
## After all rewrite code has been written
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { MWMediaType } from "providers";
|
||||||
import { Redirect, Route, Switch } from "react-router-dom";
|
import { Redirect, Route, Switch } from "react-router-dom";
|
||||||
import { BookmarkContextProvider } from "state/bookmark";
|
import { BookmarkContextProvider } from "state/bookmark";
|
||||||
import { WatchedContextProvider } from "state/watched";
|
import { WatchedContextProvider } from "state/watched";
|
||||||
import { NotFoundPage } from "views/NotFoundView";
|
import { NotFoundPage } from "views/notfound/NotFoundView";
|
||||||
import "./index.css";
|
import "./index.css";
|
||||||
import { MediaView } from "./views/MediaView";
|
import { MediaView } from "./views/MediaView";
|
||||||
import { SearchView } from "./views/SearchView";
|
import { SearchView } from "./views/SearchView";
|
||||||
|
|
|
@ -8,7 +8,7 @@ export interface WatchedMediaCardProps {
|
||||||
|
|
||||||
export function WatchedMediaCard(props: WatchedMediaCardProps) {
|
export function WatchedMediaCard(props: WatchedMediaCardProps) {
|
||||||
const { watched } = useWatchedContext();
|
const { watched } = useWatchedContext();
|
||||||
const foundWatched = getWatchedFromPortable(watched, props.media);
|
const foundWatched = getWatchedFromPortable(watched.items, props.media);
|
||||||
const watchedPercentage = (foundWatched && foundWatched.percentage) || 0;
|
const watchedPercentage = (foundWatched && foundWatched.percentage) || 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { MWMediaMeta } from "providers";
|
import { MWMediaMeta, getProviderMetadata } from "providers";
|
||||||
import React, { createContext, ReactNode, useContext, useState } from "react";
|
import React, { createContext, ReactNode, useContext, useState } from "react";
|
||||||
import { VideoProgressStore } from "./store";
|
import { VideoProgressStore } from "./store";
|
||||||
|
|
||||||
|
@ -13,11 +13,13 @@ interface WatchedStoreData {
|
||||||
|
|
||||||
interface WatchedStoreDataWrapper {
|
interface WatchedStoreDataWrapper {
|
||||||
updateProgress(media: MWMediaMeta, progress: number, total: number): void;
|
updateProgress(media: MWMediaMeta, progress: number, total: number): void;
|
||||||
|
getFilteredWatched(): WatchedStoreItem[];
|
||||||
watched: WatchedStoreData;
|
watched: WatchedStoreData;
|
||||||
}
|
}
|
||||||
|
|
||||||
const WatchedContext = createContext<WatchedStoreDataWrapper>({
|
const WatchedContext = createContext<WatchedStoreDataWrapper>({
|
||||||
updateProgress: () => {},
|
updateProgress: () => {},
|
||||||
|
getFilteredWatched: () => [],
|
||||||
watched: {
|
watched: {
|
||||||
items: [],
|
items: [],
|
||||||
},
|
},
|
||||||
|
@ -42,13 +44,9 @@ export function WatchedContextProvider(props: { children: ReactNode }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const contextValue = {
|
const contextValue = {
|
||||||
updateProgress(
|
updateProgress(media: MWMediaMeta, progress: number, total: number): void {
|
||||||
media: MWMediaMeta,
|
|
||||||
progress: number,
|
|
||||||
total: number
|
|
||||||
): void {
|
|
||||||
setWatched((data: WatchedStoreData) => {
|
setWatched((data: WatchedStoreData) => {
|
||||||
let item = getWatchedFromPortable(data, media);
|
let item = getWatchedFromPortable(data.items, media);
|
||||||
if (!item) {
|
if (!item) {
|
||||||
item = {
|
item = {
|
||||||
mediaId: media.mediaId,
|
mediaId: media.mediaId,
|
||||||
|
@ -71,6 +69,11 @@ export function WatchedContextProvider(props: { children: ReactNode }) {
|
||||||
return data;
|
return data;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
getFilteredWatched() {
|
||||||
|
return watched.items.filter((item) => {
|
||||||
|
return getProviderMetadata(item.providerId)?.enabled;
|
||||||
|
});
|
||||||
|
},
|
||||||
watched,
|
watched,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -86,10 +89,10 @@ export function useWatchedContext() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getWatchedFromPortable(
|
export function getWatchedFromPortable(
|
||||||
store: WatchedStoreData,
|
items: WatchedStoreItem[],
|
||||||
media: MWMediaMeta
|
media: MWMediaMeta
|
||||||
): WatchedStoreItem | undefined {
|
): WatchedStoreItem | undefined {
|
||||||
return store.items.find((v) => {
|
return items.find((v) => {
|
||||||
return (
|
return (
|
||||||
v.mediaId === media.mediaId &&
|
v.mediaId === media.mediaId &&
|
||||||
v.providerId === media.providerId &&
|
v.providerId === media.providerId &&
|
||||||
|
|
|
@ -18,11 +18,13 @@ import {
|
||||||
MWMediaProvider,
|
MWMediaProvider,
|
||||||
} from "providers";
|
} from "providers";
|
||||||
import { ReactNode, useEffect, useState } from "react";
|
import { ReactNode, useEffect, useState } from "react";
|
||||||
|
import { useHistory } from "react-router-dom";
|
||||||
import {
|
import {
|
||||||
getIfBookmarkedFromPortable,
|
getIfBookmarkedFromPortable,
|
||||||
useBookmarkContext,
|
useBookmarkContext,
|
||||||
} from "state/bookmark";
|
} from "state/bookmark";
|
||||||
import { getWatchedFromPortable, useWatchedContext } from "state/watched";
|
import { getWatchedFromPortable, useWatchedContext } from "state/watched";
|
||||||
|
import { NotFoundChecks } from "./notfound/NotFoundChecks";
|
||||||
|
|
||||||
interface StyledMediaViewProps {
|
interface StyledMediaViewProps {
|
||||||
media: MWMedia;
|
media: MWMedia;
|
||||||
|
@ -31,9 +33,9 @@ interface StyledMediaViewProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
function StyledMediaView(props: StyledMediaViewProps) {
|
function StyledMediaView(props: StyledMediaViewProps) {
|
||||||
const store = useWatchedContext();
|
const watchedStore = useWatchedContext();
|
||||||
const startAtTime: number | undefined = getWatchedFromPortable(
|
const startAtTime: number | undefined = getWatchedFromPortable(
|
||||||
store.watched,
|
watchedStore.watched.items,
|
||||||
props.media
|
props.media
|
||||||
)?.progress;
|
)?.progress;
|
||||||
const { setItemBookmark, getFilteredBookmarks } = useBookmarkContext();
|
const { setItemBookmark, getFilteredBookmarks } = useBookmarkContext();
|
||||||
|
@ -48,7 +50,7 @@ function StyledMediaView(props: StyledMediaViewProps) {
|
||||||
if (el.currentTime <= 30) {
|
if (el.currentTime <= 30) {
|
||||||
return; // Don't update stored progress if less than 30s into the video
|
return; // Don't update stored progress if less than 30s into the video
|
||||||
}
|
}
|
||||||
store.updateProgress(props.media, el.currentTime, el.duration);
|
watchedStore.updateProgress(props.media, el.currentTime, el.duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -104,8 +106,8 @@ function LoadingMediaView(props: { error?: boolean }) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function MediaView() {
|
function MediaViewContent(props: { portable: MWPortableMedia }) {
|
||||||
const mediaPortable: MWPortableMedia | undefined = usePortableMedia();
|
const mediaPortable = props.portable;
|
||||||
const [streamUrl, setStreamUrl] = useState<MWMediaStream | undefined>();
|
const [streamUrl, setStreamUrl] = useState<MWMediaStream | undefined>();
|
||||||
const [media, setMedia] = useState<MWMedia | undefined>();
|
const [media, setMedia] = useState<MWMedia | undefined>();
|
||||||
const [fetchAllData, loading, error] = useLoading((mediaPortable) => {
|
const [fetchAllData, loading, error] = useLoading((mediaPortable) => {
|
||||||
|
@ -139,18 +141,31 @@ export function MediaView() {
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return <>{content}</>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function MediaView() {
|
||||||
|
const mediaPortable: MWPortableMedia | undefined = usePortableMedia();
|
||||||
|
const reactHistory = useHistory();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full">
|
<div className="flex min-h-screen w-full">
|
||||||
<Navigation>
|
<Navigation>
|
||||||
<ArrowLink
|
<ArrowLink
|
||||||
onClick={() => window.history.back()}
|
onClick={() => {
|
||||||
|
reactHistory.action !== "POP"
|
||||||
|
? reactHistory.goBack()
|
||||||
|
: reactHistory.push("/");
|
||||||
|
}}
|
||||||
direction="left"
|
direction="left"
|
||||||
linkText="Go back"
|
linkText="Go back"
|
||||||
/>
|
/>
|
||||||
</Navigation>
|
</Navigation>
|
||||||
<div className="container mx-auto mt-40 mb-16 max-w-[1100px]">
|
<NotFoundChecks portable={mediaPortable}>
|
||||||
{content}
|
<div className="container mx-auto mt-40 mb-16 max-w-[1100px]">
|
||||||
</div>
|
<MediaViewContent portable={mediaPortable as MWPortableMedia} />
|
||||||
|
</div>
|
||||||
|
</NotFoundChecks>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -172,11 +172,11 @@ export function SearchView() {
|
||||||
|
|
||||||
function ExtraItems() {
|
function ExtraItems() {
|
||||||
const { getFilteredBookmarks } = useBookmarkContext();
|
const { getFilteredBookmarks } = useBookmarkContext();
|
||||||
const { watched } = useWatchedContext();
|
const { getFilteredWatched } = useWatchedContext();
|
||||||
|
|
||||||
const bookmarks = getFilteredBookmarks();
|
const bookmarks = getFilteredBookmarks();
|
||||||
|
|
||||||
const watchedItems = watched.items.filter(
|
const watchedItems = getFilteredWatched().filter(
|
||||||
(v) => !getIfBookmarkedFromPortable(bookmarks, v)
|
(v) => !getIfBookmarkedFromPortable(bookmarks, v)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
27
src/views/notfound/NotFoundChecks.tsx
Normal file
27
src/views/notfound/NotFoundChecks.tsx
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import { getProviderMetadata, MWPortableMedia } from "providers";
|
||||||
|
import { ReactNode } from "react";
|
||||||
|
import { NotFoundMedia, NotFoundProvider } from "./NotFoundView";
|
||||||
|
|
||||||
|
export interface NotFoundChecksProps {
|
||||||
|
portable: MWPortableMedia | undefined;
|
||||||
|
children?: ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Component that only renders children if the passed-in portable is fully correct
|
||||||
|
*/
|
||||||
|
export function NotFoundChecks(props: NotFoundChecksProps) {
|
||||||
|
const providerMeta = props.portable
|
||||||
|
? getProviderMetadata(props.portable.providerId)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
if (!providerMeta || !providerMeta.exists) {
|
||||||
|
return <NotFoundMedia />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!providerMeta.enabled) {
|
||||||
|
return <NotFoundProvider />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <>{props.children}</>;
|
||||||
|
}
|
|
@ -18,7 +18,7 @@ function NotFoundWrapper(props: { children?: ReactNode }) {
|
||||||
|
|
||||||
export function NotFoundMedia() {
|
export function NotFoundMedia() {
|
||||||
return (
|
return (
|
||||||
<NotFoundWrapper>
|
<div className="flex flex-1 flex-col items-center justify-center p-5 text-center">
|
||||||
<IconPatch
|
<IconPatch
|
||||||
icon={Icons.EYE_SLASH}
|
icon={Icons.EYE_SLASH}
|
||||||
className="text-bink-600 mb-6 text-xl"
|
className="text-bink-600 mb-6 text-xl"
|
||||||
|
@ -29,13 +29,13 @@ export function NotFoundMedia() {
|
||||||
you tampered with the URL
|
you tampered with the URL
|
||||||
</p>
|
</p>
|
||||||
<ArrowLink to="/" linkText="Back to home" />
|
<ArrowLink to="/" linkText="Back to home" />
|
||||||
</NotFoundWrapper>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function NotFoundProvider() {
|
export function NotFoundProvider() {
|
||||||
return (
|
return (
|
||||||
<NotFoundWrapper>
|
<div className="flex flex-1 flex-col items-center justify-center p-5 text-center">
|
||||||
<IconPatch
|
<IconPatch
|
||||||
icon={Icons.EYE_SLASH}
|
icon={Icons.EYE_SLASH}
|
||||||
className="text-bink-600 mb-6 text-xl"
|
className="text-bink-600 mb-6 text-xl"
|
||||||
|
@ -46,7 +46,7 @@ export function NotFoundProvider() {
|
||||||
to disable it.
|
to disable it.
|
||||||
</p>
|
</p>
|
||||||
<ArrowLink to="/" linkText="Back to home" />
|
<ArrowLink to="/" linkText="Back to home" />
|
||||||
</NotFoundWrapper>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue