import { ReactNode, useEffect, useMemo, useRef } from "react"; import { useTranslation } from "react-i18next"; import { getCachedMetadata } from "@/backend/helpers/providerApi"; import { Loading } from "@/components/layout/Loading"; import { useEmbedScraping, useSourceScraping, } from "@/components/player/hooks/useSourceSelection"; import { Menu } from "@/components/player/internals/ContextMenu"; import { SelectableLink } from "@/components/player/internals/ContextMenu/Links"; import { useOverlayRouter } from "@/hooks/useOverlayRouter"; import { usePlayerStore } from "@/stores/player/store"; export interface SourceSelectionViewProps { id: string; onChoose?: (id: string) => void; } export interface EmbedSelectionViewProps { id: string; sourceId: string | null; } export function EmbedOption(props: { embedId: string; url: string; sourceId: string; routerId: string; }) { const { t } = useTranslation(); const unknownEmbedName = t("player.menus.sources.unknownOption"); const embedName = useMemo(() => { if (!props.embedId) return unknownEmbedName; const sourceMeta = getCachedMetadata().find((s) => s.id === props.embedId); return sourceMeta?.name ?? unknownEmbedName; }, [props.embedId, unknownEmbedName]); const { run, errored, loading } = useEmbedScraping( props.routerId, props.sourceId, props.url, props.embedId ); return ( {embedName} ); } export function EmbedSelectionView({ sourceId, id }: EmbedSelectionViewProps) { const { t } = useTranslation(); const router = useOverlayRouter(id); const { run, watching, notfound, loading, items, errored } = useSourceScraping(sourceId, id); const sourceName = useMemo(() => { if (!sourceId) return "..."; const sourceMeta = getCachedMetadata().find((s) => s.id === sourceId); return sourceMeta?.name ?? "..."; }, [sourceId]); const lastSourceId = useRef(null); useEffect(() => { if (lastSourceId.current === sourceId) return; lastSourceId.current = sourceId; if (!sourceId) return; run(); }, [run, sourceId]); let content: ReactNode = null; if (loading) content = ( ); else if (notfound) content = ( {t("player.menus.sources.noStream.text")} ); else if (items?.length === 0) content = ( {t("player.menus.sources.noEmbeds.text")} ); else if (errored) content = ( {t("player.menus.sources.failed.text")} ); else if (watching) content = null; // when it starts watching, empty the display else if (items && sourceId) content = items.map((v) => ( )); return ( <> router.navigate("/source")}> {sourceName} {content} ); } export function SourceSelectionView({ id, onChoose, }: SourceSelectionViewProps) { const { t } = useTranslation(); const router = useOverlayRouter(id); const metaType = usePlayerStore((s) => s.meta?.type); const currentSourceId = usePlayerStore((s) => s.sourceId); const sources = useMemo(() => { if (!metaType) return []; return getCachedMetadata() .filter((v) => v.type === "source") .filter((v) => v.mediaTypes?.includes(metaType)); }, [metaType]); return ( <> router.navigate("/")}> {t("player.menus.sources.title")} {sources.map((v) => ( { onChoose?.(v.id); router.navigate("/source/embeds"); }} selected={v.id === currentSourceId} > {v.name} ))} ); }