2022-02-28 00:08:20 +01:00
|
|
|
import { IconPatch } from "components/buttons/IconPatch";
|
|
|
|
import { Icons } from "components/Icon";
|
|
|
|
import { Navigation } from "components/layout/Navigation";
|
|
|
|
import { Paper } from "components/layout/Paper";
|
|
|
|
import { SkeletonVideoPlayer, VideoPlayer } from "components/media/VideoPlayer";
|
|
|
|
import { ArrowLink } from "components/text/ArrowLink";
|
|
|
|
import { DotList } from "components/text/DotList";
|
|
|
|
import { Title } from "components/text/Title";
|
|
|
|
import { useLoading } from "hooks/useLoading";
|
|
|
|
import { usePortableMedia } from "hooks/usePortableMedia";
|
|
|
|
import {
|
|
|
|
MWPortableMedia,
|
|
|
|
getStream,
|
|
|
|
MWMediaStream,
|
|
|
|
MWMedia,
|
|
|
|
convertPortableToMedia,
|
|
|
|
getProviderFromId,
|
|
|
|
MWMediaProvider,
|
|
|
|
} from "providers";
|
|
|
|
import { ReactNode, useEffect, useState } from "react";
|
2022-03-06 12:56:22 +01:00
|
|
|
import { useHistory } from "react-router-dom";
|
2022-02-28 00:08:20 +01:00
|
|
|
import {
|
|
|
|
getIfBookmarkedFromPortable,
|
|
|
|
useBookmarkContext,
|
|
|
|
} from "state/bookmark";
|
|
|
|
import { getWatchedFromPortable, useWatchedContext } from "state/watched";
|
2022-03-06 12:56:22 +01:00
|
|
|
import { NotFoundChecks } from "./notfound/NotFoundChecks";
|
2022-02-28 00:08:20 +01:00
|
|
|
|
|
|
|
interface StyledMediaViewProps {
|
|
|
|
media: MWMedia;
|
|
|
|
stream: MWMediaStream;
|
|
|
|
provider: MWMediaProvider;
|
|
|
|
}
|
|
|
|
|
|
|
|
function StyledMediaView(props: StyledMediaViewProps) {
|
2022-03-06 12:56:22 +01:00
|
|
|
const watchedStore = useWatchedContext();
|
2022-02-28 00:08:20 +01:00
|
|
|
const startAtTime: number | undefined = getWatchedFromPortable(
|
2022-03-06 12:56:22 +01:00
|
|
|
watchedStore.watched.items,
|
2022-02-28 00:08:20 +01:00
|
|
|
props.media
|
|
|
|
)?.progress;
|
2022-03-06 12:11:16 +01:00
|
|
|
const { setItemBookmark, getFilteredBookmarks } = useBookmarkContext();
|
|
|
|
const isBookmarked = getIfBookmarkedFromPortable(
|
|
|
|
getFilteredBookmarks(),
|
|
|
|
props.media
|
|
|
|
);
|
2022-02-28 00:08:20 +01:00
|
|
|
|
|
|
|
function updateProgress(e: Event) {
|
|
|
|
if (!props.media) return;
|
|
|
|
const el: HTMLVideoElement = e.currentTarget as HTMLVideoElement;
|
|
|
|
if (el.currentTime <= 30) {
|
|
|
|
return; // Don't update stored progress if less than 30s into the video
|
|
|
|
}
|
2022-03-06 12:56:22 +01:00
|
|
|
watchedStore.updateProgress(props.media, el.currentTime, el.duration);
|
2022-02-28 00:08:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
|
|
<>
|
|
|
|
<VideoPlayer
|
|
|
|
source={props.stream}
|
|
|
|
onProgress={updateProgress}
|
|
|
|
startAt={startAtTime}
|
|
|
|
/>
|
|
|
|
<Paper className="mt-5">
|
|
|
|
<div className="flex">
|
|
|
|
<div className="flex-1">
|
|
|
|
<Title>{props.media.title}</Title>
|
|
|
|
<DotList
|
|
|
|
className="mt-3 text-sm"
|
|
|
|
content={[
|
|
|
|
props.provider.displayName,
|
|
|
|
props.media.mediaType,
|
|
|
|
props.media.year,
|
|
|
|
]}
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
<div>
|
|
|
|
<IconPatch
|
|
|
|
icon={Icons.BOOKMARK}
|
|
|
|
active={isBookmarked}
|
|
|
|
onClick={() => setItemBookmark(props.media, !isBookmarked)}
|
|
|
|
clickable
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</Paper>
|
|
|
|
</>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
function LoadingMediaView(props: { error?: boolean }) {
|
|
|
|
return (
|
|
|
|
<>
|
|
|
|
<SkeletonVideoPlayer error={props.error} />
|
|
|
|
<Paper className="mt-5">
|
|
|
|
<div className="flex">
|
|
|
|
<div className="flex-1">
|
|
|
|
<div className="bg-denim-500 mb-2 h-4 w-48 rounded-full" />
|
|
|
|
<div>
|
|
|
|
<span className="bg-denim-400 mr-4 inline-block h-2 w-12 rounded-full" />
|
|
|
|
<span className="bg-denim-400 mr-4 inline-block h-2 w-12 rounded-full" />
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</Paper>
|
|
|
|
</>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-03-06 12:56:22 +01:00
|
|
|
function MediaViewContent(props: { portable: MWPortableMedia }) {
|
|
|
|
const mediaPortable = props.portable;
|
2022-02-28 00:08:20 +01:00
|
|
|
const [streamUrl, setStreamUrl] = useState<MWMediaStream | undefined>();
|
|
|
|
const [media, setMedia] = useState<MWMedia | undefined>();
|
|
|
|
const [fetchAllData, loading, error] = useLoading((mediaPortable) => {
|
|
|
|
const streamPromise = getStream(mediaPortable);
|
|
|
|
const mediaPromise = convertPortableToMedia(mediaPortable);
|
|
|
|
return Promise.all([streamPromise, mediaPromise]);
|
|
|
|
});
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
(async () => {
|
|
|
|
if (mediaPortable) {
|
|
|
|
const resultData = await fetchAllData(mediaPortable);
|
|
|
|
if (!resultData) return;
|
|
|
|
setStreamUrl(resultData[0]);
|
|
|
|
setMedia(resultData[1]);
|
|
|
|
}
|
|
|
|
})();
|
2022-03-06 13:42:27 +01:00
|
|
|
}, [mediaPortable, setStreamUrl, fetchAllData]);
|
2022-02-28 00:08:20 +01:00
|
|
|
|
|
|
|
let content: ReactNode;
|
|
|
|
if (loading) content = <LoadingMediaView />;
|
|
|
|
else if (error) content = <LoadingMediaView error />;
|
|
|
|
else if (mediaPortable && media && streamUrl)
|
|
|
|
content = (
|
|
|
|
<StyledMediaView
|
|
|
|
provider={
|
|
|
|
getProviderFromId(mediaPortable.providerId) as MWMediaProvider
|
|
|
|
}
|
|
|
|
media={media}
|
|
|
|
stream={streamUrl}
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
|
2022-03-06 12:56:22 +01:00
|
|
|
return <>{content}</>;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function MediaView() {
|
|
|
|
const mediaPortable: MWPortableMedia | undefined = usePortableMedia();
|
|
|
|
const reactHistory = useHistory();
|
|
|
|
|
2022-02-28 00:08:20 +01:00
|
|
|
return (
|
2022-03-06 12:56:22 +01:00
|
|
|
<div className="flex min-h-screen w-full">
|
2022-02-28 00:08:20 +01:00
|
|
|
<Navigation>
|
|
|
|
<ArrowLink
|
2022-03-06 12:56:22 +01:00
|
|
|
onClick={() => {
|
|
|
|
reactHistory.action !== "POP"
|
|
|
|
? reactHistory.goBack()
|
|
|
|
: reactHistory.push("/");
|
|
|
|
}}
|
2022-02-28 00:08:20 +01:00
|
|
|
direction="left"
|
|
|
|
linkText="Go back"
|
|
|
|
/>
|
|
|
|
</Navigation>
|
2022-03-06 12:56:22 +01:00
|
|
|
<NotFoundChecks portable={mediaPortable}>
|
|
|
|
<div className="container mx-auto mt-40 mb-16 max-w-[1100px]">
|
|
|
|
<MediaViewContent portable={mediaPortable as MWPortableMedia} />
|
|
|
|
</div>
|
|
|
|
</NotFoundChecks>
|
2022-02-28 00:08:20 +01:00
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|