mirror of
https://github.com/p-stream/providers.git
synced 2026-01-11 20:10:33 +00:00
This commit is contained in:
parent
abefc21dc7
commit
488dbba9f9
4 changed files with 321 additions and 0 deletions
|
|
@ -29,6 +29,7 @@ import { cinemaosEmbeds } from './embeds/cinemaos';
|
|||
import { closeLoadScraper } from './embeds/closeload';
|
||||
import { droploadScraper } from './embeds/dropload';
|
||||
import { filelionsScraper } from './embeds/filelions';
|
||||
import { hubcloudScraper } from './embeds/hubcloud';
|
||||
import { madplayBaseEmbed, madplayNsapiEmbed, madplayNsapiVidFastEmbed, madplayRoperEmbed } from './embeds/madplay';
|
||||
import { mp4hydraServer1Scraper, mp4hydraServer2Scraper } from './embeds/mp4hydra';
|
||||
import { myanimedubScraper } from './embeds/myanimedub';
|
||||
|
|
@ -80,7 +81,9 @@ import { cinemaosScraper } from './sources/cinemaos';
|
|||
import { coitusScraper } from './sources/coitus';
|
||||
import { cuevana3Scraper } from './sources/cuevana3';
|
||||
import { debridScraper } from './sources/debrid';
|
||||
import { einschaltenScraper } from './sources/einschalten';
|
||||
import { embedsuScraper } from './sources/embedsu';
|
||||
import { fourkhdhubScraper } from './sources/fourkhdhub';
|
||||
import { hdRezkaScraper } from './sources/hdrezka';
|
||||
import { iosmirrorScraper } from './sources/iosmirror';
|
||||
import { iosmirrorPVScraper } from './sources/iosmirrorpv';
|
||||
|
|
@ -148,7 +151,9 @@ export function gatherAllSources(): Array<Sourcerer> {
|
|||
primewireScraper,
|
||||
movies4fScraper,
|
||||
debridScraper,
|
||||
fourkhdhubScraper,
|
||||
cinehdplusScraper,
|
||||
einschaltenScraper,
|
||||
];
|
||||
}
|
||||
|
||||
|
|
@ -217,6 +222,7 @@ export function gatherAllEmbeds(): Array<Embed> {
|
|||
vidhideSpanishScraper,
|
||||
vidhideEnglishScraper,
|
||||
filelionsScraper,
|
||||
hubcloudScraper,
|
||||
droploadScraper,
|
||||
supervideoScraper,
|
||||
];
|
||||
|
|
|
|||
83
src/providers/embeds/hubcloud.ts
Normal file
83
src/providers/embeds/hubcloud.ts
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
import { load } from 'cheerio';
|
||||
|
||||
import { makeEmbed } from '@/providers/base';
|
||||
import { NotFoundError } from '@/utils/errors';
|
||||
|
||||
export const hubcloudScraper = makeEmbed({
|
||||
id: 'hubcloud',
|
||||
name: 'HubCloud',
|
||||
rank: 140,
|
||||
async scrape(ctx) {
|
||||
// Fetch the HubCloud page with referer
|
||||
const html = await ctx.proxiedFetcher(ctx.url, {
|
||||
headers: {
|
||||
Referer: ctx.url, // Use the HubCloud URL as referer
|
||||
},
|
||||
});
|
||||
|
||||
// Extract redirect URL from var url = '...'
|
||||
const redirectUrlMatch = html.match(/var url\s*=\s*['"](.*?)['"]/);
|
||||
if (!redirectUrlMatch) throw new NotFoundError('Redirect URL not found');
|
||||
|
||||
// Fetch the links page
|
||||
const linksHtml = await ctx.proxiedFetcher(redirectUrlMatch[1], {
|
||||
headers: {
|
||||
Referer: ctx.url,
|
||||
},
|
||||
});
|
||||
|
||||
const $ = load(linksHtml);
|
||||
|
||||
const qualities: Record<string, { type: 'mp4'; url: string }> = {};
|
||||
|
||||
// Extract FSL links
|
||||
$('a')
|
||||
.filter((_i, el) => {
|
||||
const text = $(el).text();
|
||||
return text.includes('FSL') || text.includes('Download File');
|
||||
})
|
||||
.each((i, el) => {
|
||||
const url = $(el).attr('href');
|
||||
if (url) {
|
||||
const fullUrl = url.startsWith('http') ? url : `https://hubcloud.fyi${url}`;
|
||||
qualities[`fsl_${i}`] = {
|
||||
type: 'mp4',
|
||||
url: fullUrl,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// Extract PixelServer links
|
||||
$('a')
|
||||
.filter((_i, el) => $(el).text().includes('PixelServer'))
|
||||
.each((i, el) => {
|
||||
const url = $(el).attr('href');
|
||||
if (url) {
|
||||
// Convert PixelServer URL to API endpoint
|
||||
const apiUrl = url.replace('/u/', '/api/file/');
|
||||
const fullUrl = apiUrl.startsWith('http') ? apiUrl : `https://hubcloud.fyi${apiUrl}`;
|
||||
qualities[`pixelserver_${i}`] = {
|
||||
type: 'mp4',
|
||||
url: fullUrl,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
if (Object.keys(qualities).length === 0) throw new NotFoundError('No playable links found');
|
||||
|
||||
return {
|
||||
stream: [
|
||||
{
|
||||
id: 'primary',
|
||||
type: 'file',
|
||||
qualities,
|
||||
flags: [],
|
||||
headers: {
|
||||
referer: 'https://hubcloud.fyi',
|
||||
},
|
||||
captions: [],
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
});
|
||||
47
src/providers/sources/einschalten.ts
Normal file
47
src/providers/sources/einschalten.ts
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
import { flags } from '@/entrypoint/utils/targets';
|
||||
import { SourcererOutput, makeSourcerer } from '@/providers/base';
|
||||
import { MovieScrapeContext } from '@/utils/context';
|
||||
import { NotFoundError } from '@/utils/errors';
|
||||
|
||||
interface EinschaltenResponse {
|
||||
releaseName: string;
|
||||
streamUrl: string;
|
||||
}
|
||||
|
||||
const baseUrl = 'https://einschalten.in';
|
||||
|
||||
async function scrapeMovie(ctx: MovieScrapeContext): Promise<SourcererOutput> {
|
||||
const apiUrl = `${baseUrl}/api/movies/${ctx.media.tmdbId}/watch`;
|
||||
|
||||
const response = await ctx.proxiedFetcher<EinschaltenResponse>(apiUrl, {
|
||||
headers: {
|
||||
Referer: baseUrl,
|
||||
'User-Agent':
|
||||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36',
|
||||
},
|
||||
});
|
||||
|
||||
if (!response?.streamUrl) {
|
||||
throw new NotFoundError('No stream URL found');
|
||||
}
|
||||
|
||||
ctx.progress(90);
|
||||
|
||||
return {
|
||||
embeds: [
|
||||
{
|
||||
embedId: 'dood',
|
||||
url: response.streamUrl,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
export const einschaltenScraper = makeSourcerer({
|
||||
id: 'einschalten',
|
||||
name: 'Einschalten',
|
||||
rank: 170,
|
||||
disabled: false,
|
||||
flags: [],
|
||||
scrapeMovie,
|
||||
});
|
||||
185
src/providers/sources/fourkhdhub.ts
Normal file
185
src/providers/sources/fourkhdhub.ts
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
import { load } from 'cheerio';
|
||||
|
||||
import { SourcererOutput, makeSourcerer } from '@/providers/base';
|
||||
import { MovieScrapeContext, ShowScrapeContext } from '@/utils/context';
|
||||
import { NotFoundError } from '@/utils/errors';
|
||||
|
||||
// ROT13 cipher implementation
|
||||
function rot13Cipher(input: string): string {
|
||||
return input.replace(/[a-zA-Z]/g, (char) => {
|
||||
const code = char.charCodeAt(0);
|
||||
const base = code >= 65 && code <= 90 ? 65 : 97; // A or a
|
||||
return String.fromCharCode(((code - base + 13) % 26) + base);
|
||||
});
|
||||
}
|
||||
|
||||
// Levenshtein distance implementation for string similarity
|
||||
function levenshteinDistance(a: string, b: string): number {
|
||||
if (a.length === 0) return b.length;
|
||||
if (b.length === 0) return a.length;
|
||||
|
||||
const matrix = Array.from({ length: b.length + 1 }, (_, i) => [i]);
|
||||
matrix[0] = Array.from({ length: a.length + 1 }, (_, i) => i);
|
||||
|
||||
for (let i = 1; i <= b.length; i++) {
|
||||
for (let j = 1; j <= a.length; j++) {
|
||||
const cost = a[j - 1] === b[i - 1] ? 0 : 1;
|
||||
matrix[i][j] = Math.min(
|
||||
matrix[i - 1][j] + 1, // deletion
|
||||
matrix[i][j - 1] + 1, // insertion
|
||||
matrix[i - 1][j - 1] + cost, // substitution
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return matrix[b.length][a.length];
|
||||
}
|
||||
|
||||
const baseUrl = 'https://4khdhub.fans';
|
||||
|
||||
// Helper function to resolve redirect URLs
|
||||
async function resolveRedirectUrl(ctx: ShowScrapeContext | MovieScrapeContext, redirectUrl: URL): Promise<URL> {
|
||||
const redirectHtml = await ctx.proxiedFetcher(redirectUrl.toString());
|
||||
const redirectDataMatch = redirectHtml.match(/'o','(.*?)'/) as RegExpMatchArray;
|
||||
if (!redirectDataMatch) throw new NotFoundError('No redirect data found');
|
||||
|
||||
const redirectData = JSON.parse(atob(rot13Cipher(atob(atob(redirectDataMatch[1]))))) as { o: string };
|
||||
return new URL(atob(redirectData.o));
|
||||
}
|
||||
|
||||
// Helper function to extract embed URL (HubCloud URL)
|
||||
async function extractEmbedUrl(
|
||||
ctx: ShowScrapeContext | MovieScrapeContext,
|
||||
$: ReturnType<typeof load>,
|
||||
$item: ReturnType<ReturnType<typeof load>>,
|
||||
): Promise<string> {
|
||||
// Try HubCloud first
|
||||
const hubCloudLink = $item.find('a:contains("HubCloud")').first();
|
||||
if (hubCloudLink.length > 0) {
|
||||
const redirectUrl = new URL(hubCloudLink.attr('href') || '', baseUrl);
|
||||
return (await resolveRedirectUrl(ctx, redirectUrl)).toString();
|
||||
}
|
||||
|
||||
// Fallback to HubDrive
|
||||
const hubDriveLink = $item.find('a:contains("HubDrive")').first();
|
||||
if (hubDriveLink.length > 0) {
|
||||
const redirectUrl = new URL(hubDriveLink.attr('href') || '', baseUrl);
|
||||
const hubDriveHtml = await ctx.proxiedFetcher((await resolveRedirectUrl(ctx, redirectUrl)).toString());
|
||||
const $hubDrive = load(hubDriveHtml);
|
||||
const hubCloudUrl = $hubDrive('a:contains("HubCloud")').attr('href');
|
||||
if (hubCloudUrl) {
|
||||
return hubCloudUrl.startsWith('http') ? hubCloudUrl : `https://hubcloud.fyi${hubCloudUrl}`;
|
||||
}
|
||||
}
|
||||
|
||||
throw new NotFoundError('No valid embed URL found');
|
||||
}
|
||||
|
||||
// Main scraper function
|
||||
async function scrapeFourKHDHub(ctx: ShowScrapeContext | MovieScrapeContext): Promise<SourcererOutput> {
|
||||
// Build search URL
|
||||
const searchUrl = new URL(`/?s=${encodeURIComponent(`${ctx.media.title} ${ctx.media.releaseYear}`)}`, baseUrl);
|
||||
|
||||
// Fetch search results
|
||||
const searchPage = await ctx.proxiedFetcher(searchUrl.toString());
|
||||
const $search = load(searchPage);
|
||||
|
||||
// Find matching movie/TV show
|
||||
const isSeries = ctx.media.type === 'show';
|
||||
const contentType = isSeries ? 'Series' : 'Movies';
|
||||
|
||||
const matchingCard = $search(`.movie-card:has(.movie-card-format:contains("${contentType}"))`)
|
||||
.filter((_, el) => {
|
||||
const movieCardYear = parseInt($search('.movie-card-meta', el).text(), 10);
|
||||
return Math.abs(movieCardYear - ctx.media.releaseYear) <= 1;
|
||||
})
|
||||
.filter((_, el) => {
|
||||
const movieCardTitle = $search('.movie-card-title', el)
|
||||
.text()
|
||||
.replace(/\[.*?]/, '')
|
||||
.trim();
|
||||
return levenshteinDistance(movieCardTitle.toLowerCase(), ctx.media.title.toLowerCase()) < 5;
|
||||
})
|
||||
.first();
|
||||
|
||||
if (!matchingCard.length) throw new NotFoundError('No matching content found');
|
||||
|
||||
const contentUrl = $search(matchingCard).attr('href');
|
||||
if (!contentUrl) throw new NotFoundError('No content URL found');
|
||||
|
||||
ctx.progress(30);
|
||||
|
||||
// Fetch content page
|
||||
const contentPage = await ctx.proxiedFetcher(contentUrl, { baseUrl });
|
||||
const $content = load(contentPage);
|
||||
|
||||
const embeds: Array<{
|
||||
embedId: string;
|
||||
url: string;
|
||||
}> = [];
|
||||
|
||||
if (isSeries) {
|
||||
// Handle TV shows
|
||||
const showCtx = ctx as ShowScrapeContext;
|
||||
const seasonNumber = showCtx.media.season.number;
|
||||
const episodeNumber = showCtx.media.episode.number;
|
||||
|
||||
const episodeItems = $content(`.episode-item`)
|
||||
.filter((_i, el) =>
|
||||
$content('.episode-title', el)
|
||||
.text()
|
||||
.includes(`S${String(seasonNumber).padStart(2, '0')}`),
|
||||
)
|
||||
.map((_i, el) => ({
|
||||
downloadItem: $content('.episode-download-item', el)
|
||||
.filter((_j, item) =>
|
||||
$content(item)
|
||||
.text()
|
||||
.includes(`Episode-${String(episodeNumber).padStart(2, '0')}`),
|
||||
)
|
||||
.first(),
|
||||
}))
|
||||
.get()
|
||||
.filter(({ downloadItem }) => downloadItem.length > 0);
|
||||
|
||||
for (const { downloadItem } of episodeItems) {
|
||||
try {
|
||||
const embedUrl = await extractEmbedUrl(ctx, $content, downloadItem);
|
||||
embeds.push({ embedId: 'hubcloud', url: embedUrl });
|
||||
} catch (error) {
|
||||
// Skip failed extractions
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Handle movies
|
||||
const downloadItems = $content('.download-item').get();
|
||||
|
||||
for (const item of downloadItems) {
|
||||
try {
|
||||
const $item = $content(item);
|
||||
const embedUrl = await extractEmbedUrl(ctx, $content, $item);
|
||||
embeds.push({ embedId: 'hubcloud', url: embedUrl });
|
||||
} catch (error) {
|
||||
// Skip failed extractions
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (embeds.length === 0) throw new NotFoundError('No embeds found');
|
||||
|
||||
ctx.progress(90);
|
||||
|
||||
return { embeds };
|
||||
}
|
||||
|
||||
export const fourkhdhubScraper = makeSourcerer({
|
||||
id: '4khdhub',
|
||||
name: '4KHDHub',
|
||||
rank: 160,
|
||||
disabled: false,
|
||||
flags: [],
|
||||
scrapeMovie: scrapeFourKHDHub,
|
||||
scrapeShow: scrapeFourKHDHub,
|
||||
});
|
||||
Loading…
Reference in a new issue