Revert "feat: add skip source button during scraping"

This reverts commit bf14a85f34.
This commit is contained in:
Pas 2025-12-03 18:20:04 -07:00
parent d1356405d2
commit 0576c9fed0
9 changed files with 29 additions and 184 deletions

View file

@ -852,7 +852,6 @@
"title": "Failed to play video!" "title": "Failed to play video!"
}, },
"scraping": { "scraping": {
"skip": "Skip source",
"items": { "items": {
"failure": "Error occurred", "failure": "Error occurred",
"notFound": "Doesn't have the video (╥﹏╥)", "notFound": "Doesn't have the video (╥﹏╥)",

View file

@ -65,7 +65,6 @@ const segmentStatusMap: Record<
failure: "failed", failure: "failed",
pending: null, pending: null,
waiting: null, waiting: null,
skipped: "notfound",
}; };
export function scrapeSourceOutputToProviderMetric( export function scrapeSourceOutputToProviderMetric(

View file

@ -1,7 +1,6 @@
import { import {
Fetcher, Fetcher,
makeSimpleProxyFetcher, makeSimpleProxyFetcher,
makeStandardFetcher,
setM3U8ProxyUrl, setM3U8ProxyUrl,
} from "@p-stream/providers"; } from "@p-stream/providers";
@ -83,19 +82,8 @@ export function setupM3U8Proxy() {
export function makeLoadBalancedSimpleProxyFetcher() { export function makeLoadBalancedSimpleProxyFetcher() {
const fetcher: Fetcher = async (a, b) => { const fetcher: Fetcher = async (a, b) => {
const proxyUrl = getLoadbalancedProxyUrl();
// If no proxy URL is available, fall back to direct fetch
if (!proxyUrl) {
console.warn(
"[makeLoadBalancedSimpleProxyFetcher] No proxy URL available, using direct fetch",
);
const directFetcher = makeStandardFetcher(fetchButWithApiTokens);
return directFetcher(a, b);
}
const currentFetcher = makeSimpleProxyFetcher( const currentFetcher = makeSimpleProxyFetcher(
proxyUrl, getLoadbalancedProxyUrl(),
fetchButWithApiTokens, fetchButWithApiTokens,
); );
return currentFetcher(a, b); return currentFetcher(a, b);

View file

@ -9,13 +9,7 @@ import {
import { Transition } from "@/components/utils/Transition"; import { Transition } from "@/components/utils/Transition";
export interface ScrapeItemProps { export interface ScrapeItemProps {
status: status: "failure" | "pending" | "notfound" | "success" | "waiting";
| "failure"
| "pending"
| "notfound"
| "success"
| "waiting"
| "skipped";
name: string; name: string;
id?: string; id?: string;
percentage?: number; percentage?: number;
@ -30,7 +24,6 @@ const statusTextMap: Partial<Record<ScrapeCardProps["status"], string>> = {
notfound: "player.scraping.items.notFound", notfound: "player.scraping.items.notFound",
failure: "player.scraping.items.failure", failure: "player.scraping.items.failure",
pending: "player.scraping.items.pending", pending: "player.scraping.items.pending",
skipped: "player.scraping.items.notFound",
}; };
const statusMap: Record<ScrapeCardProps["status"], StatusCircleProps["type"]> = const statusMap: Record<ScrapeCardProps["status"], StatusCircleProps["type"]> =
@ -40,7 +33,6 @@ const statusMap: Record<ScrapeCardProps["status"], StatusCircleProps["type"]> =
pending: "loading", pending: "loading",
success: "success", success: "success",
waiting: "waiting", waiting: "waiting",
skipped: "noresult",
}; };
export function ScrapeItem(props: ScrapeItemProps) { export function ScrapeItem(props: ScrapeItemProps) {

View file

@ -22,13 +22,7 @@ export interface ScrapingSegment {
name: string; name: string;
id: string; id: string;
embedId?: string; embedId?: string;
status: status: "failure" | "pending" | "notfound" | "success" | "waiting";
| "failure"
| "pending"
| "notfound"
| "success"
| "waiting"
| "skipped";
reason?: string; reason?: string;
error?: any; error?: any;
percentage: number; percentage: number;
@ -42,7 +36,6 @@ function useBaseScrape() {
const [sources, setSources] = useState<Record<string, ScrapingSegment>>({}); const [sources, setSources] = useState<Record<string, ScrapingSegment>>({});
const [sourceOrder, setSourceOrder] = useState<ScrapingItems[]>([]); const [sourceOrder, setSourceOrder] = useState<ScrapingItems[]>([]);
const [currentSource, setCurrentSource] = useState<string>(); const [currentSource, setCurrentSource] = useState<string>();
const abortControllerRef = useRef<AbortController | null>(null);
const lastId = useRef<string | null>(null); const lastId = useRef<string | null>(null);
const initEvent = useCallback((evt: ScraperEvent<"init">) => { const initEvent = useCallback((evt: ScraperEvent<"init">) => {
@ -71,16 +64,12 @@ function useBaseScrape() {
const lastIdTmp = lastId.current; const lastIdTmp = lastId.current;
setSources((s) => { setSources((s) => {
if (s[id]) s[id].status = "pending"; if (s[id]) s[id].status = "pending";
// Only mark as success if it's pending - don't overwrite skipped status if (lastIdTmp && s[lastIdTmp] && s[lastIdTmp].status === "pending")
if (lastIdTmp && s[lastIdTmp] && s[lastIdTmp].status === "pending") {
s[lastIdTmp].status = "success"; s[lastIdTmp].status = "success";
}
return { ...s }; return { ...s };
}); });
setCurrentSource(id); setCurrentSource(id);
lastId.current = id; lastId.current = id;
// Create new AbortController for this source
abortControllerRef.current = new AbortController();
}, []); }, []);
const updateEvent = useCallback((evt: ScraperEvent<"update">) => { const updateEvent = useCallback((evt: ScraperEvent<"update">) => {
@ -139,35 +128,6 @@ function useBaseScrape() {
return output; return output;
}, []); }, []);
const skipCurrentSource = useCallback(() => {
if (currentSource) {
// Get the parent source ID (remove embed suffix like "-0", "-1", etc.)
const parentSourceId = currentSource.split("-")[0];
// Abort the current operation FIRST - abort all pending requests immediately
if (abortControllerRef.current) {
abortControllerRef.current.abort();
}
// Mark the parent source and all its embeds as skipped AFTER aborting
// This ensures the abort happens immediately and can interrupt ongoing operations
setSources((s) => {
Object.keys(s).forEach((key) => {
// Check if this is the parent source or one of its embeds
if (key === parentSourceId || key.startsWith(`${parentSourceId}-`)) {
if (s[key]) {
// Mark as skipped regardless of current status (even if it succeeded)
s[key].status = "skipped";
s[key].reason = "Skipped by user";
s[key].percentage = 100;
}
}
});
return { ...s };
});
}
}, [currentSource]);
return { return {
initEvent, initEvent,
startEvent, startEvent,
@ -178,8 +138,6 @@ function useBaseScrape() {
sources, sources,
sourceOrder, sourceOrder,
currentSource, currentSource,
skipCurrentSource,
abortControllerRef,
}; };
} }
@ -194,8 +152,6 @@ export function useScrape() {
getResult, getResult,
startEvent, startEvent,
startScrape, startScrape,
skipCurrentSource,
abortControllerRef,
} = useBaseScrape(); } = useBaseScrape();
const preferredSourceOrder = usePreferencesStore((s) => s.sourceOrder); const preferredSourceOrder = usePreferencesStore((s) => s.sourceOrder);
@ -215,7 +171,6 @@ export function useScrape() {
async (media: ScrapeMedia, startFromSourceId?: string) => { async (media: ScrapeMedia, startFromSourceId?: string) => {
const providerInstance = getProviders(); const providerInstance = getProviders();
const allSources = providerInstance.listSources(); const allSources = providerInstance.listSources();
const playerState = usePlayerStore.getState(); const playerState = usePlayerStore.getState();
const failedSources = playerState.failedSources; const failedSources = playerState.failedSources;
const failedEmbeds = playerState.failedEmbeds; const failedEmbeds = playerState.failedEmbeds;
@ -279,7 +234,6 @@ export function useScrape() {
: undefined; : undefined;
const providerApiUrl = getLoadbalancedProviderApiUrl(); const providerApiUrl = getLoadbalancedProviderApiUrl();
if (providerApiUrl && !isExtensionActiveCached()) { if (providerApiUrl && !isExtensionActiveCached()) {
startScrape(); startScrape();
const baseUrlMaker = makeProviderUrl(providerApiUrl); const baseUrlMaker = makeProviderUrl(providerApiUrl);
@ -304,25 +258,10 @@ export function useScrape() {
startScrape(); startScrape();
const providers = getProviders(); const providers = getProviders();
// Create initial abort controller if it doesn't exist
if (!abortControllerRef.current) {
abortControllerRef.current = new AbortController();
}
// Create a wrapper that always gets the current abort controller
const getCurrentAbortController = () => abortControllerRef.current;
const output = await providers.runAll({ const output = await providers.runAll({
media, media,
sourceOrder: filteredSourceOrder, sourceOrder: filteredSourceOrder,
embedOrder: filteredEmbedOrder, embedOrder: filteredEmbedOrder,
abortController: {
get signal() {
const controller = getCurrentAbortController();
return controller ? controller.signal : undefined;
},
} as AbortController,
events: { events: {
init: initEvent, init: initEvent,
start: startEvent, start: startEvent,
@ -349,7 +288,6 @@ export function useScrape() {
preferredEmbedOrder, preferredEmbedOrder,
enableEmbedOrder, enableEmbedOrder,
disabledEmbeds, disabledEmbeds,
abortControllerRef,
], ],
); );
@ -366,7 +304,6 @@ export function useScrape() {
sourceOrder, sourceOrder,
sources, sources,
currentSource, currentSource,
skipCurrentSource,
}; };
} }

View file

@ -38,7 +38,6 @@ export function RealPlayerView() {
episode?: string; episode?: string;
season?: string; season?: string;
}>(); }>();
const [skipSourceFn, setSkipSourceFn] = useState<(() => void) | null>(null);
const [errorData, setErrorData] = useState<{ const [errorData, setErrorData] = useState<{
sources: Record<string, ScrapingSegment>; sources: Record<string, ScrapingSegment>;
sourceOrder: ScrapingItems[]; sourceOrder: ScrapingItems[];
@ -205,11 +204,7 @@ export function RealPlayerView() {
); );
return ( return (
<PlayerPart <PlayerPart backUrl={backUrl} onMetaChange={metaChange}>
backUrl={backUrl}
onMetaChange={metaChange}
skipSourceFn={skipSourceFn}
>
{status === playerStatus.IDLE ? ( {status === playerStatus.IDLE ? (
<MetaPart onGetMeta={handleMetaReceived} /> <MetaPart onGetMeta={handleMetaReceived} />
) : null} ) : null}
@ -228,7 +223,6 @@ export function RealPlayerView() {
key={`scraping-${resumeFromSourceId || "default"}`} key={`scraping-${resumeFromSourceId || "default"}`}
media={scrapeMedia} media={scrapeMedia}
startFromSourceId={resumeFromSourceId || undefined} startFromSourceId={resumeFromSourceId || undefined}
onSkipSourceReady={(fn) => setSkipSourceFn(() => fn)}
onResult={(sources, sourceOrder) => { onResult={(sources, sourceOrder) => {
setErrorData({ setErrorData({
sourceOrder, sourceOrder,
@ -238,29 +232,7 @@ export function RealPlayerView() {
// Clear resume state after scraping // Clear resume state after scraping
setResumeFromSourceId(null); setResumeFromSourceId(null);
}} }}
onGetStream={(out, sources) => { onGetStream={playAfterScrape}
// Check if the source was skipped by user
if (out) {
const outSourceId = out.sourceId;
const parentSourceId = outSourceId.split("-")[0];
// Check both the parent and the specific embed
const parentData = sources[parentSourceId];
const embedData = sources[outSourceId];
// If the source or embed was skipped by user, don't play it
// Just ignore the result and let scraping continue to next source
if (
parentData?.status === "skipped" ||
embedData?.status === "skipped" ||
parentData?.reason === "Skipped by user" ||
embedData?.reason === "Skipped by user"
) {
return;
}
}
playAfterScrape(out);
}}
/> />
) )
) : null} ) : null}

View file

@ -20,7 +20,6 @@ export interface PlayerPartProps {
backUrl: string; backUrl: string;
onLoad?: () => void; onLoad?: () => void;
onMetaChange?: (meta: PlayerMeta) => void; onMetaChange?: (meta: PlayerMeta) => void;
skipSourceFn?: (() => void) | null;
} }
export function PlayerPart(props: PlayerPartProps) { export function PlayerPart(props: PlayerPartProps) {
@ -140,15 +139,11 @@ export function PlayerPart(props: PlayerPartProps) {
</div> </div>
</Player.TopControls> </Player.TopControls>
<Player.BottomControls <Player.BottomControls show={showTargets}>
show={showTargets || status === playerStatus.SCRAPING}
>
{status !== playerStatus.PLAYING && !manualSourceSelection && <Tips />} {status !== playerStatus.PLAYING && !manualSourceSelection && <Tips />}
<div className="flex items-center justify-center space-x-3 h-full"> <div className="flex items-center justify-center space-x-3 h-full">
{status === playerStatus.SCRAPING ? ( {status === playerStatus.SCRAPING ? (
<ScrapingPartInterruptButton <ScrapingPartInterruptButton />
skipCurrentSource={props.skipSourceFn || undefined}
/>
) : null} ) : null}
{status === playerStatus.PLAYING ? ( {status === playerStatus.PLAYING ? (
<> <>

View file

@ -26,28 +26,18 @@ import { WarningPart } from "../util/WarningPart";
export interface ScrapingProps { export interface ScrapingProps {
media: ScrapeMedia; media: ScrapeMedia;
onGetStream?: ( onGetStream?: (stream: AsyncReturnType<ProviderControls["runAll"]>) => void;
stream: AsyncReturnType<ProviderControls["runAll"]>,
sources: Record<string, ScrapingSegment>,
) => void;
onResult?: ( onResult?: (
sources: Record<string, ScrapingSegment>, sources: Record<string, ScrapingSegment>,
sourceOrder: ScrapingItems[], sourceOrder: ScrapingItems[],
) => void; ) => void;
startFromSourceId?: string; startFromSourceId?: string;
onSkipSourceReady?: (skipFn: () => void) => void;
} }
export function ScrapingPart(props: ScrapingProps) { export function ScrapingPart(props: ScrapingProps) {
const { report } = useReportProviders(); const { report } = useReportProviders();
const { const { startScraping, resumeScraping, sourceOrder, sources, currentSource } =
startScraping, useScrape();
resumeScraping,
sourceOrder,
sources,
currentSource,
skipCurrentSource,
} = useScrape();
const isMounted = useMountedState(); const isMounted = useMountedState();
const { t } = useTranslation(); const { t } = useTranslation();
@ -73,44 +63,30 @@ export function ScrapingPart(props: ScrapingProps) {
}, [sourceOrder, sources]); }, [sourceOrder, sources]);
const started = useRef<string | null>(null); const started = useRef<string | null>(null);
// Pass skip function to parent
useEffect(() => {
props.onSkipSourceReady?.(skipCurrentSource);
}, [skipCurrentSource, props]);
useEffect(() => { useEffect(() => {
// Only start scraping if we haven't started with this startFromSourceId before // Only start scraping if we haven't started with this startFromSourceId before
const currentKey = props.startFromSourceId || "default"; const currentKey = props.startFromSourceId || "default";
if (started.current === currentKey) { if (started.current === currentKey) return;
return;
}
started.current = currentKey; started.current = currentKey;
(async () => { (async () => {
try { const output = props.startFromSourceId
const output = props.startFromSourceId ? await resumeScraping(props.media, props.startFromSourceId)
? await resumeScraping(props.media, props.startFromSourceId) : await startScraping(props.media);
: await startScraping(props.media); if (!isMounted()) return;
if (!isMounted()) { props.onResult?.(
return; resultRef.current.sources,
} resultRef.current.sourceOrder,
props.onResult?.( );
resultRef.current.sources, report(
scrapePartsToProviderMetric(
props.media,
resultRef.current.sourceOrder, resultRef.current.sourceOrder,
); resultRef.current.sources,
report( ),
scrapePartsToProviderMetric( );
props.media, props.onGetStream?.(output);
resultRef.current.sourceOrder, })().catch(() => setFailedStartScrape(true));
resultRef.current.sources,
),
);
props.onGetStream?.(output, resultRef.current.sources);
} catch (error) {
setFailedStartScrape(true);
}
})();
}, [startScraping, resumeScraping, props, report, isMounted]); }, [startScraping, resumeScraping, props, report, isMounted]);
let currentProviderIndex = sourceOrder.findIndex( let currentProviderIndex = sourceOrder.findIndex(
@ -186,9 +162,7 @@ export function ScrapingPart(props: ScrapingProps) {
); );
} }
export function ScrapingPartInterruptButton(props: { export function ScrapingPartInterruptButton() {
skipCurrentSource?: () => void;
}) {
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
@ -209,16 +183,6 @@ export function ScrapingPartInterruptButton(props: {
> >
{t("notFound.reloadButton")} {t("notFound.reloadButton")}
</Button> </Button>
{props.skipCurrentSource && (
<Button
onClick={props.skipCurrentSource}
theme="purple"
padding="md:px-17 p-3"
className="mt-6"
>
{t("player.scraping.skip")}
</Button>
)}
</div> </div>
); );
} }

View file

@ -1,5 +1,4 @@
/* eslint-disable no-console */ /* eslint-disable no-console */
import { ScrapeMedia } from "@p-stream/providers"; import { ScrapeMedia } from "@p-stream/providers";
import { MakeSlice } from "@/stores/player/slices/types"; import { MakeSlice } from "@/stores/player/slices/types";