A small web app for watching movies and shows easily
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

148 lines
4.6 KiB

import { useParams } from "react-router-dom";
import { useEffect, useState } from "react";
import { DecoratedVideoPlayer } from "@/components/video/DecoratedVideoPlayer";
import { MWStream } from "@/backend/helpers/streams";
import { SelectedMediaData, useScrape } from "@/hooks/useScrape";
import { VideoPlayerHeader } from "@/components/video/parts/VideoPlayerHeader";
import { DetailedMeta, getMetaFromId } from "@/backend/metadata/getmeta";
import { JWMediaToMediaType } from "@/backend/metadata/justwatch";
import { SourceControl } from "@/components/video/controls/SourceControl";
import { Loading } from "@/components/layout/Loading";
import { useLoading } from "@/hooks/useLoading";
import { MWMediaType } from "@/backend/metadata/types";
import { useGoBack } from "@/hooks/useGoBack";
import { IconPatch } from "@/components/buttons/IconPatch";
import { Icons } from "@/components/Icon";
import { MediaFetchErrorView } from "./MediaErrorView";
import { MediaScrapeLog } from "./MediaScrapeLog";
import { NotFoundMedia, NotFoundWrapper } from "../notfound/NotFoundView";
function MediaViewLoading(props: { onGoBack(): void }) {
return (
<div className="relative flex h-screen items-center justify-center">
<div className="absolute inset-x-0 top-0 p-6">
<VideoPlayerHeader onClick={props.onGoBack} />
</div>
<div className="flex flex-col items-center">
<Loading className="mb-4" />
<p className="mb-8 text-denim-700">Finding the best video for you</p>
</div>
</div>
);
}
interface MediaViewScrapingProps {
onStream(stream: MWStream): void;
onGoBack(): void;
meta: DetailedMeta;
selected: SelectedMediaData;
}
function MediaViewScraping(props: MediaViewScrapingProps) {
const { eventLog, stream, pending } = useScrape(props.meta, props.selected);
useEffect(() => {
if (stream) {
props.onStream(stream);
}
}, [stream, props]);
return (
<div className="relative flex h-screen items-center justify-center">
<div className="absolute inset-x-0 top-0 py-6 px-8">
<VideoPlayerHeader
onClick={props.onGoBack}
title={props.meta.meta.title}
/>
</div>
<div className="flex flex-col items-center transition-opacity duration-200">
{pending ? (
<>
<Loading />
<p className="mb-8 text-denim-700">
Finding the best video for you
</p>
</>
) : (
<>
<IconPatch icon={Icons.EYE_SLASH} className="mb-8 text-bink-700" />
<p className="mb-8 text-denim-700">
Whoops, could&apos;t find any videos for you
</p>
</>
)}
<div
className={`flex flex-col items-center transition-opacity duration-200 ${
pending ? "opacity-100" : "opacity-0"
}`}
>
<MediaScrapeLog events={eventLog} />
</div>
</div>
</div>
);
}
export function MediaView() {
const params = useParams<{ media: string }>();
const goBack = useGoBack();
const [meta, setMeta] = useState<DetailedMeta | null>(null);
const [selected, setSelected] = useState<SelectedMediaData | null>(null);
const [exec, loading, error] = useLoading(async (mediaParams: string) => {
let type: MWMediaType;
let id = "";
try {
const [t, i] = mediaParams.split("-", 2);
type = JWMediaToMediaType(t);
id = i;
} catch (err) {
return null;
}
return getMetaFromId(type, id);
});
const [stream, setStream] = useState<MWStream | null>(null);
useEffect(() => {
exec(params.media).then((v) => {
setMeta(v ?? null);
if (v)
setSelected({
type: v.meta.type,
episode: 0 as any,
season: 0 as any,
});
else setSelected(null);
});
}, [exec, params.media]);
// TODO watched store
if (loading) return <MediaViewLoading onGoBack={goBack} />;
if (error) return <MediaFetchErrorView />;
if (!meta || !selected)
return (
<NotFoundWrapper video>
<NotFoundMedia />
</NotFoundWrapper>
);
// scraping view will start scraping and return with onStream
if (!stream)
return (
<MediaViewScraping
meta={meta}
selected={selected}
onGoBack={goBack}
onStream={setStream}
/>
);
// show stream once we have a stream
return (
<div className="h-screen w-screen">
<DecoratedVideoPlayer title={meta.meta.title} onGoBack={goBack} autoPlay>
<SourceControl source={stream.streamUrl} type={stream.type} />
</DecoratedVideoPlayer>
</div>
);
}