mirror of
https://github.com/p-stream/providers.git
synced 2026-03-11 17:55:36 +00:00
update zoechip
This commit is contained in:
parent
a0e45a46bb
commit
2181ab3eed
11 changed files with 243 additions and 359 deletions
BIN
src/providers/.DS_Store
vendored
Normal file
BIN
src/providers/.DS_Store
vendored
Normal file
Binary file not shown.
|
|
@ -12,6 +12,7 @@ import { mp4hydraScraper } from '@/providers/sources/mp4hydra';
|
|||
import { tugaflixScraper } from '@/providers/sources/tugaflix';
|
||||
import { vidsrcScraper } from '@/providers/sources/vidsrc';
|
||||
import { vidsrcsuScraper } from '@/providers/sources/vidsrcsu';
|
||||
import { zoechipScraper } from '@/providers/sources/zoechip';
|
||||
|
||||
import {
|
||||
autoembedBengaliScraper,
|
||||
|
|
@ -86,6 +87,7 @@ export function gatherAllSources(): Array<Sourcerer> {
|
|||
fsharetvScraper,
|
||||
vidsrcsuScraper,
|
||||
vidsrcScraper,
|
||||
zoechipScraper,
|
||||
mp4hydraScraper,
|
||||
embedsuScraper,
|
||||
slidemoviesScraper,
|
||||
|
|
|
|||
BIN
src/providers/archive/sources/.DS_Store
vendored
BIN
src/providers/archive/sources/.DS_Store
vendored
Binary file not shown.
|
|
@ -1,71 +0,0 @@
|
|||
// import { mixdropScraper } from '@/providers/embeds/mixdrop';
|
||||
// import { upcloudScraper } from '@/providers/embeds/upcloud';
|
||||
// import { upstreamScraper } from '@/providers/embeds/upstream';
|
||||
// import { vidCloudScraper } from '@/providers/embeds/vidcloud';
|
||||
// import { getZoeChipSourceURL, getZoeChipSources } from '@/providers/sources/zoechip/scrape';
|
||||
// import { MovieScrapeContext, ShowScrapeContext } from '@/utils/context';
|
||||
|
||||
// export const zoeBase = 'https://zoechip.cc';
|
||||
|
||||
// export type ZoeChipSourceDetails = {
|
||||
// type: string; // Only seen "iframe" so far
|
||||
// link: string;
|
||||
// sources: string[]; // Never seen this populated, assuming it's a string array
|
||||
// tracks: string[]; // Never seen this populated, assuming it's a string array
|
||||
// title: string;
|
||||
// };
|
||||
|
||||
// export async function formatSource(
|
||||
// ctx: MovieScrapeContext | ShowScrapeContext,
|
||||
// source: { embed: string; episodeId: string },
|
||||
// ) {
|
||||
// const link = await getZoeChipSourceURL(ctx, source.episodeId);
|
||||
// if (link) {
|
||||
// const embed = {
|
||||
// embedId: '',
|
||||
// url: link,
|
||||
// };
|
||||
|
||||
// const parsedUrl = new URL(link);
|
||||
|
||||
// switch (parsedUrl.host) {
|
||||
// case 'rabbitstream.net':
|
||||
// embed.embedId = upcloudScraper.id;
|
||||
// break;
|
||||
// case 'upstream.to':
|
||||
// embed.embedId = upstreamScraper.id;
|
||||
// break;
|
||||
// case 'mixdrop.co':
|
||||
// embed.embedId = mixdropScraper.id;
|
||||
// break;
|
||||
// default:
|
||||
// return null;
|
||||
// }
|
||||
|
||||
// return embed;
|
||||
// }
|
||||
// }
|
||||
|
||||
// export async function createZoeChipStreamData(ctx: MovieScrapeContext | ShowScrapeContext, id: string) {
|
||||
// const sources = await getZoeChipSources(ctx, id);
|
||||
// const embeds: {
|
||||
// embedId: string;
|
||||
// url: string;
|
||||
// }[] = [];
|
||||
|
||||
// for (const source of sources) {
|
||||
// const formatted = await formatSource(ctx, source);
|
||||
// if (formatted) {
|
||||
// // Zoechip does not return titles for their sources, so we can not check if a source is upcloud or vidcloud because the domain is the same.
|
||||
// const upCloudAlreadyExists = embeds.find((e) => e.embedId === upcloudScraper.id);
|
||||
// if (formatted.embedId === upcloudScraper.id && upCloudAlreadyExists) {
|
||||
// formatted.embedId = vidCloudScraper.id;
|
||||
// }
|
||||
// embeds.push(formatted);
|
||||
// }
|
||||
// }
|
||||
|
||||
// return {
|
||||
// embeds,
|
||||
// };
|
||||
// }
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
// import { flags } from '@/entrypoint/utils/targets';
|
||||
// import { makeSourcerer } from '@/providers/base';
|
||||
// import { scrapeMovie } from '@/providers/sources/zoechip/scrape-movie';
|
||||
// import { scrapeShow } from '@/providers/sources/zoechip/scrape-show';
|
||||
|
||||
// export const zoechipScraper = makeSourcerer({
|
||||
// id: 'zoechip',
|
||||
// name: 'ZoeChip',
|
||||
// rank: 240,
|
||||
// flags: [flags.CORS_ALLOWED],
|
||||
// disabled: true,
|
||||
// scrapeMovie,
|
||||
// scrapeShow,
|
||||
// });
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
// import { createZoeChipStreamData } from '@/providers/sources/zoechip/common';
|
||||
// import { getZoeChipMovieID } from '@/providers/sources/zoechip/search';
|
||||
// import { MovieScrapeContext } from '@/utils/context';
|
||||
// import { NotFoundError } from '@/utils/errors';
|
||||
|
||||
// export async function scrapeMovie(ctx: MovieScrapeContext) {
|
||||
// const movieID = await getZoeChipMovieID(ctx, ctx.media);
|
||||
// if (!movieID) {
|
||||
// throw new NotFoundError('no search results match');
|
||||
// }
|
||||
|
||||
// return createZoeChipStreamData(ctx, movieID);
|
||||
// }
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
// import { createZoeChipStreamData } from '@/providers/sources/zoechip/common';
|
||||
// import { getZoeChipEpisodeID, getZoeChipSeasonID } from '@/providers/sources/zoechip/scrape';
|
||||
// import { getZoeChipShowID } from '@/providers/sources/zoechip/search';
|
||||
// import { ShowScrapeContext } from '@/utils/context';
|
||||
// import { NotFoundError } from '@/utils/errors';
|
||||
|
||||
// export async function scrapeShow(ctx: ShowScrapeContext) {
|
||||
// const showID = await getZoeChipShowID(ctx, ctx.media);
|
||||
// if (!showID) {
|
||||
// throw new NotFoundError('no search results match');
|
||||
// }
|
||||
|
||||
// const seasonID = await getZoeChipSeasonID(ctx, ctx.media, showID);
|
||||
// if (!seasonID) {
|
||||
// throw new NotFoundError('no season found');
|
||||
// }
|
||||
|
||||
// const episodeID = await getZoeChipEpisodeID(ctx, ctx.media, seasonID);
|
||||
// if (!episodeID) {
|
||||
// throw new NotFoundError('no episode found');
|
||||
// }
|
||||
|
||||
// return createZoeChipStreamData(ctx, episodeID);
|
||||
// }
|
||||
|
|
@ -1,126 +0,0 @@
|
|||
// import { load } from 'cheerio';
|
||||
|
||||
// import { ShowMedia } from '@/entrypoint/utils/media';
|
||||
// import { ZoeChipSourceDetails, zoeBase } from '@/providers/sources/zoechip/common';
|
||||
// import { MovieScrapeContext, ScrapeContext, ShowScrapeContext } from '@/utils/context';
|
||||
|
||||
// export async function getZoeChipSources(ctx: MovieScrapeContext | ShowScrapeContext, id: string) {
|
||||
// // Movies use /ajax/episode/list/ID
|
||||
// // Shows use /ajax/episode/servers/ID
|
||||
// const endpoint = ctx.media.type === 'movie' ? 'list' : 'servers';
|
||||
// const html = await ctx.proxiedFetcher<string>(`/ajax/episode/${endpoint}/${id}`, {
|
||||
// baseUrl: zoeBase,
|
||||
// });
|
||||
// const $ = load(html);
|
||||
|
||||
// return $('.nav-item a')
|
||||
// .toArray()
|
||||
// .map((el) => {
|
||||
// // Movies use data-linkid
|
||||
// // Shows use data-id
|
||||
// const idAttribute = ctx.media.type === 'movie' ? 'data-linkid' : 'data-id';
|
||||
// const element = $(el);
|
||||
// const embedTitle = element.attr('title');
|
||||
// const linkId = element.attr(idAttribute);
|
||||
|
||||
// if (!embedTitle || !linkId) {
|
||||
// throw new Error('invalid sources');
|
||||
// }
|
||||
|
||||
// return {
|
||||
// embed: embedTitle,
|
||||
// episodeId: linkId,
|
||||
// };
|
||||
// });
|
||||
// }
|
||||
|
||||
// export async function getZoeChipSourceURL(ctx: ScrapeContext, sourceID: string): Promise<string | null> {
|
||||
// const details = await ctx.proxiedFetcher<ZoeChipSourceDetails>(`/ajax/sources/${sourceID}`, {
|
||||
// baseUrl: zoeBase,
|
||||
// });
|
||||
|
||||
// // TODO - Support non-iframe sources
|
||||
// if (details.type !== 'iframe') {
|
||||
// return null;
|
||||
// }
|
||||
|
||||
// // TODO - Extract the other data from the source
|
||||
|
||||
// return details.link;
|
||||
// }
|
||||
|
||||
// export async function getZoeChipSeasonID(ctx: ScrapeContext, media: ShowMedia, showID: string): Promise<string | null> {
|
||||
// const html = await ctx.proxiedFetcher<string>(`/ajax/season/list/${showID}`, {
|
||||
// baseUrl: zoeBase,
|
||||
// });
|
||||
|
||||
// const $ = load(html);
|
||||
|
||||
// const seasons = $('.dropdown-menu a')
|
||||
// .toArray()
|
||||
// .map((el) => {
|
||||
// const element = $(el);
|
||||
// const seasonID = element.attr('data-id');
|
||||
// const seasonNumber = element.html()?.split(' ')[1];
|
||||
|
||||
// if (!seasonID || !seasonNumber || Number.isNaN(Number(seasonNumber))) {
|
||||
// throw new Error('invalid season');
|
||||
// }
|
||||
|
||||
// return {
|
||||
// id: seasonID,
|
||||
// season: Number(seasonNumber),
|
||||
// };
|
||||
// });
|
||||
|
||||
// const foundSeason = seasons.find((season) => season.season === media.season.number);
|
||||
|
||||
// if (!foundSeason) {
|
||||
// return null;
|
||||
// }
|
||||
|
||||
// return foundSeason.id;
|
||||
// }
|
||||
|
||||
// export async function getZoeChipEpisodeID(
|
||||
// ctx: ScrapeContext,
|
||||
// media: ShowMedia,
|
||||
// seasonID: string,
|
||||
// ): Promise<string | null> {
|
||||
// const episodeNumberRegex = /Eps (\d*):/;
|
||||
// const html = await ctx.proxiedFetcher<string>(`/ajax/season/episodes/${seasonID}`, {
|
||||
// baseUrl: zoeBase,
|
||||
// });
|
||||
|
||||
// const $ = load(html);
|
||||
|
||||
// const episodes = $('.eps-item')
|
||||
// .toArray()
|
||||
// .map((el) => {
|
||||
// const element = $(el);
|
||||
// const episodeID = element.attr('data-id');
|
||||
// const title = element.attr('title');
|
||||
|
||||
// if (!episodeID || !title) {
|
||||
// throw new Error('invalid episode');
|
||||
// }
|
||||
|
||||
// const regexResult = title.match(episodeNumberRegex);
|
||||
// if (!regexResult || Number.isNaN(Number(regexResult[1]))) {
|
||||
// throw new Error('invalid episode');
|
||||
// }
|
||||
|
||||
// return {
|
||||
// id: episodeID,
|
||||
// episode: Number(regexResult[1]),
|
||||
// };
|
||||
// });
|
||||
|
||||
// const foundEpisode = episodes.find((episode) => episode.episode === media.episode.number);
|
||||
|
||||
// if (!foundEpisode) {
|
||||
// return null;
|
||||
// }
|
||||
|
||||
// return foundEpisode.id;
|
||||
// }
|
||||
|
|
@ -1,111 +0,0 @@
|
|||
// import { load } from 'cheerio';
|
||||
|
||||
// import { MovieMedia, ShowMedia } from '@/entrypoint/utils/media';
|
||||
// import { zoeBase } from '@/providers/sources/zoechip/common';
|
||||
// import { compareMedia } from '@/utils/compare';
|
||||
// import { ScrapeContext } from '@/utils/context';
|
||||
|
||||
// export async function getZoeChipSearchResults(ctx: ScrapeContext, media: MovieMedia | ShowMedia) {
|
||||
// const titleCleaned = media.title.toLocaleLowerCase().replace(/ /g, '-');
|
||||
|
||||
// const html = await ctx.proxiedFetcher<string>(`/search/${titleCleaned}`, {
|
||||
// baseUrl: zoeBase,
|
||||
// });
|
||||
|
||||
// const $ = load(html);
|
||||
// return $('.film_list-wrap .flw-item .film-detail')
|
||||
// .toArray()
|
||||
// .map((element) => {
|
||||
// const movie = $(element);
|
||||
// const anchor = movie.find('.film-name a');
|
||||
// const info = movie.find('.fd-infor');
|
||||
|
||||
// const title = anchor.attr('title');
|
||||
// const href = anchor.attr('href');
|
||||
// const type = info.find('.fdi-type').html();
|
||||
// let year = info.find('.fdi-item').html();
|
||||
// const id = href?.split('-').pop();
|
||||
|
||||
// if (!title) {
|
||||
// return null;
|
||||
// }
|
||||
|
||||
// if (!href) {
|
||||
// return null;
|
||||
// }
|
||||
|
||||
// if (!type) {
|
||||
// return null;
|
||||
// }
|
||||
|
||||
// // TV shows on ZoeChip do not have a year in their search results
|
||||
// // Allow TV shows to pass this failure
|
||||
// if (!year || Number.isNaN(Number(year))) {
|
||||
// if (type === 'TV') {
|
||||
// year = '0';
|
||||
// } else {
|
||||
// return null;
|
||||
// }
|
||||
// }
|
||||
|
||||
// if (!id) {
|
||||
// return null;
|
||||
// }
|
||||
|
||||
// return {
|
||||
// title,
|
||||
// year: Number(year),
|
||||
// id,
|
||||
// type,
|
||||
// href,
|
||||
// };
|
||||
// });
|
||||
// }
|
||||
|
||||
// export async function getZoeChipMovieID(ctx: ScrapeContext, media: MovieMedia): Promise<string | null> {
|
||||
// const searchResults = await getZoeChipSearchResults(ctx, media);
|
||||
|
||||
// const matchingItem = searchResults.find((v) => v && v.type === 'Movie' && compareMedia(media, v.title, v.year));
|
||||
|
||||
// if (!matchingItem) {
|
||||
// return null;
|
||||
// }
|
||||
|
||||
// return matchingItem.id;
|
||||
// }
|
||||
|
||||
// export async function getZoeChipShowID(ctx: ScrapeContext, media: ShowMedia): Promise<string | null> {
|
||||
// // ZoeChip TV shows don't have a year on their search results
|
||||
// // This makes it hard to filter between shows with the same name
|
||||
// // To find the year, we must query each shows details page
|
||||
// // This is slower, but more reliable
|
||||
|
||||
// const releasedRegex = /<\/strong><\/span> (\d.*)-\d.*-\d.*/;
|
||||
// const searchResults = await getZoeChipSearchResults(ctx, media);
|
||||
|
||||
// const filtered = searchResults.filter((v) => v && v.type === 'TV' && compareMedia(media, v.title));
|
||||
|
||||
// for (const result of filtered) {
|
||||
// // This gets filtered above but the linter Gods don't think so
|
||||
// if (!result) {
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// const html = await ctx.proxiedFetcher<string>(result.href, {
|
||||
// baseUrl: zoeBase,
|
||||
// });
|
||||
|
||||
// // The HTML is not structured in a way that makes using Cheerio clean
|
||||
// // There are no unique IDs or classes to query, resulting in long ugly queries
|
||||
// // Regex is faster and cleaner in this case
|
||||
// const regexResult = html.match(releasedRegex);
|
||||
// if (regexResult) {
|
||||
// const year = Number(regexResult[1]);
|
||||
// if (!Number.isNaN(year) && compareMedia(media, result.title, year)) {
|
||||
// return result.id;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// return null;
|
||||
// }
|
||||
BIN
src/providers/sources/.DS_Store
vendored
BIN
src/providers/sources/.DS_Store
vendored
Binary file not shown.
241
src/providers/sources/zoechip.ts
Normal file
241
src/providers/sources/zoechip.ts
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
import { load } from 'cheerio';
|
||||
import { unpack } from 'unpacker';
|
||||
|
||||
import { flags } from '@/entrypoint/utils/targets';
|
||||
import { SourcererOutput, makeSourcerer } from '@/providers/base';
|
||||
import { MovieScrapeContext, ShowScrapeContext } from '@/utils/context';
|
||||
import { NotFoundError } from '@/utils/errors';
|
||||
|
||||
const zoeBase = 'https://zoechip.org';
|
||||
|
||||
function createSlug(title: string): string {
|
||||
return title
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9\s-]/g, '')
|
||||
.replace(/\s+/g, '-')
|
||||
.replace(/-+/g, '-')
|
||||
.trim();
|
||||
}
|
||||
|
||||
async function extractFileFromFilemoon(
|
||||
ctx: MovieScrapeContext | ShowScrapeContext,
|
||||
filemoonUrl: string,
|
||||
): Promise<string | null> {
|
||||
const headers = {
|
||||
Referer: zoeBase,
|
||||
'User-Agent':
|
||||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
|
||||
};
|
||||
|
||||
try {
|
||||
// console.log(`Extracting from Filemoon URL: ${filemoonUrl}`);
|
||||
|
||||
// Follow redirects to get the actual iframe URL
|
||||
const redirectResponse = await ctx.proxiedFetcher.full(filemoonUrl, {
|
||||
method: 'HEAD',
|
||||
headers,
|
||||
});
|
||||
|
||||
const redirectUrl = redirectResponse.finalUrl;
|
||||
// console.log(`Redirect URL: ${redirectUrl}`);
|
||||
|
||||
if (!redirectUrl) {
|
||||
// console.log('No redirect URL found');
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get the redirect page content
|
||||
const redirectHtml = await ctx.proxiedFetcher<string>(redirectUrl, {
|
||||
headers,
|
||||
});
|
||||
|
||||
const redirectCheerio = load(redirectHtml);
|
||||
const iframeUrl = redirectCheerio('iframe').attr('src');
|
||||
// console.log(`Iframe URL: ${iframeUrl}`);
|
||||
|
||||
if (!iframeUrl) {
|
||||
// console.log('No iframe URL found');
|
||||
throw new NotFoundError('No iframe URL found');
|
||||
}
|
||||
|
||||
// Fetch the iframe content
|
||||
const iframeHtml = await ctx.proxiedFetcher<string>(iframeUrl, {
|
||||
headers,
|
||||
});
|
||||
|
||||
// Extract the packed JavaScript code
|
||||
const evalMatch = iframeHtml.match(/eval\(function\(p,a,c,k,e,.*\)\)/i);
|
||||
if (!evalMatch) {
|
||||
// console.log('No packed JavaScript found');
|
||||
throw new NotFoundError('No packed JavaScript found');
|
||||
}
|
||||
|
||||
// console.log('Found packed JavaScript, unpacking...');
|
||||
|
||||
// Unpack the JavaScript
|
||||
const unpacked = unpack(evalMatch[0]);
|
||||
// console.log(`Unpacked JavaScript (first 200 chars): ${unpacked.substring(0, 200)}`);
|
||||
|
||||
// Extract the file URL from the unpacked code
|
||||
const fileMatch = unpacked.match(/file\s*:\s*"([^"]+)"/i);
|
||||
if (!fileMatch) {
|
||||
// console.log('No file URL found in unpacked JavaScript');
|
||||
throw new NotFoundError('No file URL found in unpacked JavaScript');
|
||||
}
|
||||
|
||||
const fileUrl = fileMatch[1];
|
||||
// console.log(`Extracted file URL: ${fileUrl}`);
|
||||
return fileUrl;
|
||||
} catch (error) {
|
||||
// console.error('Error in extractFileFromFilemoon:', error);
|
||||
throw new NotFoundError('Failed to extract file URL from streaming server');
|
||||
}
|
||||
}
|
||||
|
||||
async function comboScraper(ctx: MovieScrapeContext | ShowScrapeContext): Promise<SourcererOutput> {
|
||||
const headers = {
|
||||
Referer: zoeBase,
|
||||
'User-Agent':
|
||||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
|
||||
};
|
||||
|
||||
// console.log(`Starting scrape for ${ctx.media.type}: ${ctx.media.title}`);
|
||||
|
||||
let url: string;
|
||||
let movieId: string | undefined;
|
||||
|
||||
try {
|
||||
// Construct URLs based on media type
|
||||
if (ctx.media.type === 'movie') {
|
||||
const slug = createSlug(ctx.media.title);
|
||||
url = `${zoeBase}/film/${slug}-${ctx.media.releaseYear}`;
|
||||
// console.log(`Movie URL: ${url}`);
|
||||
} else {
|
||||
const slug = createSlug(ctx.media.title);
|
||||
url = `${zoeBase}/episode/${slug}-season-${ctx.media.season.number}-episode-${ctx.media.episode.number}`;
|
||||
// console.log(`Show URL: ${url}`);
|
||||
}
|
||||
|
||||
ctx.progress(20);
|
||||
|
||||
// Get the page and extract movie ID
|
||||
const html = await ctx.proxiedFetcher<string>(url, { headers });
|
||||
const $ = load(html);
|
||||
|
||||
movieId = $('div#show_player_ajax').attr('movie-id');
|
||||
// console.log(`Movie ID: ${movieId}`);
|
||||
|
||||
if (!movieId) {
|
||||
// Try alternative methods to find content
|
||||
// console.log('No movie ID found, trying alternative search...');
|
||||
|
||||
// Look for other possible IDs
|
||||
const altId =
|
||||
$('[data-movie-id]').attr('data-movie-id') ||
|
||||
$('[movie-id]').attr('movie-id') ||
|
||||
$('.player-wrapper').attr('data-id');
|
||||
|
||||
if (altId) {
|
||||
movieId = altId;
|
||||
// console.log(`Found alternative ID: ${movieId}`);
|
||||
} else {
|
||||
throw new NotFoundError(`No content found for ${ctx.media.type === 'movie' ? 'movie' : 'episode'}`);
|
||||
}
|
||||
}
|
||||
|
||||
ctx.progress(40);
|
||||
|
||||
// Make AJAX request to get sources
|
||||
const ajaxUrl = `${zoeBase}/wp-admin/admin-ajax.php`;
|
||||
const ajaxHeaders = {
|
||||
...headers,
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
||||
Referer: url,
|
||||
};
|
||||
|
||||
const body = new URLSearchParams({
|
||||
action: 'lazy_player',
|
||||
movieID: movieId,
|
||||
});
|
||||
|
||||
// console.log('Making AJAX request for sources...');
|
||||
const ajaxHtml = await ctx.proxiedFetcher<string>(ajaxUrl, {
|
||||
method: 'POST',
|
||||
headers: ajaxHeaders,
|
||||
body: body.toString(),
|
||||
});
|
||||
|
||||
const $ajax = load(ajaxHtml);
|
||||
const filemoonUrl = $ajax('ul.nav a:contains(Filemoon)').attr('data-server');
|
||||
// console.log(`Filemoon URL: ${filemoonUrl}`);
|
||||
|
||||
if (!filemoonUrl) {
|
||||
// Try to find other available servers
|
||||
const allServers = $ajax('ul.nav a')
|
||||
.map((_, el) => ({
|
||||
name: $ajax(el).text().trim(),
|
||||
url: $ajax(el).attr('data-server'),
|
||||
}))
|
||||
.get();
|
||||
|
||||
// console.log('Available servers:', allServers);
|
||||
|
||||
if (allServers.length === 0) {
|
||||
throw new NotFoundError('No streaming servers found');
|
||||
}
|
||||
|
||||
throw new NotFoundError('Filemoon server not available');
|
||||
}
|
||||
|
||||
ctx.progress(60);
|
||||
|
||||
// Extract file URL from Filemoon
|
||||
const fileUrl = await extractFileFromFilemoon(ctx, filemoonUrl);
|
||||
if (!fileUrl) {
|
||||
throw new NotFoundError('Failed to extract file URL from streaming server');
|
||||
}
|
||||
|
||||
ctx.progress(90);
|
||||
|
||||
return {
|
||||
stream: [
|
||||
{
|
||||
id: 'primary',
|
||||
type: 'hls' as const,
|
||||
playlist: fileUrl,
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
captions: [],
|
||||
},
|
||||
],
|
||||
embeds: [],
|
||||
};
|
||||
} catch (error) {
|
||||
// console.error('Error during scraping:', error);
|
||||
|
||||
if (error instanceof NotFoundError) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Provide more specific error messages
|
||||
if (error instanceof Error) {
|
||||
if (error.message.includes('fetch')) {
|
||||
throw new NotFoundError('Failed to connect to ZoeChip');
|
||||
}
|
||||
if (error.message.includes('timeout')) {
|
||||
throw new NotFoundError('Request timed out');
|
||||
}
|
||||
}
|
||||
|
||||
throw new NotFoundError(`ZoeChip scraping failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
||||
}
|
||||
}
|
||||
|
||||
export const zoechipScraper = makeSourcerer({
|
||||
id: 'zoechip',
|
||||
name: 'ZoeChip',
|
||||
rank: 170,
|
||||
flags: [flags.CORS_ALLOWED],
|
||||
scrapeMovie: comboScraper,
|
||||
scrapeShow: comboScraper,
|
||||
});
|
||||
Loading…
Reference in a new issue