mirror of
https://github.com/p-stream/providers.git
synced 2026-01-11 20:10:33 +00:00
add movies4F
This commit is contained in:
parent
50a600ab34
commit
3f90c3d413
2 changed files with 213 additions and 0 deletions
|
|
@ -81,6 +81,7 @@ import { iosmirrorScraper } from './sources/iosmirror';
|
|||
import { iosmirrorPVScraper } from './sources/iosmirrorpv';
|
||||
import { lookmovieScraper } from './sources/lookmovie';
|
||||
import { madplayScraper } from './sources/madplay';
|
||||
import { movies4fScraper } from './sources/movies4f';
|
||||
import { myanimeScraper } from './sources/myanime';
|
||||
import { nunflixScraper } from './sources/nunflix';
|
||||
import { pelisplushdScraper } from './sources/pelisplushd';
|
||||
|
|
@ -140,6 +141,7 @@ export function gatherAllSources(): Array<Sourcerer> {
|
|||
turbovidSourceScraper,
|
||||
pelisplushdScraper,
|
||||
primewireScraper,
|
||||
movies4fScraper,
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
|||
211
src/providers/sources/movies4f.ts
Normal file
211
src/providers/sources/movies4f.ts
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
import { load } from 'cheerio';
|
||||
|
||||
import { flags } from '@/entrypoint/utils/targets';
|
||||
import { SourcererOutput, makeSourcerer } from '@/providers/base';
|
||||
import { MovieScrapeContext, ShowScrapeContext } from '@/utils/context';
|
||||
import { NotFoundError } from '@/utils/errors';
|
||||
|
||||
const baseUrl = 'https://movies4f.com';
|
||||
const headers = {
|
||||
Referer: 'https://movies4f.com/',
|
||||
Origin: 'https://movies4f.com',
|
||||
'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',
|
||||
};
|
||||
|
||||
async function comboScraper(ctx: ShowScrapeContext | MovieScrapeContext): Promise<SourcererOutput> {
|
||||
// Build search query - try without year first, then with year if no results
|
||||
let searchQuery = encodeURIComponent(ctx.media.title);
|
||||
let searchUrl = `${baseUrl}/search?q=${searchQuery}`;
|
||||
|
||||
let searchPage = await ctx.proxiedFetcher<string>(searchUrl, {
|
||||
headers: {
|
||||
'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 no results found, try with year
|
||||
if (!searchPage.includes('/film/')) {
|
||||
searchQuery = encodeURIComponent(`${ctx.media.title} ${ctx.media.releaseYear}`);
|
||||
searchUrl = `${baseUrl}/search?q=${searchQuery}`;
|
||||
searchPage = await ctx.proxiedFetcher<string>(searchUrl, {
|
||||
headers: {
|
||||
'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',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
ctx.progress(40);
|
||||
|
||||
// Parse search results using regex since cheerio has issues with this HTML
|
||||
let filmUrl: string | null = null;
|
||||
|
||||
// Use regex to find complete film card structures
|
||||
const filmCardRegex =
|
||||
/<a[^>]*href="([^"]*\/film\/\d+\/[^"]*)"[^>]*class="[^"]*poster[^"]*"[^>]*>[\s\S]*?<img[^>]*alt="([^"]*)"[^>]*>/g;
|
||||
let filmMatch;
|
||||
|
||||
for (;;) {
|
||||
filmMatch = filmCardRegex.exec(searchPage);
|
||||
if (filmMatch === null) break;
|
||||
const link = filmMatch[1];
|
||||
const title = filmMatch[2];
|
||||
|
||||
// Check if this matches our media
|
||||
const normalizedTitle = title.toLowerCase().replace(/[^a-z0-9]/g, '');
|
||||
const normalizedSearchTitle = ctx.media.title.toLowerCase().replace(/[^a-z0-9]/g, '');
|
||||
|
||||
if (normalizedTitle.includes(normalizedSearchTitle)) {
|
||||
// For TV shows, check if it contains season/episode info
|
||||
if (ctx.media.type === 'show') {
|
||||
const episode = ctx.media.episode.number;
|
||||
const episodeUrl = `${baseUrl}${link}/episode-${episode}`;
|
||||
|
||||
// Check if this is a TV series by looking for season indicators
|
||||
if (title.toLowerCase().includes('season') || link.includes('/film/')) {
|
||||
filmUrl = episodeUrl;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// For movies
|
||||
filmUrl = `${baseUrl}${link}`;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!filmUrl) {
|
||||
throw new NotFoundError('No matching film found in search results');
|
||||
}
|
||||
|
||||
ctx.progress(50);
|
||||
|
||||
// Load the film page
|
||||
const filmPage = await ctx.proxiedFetcher<string>(filmUrl, {
|
||||
headers: {
|
||||
'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',
|
||||
},
|
||||
});
|
||||
|
||||
ctx.progress(60);
|
||||
|
||||
// Extract embed iframe URL
|
||||
const $film = load(filmPage);
|
||||
const iframeSrc = $film('iframe#iframeStream').attr('src');
|
||||
|
||||
if (!iframeSrc) {
|
||||
throw new NotFoundError('No embed iframe found');
|
||||
}
|
||||
|
||||
// Extract video ID from embed URL
|
||||
const embedUrl = new URL(iframeSrc);
|
||||
const videoId = embedUrl.searchParams.get('id');
|
||||
|
||||
if (!videoId) {
|
||||
throw new NotFoundError('No video ID found in embed URL');
|
||||
}
|
||||
|
||||
ctx.progress(70);
|
||||
|
||||
// Get tokens by posting to geturl endpoint
|
||||
const tokenResponse = await ctx.proxiedFetcher<string>('https://moviking.childish2x2.fun/geturl', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data; boundary=----geckoformboundaryc5f480bcac13a77346dab33881da6bfb',
|
||||
'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',
|
||||
Referer: iframeSrc,
|
||||
},
|
||||
body: `------geckoformboundaryc5f480bcac13a77346dab33881da6bfb
|
||||
Content-Disposition: form-data; name="renderer"
|
||||
|
||||
ANGLE (NVIDIA, NVIDIA GeForce GTX 980 Direct3D11 vs_5_0 ps_5_0), or similar
|
||||
------geckoformboundaryc5f480bcac13a77346dab33881da6bfb
|
||||
Content-Disposition: form-data; name="id"
|
||||
|
||||
6164426f797cf4b2fe93e4b20c0a4338
|
||||
------geckoformboundaryc5f480bcac13a77346dab33881da6bfb
|
||||
Content-Disposition: form-data; name="videoId"
|
||||
|
||||
${videoId}
|
||||
------geckoformboundaryc5f480bcac13a77346dab33881da6bfb
|
||||
Content-Disposition: form-data; name="domain"
|
||||
|
||||
${baseUrl}/
|
||||
------geckoformboundaryc5f480bcac13a77346dab33881da6bfb--`,
|
||||
});
|
||||
|
||||
ctx.progress(80);
|
||||
|
||||
// Parse tokens from response
|
||||
const tokenMatch = tokenResponse.match(/token1=(\w+)&token2=(\w+)&token3=(\w+)/);
|
||||
if (!tokenMatch) {
|
||||
throw new NotFoundError('Failed to extract tokens');
|
||||
}
|
||||
|
||||
const [, token1, token2, token3] = tokenMatch;
|
||||
|
||||
// Get streaming page with tokens
|
||||
const streamingUrl = `https://cdn4.zenty.store/streaming?id=${videoId}&web=movies4f.com&token1=${token1}&token2=${token2}&token3=${token3}&cdn=https%3A%2F%2Fcdn4.zenty.store&lang=en`;
|
||||
|
||||
const streamingPage = await ctx.proxiedFetcher<string>(streamingUrl, {
|
||||
headers: {
|
||||
'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',
|
||||
Referer: 'https://moviking.childish2x2.fun/',
|
||||
},
|
||||
});
|
||||
|
||||
ctx.progress(90);
|
||||
|
||||
// Extract M3U8 URL from streaming page using regex
|
||||
// The URL is constructed as: url = 'https://cdn.neuronix.sbs/segment/' + videoId + '/?token1=' + token1 + '&token3=' + token3
|
||||
const urlRegex = /url = '([^']+)'/;
|
||||
const urlMatch = streamingPage.match(urlRegex);
|
||||
|
||||
if (!urlMatch) {
|
||||
throw new NotFoundError('Failed to extract stream URL from streaming page');
|
||||
}
|
||||
|
||||
const streamBaseUrl = urlMatch[1];
|
||||
|
||||
// Extract videoId from the streaming URL parameters
|
||||
const videoIdMatch = streamingUrl.match(/id=([^&]+)/);
|
||||
if (!videoIdMatch) {
|
||||
throw new NotFoundError('Failed to extract videoId from streaming URL');
|
||||
}
|
||||
|
||||
const streamVideoId = videoIdMatch[1];
|
||||
|
||||
// Construct the full stream URL
|
||||
const streamUrl = `${streamBaseUrl}${streamVideoId}/?token1=${token1}&token3=${token3}`;
|
||||
|
||||
ctx.progress(95);
|
||||
|
||||
return {
|
||||
embeds: [],
|
||||
stream: [
|
||||
{
|
||||
id: 'primary',
|
||||
type: 'hls',
|
||||
playlist: streamUrl,
|
||||
headers,
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
captions: [],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
export const movies4fScraper = makeSourcerer({
|
||||
id: 'movies4f',
|
||||
name: 'M4F',
|
||||
rank: 166,
|
||||
disabled: false,
|
||||
flags: [],
|
||||
scrapeMovie: comboScraper,
|
||||
scrapeShow: comboScraper,
|
||||
});
|
||||
Loading…
Reference in a new issue