pstream is dead; long live pstream taciturnaxolotl.github.io/pstream-ng/
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

remove Provider API logic and OLD turnstile code

Pas ecd5daea eb7659ca

+27 -384
-2
Dockerfile
··· 22 22 ARG ONBOARDING_PROXY_INSTALL_LINK 23 23 ARG DISALLOWED_IDS 24 24 ARG CDN_REPLACEMENTS 25 - ARG TURNSTILE_KEY 26 25 ARG ALLOW_AUTOPLAY="false" 27 26 28 27 ENV VITE_PWA_ENABLED=${PWA_ENABLED} ··· 39 38 ENV VITE_ONBOARDING_PROXY_INSTALL_LINK=${ONBOARDING_PROXY_INSTALL_LINK} 40 39 ENV VITE_DISALLOWED_IDS=${DISALLOWED_IDS} 41 40 ENV VITE_CDN_REPLACEMENTS=${CDN_REPLACEMENTS} 42 - ENV VITE_TURNSTILE_KEY=${TURNSTILE_KEY} 43 41 ENV VITE_ALLOW_AUTOPLAY=${ALLOW_AUTOPLAY} 44 42 45 43 COPY . ./
-1
docker-compose.yaml
··· 19 19 # ONBOARDING_PROXY_INSTALL_LINK: "" 20 20 # DISALLOWED_IDS: "" 21 21 # CDN_REPLACEMENTS: "" 22 - # TURNSTILE_KEY: "" 23 22 ports: 24 23 - "80:80" 25 24 restart: unless-stopped
-6
src/assets/locales/en.json
··· 947 947 "remaining": "{{timeLeft}} left • Finish at {{timeFinished, datetime}}", 948 948 "shortRegular": "{{timeWatched}}", 949 949 "shortRemaining": "-{{timeLeft}}" 950 - }, 951 - "turnstile": { 952 - "description": "Please prove your humanity by completing the quick challenge, this is to keep P-Stream safe.", 953 - "error": "Failed to verify your humanity - stream failed to load. Clear your cache and try again, or switch to a different source (tap the gear).", 954 - "title": "Are You a Robot 🤖?", 955 - "verifyingHumanity": "Verifying your humanity... (^▽^)👍" 956 950 } 957 951 }, 958 952 "support": {
+2 -142
src/backend/helpers/providerApi.ts
··· 1 - import { MetaOutput, NotFoundError, ScrapeMedia } from "@p-stream/providers"; 1 + import { MetaOutput } from "@p-stream/providers"; 2 2 import { jwtDecode } from "jwt-decode"; 3 - 4 - import { mwFetch } from "@/backend/helpers/fetch"; 5 - import { getTurnstileToken, isTurnstileInitialized } from "@/stores/turnstile"; 6 3 7 4 let metaDataCache: MetaOutput[] | null = null; 8 5 let token: null | string = null; ··· 31 28 return null; 32 29 } 33 30 34 - export async function fetchMetadata(base: string) { 35 - if (metaDataCache) return; 36 - const data = await mwFetch<MetaOutput[][]>(`${base}/metadata`); 37 - metaDataCache = data.flat(); 38 - } 39 - 40 - function scrapeMediaToQueryMedia(media: ScrapeMedia) { 41 - let extra: Record<string, string> = {}; 42 - if (media.type === "show") { 43 - extra = { 44 - episodeNumber: media.episode.number.toString(), 45 - episodeTmdbId: media.episode.tmdbId, 46 - seasonNumber: media.season.number.toString(), 47 - seasonTmdbId: media.season.tmdbId, 48 - }; 49 - } 50 - 51 - return { 52 - type: media.type, 53 - releaseYear: media.releaseYear.toString(), 54 - imdbId: media.imdbId, 55 - tmdbId: media.tmdbId, 56 - title: media.title, 57 - ...extra, 58 - }; 59 - } 60 - 61 - function addQueryDataToUrl(url: URL, data: Record<string, string | undefined>) { 62 - Object.entries(data).forEach((entry) => { 63 - if (entry[1]) url.searchParams.set(entry[0], entry[1]); 64 - }); 65 - } 66 - 67 - export function makeProviderUrl(base: string) { 68 - const makeUrl = (p: string) => new URL(`${base}${p}`); 69 - return { 70 - scrapeSource(sourceId: string, media: ScrapeMedia) { 71 - const url = makeUrl("/scrape/source"); 72 - addQueryDataToUrl(url, scrapeMediaToQueryMedia(media)); 73 - addQueryDataToUrl(url, { id: sourceId }); 74 - return url.toString(); 75 - }, 76 - scrapeAll( 77 - media: ScrapeMedia, 78 - sourceOrder?: string[], 79 - embedOrder?: string[], 80 - ) { 81 - const url = makeUrl("/scrape"); 82 - addQueryDataToUrl(url, scrapeMediaToQueryMedia(media)); 83 - if (sourceOrder && sourceOrder.length > 0) { 84 - url.searchParams.set("sourceOrder", sourceOrder.join(",")); 85 - } 86 - if (embedOrder && embedOrder.length > 0) { 87 - url.searchParams.set("embedOrder", embedOrder.join(",")); 88 - } 89 - return url.toString(); 90 - }, 91 - scrapeEmbed(embedId: string, embedUrl: string) { 92 - const url = makeUrl("/scrape/embed"); 93 - addQueryDataToUrl(url, { id: embedId, url: embedUrl }); 94 - return url.toString(); 95 - }, 96 - }; 97 - } 98 - 99 31 export async function getApiToken(): Promise<string | null> { 100 - let apiToken = getTokenIfValid(); 101 - if (!apiToken && isTurnstileInitialized()) { 102 - apiToken = `turnstile|${await getTurnstileToken()}`; 103 - } 104 - return apiToken; 105 - } 106 - 107 - function parseEventInput(inp: string): any { 108 - if (inp.length === 0) return {}; 109 - return JSON.parse(inp); 110 - } 111 - 112 - export async function connectServerSideEvents<T>( 113 - url: string, 114 - endEvents: string[], 115 - ) { 116 - const apiToken = await getApiToken(); 117 - 118 - // insert token, if its set 119 - const parsedUrl = new URL(url); 120 - if (apiToken) parsedUrl.searchParams.set("token", apiToken); 121 - const eventSource = new EventSource(parsedUrl.toString()); 122 - 123 - let promReject: (reason?: any) => void; 124 - let promResolve: (value: T) => void; 125 - const promise = new Promise<T>((resolve, reject) => { 126 - promResolve = resolve; 127 - promReject = reject; 128 - }); 129 - 130 - endEvents.forEach((evt) => { 131 - eventSource.addEventListener(evt, (e) => { 132 - eventSource.close(); 133 - promResolve(parseEventInput(e.data)); 134 - }); 135 - }); 136 - 137 - eventSource.addEventListener("token", (e) => { 138 - setApiToken(parseEventInput(e.data)); 139 - }); 140 - 141 - eventSource.addEventListener("error", (err: MessageEvent<any>) => { 142 - eventSource.close(); 143 - if (err.data) { 144 - const data = JSON.parse(err.data); 145 - let errObj = new Error("scrape error"); 146 - if (data.name === NotFoundError.name) 147 - errObj = new NotFoundError("Notfound from server"); 148 - Object.assign(errObj, data); 149 - promReject(errObj); 150 - return; 151 - } 152 - 153 - console.error("Failed to connect to SSE", err); 154 - promReject(err); 155 - }); 156 - 157 - eventSource.addEventListener("message", (ev) => { 158 - if (!ev) { 159 - eventSource.close(); 160 - return; 161 - } 162 - setTimeout(() => { 163 - promReject(new Error("SSE closed improperly")); 164 - }, 1000); 165 - }); 166 - 167 - return { 168 - promise: () => promise, 169 - on<Data>(event: string, cb: (data: Data) => void) { 170 - eventSource.addEventListener(event, (e) => cb(JSON.parse(e.data))); 171 - }, 172 - }; 32 + return getTokenIfValid(); 173 33 }
+1 -7
src/backend/providers/fetchers.ts
··· 6 6 7 7 import { sendExtensionRequest } from "@/backend/extension/messaging"; 8 8 import { getApiToken, setApiToken } from "@/backend/helpers/providerApi"; 9 - import { 10 - getM3U8ProxyUrls, 11 - getProviderApiUrls, 12 - getProxyUrls, 13 - } from "@/utils/proxyUrls"; 9 + import { getM3U8ProxyUrls, getProxyUrls } from "@/utils/proxyUrls"; 14 10 15 11 import { convertBodyToObject, getBodyTypeFromBody } from "../extension/request"; 16 12 ··· 28 24 } 29 25 30 26 export const getLoadbalancedProxyUrl = makeLoadbalancedList(getProxyUrls); 31 - export const getLoadbalancedProviderApiUrl = 32 - makeLoadbalancedList(getProviderApiUrls); 33 27 function getEnabledM3U8ProxyUrls() { 34 28 const allM3U8ProxyUrls = getM3U8ProxyUrls(); 35 29 const enabledProxies = localStorage.getItem("m3u8-proxy-enabled");
+1 -42
src/components/overlays/OverlayDisplay.tsx
··· 2 2 import FocusTrap from "focus-trap-react"; 3 3 import { ReactNode, useCallback, useEffect, useRef, useState } from "react"; 4 4 import { createPortal } from "react-dom"; 5 - import { useTranslation } from "react-i18next"; 6 5 7 6 import { Transition } from "@/components/utils/Transition"; 8 7 import { 9 8 useInternalOverlayRouter, 10 9 useRouterAnchorUpdate, 11 10 } from "@/hooks/useOverlayRouter"; 12 - import { TurnstileProvider, getTurnstile } from "@/stores/turnstile"; 13 11 14 12 export interface OverlayProps { 15 13 id: string; ··· 17 15 darken?: boolean; 18 16 } 19 17 20 - function TurnstileInteractive() { 21 - const { t } = useTranslation(); 22 - const [show, setShow] = useState(false); 23 - 24 - useEffect(() => { 25 - getTurnstile(); 26 - }, []); 27 - 28 - // this may not rerender with different dom structure, must be exactly the same always 29 - return ( 30 - <div 31 - className={classNames( 32 - "absolute w-full max-w-[43em] max-h-full p-5 md:p-10 rounded-lg bg-dropdown-altBackground select-none z-50 top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 transform overflow-auto", 33 - show ? "" : "hidden", 34 - )} 35 - > 36 - <div className="w-full h-full grid lg:grid-cols-[1fr,auto] gap-6 md:gap-7 items-center"> 37 - <div className="text-left"> 38 - <h2 className="text-type-emphasis font-bold text-lg md:text-xl mb-4 md:mb-6"> 39 - {t("player.turnstile.title")} 40 - </h2> 41 - <p className="text-type-emphasis"> 42 - {t("player.turnstile.description")} 43 - </p> 44 - </div> 45 - <TurnstileProvider 46 - isInPopout 47 - onUpdateShow={(shouldShow) => setShow(shouldShow)} 48 - /> 49 - </div> 50 - </div> 51 - ); 52 - } 53 - 54 18 export function OverlayDisplay(props: { children: ReactNode }) { 55 19 const router = useInternalOverlayRouter("hello world :)"); 56 20 const refRouter = useRef(router); ··· 63 27 r.close(); 64 28 }; 65 29 }, []); 66 - return ( 67 - <div className="popout-location"> 68 - <TurnstileInteractive /> 69 - {props.children} 70 - </div> 71 - ); 30 + return <div className="popout-location">{props.children}</div>; 72 31 } 73 32 74 33 export function OverlayPortal(props: {
+12 -49
src/components/player/hooks/useSourceSelection.ts
··· 8 8 import { isExtensionActiveCached } from "@/backend/extension/messaging"; 9 9 import { prepareStream } from "@/backend/extension/streams"; 10 10 import { 11 - connectServerSideEvents, 12 - makeProviderUrl, 13 - } from "@/backend/helpers/providerApi"; 14 - import { 15 11 scrapeSourceOutputToProviderMetric, 16 12 useReportProviders, 17 13 } from "@/backend/helpers/report"; 18 - import { getLoadbalancedProviderApiUrl } from "@/backend/providers/fetchers"; 19 14 import { getProviders } from "@/backend/providers/providers"; 20 15 import { convertProviderCaption } from "@/components/player/utils/captions"; 21 16 import { convertRunoutputToSource } from "@/components/player/utils/convertRunoutputToSource"; ··· 60 55 ); 61 56 62 57 const [request, run] = useAsyncFn(async () => { 63 - const providerApiUrl = getLoadbalancedProviderApiUrl(); 64 58 let result: EmbedOutput | undefined; 65 59 if (!meta) return; 66 60 try { 67 - if (providerApiUrl && !isExtensionActiveCached()) { 68 - const baseUrlMaker = makeProviderUrl(providerApiUrl); 69 - const conn = await connectServerSideEvents<EmbedOutput>( 70 - baseUrlMaker.scrapeEmbed(embedId, url), 71 - ["completed", "noOutput"], 72 - ); 73 - result = await conn.promise(); 74 - } else { 75 - result = await getProviders().runEmbedScraper({ 76 - id: embedId, 77 - url, 78 - }); 79 - } 61 + result = await getProviders().runEmbedScraper({ 62 + id: embedId, 63 + url, 64 + }); 80 65 } catch (err) { 81 66 console.error(`Failed to scrape ${embedId}`, err); 82 67 const notFound = err instanceof NotFoundError; ··· 148 133 if (!sourceId || !meta) return null; 149 134 setEmbedId(null); 150 135 const scrapeMedia = metaToScrapeMedia(meta); 151 - const providerApiUrl = getLoadbalancedProviderApiUrl(); 152 136 153 137 let result: SourcererOutput | undefined; 154 138 try { 155 - if (providerApiUrl && !isExtensionActiveCached()) { 156 - const baseUrlMaker = makeProviderUrl(providerApiUrl); 157 - const conn = await connectServerSideEvents<SourcererOutput>( 158 - baseUrlMaker.scrapeSource(sourceId, scrapeMedia), 159 - ["completed", "noOutput"], 160 - ); 161 - result = await conn.promise(); 162 - } else { 163 - result = await getProviders().runSourceScraper({ 164 - id: sourceId, 165 - media: scrapeMedia, 166 - }); 167 - } 139 + result = await getProviders().runSourceScraper({ 140 + id: sourceId, 141 + media: scrapeMedia, 142 + }); 168 143 } catch (err) { 169 144 console.error(`Failed to scrape ${sourceId}`, err); 170 145 const notFound = err instanceof NotFoundError; ··· 199 174 let embedResult: EmbedOutput | undefined; 200 175 if (!meta) return; 201 176 try { 202 - if (providerApiUrl && !isExtensionActiveCached()) { 203 - const baseUrlMaker = makeProviderUrl(providerApiUrl); 204 - const conn = await connectServerSideEvents<EmbedOutput>( 205 - baseUrlMaker.scrapeEmbed( 206 - result.embeds[0].embedId, 207 - result.embeds[0].url, 208 - ), 209 - ["completed", "noOutput"], 210 - ); 211 - embedResult = await conn.promise(); 212 - } else { 213 - embedResult = await getProviders().runEmbedScraper({ 214 - id: result.embeds[0].embedId, 215 - url: result.embeds[0].url, 216 - }); 217 - } 177 + embedResult = await getProviders().runEmbedScraper({ 178 + id: result.embeds[0].embedId, 179 + url: result.embeds[0].url, 180 + }); 218 181 } catch (err) { 219 182 console.error(`Failed to scrape ${result.embeds[0].embedId}`, err); 220 183 const notFound = err instanceof NotFoundError;
+1 -29
src/hooks/useProviderScrape.tsx
··· 3 3 4 4 import { isExtensionActiveCached } from "@/backend/extension/messaging"; 5 5 import { prepareStream } from "@/backend/extension/streams"; 6 - import { 7 - connectServerSideEvents, 8 - getCachedMetadata, 9 - makeProviderUrl, 10 - } from "@/backend/helpers/providerApi"; 11 - import { getLoadbalancedProviderApiUrl } from "@/backend/providers/fetchers"; 6 + import { getCachedMetadata } from "@/backend/helpers/providerApi"; 12 7 import { getProviders } from "@/backend/providers/providers"; 13 8 import { getMediaKey } from "@/stores/player/slices/source"; 14 9 import { usePlayerStore } from "@/stores/player/store"; ··· 244 239 (id) => !allFailedEmbedIds.includes(id), 245 240 ) 246 241 : undefined; 247 - 248 - const providerApiUrl = getLoadbalancedProviderApiUrl(); 249 - if (providerApiUrl && !isExtensionActiveCached()) { 250 - startScrape(); 251 - const baseUrlMaker = makeProviderUrl(providerApiUrl); 252 - const conn = await connectServerSideEvents<RunOutput | "">( 253 - baseUrlMaker.scrapeAll( 254 - media, 255 - filteredSourceOrder, 256 - filteredEmbedOrder, 257 - ), 258 - ["completed", "noOutput"], 259 - ); 260 - conn.on("init", initEvent); 261 - conn.on("start", startEvent); 262 - conn.on("update", updateEvent); 263 - conn.on("discoverEmbeds", discoverEmbedsEvent); 264 - const sseOutput = await conn.promise(); 265 - if (sseOutput && isExtensionActiveCached()) 266 - await prepareStream(sseOutput.stream); 267 - 268 - return getResult(sseOutput === "" ? null : sseOutput); 269 - } 270 242 271 243 startScrape(); 272 244 const providers = getProviders();
+6 -41
src/pages/parts/player/MetaPart.tsx
··· 5 5 6 6 import { isAllowedExtensionVersion } from "@/backend/extension/compatibility"; 7 7 import { extensionInfo, sendPage } from "@/backend/extension/messaging"; 8 - import { 9 - fetchMetadata, 10 - setCachedMetadata, 11 - } from "@/backend/helpers/providerApi"; 8 + import { setCachedMetadata } from "@/backend/helpers/providerApi"; 12 9 import { DetailedMeta, getMetaFromId } from "@/backend/metadata/getmeta"; 13 10 import { decodeTMDBId } from "@/backend/metadata/tmdb"; 14 11 import { MWMediaType } from "@/backend/metadata/types/mw"; 15 - import { getLoadbalancedProviderApiUrl } from "@/backend/providers/fetchers"; 16 12 import { getProviders } from "@/backend/providers/providers"; 17 13 import { Button } from "@/components/buttons/Button"; 18 14 import { Icons } from "@/components/Icon"; ··· 52 48 if (!info.hasPermission) throw new Error("extension-no-permission"); 53 49 } 54 50 55 - // use api metadata or providers metadata 56 - const providerApiUrl = getLoadbalancedProviderApiUrl(); 57 - if (providerApiUrl && !isValidExtension) { 58 - try { 59 - await fetchMetadata(providerApiUrl); 60 - } catch (err) { 61 - throw new Error("failed-api-metadata"); 62 - } 63 - } else { 64 - setCachedMetadata([ 65 - ...getProviders().listSources(), 66 - ...getProviders().listEmbeds(), 67 - ]); 68 - } 51 + // use providers metadata 52 + setCachedMetadata([ 53 + ...getProviders().listSources(), 54 + ...getProviders().listEmbeds(), 55 + ]); 69 56 70 57 // get media meta data 71 58 let data: ReturnType<typeof decodeTMDBId> = null; ··· 147 134 </IconPill> 148 135 <Title>{t("player.metadata.legal.title")}</Title> 149 136 <Paragraph>{t("player.metadata.legal.text")}</Paragraph> 150 - <Button 151 - href="/" 152 - theme="purple" 153 - padding="md:px-12 p-2.5" 154 - className="mt-6" 155 - > 156 - {t("player.metadata.failed.homeButton")} 157 - </Button> 158 - </ErrorContainer> 159 - </ErrorLayout> 160 - ); 161 - } 162 - 163 - if (error && error.message === "failed-api-metadata") { 164 - return ( 165 - <ErrorLayout> 166 - <ErrorContainer> 167 - <IconPill icon={Icons.WAND}> 168 - {t("player.metadata.failed.badge")} 169 - </IconPill> 170 - <Title>{t("player.metadata.api.text")}</Title> 171 - <Paragraph>{t("player.metadata.api.title")}</Paragraph> 172 137 <Button 173 138 href="/" 174 139 theme="purple"
+1 -4
src/pages/parts/player/ScrapeErrorPart.tsx
··· 19 19 import { usePreferencesStore } from "@/stores/preferences"; 20 20 import { getExtensionState } from "@/utils/extension"; 21 21 import type { ExtensionStatus } from "@/utils/extension"; 22 - import { getProviderApiUrls } from "@/utils/proxyUrls"; 23 22 24 23 import { ErrorCardInModal } from "../errors/ErrorCard"; 25 24 ··· 42 41 const error = useMemo(() => { 43 42 const data = props.data; 44 43 let str = ""; 45 - const apiUrls = getProviderApiUrls(); 46 - str += `URL - ${location.pathname}\n`; 47 - str += `API - ${apiUrls.length > 0}\n\n`; 44 + str += `URL - ${location.pathname}\n\n`; 48 45 Object.values(data.sources).forEach((v) => { 49 46 str += `${v.id}: ${v.status}\n`; 50 47 if (v.reason) str += `${v.reason}\n`;
+2 -2
src/pages/parts/player/ScrapingPart.tsx
··· 96 96 currentProviderIndex = sourceOrder.length - 1; 97 97 98 98 if (failedStartScrape) 99 - return <WarningPart>{t("player.turnstile.error")}</WarningPart>; 99 + return <WarningPart>{t("player.scraping.items.failure")}</WarningPart>; 100 100 101 101 return ( 102 102 <div ··· 106 106 {!sourceOrder || sourceOrder.length === 0 ? ( 107 107 <div className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 text-center flex flex-col justify-center z-0"> 108 108 <Loading className="mb-8" /> 109 - <p>{t("player.turnstile.verifyingHumanity")}</p> 109 + <p>{t("player.scraping.items.pending")}</p> 110 110 </div> 111 111 ) : null} 112 112 <div
-4
src/setup/config.ts
··· 18 18 NORMAL_ROUTER: boolean; 19 19 BACKEND_URL: string; 20 20 DISALLOWED_IDS: string; 21 - TURNSTILE_KEY: string; 22 21 CDN_REPLACEMENTS: string; 23 22 HAS_ONBOARDING: string; 24 23 ONBOARDING_CHROME_EXTENSION_INSTALL_LINK: string; ··· 51 50 M3U8_PROXY_URLS: string[]; 52 51 BACKEND_URL: string | null; 53 52 DISALLOWED_IDS: string[]; 54 - TURNSTILE_KEY: string | null; 55 53 CDN_REPLACEMENTS: Array<string[]>; 56 54 HAS_ONBOARDING: boolean; 57 55 ALLOW_AUTOPLAY: boolean; ··· 88 86 NORMAL_ROUTER: import.meta.env.VITE_NORMAL_ROUTER, 89 87 BACKEND_URL: import.meta.env.VITE_BACKEND_URL, 90 88 DISALLOWED_IDS: import.meta.env.VITE_DISALLOWED_IDS, 91 - TURNSTILE_KEY: import.meta.env.VITE_TURNSTILE_KEY, 92 89 CDN_REPLACEMENTS: import.meta.env.VITE_CDN_REPLACEMENTS, 93 90 HAS_ONBOARDING: import.meta.env.VITE_HAS_ONBOARDING, 94 91 ALLOW_AUTOPLAY: import.meta.env.VITE_ALLOW_AUTOPLAY, ··· 153 150 NORMAL_ROUTER: getKey("NORMAL_ROUTER", "false") === "true", 154 151 HAS_ONBOARDING: getKey("HAS_ONBOARDING", "false") === "true", 155 152 ALLOW_AUTOPLAY: getKey("ALLOW_AUTOPLAY", "false") === "true", 156 - TURNSTILE_KEY: getKey("TURNSTILE_KEY"), 157 153 DISALLOWED_IDS: getKey("DISALLOWED_IDS", "") 158 154 .split(",") 159 155 .map((v) => v.trim())
-48
src/stores/turnstile/index.tsx
··· 1 - import { Turnstile } from "@marsidev/react-turnstile"; 2 - import classNames from "classnames"; 3 - import { useRef } from "react"; 4 1 import { create } from "zustand"; 5 2 import { immer } from "zustand/middleware/immer"; 6 3 7 4 import { reportCaptchaSolve } from "@/backend/helpers/report"; 8 - import { conf } from "@/setup/config"; 9 5 10 6 export interface TurnstileStore { 11 7 isInWidget: boolean; ··· 84 80 throw err; 85 81 } 86 82 } 87 - 88 - export function TurnstileProvider(props: { 89 - isInPopout?: boolean; 90 - onUpdateShow?: (show: boolean) => void; 91 - }) { 92 - const siteKey = conf().TURNSTILE_KEY; 93 - const idRef = useRef<string | null>(null); 94 - const setTurnstile = useTurnstileStore((s) => s.setTurnstile); 95 - const processToken = useTurnstileStore((s) => s.processToken); 96 - if (!siteKey) return null; 97 - return ( 98 - <div 99 - className={classNames({ 100 - hidden: !props.isInPopout, 101 - })} 102 - > 103 - <Turnstile 104 - siteKey={siteKey} 105 - options={{ 106 - refreshExpired: "never", 107 - theme: "light", 108 - }} 109 - onWidgetLoad={(widgetId) => { 110 - idRef.current = widgetId; 111 - setTurnstile(widgetId, "mwturnstile", !!props.isInPopout); 112 - }} 113 - onError={() => { 114 - const id = idRef.current; 115 - if (!id) return; 116 - processToken(null, id); 117 - }} 118 - onSuccess={(token) => { 119 - const id = idRef.current; 120 - if (!id) return; 121 - processToken(token, id); 122 - props.onUpdateShow?.(false); 123 - }} 124 - onBeforeInteractive={() => { 125 - props.onUpdateShow?.(true); 126 - }} 127 - /> 128 - </div> 129 - ); 130 - }
+1 -7
src/utils/proxyUrls.ts
··· 2 2 import { useAuthStore } from "@/stores/auth"; 3 3 4 4 const originalUrls = conf().PROXY_URLS; 5 - const types = ["proxy", "api"] as const; 5 + const types = ["proxy"] as const; 6 6 7 7 type ParsedUrlType = (typeof types)[number]; 8 8 ··· 73 73 export function getM3U8ProxyUrls(): string[] { 74 74 return conf().M3U8_PROXY_URLS; 75 75 } 76 - 77 - export function getProviderApiUrls() { 78 - return getParsedUrls() 79 - .filter((v) => v.type === "api") 80 - .map((v) => v.url); 81 - }