add webtor

This commit is contained in:
Pas 2025-01-30 11:47:25 -07:00
parent 7b7e019afb
commit 06a9ceb7a5
5 changed files with 195 additions and 0 deletions

View file

@ -76,6 +76,7 @@ import { voeScraper } from './embeds/voe';
import { warezcdnembedHlsScraper } from './embeds/warezcdn/hls';
import { warezcdnembedMp4Scraper } from './embeds/warezcdn/mp4';
import { warezPlayerScraper } from './embeds/warezcdn/warezplayer';
import { webtor1080Scraper, webtor480Scraper, webtor4kScraper, webtor720Scraper } from './embeds/webtor';
import { wootlyScraper } from './embeds/wootly';
import { goojaraScraper } from './sources/goojara';
import { hdRezkaScraper } from './sources/hdrezka';
@ -88,6 +89,7 @@ import { smashyStreamScraper } from './sources/smashystream';
import { soaperTvScraper } from './sources/soapertv';
import { vidSrcToScraper } from './sources/vidsrcto';
import { warezcdnScraper } from './sources/warezcdn';
import { webtorScraper } from './sources/webtor';
export function gatherAllSources(): Array<Sourcerer> {
// all sources are gathered here
@ -124,6 +126,7 @@ export function gatherAllSources(): Array<Sourcerer> {
vidsrcsuScraper,
TASFScraper,
mp4hydraScraper,
webtorScraper,
];
}
@ -186,5 +189,9 @@ export function gatherAllEmbeds(): Array<Embed> {
VidsrcsuServer9Scraper,
VidsrcsuServer10Scraper,
VidsrcsuServer11Scraper,
webtor4kScraper,
webtor1080Scraper,
webtor720Scraper,
webtor480Scraper,
];
}

View file

@ -0,0 +1,32 @@
import { flags } from '@/entrypoint/utils/targets';
import { makeEmbed } from '@/providers/base';
const providers = [
{ id: 'webtor-1080', rank: 80 }, // 1080p should be a higher rank becuase it loads faster
{ id: 'webtor-4k', rank: 79 },
{ id: 'webtor-720', rank: 78 },
{ id: 'webtor-480', rank: 77 },
];
function embed(provider: { id: string; rank: number }) {
return makeEmbed({
id: provider.id,
name: `Webtor ${provider.id.split('-')[1].toUpperCase()}`,
rank: provider.rank,
async scrape(ctx) {
return {
stream: [
{
id: 'primary',
type: 'hls',
playlist: ctx.url,
flags: [flags.CORS_ALLOWED],
captions: [],
},
],
};
},
});
}
export const [webtor4kScraper, webtor1080Scraper, webtor720Scraper, webtor480Scraper] = providers.map(embed);

View file

@ -0,0 +1,69 @@
import { Stream } from './types';
const trackers = [
'udp://tracker.opentrackr.org:1337/announce',
'udp://open.demonii.com:1337/announce',
'udp://open.tracker.cl:1337/announce',
'udp://open.stealth.si:80/announce',
'udp://tracker.torrent.eu.org:451/announce',
'udp://explodie.org:6969/announce',
'udp://tracker.qu.ax:6969/announce',
'udp://tracker.ololosh.space:6969/announce',
'udp://tracker.dump.cl:6969/announce',
'udp://tracker.dler.org:6969/announce',
'udp://tracker.bittor.pw:1337/announce',
'udp://tracker-udp.gbitt.info:80/announce',
'udp://opentracker.io:6969/announce',
'udp://open.free-tracker.ga:6969/announce',
'udp://ns-1.x-fins.com:6969/announce',
'udp://leet-tracker.moe:1337/announce',
'udp://isk.richardsw.club:6969/announce',
'udp://discord.heihachi.pw:6969/announce',
'http://www.torrentsnipe.info:2701/announce',
'http://www.genesis-sp.org:2710/announce',
];
export function getMagnetUrl(infoHash: string, name: string): string {
const encodedName = encodeURIComponent(name);
const trackerParams = trackers.map((tracker) => `&tr=${encodeURIComponent(tracker)}`).join('');
return `magnet:?xt=urn:btih:${infoHash}&dn=${encodedName}${trackerParams}`;
}
export function constructProxyUrl(magnetUrl: string): string {
const encodedMagnet = encodeURIComponent(magnetUrl);
return `https://savingshub.online/api/fetchHls?magnet=${encodedMagnet}`;
}
export function categorizeStreams(streams: Stream[]): Record<string, Stream[]> {
const categories: Record<string, Stream[]> = {
'4k': [],
'1080p': [],
'720p': [],
'480p': [],
};
streams.forEach((stream) => {
const name = stream.name.toLowerCase();
if (name.includes('4k')) {
categories['4k'].push(stream);
} else if (name.includes('1080p')) {
categories['1080p'].push(stream);
} else if (name.includes('720p')) {
categories['720p'].push(stream);
} else if (name.includes('480p')) {
categories['480p'].push(stream);
}
});
return categories;
}
export function getTopStreamsBySeeders(categoryStreams: Stream[], limit: number): Stream[] {
return categoryStreams
.sort((a, b) => {
const seedersA = parseInt(a.title.match(/👤 (\d+) /)?.[1] || '0', 10);
const seedersB = parseInt(b.title.match(/👤 (\d+) /)?.[1] || '0', 10);
return seedersB - seedersA;
})
.slice(0, limit);
}

View file

@ -0,0 +1,70 @@
import { flags } from '@/entrypoint/utils/targets';
import { SourcererOutput, makeSourcerer } from '@/providers/base';
import { MovieScrapeContext, ShowScrapeContext } from '@/utils/context';
import { categorizeStreams, constructProxyUrl, getMagnetUrl, getTopStreamsBySeeders } from './common';
import { Response } from './types';
async function comboScraper(ctx: ShowScrapeContext | MovieScrapeContext): Promise<SourcererOutput> {
const search =
ctx.media.type === 'movie'
? `movie/${ctx.media.imdbId}.json`
: `series/${ctx.media.imdbId}:${ctx.media.season.number}:${ctx.media.episode.number}.json`;
const response: Response = await ctx
.fetcher(
`https://torrentio.strem.fun/providers=yts,eztv,rarbg,1337x,thepiratebay,kickasstorrents,torrentgalaxy,magnetdl,horriblesubs,nyaasi,tokyotosho,anidex/stream/${search}`,
)
.then((res) => (typeof res === 'string' ? JSON.parse(res) : res));
ctx.progress(50);
const categories = categorizeStreams(response.streams);
const embeds: { embedId: string; url: string }[] = [];
const qualityResults = await Promise.all(
Object.entries(categories).map(async ([category, streams]) => {
const [topStream] = getTopStreamsBySeeders(streams, 1);
if (!topStream) return null;
try {
const magnet = getMagnetUrl(topStream.infoHash, topStream.name);
const apiUrl = constructProxyUrl(magnet);
const apiResponse = await ctx.fetcher(apiUrl);
const responseData = typeof apiResponse === 'string' ? JSON.parse(apiResponse) : apiResponse;
if (!responseData?.m3u8Link) throw new Error('No m3u8 link in response');
return {
quality: category,
url: responseData.m3u8Link,
};
} catch (error) {
console.error(`Failed to fetch ${category}:`, error);
return null;
}
}),
);
qualityResults.forEach((result) => {
if (result?.url) {
embeds.push({
embedId: `webtor-${result.quality.replace('p', '')}`,
url: result.url,
});
}
});
ctx.progress(90);
return { embeds };
}
export const webtorScraper = makeSourcerer({
id: 'webtor',
name: 'Webtor',
rank: 5,
disabled: false,
flags: [flags.CORS_ALLOWED],
scrapeMovie: comboScraper,
scrapeShow: comboScraper,
});

View file

@ -0,0 +1,17 @@
export interface Stream {
name: string;
title: string;
infoHash: string;
fileIdx: number;
behaviorHints: {
bingeGroup: string;
filename: string;
};
}
export interface Response {
streams: Stream[];
cacheMaxAge: number;
staleRevalidate: number;
staleError: number;
}