mirror of
https://github.com/sussy-code/smov.git
synced 2024-12-29 16:07:40 +01:00
Metadata fetching
This commit is contained in:
parent
e48af381c5
commit
2bf0b5b03c
5 changed files with 89 additions and 41 deletions
|
@ -1,4 +1,22 @@
|
|||
import { ScrapeMedia } from "@movie-web/providers";
|
||||
import { MetaOutput, ScrapeMedia } from "@movie-web/providers";
|
||||
|
||||
import { mwFetch } from "@/backend/helpers/fetch";
|
||||
|
||||
let metaDataCache: MetaOutput[] | null = null;
|
||||
|
||||
export function setCachedMetadata(data: MetaOutput[]) {
|
||||
metaDataCache = data;
|
||||
}
|
||||
|
||||
export function getCachedMetadata(): MetaOutput[] {
|
||||
return metaDataCache ?? [];
|
||||
}
|
||||
|
||||
export async function fetchMetadata(base: string) {
|
||||
if (metaDataCache) return;
|
||||
const data = await mwFetch<MetaOutput[][]>(`${base}/metadata`);
|
||||
metaDataCache = data.flat();
|
||||
}
|
||||
|
||||
function scrapeMediaToQueryMedia(media: ScrapeMedia) {
|
||||
let extra: Record<string, string> = {};
|
||||
|
@ -15,6 +33,7 @@ function scrapeMediaToQueryMedia(media: ScrapeMedia) {
|
|||
type: media.type,
|
||||
releaseYear: media.releaseYear.toString(),
|
||||
imdbId: media.imdbId,
|
||||
tmdbId: media.tmdbId,
|
||||
title: media.title,
|
||||
...extra,
|
||||
};
|
||||
|
@ -48,8 +67,31 @@ export function makeProviderUrl(base: string) {
|
|||
};
|
||||
}
|
||||
|
||||
export function connectServerSideEvents(url: string, endEvents: string[]) {
|
||||
const;
|
||||
export function connectServerSideEvents<T>(url: string, endEvents: string[]) {
|
||||
const eventSource = new EventSource(url);
|
||||
let promReject: (reason?: any) => void;
|
||||
let promResolve: (value: T) => void;
|
||||
const promise = new Promise<T>((resolve, reject) => {
|
||||
promResolve = resolve;
|
||||
promReject = reject;
|
||||
});
|
||||
|
||||
return {};
|
||||
endEvents.forEach((evt) => {
|
||||
eventSource.addEventListener(evt, (e) => {
|
||||
eventSource.close();
|
||||
promResolve(JSON.parse(e.data));
|
||||
});
|
||||
});
|
||||
|
||||
eventSource.addEventListener("error", (err) => {
|
||||
console.error("Failed to connect to SSE", err);
|
||||
promReject(err);
|
||||
});
|
||||
|
||||
return {
|
||||
promise: () => promise,
|
||||
on<Data>(event: string, cb: (data: Data) => void) {
|
||||
eventSource.addEventListener(event, (e) => cb(JSON.parse(e.data)));
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { useMemo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { getCachedMetadata } from "@/backend/helpers/providerApi";
|
||||
import { Toggle } from "@/components/buttons/Toggle";
|
||||
import { Icon, Icons } from "@/components/Icon";
|
||||
import { useCaptions } from "@/components/player/hooks/useCaptions";
|
||||
|
@ -10,7 +11,6 @@ import { useOverlayRouter } from "@/hooks/useOverlayRouter";
|
|||
import { usePlayerStore } from "@/stores/player/store";
|
||||
import { qualityToString } from "@/stores/player/utils/qualities";
|
||||
import { useSubtitleStore } from "@/stores/subtitles";
|
||||
import { providers } from "@/utils/providers";
|
||||
|
||||
export function SettingsMenu({ id }: { id: string }) {
|
||||
const { t } = useTranslation();
|
||||
|
@ -23,7 +23,10 @@ export function SettingsMenu({ id }: { id: string }) {
|
|||
const currentSourceId = usePlayerStore((s) => s.sourceId);
|
||||
const sourceName = useMemo(() => {
|
||||
if (!currentSourceId) return "...";
|
||||
return providers.getMetadata(currentSourceId)?.name ?? "...";
|
||||
const source = getCachedMetadata().find(
|
||||
(src) => src.id === currentSourceId
|
||||
);
|
||||
return source?.name ?? "...";
|
||||
}, [currentSourceId]);
|
||||
const { toggleLastUsed } = useCaptions();
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
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,
|
||||
|
@ -33,7 +34,7 @@ export function EmbedOption(props: {
|
|||
|
||||
const embedName = useMemo(() => {
|
||||
if (!props.embedId) return unknownEmbedName;
|
||||
const sourceMeta = providers.getMetadata(props.embedId);
|
||||
const sourceMeta = getCachedMetadata().find((s) => s.id === props.embedId);
|
||||
return sourceMeta?.name ?? unknownEmbedName;
|
||||
}, [props.embedId, unknownEmbedName]);
|
||||
|
||||
|
@ -61,7 +62,7 @@ export function EmbedSelectionView({ sourceId, id }: EmbedSelectionViewProps) {
|
|||
|
||||
const sourceName = useMemo(() => {
|
||||
if (!sourceId) return "...";
|
||||
const sourceMeta = providers.getMetadata(sourceId);
|
||||
const sourceMeta = getCachedMetadata().find((s) => s.id === sourceId);
|
||||
return sourceMeta?.name ?? "...";
|
||||
}, [sourceId]);
|
||||
|
||||
|
|
|
@ -5,7 +5,11 @@ import {
|
|||
} from "@movie-web/providers";
|
||||
import { RefObject, useCallback, useEffect, useRef, useState } from "react";
|
||||
|
||||
import { makeProviderUrl } from "@/backend/helpers/providerApi";
|
||||
import {
|
||||
connectServerSideEvents,
|
||||
getCachedMetadata,
|
||||
makeProviderUrl,
|
||||
} from "@/backend/helpers/providerApi";
|
||||
import { getLoadbalancedProviderApiUrl, providers } from "@/utils/providers";
|
||||
|
||||
export interface ScrapingItems {
|
||||
|
@ -37,7 +41,7 @@ function useBaseScrape() {
|
|||
setSources(
|
||||
evt.sourceIds
|
||||
.map((v) => {
|
||||
const source = providers.getMetadata(v);
|
||||
const source = getCachedMetadata().find((s) => s.id === v);
|
||||
if (!source) throw new Error("invalid source id");
|
||||
const out: ScrapingSegment = {
|
||||
name: source.name,
|
||||
|
@ -80,7 +84,9 @@ function useBaseScrape() {
|
|||
(evt: ScraperEvent<"discoverEmbeds">) => {
|
||||
setSources((s) => {
|
||||
evt.embeds.forEach((v) => {
|
||||
const source = providers.getMetadata(v.embedScraperId);
|
||||
const source = getCachedMetadata().find(
|
||||
(src) => src.id === v.embedScraperId
|
||||
);
|
||||
if (!source) throw new Error("invalid source id");
|
||||
const out: ScrapingSegment = {
|
||||
embedId: v.embedScraperId,
|
||||
|
@ -149,37 +155,18 @@ export function useScrape() {
|
|||
const providerApiUrl = getLoadbalancedProviderApiUrl();
|
||||
if (providerApiUrl) {
|
||||
startScrape();
|
||||
const sseOutput = await new Promise<RunOutput | null>(
|
||||
(resolve, reject) => {
|
||||
const baseUrlMaker = makeProviderUrl(providerApiUrl);
|
||||
const scrapeEvents = new EventSource(baseUrlMaker.scrapeAll(media));
|
||||
scrapeEvents.addEventListener("init", (e) => {
|
||||
initEvent(JSON.parse(e.data));
|
||||
});
|
||||
scrapeEvents.addEventListener("error", (err) => {
|
||||
console.error("failed to use provider api", err);
|
||||
reject(err);
|
||||
});
|
||||
scrapeEvents.addEventListener("start", (e) =>
|
||||
startEvent(JSON.parse(e.data))
|
||||
);
|
||||
scrapeEvents.addEventListener("update", (e) =>
|
||||
updateEvent(JSON.parse(e.data))
|
||||
);
|
||||
scrapeEvents.addEventListener("discoverEmbeds", (e) =>
|
||||
discoverEmbedsEvent(JSON.parse(e.data))
|
||||
);
|
||||
scrapeEvents.addEventListener("completed", (e) => {
|
||||
scrapeEvents.close();
|
||||
resolve(JSON.parse(e.data));
|
||||
});
|
||||
scrapeEvents.addEventListener("noOutput", () => {
|
||||
scrapeEvents.close();
|
||||
resolve(null);
|
||||
});
|
||||
}
|
||||
const baseUrlMaker = makeProviderUrl(providerApiUrl);
|
||||
const conn = connectServerSideEvents<RunOutput | "">(
|
||||
baseUrlMaker.scrapeAll(media),
|
||||
["completed", "noOutput"]
|
||||
);
|
||||
return getResult(sseOutput);
|
||||
conn.on("init", initEvent);
|
||||
conn.on("start", startEvent);
|
||||
conn.on("update", updateEvent);
|
||||
conn.on("discoverEmbeds", discoverEmbedsEvent);
|
||||
const sseOutput = await conn.promise();
|
||||
|
||||
return getResult(sseOutput === "" ? null : sseOutput);
|
||||
}
|
||||
|
||||
if (!providers) return null;
|
||||
|
|
|
@ -3,6 +3,10 @@ import { useHistory, useParams } from "react-router-dom";
|
|||
import { useAsync } from "react-use";
|
||||
import type { AsyncReturnType } from "type-fest";
|
||||
|
||||
import {
|
||||
fetchMetadata,
|
||||
setCachedMetadata,
|
||||
} from "@/backend/helpers/providerApi";
|
||||
import { DetailedMeta, getMetaFromId } from "@/backend/metadata/getmeta";
|
||||
import { decodeTMDBId } from "@/backend/metadata/tmdb";
|
||||
import { MWMediaType } from "@/backend/metadata/types/mw";
|
||||
|
@ -14,6 +18,7 @@ import { Paragraph } from "@/components/text/Paragraph";
|
|||
import { Title } from "@/components/text/Title";
|
||||
import { ErrorContainer, ErrorLayout } from "@/pages/layouts/ErrorLayout";
|
||||
import { conf } from "@/setup/config";
|
||||
import { getLoadbalancedProviderApiUrl, providers } from "@/utils/providers";
|
||||
|
||||
export interface MetaPartProps {
|
||||
onGetMeta?: (meta: DetailedMeta, episodeId?: string) => void;
|
||||
|
@ -36,6 +41,16 @@ export function MetaPart(props: MetaPartProps) {
|
|||
const history = useHistory();
|
||||
|
||||
const { error, value, loading } = useAsync(async () => {
|
||||
const providerApiUrl = getLoadbalancedProviderApiUrl();
|
||||
if (providerApiUrl) {
|
||||
await fetchMetadata(providerApiUrl);
|
||||
} else {
|
||||
setCachedMetadata([
|
||||
...providers.listSources(),
|
||||
...providers.listEmbeds(),
|
||||
]);
|
||||
}
|
||||
|
||||
let data: ReturnType<typeof decodeTMDBId> = null;
|
||||
try {
|
||||
data = decodeTMDBId(params.media);
|
||||
|
|
Loading…
Reference in a new issue