mirror of
https://github.com/p-stream/p-stream.git
synced 2026-04-19 20:32:05 +00:00
140 lines
3.6 KiB
TypeScript
140 lines
3.6 KiB
TypeScript
import { ScrapeMedia } from "@movie-web/providers";
|
|
import { ofetch } from "ofetch";
|
|
import { useCallback } from "react";
|
|
|
|
import { ScrapingItems, ScrapingSegment } from "@/hooks/useProviderScrape";
|
|
import { PlayerMeta } from "@/stores/player/slices/source";
|
|
|
|
// for anybody who cares - these are anonymous metrics.
|
|
// They are just used for figuring out if providers are broken or not
|
|
const metricsEndpoint = "https://backend.movie-web.app/metrics/providers";
|
|
|
|
export type ProviderMetric = {
|
|
tmdbId: string;
|
|
type: string;
|
|
title: string;
|
|
seasonId?: string;
|
|
episodeId?: string;
|
|
status: "failed" | "notfound" | "success";
|
|
providerId: string;
|
|
embedId?: string;
|
|
errorMessage?: string;
|
|
fullError?: string;
|
|
};
|
|
|
|
function getStackTrace(error: Error, lines: number) {
|
|
const topMessage = error.toString();
|
|
const stackTraceLines = (error.stack ?? "").split("\n", lines + 1);
|
|
stackTraceLines.pop();
|
|
return `${topMessage}\n\n${stackTraceLines.join("\n")}`;
|
|
}
|
|
|
|
export async function reportProviders(items: ProviderMetric[]): Promise<void> {
|
|
return ofetch(metricsEndpoint, {
|
|
method: "POST",
|
|
body: {
|
|
items,
|
|
},
|
|
});
|
|
}
|
|
|
|
const segmentStatusMap: Record<
|
|
ScrapingSegment["status"],
|
|
ProviderMetric["status"] | null
|
|
> = {
|
|
success: "success",
|
|
notfound: "notfound",
|
|
failure: "failed",
|
|
pending: null,
|
|
waiting: null,
|
|
};
|
|
|
|
export function scrapeSourceOutputToProviderMetric(
|
|
media: PlayerMeta,
|
|
providerId: string,
|
|
embedId: string | null,
|
|
status: ProviderMetric["status"],
|
|
err: unknown | null
|
|
): ProviderMetric {
|
|
const episodeId = media.episode?.tmdbId;
|
|
const seasonId = media.season?.tmdbId;
|
|
let error: undefined | Error;
|
|
if (err instanceof Error) error = err;
|
|
|
|
return {
|
|
status,
|
|
providerId,
|
|
title: media.title,
|
|
tmdbId: media.tmdbId,
|
|
type: media.type,
|
|
embedId: embedId ?? undefined,
|
|
episodeId,
|
|
seasonId,
|
|
errorMessage: error?.message,
|
|
fullError: error ? getStackTrace(error, 5) : undefined,
|
|
};
|
|
}
|
|
|
|
export function scrapeSegmentToProviderMetric(
|
|
media: ScrapeMedia,
|
|
providerId: string,
|
|
segment: ScrapingSegment
|
|
): ProviderMetric | null {
|
|
const status = segmentStatusMap[segment.status];
|
|
if (!status) return null;
|
|
let episodeId: string | undefined;
|
|
let seasonId: string | undefined;
|
|
if (media.type === "show") {
|
|
episodeId = media.episode.tmdbId;
|
|
seasonId = media.season.tmdbId;
|
|
}
|
|
let error: undefined | Error;
|
|
if (segment.error instanceof Error) error = segment.error;
|
|
|
|
return {
|
|
status,
|
|
providerId,
|
|
title: media.title,
|
|
tmdbId: media.tmdbId,
|
|
type: media.type,
|
|
embedId: segment.embedId,
|
|
episodeId,
|
|
seasonId,
|
|
errorMessage: segment.reason ?? error?.message,
|
|
fullError: error ? getStackTrace(error, 5) : undefined,
|
|
};
|
|
}
|
|
|
|
export function scrapePartsToProviderMetric(
|
|
media: ScrapeMedia,
|
|
order: ScrapingItems[],
|
|
sources: Record<string, ScrapingSegment>
|
|
): ProviderMetric[] {
|
|
const output: ProviderMetric[] = [];
|
|
|
|
order.forEach((orderItem) => {
|
|
const source = sources[orderItem.id];
|
|
orderItem.children.forEach((embedId) => {
|
|
const embed = sources[embedId];
|
|
if (!embed.embedId) return;
|
|
const metric = scrapeSegmentToProviderMetric(media, source.id, embed);
|
|
if (!metric) return;
|
|
output.push(metric);
|
|
});
|
|
|
|
const metric = scrapeSegmentToProviderMetric(media, source.id, source);
|
|
if (!metric) return;
|
|
output.push(metric);
|
|
});
|
|
|
|
return output;
|
|
}
|
|
|
|
export function useReportProviders() {
|
|
const report = useCallback((items: ProviderMetric[]) => {
|
|
if (items.length === 0) return;
|
|
reportProviders(items);
|
|
}, []);
|
|
|
|
return { report };
|
|
}
|