mirror of
https://github.com/p-stream/providers.git
synced 2026-01-11 20:10:33 +00:00
add debrid source (not enabled yet)
This commit is contained in:
parent
35199c5b8e
commit
cafa41add6
3 changed files with 209 additions and 0 deletions
|
|
@ -98,6 +98,7 @@ import vidnestScraper from './sources/vidnest';
|
|||
import { warezcdnScraper } from './sources/warezcdn';
|
||||
import { wecimaScraper } from './sources/wecima';
|
||||
import { zunimeScraper } from './sources/zunime';
|
||||
import { debridScraper } from './sources/debrid';
|
||||
|
||||
export function gatherAllSources(): Array<Sourcerer> {
|
||||
// all sources are gathered here
|
||||
|
|
@ -142,6 +143,7 @@ export function gatherAllSources(): Array<Sourcerer> {
|
|||
pelisplushdScraper,
|
||||
primewireScraper,
|
||||
movies4fScraper,
|
||||
debridScraper,
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
|||
186
src/providers/sources/debrid/index.ts
Normal file
186
src/providers/sources/debrid/index.ts
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
import { flags } from '@/entrypoint/utils/targets';
|
||||
import { SourcererOutput, makeSourcerer } from '@/providers/base';
|
||||
import { MovieScrapeContext, ShowScrapeContext } from '@/utils/context';
|
||||
import { NotFoundError } from '@/utils/errors';
|
||||
|
||||
import { debridProviders, torrentioResponse } from './types';
|
||||
|
||||
const OVERRIDE_TOKEN = '';
|
||||
const OVERRIDE_SERVICE = ''; // torbox or realdebrid (or real-debrid)
|
||||
|
||||
const getDebridToken = (): string | null => {
|
||||
try {
|
||||
if (OVERRIDE_TOKEN) return OVERRIDE_TOKEN;
|
||||
} catch {
|
||||
// Ignore
|
||||
}
|
||||
try {
|
||||
if (typeof window === 'undefined') return null;
|
||||
const prefData = window.localStorage.getItem('__MW::preferences');
|
||||
if (!prefData) return null;
|
||||
const parsedAuth = JSON.parse(prefData);
|
||||
return parsedAuth?.state?.debridToken || null;
|
||||
} catch (e) {
|
||||
console.error('Error getting debrid token:', e);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
// torbox or realdebrid (coverted to `real-debrid` which is the type for torrentio)
|
||||
const getDebridService = (): debridProviders => {
|
||||
try {
|
||||
if (OVERRIDE_SERVICE) return OVERRIDE_SERVICE as debridProviders;
|
||||
} catch {
|
||||
// Ignore
|
||||
}
|
||||
try {
|
||||
if (typeof window === 'undefined') return 'real-debrid';
|
||||
const prefData = window.localStorage.getItem('__MW::preferences');
|
||||
if (!prefData) return 'real-debrid';
|
||||
const parsedPrefs = JSON.parse(prefData);
|
||||
const saved = parsedPrefs?.state?.debridService;
|
||||
if (saved === 'realdebrid' || !saved) return 'real-debrid';
|
||||
return saved as debridProviders;
|
||||
} catch (e) {
|
||||
console.error('Error getting debrid service (defaulting to real-debrid):', e);
|
||||
return 'real-debrid';
|
||||
}
|
||||
};
|
||||
|
||||
type DebridParsedStream = {
|
||||
resolution?: string;
|
||||
year?: number;
|
||||
source?: string;
|
||||
bitDepth?: string;
|
||||
codec?: string;
|
||||
audio?: string;
|
||||
container?: string;
|
||||
seasons?: number[];
|
||||
season?: number;
|
||||
episodes?: number[];
|
||||
episode?: number;
|
||||
complete?: boolean;
|
||||
unrated?: boolean;
|
||||
remastered?: boolean;
|
||||
languages?: string[];
|
||||
dubbed?: boolean;
|
||||
title: string;
|
||||
url: string;
|
||||
};
|
||||
|
||||
function normalizeQuality(resolution?: string): '4k' | 1080 | 720 | 480 | 360 | 'unknown' {
|
||||
if (!resolution) return 'unknown';
|
||||
const res = resolution.toLowerCase();
|
||||
if (res === '4k' || res === '2160p') return '4k';
|
||||
if (res === '1080p') return 1080;
|
||||
if (res === '720p') return 720;
|
||||
if (res === '480p') return 480;
|
||||
if (res === '360p') return 360;
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
// Helper to score streams for compatibility (higher is better)
|
||||
function scoreStream(stream: DebridParsedStream): number {
|
||||
let score = 0;
|
||||
// Prefer mp4 container
|
||||
if (stream.container === 'mp4') score += 10;
|
||||
// Prefer aac audio
|
||||
if (stream.audio === 'aac') score += 5;
|
||||
// Prefer h265 codec
|
||||
if (stream.codec === 'h265') score += 2;
|
||||
// Penalize mkv container
|
||||
if (stream.container === 'mkv') score -= 2;
|
||||
// Prefer complete
|
||||
if (stream.complete) score += 1;
|
||||
return score;
|
||||
}
|
||||
|
||||
async function comboScraper(ctx: ShowScrapeContext | MovieScrapeContext): Promise<SourcererOutput> {
|
||||
const apiKey = getDebridToken();
|
||||
if (!apiKey) {
|
||||
throw new NotFoundError('Debrid API token is required');
|
||||
}
|
||||
|
||||
const debridProvider: debridProviders = getDebridService();
|
||||
|
||||
let torrentioUrl = `https://torrentio.strem.fun/${debridProvider}=${apiKey}/stream/`;
|
||||
|
||||
if (ctx.media.type === 'show') {
|
||||
torrentioUrl += `series/${ctx.media.imdbId}:${ctx.media.season.number}:${ctx.media.episode.number}.json`;
|
||||
} else {
|
||||
torrentioUrl += `movie/${ctx.media.imdbId}.json`;
|
||||
}
|
||||
const torrentioStreams: torrentioResponse = (await ctx.proxiedFetcher(torrentioUrl)).streams;
|
||||
|
||||
const response: DebridParsedStream[] = await ctx.proxiedFetcher('https://torrent-parse.pstream.mov/', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(torrentioStreams),
|
||||
});
|
||||
|
||||
// Group by quality, pick the most compatible stream for each
|
||||
const qualities: Partial<Record<'4k' | 1080 | 720 | 480 | 360 | 'unknown', { type: 'mp4'; url: string }>> = {};
|
||||
|
||||
// Group streams by normalized quality
|
||||
const byQuality: Record<string, DebridParsedStream[]> = {};
|
||||
for (const stream of response) {
|
||||
const quality = normalizeQuality(stream.resolution);
|
||||
if (!byQuality[quality]) byQuality[quality] = [];
|
||||
byQuality[quality].push(stream);
|
||||
}
|
||||
|
||||
// For each quality, pick the best compatible stream (prefer mp4+aac, fallback to mkv)
|
||||
for (const [quality, streams] of Object.entries(byQuality)) {
|
||||
// Prefer mp4 + aac
|
||||
const mp4Aac = streams.find((s) => s.container === 'mp4' && s.audio === 'aac');
|
||||
if (mp4Aac) {
|
||||
qualities[quality as keyof typeof qualities] = {
|
||||
type: 'mp4',
|
||||
url: mp4Aac.url,
|
||||
};
|
||||
continue;
|
||||
}
|
||||
// Fallback: any mp4
|
||||
const mp4 = streams.find((s) => s.container === 'mp4');
|
||||
if (mp4) {
|
||||
qualities[quality as keyof typeof qualities] = {
|
||||
type: 'mp4',
|
||||
url: mp4.url,
|
||||
};
|
||||
continue;
|
||||
}
|
||||
streams.sort((a, b) => scoreStream(b) - scoreStream(a));
|
||||
const best = streams[0];
|
||||
if (best) {
|
||||
qualities[quality as keyof typeof qualities] = {
|
||||
type: 'mp4', // has to be set as mp4 because of types..... But mkvs *can* work in a browser depending on codec, usually it cant be hevc and has to have AAC audio
|
||||
url: best.url,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
embeds: [],
|
||||
stream: [
|
||||
{
|
||||
id: 'primary',
|
||||
type: 'file',
|
||||
qualities,
|
||||
captions: [],
|
||||
flags: [],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
export const debridScraper = makeSourcerer({
|
||||
id: 'debrid',
|
||||
name: 'Debrid',
|
||||
rank: 999,
|
||||
disabled: true,
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
scrapeMovie: comboScraper,
|
||||
scrapeShow: comboScraper,
|
||||
});
|
||||
21
src/providers/sources/debrid/types.ts
Normal file
21
src/providers/sources/debrid/types.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
// these need to be the names that correspond with Torrentio's
|
||||
export type debridProviders = 'torbox' | 'real-debrid';
|
||||
|
||||
export interface stremioStream {
|
||||
name: string;
|
||||
title: string;
|
||||
url: string;
|
||||
behaviorHints?: {
|
||||
bingeGroup: string;
|
||||
filename: string;
|
||||
videoSize: number;
|
||||
videoHash: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface torrentioResponse {
|
||||
streams: stremioStream[];
|
||||
cacheMaxAge: number;
|
||||
staleRevalidate: number;
|
||||
staleError: number;
|
||||
}
|
||||
Loading…
Reference in a new issue