mirror of
https://github.com/sussy-code/providers.git
synced 2026-03-28 13:38:42 +00:00
i hate lint
This commit is contained in:
parent
e5d1822a71
commit
f09c99b021
4 changed files with 94 additions and 150 deletions
|
|
@ -1,8 +1,8 @@
|
|||
import ISO6391 from "iso-639-1";
|
||||
import ISO6391 from 'iso-639-1';
|
||||
|
||||
export const captionTypes = {
|
||||
srt: "srt",
|
||||
vtt: "vtt",
|
||||
srt: 'srt',
|
||||
vtt: 'vtt',
|
||||
};
|
||||
export type CaptionType = keyof typeof captionTypes;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import { IndividualScraperEvents } from "@/entrypoint/utils/events";
|
||||
import { ScrapeMedia } from "@/entrypoint/utils/media";
|
||||
import { FeatureMap, flagsAllowedInFeatures } from "@/entrypoint/utils/targets";
|
||||
import { UseableFetcher } from "@/fetchers/types";
|
||||
import { EmbedOutput, SourcererOutput } from "@/providers/base";
|
||||
import { ProviderList } from "@/providers/get";
|
||||
import { ScrapeContext } from "@/utils/context";
|
||||
import { NotFoundError } from "@/utils/errors";
|
||||
import { addOpenSubtitlesCaptions } from "@/utils/opensubtitles";
|
||||
import { isValidStream, validatePlayableStreams } from "@/utils/valid";
|
||||
import { IndividualScraperEvents } from '@/entrypoint/utils/events';
|
||||
import { ScrapeMedia } from '@/entrypoint/utils/media';
|
||||
import { FeatureMap, flagsAllowedInFeatures } from '@/entrypoint/utils/targets';
|
||||
import { UseableFetcher } from '@/fetchers/types';
|
||||
import { EmbedOutput, SourcererOutput } from '@/providers/base';
|
||||
import { ProviderList } from '@/providers/get';
|
||||
import { ScrapeContext } from '@/utils/context';
|
||||
import { NotFoundError } from '@/utils/errors';
|
||||
import { addOpenSubtitlesCaptions } from '@/utils/opensubtitles';
|
||||
import { isValidStream, validatePlayableStreams } from '@/utils/valid';
|
||||
|
||||
export type IndividualSourceRunnerOptions = {
|
||||
features: FeatureMap;
|
||||
|
|
@ -20,14 +20,12 @@ export type IndividualSourceRunnerOptions = {
|
|||
|
||||
export async function scrapeInvidualSource(
|
||||
list: ProviderList,
|
||||
ops: IndividualSourceRunnerOptions
|
||||
ops: IndividualSourceRunnerOptions,
|
||||
): Promise<SourcererOutput> {
|
||||
const sourceScraper = list.sources.find((v) => ops.id === v.id);
|
||||
if (!sourceScraper) throw new Error("Source with ID not found");
|
||||
if (ops.media.type === "movie" && !sourceScraper.scrapeMovie)
|
||||
throw new Error("Source is not compatible with movies");
|
||||
if (ops.media.type === "show" && !sourceScraper.scrapeShow)
|
||||
throw new Error("Source is not compatible with shows");
|
||||
if (!sourceScraper) throw new Error('Source with ID not found');
|
||||
if (ops.media.type === 'movie' && !sourceScraper.scrapeMovie) throw new Error('Source is not compatible with movies');
|
||||
if (ops.media.type === 'show' && !sourceScraper.scrapeShow) throw new Error('Source is not compatible with shows');
|
||||
|
||||
const contextBase: ScrapeContext = {
|
||||
fetcher: ops.fetcher,
|
||||
|
|
@ -36,18 +34,18 @@ export async function scrapeInvidualSource(
|
|||
ops.events?.update?.({
|
||||
id: sourceScraper.id,
|
||||
percentage: val,
|
||||
status: "pending",
|
||||
status: 'pending',
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
let output: SourcererOutput | null = null;
|
||||
if (ops.media.type === "movie" && sourceScraper.scrapeMovie)
|
||||
if (ops.media.type === 'movie' && sourceScraper.scrapeMovie)
|
||||
output = await sourceScraper.scrapeMovie({
|
||||
...contextBase,
|
||||
media: ops.media,
|
||||
});
|
||||
else if (ops.media.type === "show" && sourceScraper.scrapeShow)
|
||||
else if (ops.media.type === 'show' && sourceScraper.scrapeShow)
|
||||
output = await sourceScraper.scrapeShow({
|
||||
...contextBase,
|
||||
media: ops.media,
|
||||
|
|
@ -60,7 +58,7 @@ export async function scrapeInvidualSource(
|
|||
.filter((stream) => flagsAllowedInFeatures(ops.features, stream.flags));
|
||||
}
|
||||
|
||||
if (!output) throw new Error("output is null");
|
||||
if (!output) throw new Error('output is null');
|
||||
|
||||
// filter output with only valid embeds that are not disabled
|
||||
output.embeds = output.embeds.filter((embed) => {
|
||||
|
|
@ -71,29 +69,19 @@ export async function scrapeInvidualSource(
|
|||
|
||||
// opensubtitles
|
||||
for (const embed of output.embeds)
|
||||
embed.url = `${embed.url}${btoa("MEDIA=")}${btoa(
|
||||
embed.url = `${embed.url}${btoa('MEDIA=')}${btoa(
|
||||
`${ops.media.imdbId}${
|
||||
ops.media.type === "show"
|
||||
? `.${ops.media.season.number}.${ops.media.episode.number}`
|
||||
: ""
|
||||
}`
|
||||
ops.media.type === 'show' ? `.${ops.media.season.number}.${ops.media.episode.number}` : ''
|
||||
}`,
|
||||
)}`;
|
||||
|
||||
if (
|
||||
(!output.stream || output.stream.length === 0) &&
|
||||
output.embeds.length === 0
|
||||
)
|
||||
throw new NotFoundError("No streams found");
|
||||
if ((!output.stream || output.stream.length === 0) && output.embeds.length === 0)
|
||||
throw new NotFoundError('No streams found');
|
||||
|
||||
// only check for playable streams if there are streams, and if there are no embeds
|
||||
if (output.stream && output.stream.length > 0 && output.embeds.length === 0) {
|
||||
const playableStreams = await validatePlayableStreams(
|
||||
output.stream,
|
||||
ops,
|
||||
sourceScraper.id
|
||||
);
|
||||
if (playableStreams.length === 0)
|
||||
throw new NotFoundError("No playable streams found");
|
||||
const playableStreams = await validatePlayableStreams(output.stream, ops, sourceScraper.id);
|
||||
if (playableStreams.length === 0) throw new NotFoundError('No playable streams found');
|
||||
|
||||
// opensubtitles
|
||||
for (const playableStream of playableStreams) {
|
||||
|
|
@ -102,11 +90,9 @@ export async function scrapeInvidualSource(
|
|||
ops,
|
||||
btoa(
|
||||
`${ops.media.imdbId}${
|
||||
ops.media.type === "show"
|
||||
? `.${ops.media.season.number}.${ops.media.episode.number}`
|
||||
: ""
|
||||
}`
|
||||
)
|
||||
ops.media.type === 'show' ? `.${ops.media.season.number}.${ops.media.episode.number}` : ''
|
||||
}`,
|
||||
),
|
||||
);
|
||||
}
|
||||
output.stream = playableStreams;
|
||||
|
|
@ -125,15 +111,14 @@ export type IndividualEmbedRunnerOptions = {
|
|||
|
||||
export async function scrapeIndividualEmbed(
|
||||
list: ProviderList,
|
||||
ops: IndividualEmbedRunnerOptions
|
||||
ops: IndividualEmbedRunnerOptions,
|
||||
): Promise<EmbedOutput> {
|
||||
const embedScraper = list.embeds.find((v) => ops.id === v.id);
|
||||
if (!embedScraper) throw new Error("Embed with ID not found");
|
||||
if (!embedScraper) throw new Error('Embed with ID not found');
|
||||
|
||||
let url = ops.url;
|
||||
let media;
|
||||
if (ops.url.includes(btoa("MEDIA=")))
|
||||
[url, media] = url.split(btoa("MEDIA="));
|
||||
if (ops.url.includes(btoa('MEDIA='))) [url, media] = url.split(btoa('MEDIA='));
|
||||
|
||||
const output = await embedScraper.scrape({
|
||||
fetcher: ops.fetcher,
|
||||
|
|
@ -143,7 +128,7 @@ export async function scrapeIndividualEmbed(
|
|||
ops.events?.update?.({
|
||||
id: embedScraper.id,
|
||||
percentage: val,
|
||||
status: "pending",
|
||||
status: 'pending',
|
||||
});
|
||||
},
|
||||
});
|
||||
|
|
@ -151,23 +136,14 @@ export async function scrapeIndividualEmbed(
|
|||
output.stream = output.stream
|
||||
.filter((stream) => isValidStream(stream))
|
||||
.filter((stream) => flagsAllowedInFeatures(ops.features, stream.flags));
|
||||
if (output.stream.length === 0) throw new NotFoundError("No streams found");
|
||||
if (output.stream.length === 0) throw new NotFoundError('No streams found');
|
||||
|
||||
const playableStreams = await validatePlayableStreams(
|
||||
output.stream,
|
||||
ops,
|
||||
embedScraper.id
|
||||
);
|
||||
if (playableStreams.length === 0)
|
||||
throw new NotFoundError("No playable streams found");
|
||||
const playableStreams = await validatePlayableStreams(output.stream, ops, embedScraper.id);
|
||||
if (playableStreams.length === 0) throw new NotFoundError('No playable streams found');
|
||||
|
||||
if (media)
|
||||
for (const playableStream of playableStreams)
|
||||
playableStream.captions = await addOpenSubtitlesCaptions(
|
||||
playableStream.captions,
|
||||
ops,
|
||||
media
|
||||
);
|
||||
playableStream.captions = await addOpenSubtitlesCaptions(playableStream.captions, ops, media);
|
||||
|
||||
output.stream = playableStreams;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
import { FullScraperEvents, UpdateEvent } from "@/entrypoint/utils/events";
|
||||
import { ScrapeMedia } from "@/entrypoint/utils/media";
|
||||
import { FeatureMap, flagsAllowedInFeatures } from "@/entrypoint/utils/targets";
|
||||
import { UseableFetcher } from "@/fetchers/types";
|
||||
import { EmbedOutput, SourcererOutput } from "@/providers/base";
|
||||
import { ProviderList } from "@/providers/get";
|
||||
import { Stream } from "@/providers/streams";
|
||||
import { ScrapeContext } from "@/utils/context";
|
||||
import { NotFoundError } from "@/utils/errors";
|
||||
import { reorderOnIdList } from "@/utils/list";
|
||||
import { addOpenSubtitlesCaptions } from "@/utils/opensubtitles";
|
||||
import { isValidStream, validatePlayableStream } from "@/utils/valid";
|
||||
import { FullScraperEvents, UpdateEvent } from '@/entrypoint/utils/events';
|
||||
import { ScrapeMedia } from '@/entrypoint/utils/media';
|
||||
import { FeatureMap, flagsAllowedInFeatures } from '@/entrypoint/utils/targets';
|
||||
import { UseableFetcher } from '@/fetchers/types';
|
||||
import { EmbedOutput, SourcererOutput } from '@/providers/base';
|
||||
import { ProviderList } from '@/providers/get';
|
||||
import { Stream } from '@/providers/streams';
|
||||
import { ScrapeContext } from '@/utils/context';
|
||||
import { NotFoundError } from '@/utils/errors';
|
||||
import { reorderOnIdList } from '@/utils/list';
|
||||
import { addOpenSubtitlesCaptions } from '@/utils/opensubtitles';
|
||||
import { isValidStream, validatePlayableStream } from '@/utils/valid';
|
||||
|
||||
export type RunOutput = {
|
||||
sourceId: string;
|
||||
|
|
@ -38,20 +38,15 @@ export type ProviderRunnerOptions = {
|
|||
media: ScrapeMedia;
|
||||
};
|
||||
|
||||
export async function runAllProviders(
|
||||
list: ProviderList,
|
||||
ops: ProviderRunnerOptions
|
||||
): Promise<RunOutput | null> {
|
||||
const sources = reorderOnIdList(ops.sourceOrder ?? [], list.sources).filter(
|
||||
(source) => {
|
||||
if (ops.media.type === "movie") return !!source.scrapeMovie;
|
||||
if (ops.media.type === "show") return !!source.scrapeShow;
|
||||
return false;
|
||||
}
|
||||
);
|
||||
export async function runAllProviders(list: ProviderList, ops: ProviderRunnerOptions): Promise<RunOutput | null> {
|
||||
const sources = reorderOnIdList(ops.sourceOrder ?? [], list.sources).filter((source) => {
|
||||
if (ops.media.type === 'movie') return !!source.scrapeMovie;
|
||||
if (ops.media.type === 'show') return !!source.scrapeShow;
|
||||
return false;
|
||||
});
|
||||
const embeds = reorderOnIdList(ops.embedOrder ?? [], list.embeds);
|
||||
const embedIds = embeds.map((embed) => embed.id);
|
||||
let lastId = "";
|
||||
let lastId = '';
|
||||
|
||||
const contextBase: ScrapeContext = {
|
||||
fetcher: ops.fetcher,
|
||||
|
|
@ -60,7 +55,7 @@ export async function runAllProviders(
|
|||
ops.events?.update?.({
|
||||
id: lastId,
|
||||
percentage: val,
|
||||
status: "pending",
|
||||
status: 'pending',
|
||||
});
|
||||
},
|
||||
};
|
||||
|
|
@ -76,12 +71,12 @@ export async function runAllProviders(
|
|||
// run source scrapers
|
||||
let output: SourcererOutput | null = null;
|
||||
try {
|
||||
if (ops.media.type === "movie" && source.scrapeMovie)
|
||||
if (ops.media.type === 'movie' && source.scrapeMovie)
|
||||
output = await source.scrapeMovie({
|
||||
...contextBase,
|
||||
media: ops.media,
|
||||
});
|
||||
else if (ops.media.type === "show" && source.scrapeShow)
|
||||
else if (ops.media.type === 'show' && source.scrapeShow)
|
||||
output = await source.scrapeShow({
|
||||
...contextBase,
|
||||
media: ops.media,
|
||||
|
|
@ -89,18 +84,16 @@ export async function runAllProviders(
|
|||
if (output) {
|
||||
output.stream = (output.stream ?? [])
|
||||
.filter(isValidStream)
|
||||
.filter((stream) =>
|
||||
flagsAllowedInFeatures(ops.features, stream.flags)
|
||||
);
|
||||
.filter((stream) => flagsAllowedInFeatures(ops.features, stream.flags));
|
||||
}
|
||||
if (!output || (!output.stream?.length && !output.embeds.length)) {
|
||||
throw new NotFoundError("No streams found");
|
||||
throw new NotFoundError('No streams found');
|
||||
}
|
||||
} catch (error) {
|
||||
const updateParams: UpdateEvent = {
|
||||
id: source.id,
|
||||
percentage: 100,
|
||||
status: error instanceof NotFoundError ? "notfound" : "failure",
|
||||
status: error instanceof NotFoundError ? 'notfound' : 'failure',
|
||||
reason: error instanceof NotFoundError ? error.message : undefined,
|
||||
error: error instanceof NotFoundError ? undefined : error,
|
||||
};
|
||||
|
|
@ -108,16 +101,12 @@ export async function runAllProviders(
|
|||
ops.events?.update?.(updateParams);
|
||||
continue;
|
||||
}
|
||||
if (!output) throw new Error("Invalid media type");
|
||||
if (!output) throw new Error('Invalid media type');
|
||||
|
||||
// return stream is there are any
|
||||
if (output.stream?.[0]) {
|
||||
const playableStream = await validatePlayableStream(
|
||||
output.stream[0],
|
||||
ops,
|
||||
source.id
|
||||
);
|
||||
if (!playableStream) throw new NotFoundError("No streams found");
|
||||
const playableStream = await validatePlayableStream(output.stream[0], ops, source.id);
|
||||
if (!playableStream) throw new NotFoundError('No streams found');
|
||||
|
||||
// opensubtitles
|
||||
playableStream.captions = await addOpenSubtitlesCaptions(
|
||||
|
|
@ -125,11 +114,9 @@ export async function runAllProviders(
|
|||
ops,
|
||||
btoa(
|
||||
`${ops.media.imdbId}${
|
||||
ops.media.type === "show"
|
||||
? `.${ops.media.season.number}.${ops.media.episode.number}`
|
||||
: ""
|
||||
}`
|
||||
)
|
||||
ops.media.type === 'show' ? `.${ops.media.season.number}.${ops.media.episode.number}` : ''
|
||||
}`,
|
||||
),
|
||||
);
|
||||
|
||||
return {
|
||||
|
|
@ -144,14 +131,12 @@ export async function runAllProviders(
|
|||
const e = list.embeds.find((v) => v.id === embed.embedId);
|
||||
return e && !e.disabled;
|
||||
})
|
||||
.sort(
|
||||
(a, b) => embedIds.indexOf(a.embedId) - embedIds.indexOf(b.embedId)
|
||||
);
|
||||
.sort((a, b) => embedIds.indexOf(a.embedId) - embedIds.indexOf(b.embedId));
|
||||
|
||||
if (sortedEmbeds.length > 0) {
|
||||
ops.events?.discoverEmbeds?.({
|
||||
embeds: sortedEmbeds.map((embed, i) => ({
|
||||
id: [source.id, i].join("-"),
|
||||
id: [source.id, i].join('-'),
|
||||
embedScraperId: embed.embedId,
|
||||
})),
|
||||
sourceId: source.id,
|
||||
|
|
@ -160,10 +145,10 @@ export async function runAllProviders(
|
|||
|
||||
for (const [ind, embed] of sortedEmbeds.entries()) {
|
||||
const scraper = embeds.find((v) => v.id === embed.embedId);
|
||||
if (!scraper) throw new Error("Invalid embed returned");
|
||||
if (!scraper) throw new Error('Invalid embed returned');
|
||||
|
||||
// run embed scraper
|
||||
const id = [source.id, ind].join("-");
|
||||
const id = [source.id, ind].join('-');
|
||||
ops.events?.start?.(id);
|
||||
lastId = id;
|
||||
|
||||
|
|
@ -175,18 +160,12 @@ export async function runAllProviders(
|
|||
});
|
||||
embedOutput.stream = embedOutput.stream
|
||||
.filter(isValidStream)
|
||||
.filter((stream) =>
|
||||
flagsAllowedInFeatures(ops.features, stream.flags)
|
||||
);
|
||||
.filter((stream) => flagsAllowedInFeatures(ops.features, stream.flags));
|
||||
if (embedOutput.stream.length === 0) {
|
||||
throw new NotFoundError("No streams found");
|
||||
throw new NotFoundError('No streams found');
|
||||
}
|
||||
const playableStream = await validatePlayableStream(
|
||||
embedOutput.stream[0],
|
||||
ops,
|
||||
embed.embedId
|
||||
);
|
||||
if (!playableStream) throw new NotFoundError("No streams found");
|
||||
const playableStream = await validatePlayableStream(embedOutput.stream[0], ops, embed.embedId);
|
||||
if (!playableStream) throw new NotFoundError('No streams found');
|
||||
|
||||
// opensubtitles
|
||||
playableStream.captions = await addOpenSubtitlesCaptions(
|
||||
|
|
@ -194,18 +173,16 @@ export async function runAllProviders(
|
|||
ops,
|
||||
btoa(
|
||||
`${ops.media.imdbId}${
|
||||
ops.media.type === "show"
|
||||
? `.${ops.media.season.number}.${ops.media.episode.number}`
|
||||
: ""
|
||||
}`
|
||||
)
|
||||
ops.media.type === 'show' ? `.${ops.media.season.number}.${ops.media.episode.number}` : ''
|
||||
}`,
|
||||
),
|
||||
);
|
||||
embedOutput.stream = [playableStream];
|
||||
} catch (error) {
|
||||
const updateParams: UpdateEvent = {
|
||||
id: source.id,
|
||||
percentage: 100,
|
||||
status: error instanceof NotFoundError ? "notfound" : "failure",
|
||||
status: error instanceof NotFoundError ? 'notfound' : 'failure',
|
||||
reason: error instanceof NotFoundError ? error.message : undefined,
|
||||
error: error instanceof NotFoundError ? undefined : error,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,44 +1,35 @@
|
|||
import {
|
||||
Caption,
|
||||
labelToLanguageCode,
|
||||
removeDuplicatedLanguages,
|
||||
} from "@/providers/captions";
|
||||
import { IndividualEmbedRunnerOptions } from "@/runners/individualRunner";
|
||||
import { ProviderRunnerOptions } from "@/runners/runner";
|
||||
import { Caption, labelToLanguageCode, removeDuplicatedLanguages } from '@/providers/captions';
|
||||
import { IndividualEmbedRunnerOptions } from '@/runners/individualRunner';
|
||||
import { ProviderRunnerOptions } from '@/runners/runner';
|
||||
|
||||
export async function addOpenSubtitlesCaptions(
|
||||
captions: Caption[],
|
||||
ops: ProviderRunnerOptions | IndividualEmbedRunnerOptions,
|
||||
media: string
|
||||
media: string,
|
||||
): Promise<Caption[]> {
|
||||
try {
|
||||
const [imdbId, season, episode] = atob(media)
|
||||
.split(".")
|
||||
.split('.')
|
||||
.map((x, i) => (i === 0 ? x : Number(x) || null));
|
||||
if (!imdbId) return captions;
|
||||
const Res: {
|
||||
LanguageName: string;
|
||||
SubDownloadLink: string;
|
||||
SubFormat: "srt" | "vtt";
|
||||
SubFormat: 'srt' | 'vtt';
|
||||
}[] = await ops.proxiedFetcher(
|
||||
`https://rest.opensubtitles.org/search/${
|
||||
season && episode ? `episode-${episode}/` : ""
|
||||
}imdbid-${(imdbId as string).slice(2)}${
|
||||
season && episode ? `/season-${season}` : ""
|
||||
}`,
|
||||
season && episode ? `episode-${episode}/` : ''
|
||||
}imdbid-${(imdbId as string).slice(2)}${season && episode ? `/season-${season}` : ''}`,
|
||||
{
|
||||
headers: {
|
||||
"X-User-Agent": "VLSub 0.10.2",
|
||||
'X-User-Agent': 'VLSub 0.10.2',
|
||||
},
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
const openSubtilesCaptions: Caption[] = [];
|
||||
for (const caption of Res) {
|
||||
const url = caption.SubDownloadLink.replace(".gz", "").replace(
|
||||
"download/",
|
||||
"download/subencoding-utf8/"
|
||||
);
|
||||
const url = caption.SubDownloadLink.replace('.gz', '').replace('download/', 'download/subencoding-utf8/');
|
||||
const language = labelToLanguageCode(caption.LanguageName);
|
||||
if (!url || !language) continue;
|
||||
else
|
||||
|
|
@ -46,7 +37,7 @@ export async function addOpenSubtitlesCaptions(
|
|||
id: url,
|
||||
opensubtitles: true,
|
||||
url,
|
||||
type: caption.SubFormat || "srt",
|
||||
type: caption.SubFormat || 'srt',
|
||||
hasCorsRestrictions: false,
|
||||
language,
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue