import classNames from "classnames"; import { ReactNode, useCallback, useEffect, useMemo, useRef } from "react"; import { useAsyncFn } from "react-use"; import { Icon, Icons } from "@/components/Icon"; import { usePlayerMeta } from "@/components/player/hooks/usePlayerMeta"; import { Context } from "@/components/player/internals/ContextUtils"; import { convertRunoutputToSource } from "@/components/player/utils/convertRunoutputToSource"; import { useOverlayRouter } from "@/hooks/useOverlayRouter"; import { metaToScrapeMedia } from "@/stores/player/slices/source"; import { usePlayerStore } from "@/stores/player/store"; import { providers } from "@/utils/providers"; export interface SourceSelectionViewProps { id: string; onChoose?: (id: string) => void; } export interface EmbedSelectionViewProps { id: string; sourceId: string | null; } export function SourceOption(props: { children: React.ReactNode; selected?: boolean; onClick?: () => void; }) { return (
{props.children} {props.selected ? ( ) : null}
); } export function EmbedOption(props: { embedId: string; url: string; routerId: string; }) { const router = useOverlayRouter(props.routerId); const meta = usePlayerStore((s) => s.meta); const setSource = usePlayerStore((s) => s.setSource); const progress = usePlayerStore((s) => s.progress.time); const embedName = useMemo(() => { if (!props.embedId) return "..."; const sourceMeta = providers.getMetadata(props.embedId); return sourceMeta?.name ?? "..."; }, [props.embedId]); const [request, run] = useAsyncFn(async () => { const result = await providers.runEmbedScraper({ id: props.embedId, url: props.url, }); setSource(convertRunoutputToSource({ stream: result.stream }), progress); router.close(); }, [props.embedId, meta, router]); let content: ReactNode = null; if (request.loading) content = loading...; else if (request.error) content = Failed to scrape; return ( {embedName} {content} ); } export function EmbedSelectionView({ sourceId, id }: EmbedSelectionViewProps) { const router = useOverlayRouter(id); const meta = usePlayerStore((s) => s.meta); const setSource = usePlayerStore((s) => s.setSource); const progress = usePlayerStore((s) => s.progress.time); const sourceName = useMemo(() => { if (!sourceId) return "..."; const sourceMeta = providers.getMetadata(sourceId); return sourceMeta?.name ?? "..."; }, [sourceId]); const [request, run] = useAsyncFn(async () => { if (!sourceId || !meta) return null; const scrapeMedia = metaToScrapeMedia(meta); const result = await providers.runSourceScraper({ id: sourceId, media: scrapeMedia, }); if (result.stream) { setSource(convertRunoutputToSource({ stream: result.stream }), progress); router.close(); return null; } return result.embeds; }, [sourceId, meta, router]); const lastSourceId = useRef(null); useEffect(() => { if (lastSourceId.current === sourceId) return; lastSourceId.current = sourceId; if (!sourceId) return; run(); }, [run, sourceId]); let content: ReactNode = null; if (request.loading) content =

loading...

; else if (request.error) content =

Failed to scrape

; else if (request.value && request.value.length === 0) content =

No embeds found

; else if (request.value) content = request.value.map((v) => ( )); return ( <> router.navigate("/source")}> {sourceName} {content} ); } export function SourceSelectionView({ id, onChoose, }: SourceSelectionViewProps) { const router = useOverlayRouter(id); const metaType = usePlayerStore((s) => s.meta?.type); const sources = useMemo(() => { if (!metaType) return []; return providers .listSources() .filter((v) => v.mediaTypes?.includes(metaType)); }, [metaType]); return ( <> router.navigate("/")}> Sources {sources.map((v) => ( { onChoose?.(v.id); router.navigate("/source/embeds"); }} > {v.name} ))} ); }