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.
140 lines
4.1 KiB
140 lines
4.1 KiB
import { RunOutput } from "@movie-web/providers"; |
|
import { useCallback, useEffect, useState } from "react"; |
|
import { |
|
Navigate, |
|
useLocation, |
|
useNavigate, |
|
useParams, |
|
} from "react-router-dom"; |
|
import { useAsync } from "react-use"; |
|
|
|
import { usePlayer } from "@/components/player/hooks/usePlayer"; |
|
import { usePlayerMeta } from "@/components/player/hooks/usePlayerMeta"; |
|
import { convertProviderCaption } from "@/components/player/utils/captions"; |
|
import { convertRunoutputToSource } from "@/components/player/utils/convertRunoutputToSource"; |
|
import { ScrapingItems, ScrapingSegment } from "@/hooks/useProviderScrape"; |
|
import { useQueryParam } from "@/hooks/useQueryParams"; |
|
import { MetaPart } from "@/pages/parts/player/MetaPart"; |
|
import { PlaybackErrorPart } from "@/pages/parts/player/PlaybackErrorPart"; |
|
import { PlayerPart } from "@/pages/parts/player/PlayerPart"; |
|
import { ScrapeErrorPart } from "@/pages/parts/player/ScrapeErrorPart"; |
|
import { ScrapingPart } from "@/pages/parts/player/ScrapingPart"; |
|
import { useLastNonPlayerLink } from "@/stores/history"; |
|
import { PlayerMeta, playerStatus } from "@/stores/player/slices/source"; |
|
import { needsOnboarding } from "@/utils/onboarding"; |
|
import { parseTimestamp } from "@/utils/timestamp"; |
|
|
|
export function RealPlayerView() { |
|
const navigate = useNavigate(); |
|
const params = useParams<{ |
|
media: string; |
|
episode?: string; |
|
season?: string; |
|
}>(); |
|
const [errorData, setErrorData] = useState<{ |
|
sources: Record<string, ScrapingSegment>; |
|
sourceOrder: ScrapingItems[]; |
|
} | null>(null); |
|
const [startAtParam] = useQueryParam("t"); |
|
const { |
|
status, |
|
playMedia, |
|
reset, |
|
setScrapeNotFound, |
|
shouldStartFromBeginning, |
|
setShouldStartFromBeginning, |
|
} = usePlayer(); |
|
const { setPlayerMeta, scrapeMedia } = usePlayerMeta(); |
|
const backUrl = useLastNonPlayerLink(); |
|
|
|
const paramsData = JSON.stringify({ |
|
media: params.media, |
|
season: params.season, |
|
episode: params.episode, |
|
}); |
|
useEffect(() => { |
|
reset(); |
|
}, [paramsData, reset]); |
|
|
|
const metaChange = useCallback( |
|
(meta: PlayerMeta) => { |
|
if (meta?.type === "show") |
|
navigate( |
|
`/media/${params.media}/${meta.season?.tmdbId}/${meta.episode?.tmdbId}`, |
|
); |
|
else navigate(`/media/${params.media}`); |
|
}, |
|
[navigate, params], |
|
); |
|
|
|
const playAfterScrape = useCallback( |
|
(out: RunOutput | null) => { |
|
if (!out) return; |
|
|
|
let startAt: number | undefined; |
|
if (startAtParam) startAt = parseTimestamp(startAtParam) ?? undefined; |
|
|
|
playMedia( |
|
convertRunoutputToSource(out), |
|
convertProviderCaption(out.stream.captions), |
|
out.sourceId, |
|
shouldStartFromBeginning ? 0 : startAt, |
|
); |
|
setShouldStartFromBeginning(false); |
|
}, |
|
[ |
|
playMedia, |
|
startAtParam, |
|
shouldStartFromBeginning, |
|
setShouldStartFromBeginning, |
|
], |
|
); |
|
|
|
return ( |
|
<PlayerPart backUrl={backUrl} onMetaChange={metaChange}> |
|
{status === playerStatus.IDLE ? ( |
|
<MetaPart onGetMeta={setPlayerMeta} /> |
|
) : null} |
|
{status === playerStatus.SCRAPING && scrapeMedia ? ( |
|
<ScrapingPart |
|
media={scrapeMedia} |
|
onResult={(sources, sourceOrder) => { |
|
setErrorData({ |
|
sourceOrder, |
|
sources, |
|
}); |
|
setScrapeNotFound(); |
|
}} |
|
onGetStream={playAfterScrape} |
|
/> |
|
) : null} |
|
{status === playerStatus.SCRAPE_NOT_FOUND && errorData ? ( |
|
<ScrapeErrorPart data={errorData} /> |
|
) : null} |
|
{status === playerStatus.PLAYBACK_ERROR ? <PlaybackErrorPart /> : null} |
|
</PlayerPart> |
|
); |
|
} |
|
|
|
export function PlayerView() { |
|
const loc = useLocation(); |
|
const { loading, error, value } = useAsync(() => { |
|
return needsOnboarding(); |
|
}); |
|
|
|
if (error) throw new Error("Failed to detect onboarding"); |
|
if (loading) return null; |
|
if (value) |
|
return ( |
|
<Navigate |
|
replace |
|
to={{ |
|
pathname: "/onboarding", |
|
search: `redirect=${encodeURIComponent(loc.pathname)}`, |
|
}} |
|
/> |
|
); |
|
return <RealPlayerView />; |
|
} |
|
|
|
export default PlayerView;
|
|
|