import { useParams } from "react-router-dom"; import { useCallback, useContext, useMemo, useState } from "react"; import { Icon, Icons } from "@/components/Icon"; import { getProviders } from "@/backend/helpers/register"; import { useLoading } from "@/hooks/useLoading"; import { DetailedMeta } from "@/backend/metadata/getmeta"; import { MWMediaType } from "@/backend/metadata/types"; import { MWProviderScrapeResult } from "@/backend/helpers/provider"; import { runProvider } from "@/backend/helpers/run"; import { IconPatch } from "@/components/buttons/IconPatch"; import { Loading } from "@/components/layout/Loading"; import { useVideoPlayerState, VideoPlayerDispatchContext, } from "../VideoContext"; import { VideoPlayerIconButton } from "../parts/VideoPlayerIconButton"; import { VideoPopout } from "../parts/VideoPopout"; interface Props { className?: string; media?: DetailedMeta; } function PopoutSourceSelect(props: { media: DetailedMeta }) { const dispatch = useContext(VideoPlayerDispatchContext); const providers = useMemo( () => getProviders().filter((v) => v.type.includes(props.media.meta.type)), [props] ); const { episode, season } = useParams<{ episode: string; season: string }>(); const [selected, setSelected] = useState(null); const selectedProvider = useMemo( () => providers.find((v) => v.id === selected), [selected, providers] ); const [scrapeData, setScrapeData] = useState( null ); const [scrapeProvider, loadingProvider, errorProvider] = useLoading( async (providerId: string) => { const theProvider = providers.find((v) => v.id === providerId); if (!theProvider) throw new Error("Invalid provider"); return runProvider(theProvider, { media: props.media, progress: () => {}, type: props.media.meta.type, episode: (props.media.meta.type === MWMediaType.SERIES ? episode : undefined) as any, season: (props.media.meta.type === MWMediaType.SERIES ? season : undefined) as any, }); } ); // TODO add embed support // TODO restore startAt when changing source // TODO auto choose when only one option // TODO close when selecting item // TODO show currently selected provider // TODO clear error state when switching // const [scrapeEmbed, embedLoading, embedError] = useLoading( // async (embed: MWEmbed) => { // if (!embed.type) throw new Error("Invalid embed type"); // const theScraper = getEmbedScraperByType(embed.type); // if (!theScraper) throw new Error("Invalid scraper"); // return runEmbedScraper(theScraper, { // progress: () => {}, // url: embed.url, // }); // } // ); const selectProvider = useCallback( (id: string) => { scrapeProvider(id).then((v) => { if (!v) throw new Error("No scrape result"); setScrapeData(v); }); setSelected(id); }, [setSelected, scrapeProvider] ); if (!selectedProvider) return ( <>
Select video source
{providers.map((e) => (
selectProvider(e.id)} key={e.id} > {e.displayName}
))}
); return ( <>
{selectedProvider.displayName}
{loadingProvider ? (
) : errorProvider ? (

Something went wrong loading streams.

) : scrapeData ? (
{scrapeData.stream ? (
scrapeData.stream && dispatch({ url: scrapeData.stream.streamUrl, quality: scrapeData.stream.quality, sourceType: scrapeData.stream.type, type: "SET_SOURCE", }) } > {selectedProvider.displayName}
) : null}
) : null}
); } export function SourceSelectionControl(props: Props) { const { videoState } = useVideoPlayerState(); if (!props.media) return null; return (
videoState.openPopout("source")} />
); }