surce updates

This commit is contained in:
Devin Bot
2026-10-25 18:00:42 -07:00
parent 30ab9ba41e
commit 6634380643
18 changed files with 935 additions and 1227 deletions

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "@vyla-entertainment/sdk",
"version": "1.16.0",
"version": "1.18.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@vyla-entertainment/sdk",
"version": "1.16.0",
"version": "1.18.0",
"license": "CC-BY-NC-4.0",
"dependencies": {
"node-tls-client": "^2.1.0"

View File

@@ -56,50 +56,6 @@ export const SOURCES = [
}],
},
{
key: 'anihq-sub',
label: 'AniHQ (Sub)',
sourceFile: 'anihq',
proxyParam: 'ahsub',
timeout: 25000,
jitter: 500,
retries: 2,
multiUrl: true,
skipProxy: true
},
{
key: 'anihq-dub',
label: 'AniHQ (Dub)',
sourceFile: 'anihq',
proxyParam: 'ahdub',
timeout: 25000,
jitter: 500,
retries: 2,
multiUrl: true,
skipProxy: true
},
{
key: 'anineko-sub',
sourceFile: 'anineko',
label: 'AniNeko (Sub)',
proxyParam: 'anksub',
timeout: 25000,
jitter: 500,
retries: 2
},
{
key: 'anineko-dub',
sourceFile: 'anineko',
label: 'AniNeko (Dub)',
proxyParam: 'ankdub',
timeout: 25000,
jitter: 500,
retries: 2
},
{
key: 'bcine',
label: 'Bcine',
@@ -118,25 +74,6 @@ export const SOURCES = [
}],
},
{
key: 'cinecat',
label: 'Cinecat',
sourceFile: 'cinecat',
proxyParam: 'ccat',
timeout: 25000,
jitter: 500,
retries: 2,
multiUrl: false,
skipProxy: true,
cdnHeaders: [{
pattern: /bluevelvet\.space/i,
headers: {
Referer: 'https://zcdn.bluevelvet.space/',
Origin: 'https://zcdn.bluevelvet.space',
},
}],
},
{
key: 'cinemaarmy',
label: 'CinemaArmy',
@@ -193,6 +130,25 @@ export const SOURCES = [
}],
},
{
key: 'cno',
label: 'Cno',
sourceFile: 'cno',
proxyParam: 'cno',
timeout: 25000,
jitter: 300,
retries: 1,
multiUrl: true,
skipProxy: true,
cdnHeaders: [{
pattern: /cno\.to/i,
headers: {
Referer: 'https://cno.to/',
Origin: 'https://cno.to',
},
}],
},
{
key: 'dulo',
label: 'Dulo',
@@ -217,18 +173,18 @@ export const SOURCES = [
label: 'FlaxMovies',
sourceFile: 'flaxmovies',
proxyParam: 'fx',
timeout: 20000,
timeout: 25000,
jitter: 500,
retries: 2,
multiUrl: true,
skipProxy: true,
cdnHeaders: [{
pattern: /flix2watch\.pro/i,
pattern: /themoviebox\.uk|vidrock\.|orbitingdreams\.xyz|fastcloud\.live|deton444eik\.com/i,
headers: {
Referer: 'https://flaxmovies.xyz/',
Origin: 'https://flaxmovies.xyz',
Referer: 'https://themoviebox.uk/',
Origin: 'https://themoviebox.uk',
},
},],
}],
},
{
@@ -262,6 +218,25 @@ export const SOURCES = [
}],
},
{
key: 'kickassanime',
label: 'KickAssAnime',
sourceFile: 'kickassanime',
proxyParam: 'kaa',
timeout: 25000,
jitter: 500,
retries: 2,
multiUrl: true,
skipProxy: true,
cdnHeaders: [{
pattern: /krussdomi\.com/i,
headers: {
Referer: 'https://kaa.lt/',
Origin: 'https://kaa.lt',
},
}],
},
{
key: 'hyper',
label: 'Hyper',
@@ -334,25 +309,6 @@ export const SOURCES = [
}],
},
{
key: 'movienight',
label: 'MovieNight',
sourceFile: 'movienight',
proxyParam: 'mn',
timeout: 20000,
jitter: 500,
retries: 2,
multiUrl: true,
skipProxy: true,
cdnHeaders: [{
pattern: /movienig\.ht/i,
headers: {
Referer: 'https://movienig.ht/',
Origin: 'https://movienig.ht',
},
}],
},
{
key: 'movy',
label: 'Movy',
@@ -364,29 +320,29 @@ export const SOURCES = [
multiUrl: true,
skipProxy: true,
cdnHeaders: [{
pattern: /wecollege\.net|movy\.bz/i,
pattern: /wecollege\.net|movy\.sx|goldenkite\.top/i,
headers: {
Referer: 'https://www.movy.bz/',
Origin: 'https://www.movy.bz',
Referer: 'https://www.movy.sx/',
Origin: 'https://www.movy.sx',
},
}],
},
{
key: 'peestream',
label: 'PeeStream',
sourceFile: 'peestream',
proxyParam: 'pee',
key: 'net79',
label: 'Net79',
sourceFile: 'net79',
proxyParam: 'n79',
timeout: 25000,
jitter: 500,
retries: 2,
multiUrl: true,
skipProxy: true,
cdnHeaders: [{
pattern: /peestream\.in/i,
pattern: /hakunaymatata\.com/i,
headers: {
Referer: 'https://peestream.in/',
Origin: 'https://peestream.in',
Referer: 'https://net79.cc/',
Origin: 'https://net79.cc',
},
}],
},
@@ -437,6 +393,24 @@ export const SOURCES = [
multiUrl: true,
},
{
key: 'rivo',
label: 'Rivo',
sourceFile: 'rivo',
proxyParam: 'rv',
timeout: 25000,
jitter: 500,
retries: 2,
multiUrl: true,
cdnHeaders: [{
pattern: /rivo\.st/i,
headers: {
Referer: 'https://f.rivo.st/',
Origin: 'https://f.rivo.st',
},
}],
},
{
key: 'streamaggregator',
label: 'StreamAggregator',
@@ -467,7 +441,7 @@ export const SOURCES = [
multiUrl: true,
skipProxy: true,
cdnHeaders: [{
pattern: /thugtv\.online|thuktv\.onrender\.com/i,
pattern: /thugtv\.online|fcxmb\.com|hscow\.com/i,
headers: {
Referer: 'https://thugtv.online/',
Origin: 'https://thugtv.online',
@@ -508,15 +482,22 @@ export const SOURCES = [
},
{
key: 'vidgod',
label: 'VidGod',
sourceFile: 'vidgod',
proxyParam: 'vg',
key: 'vidora',
label: 'Vidora',
sourceFile: 'vidora',
proxyParam: 'vdra',
timeout: 25000,
jitter: 500,
retries: 2,
multiUrl: true,
skipProxy: true,
cdnHeaders: [{
pattern: /rousav\.tech|vidbolt\.xyz|vidrift\.in|goldenkite\.top|ravok\.buzz/i,
headers: {
Referer: 'https://vidora-3l2w.onrender.com/',
Origin: 'https://vidora-3l2w.onrender.com',
},
}],
},
{
@@ -531,41 +512,6 @@ export const SOURCES = [
multiUrl: true,
},
{
key: 'vidnest',
label: 'VidNest',
sourceFile: 'vidnest',
proxyParam: 'vdn',
timeout: 20000,
retries: 1,
jitter: 0,
multiUrl: true,
},
{
key: 'vidnest-sub',
label: 'VidNest (Sub)',
sourceFile: 'vidnest',
proxyParam: 'vdn',
timeout: 20000,
retries: 1,
jitter: 0,
multiUrl: true,
skipProxy: true,
},
{
key: 'vidnest-dub',
label: 'VidNest (Dub)',
sourceFile: 'vidnest',
proxyParam: 'vdn',
timeout: 20000,
retries: 1,
jitter: 0,
multiUrl: true,
skipProxy: true,
},
{
key: 'vidrock',
label: 'VidRock',
@@ -681,6 +627,25 @@ export const SOURCES = [
}],
},
{
key: 'yapgrid',
label: 'YapGrid',
sourceFile: 'yapgrid',
proxyParam: 'yap',
timeout: 25000,
jitter: 500,
retries: 2,
multiUrl: true,
skipProxy: true,
cdnHeaders: [{
pattern: /yapgrid\.com|embedwave\.cc/i,
headers: {
Referer: 'https://yapgrid.com/',
Origin: 'https://yapgrid.com',
},
}],
},
{
key: 'yastream',
label: 'YaStream',

View File

@@ -1,124 +0,0 @@
import { getTmdbInfo, USER_AGENT } from '../utils/helpers.js';
async function extractVoe(voeUrl) {
try {
let res = await fetch(voeUrl, {
headers: {
'User-Agent': USER_AGENT,
'Referer': 'https://anihq.cc/'
}
});
let html = await res.text();
const redirectMatch = html.match(/window\.location\.href\s*=\s*['"](https?:\/\/[^'"]+)['"]/);
if (redirectMatch) {
res = await fetch(redirectMatch[1], {
headers: {
'User-Agent': USER_AGENT,
'Referer': 'https://anihq.cc/'
}
});
html = await res.text();
}
const mirrorUrl = res.url;
const scriptMatch = html.match(/<script type="application\/json">\["(.*?)"\]<\/script>/);
if (!scriptMatch) return null;
let str = scriptMatch[1];
str = str.replace(/[a-zA-Z]/g, (c) => {
return String.fromCharCode((c <= "Z" ? 90 : 122) >= (c = c.charCodeAt(0) + 13) ? c : c - 26);
});
const junk = ['@$', '^^', '~@', '%?', '*~', '!!', '#&'];
junk.forEach(j => { str = str.split(j).join(''); });
str = Buffer.from(str, 'base64').toString('utf-8');
let shifted = '';
for (let i = 0; i < str.length; i++) {
shifted += String.fromCharCode(str.charCodeAt(i) - 3);
}
let reversed = shifted.split('').reverse().join('');
let finalJsonStr = Buffer.from(reversed, 'base64').toString('utf-8');
const finalData = JSON.parse(finalJsonStr);
if (finalData.file || finalData.source) {
return {
url: finalData.file || finalData.source,
mirror: mirrorUrl
};
}
} catch (e) {
return null;
}
return null;
}
function cleanSlug(title) {
return title
.toLowerCase()
.replace(/[^a-z0-9\s]/g, '')
.replace(/\s+/g, '-')
.replace(/^-+|-+$/g, '');
}
export async function getStream({ id, s, e, audio }) {
if (!s) return null;
const info = await getTmdbInfo(id, 'tv', s);
if (!info.isAnime) return null;
const seriesTitle = info.titles.find(t =>
t.toLowerCase() !== 'east blue' &&
t.length > 3
) || info.titles[0];
const cleanTitle = cleanSlug(seriesTitle);
const typeSuffix = audio === 'dub' ? 'english-dubbed' : 'english-subbed';
const url = `https://anihq.cc/watch/${cleanTitle}-episode-${e}-${typeSuffix}/`;
try {
let pageRes = await fetch(url, { headers: { 'User-Agent': USER_AGENT } });
let html = await pageRes.text();
if (!pageRes.ok) {
const searchUrl = `https://anihq.cc/search?keyword=${encodeURIComponent(seriesTitle)}`;
const searchRes = await fetch(searchUrl, { headers: { 'User-Agent': USER_AGENT } });
const searchHtml = await searchRes.text();
const searchRegex = new RegExp(`href="(https://anihq.cc/watch/[^"]+-episode-${e}-${typeSuffix}/)"`, 'i');
const match = searchHtml.match(searchRegex);
if (match) {
const retryRes = await fetch(match[1], { headers: { 'User-Agent': USER_AGENT } });
html = await retryRes.text();
} else return null;
}
const iframeMatch = html.match(/iframe[^>]+src=["'](https:\/\/(?:[a-z0-9.]+|ellenpoliticalfollow\.com)\/e\/[a-zA-Z0-9]+)["']/i);
if (!iframeMatch) return null;
const result = await extractVoe(iframeMatch[1]);
if (result && result.url) {
const mirrorOrigin = new URL(result.mirror).origin;
return {
url: result.url,
headers: {
'Referer': mirrorOrigin + '/',
'User-Agent': USER_AGENT,
'Origin': mirrorOrigin
}
};
}
} catch (err) {
return null;
}
return null;
}

View File

@@ -1,190 +0,0 @@
import { getTmdbInfo, USER_AGENT, fetchText } from '../utils/helpers.js';
const BASE_URL = "https://anineko.to";
function cleanTitle(t) { return t ? t.toLowerCase().replace(/[^a-z0-9]/g, '') : ''; }
function decodeEntities(str) {
return str.replace(/&(nbsp|amp|quot|lt|gt);/g, (m, e) => ({ "nbsp": " ", "amp": "&", "quot": "\"", "lt": "<", "gt": ">" })[e]).replace(/&#(\d+);/gi, (m, n) => String.fromCharCode(parseInt(n, 10)));
}
async function search(query) {
try {
const html = await fetchText(`${BASE_URL}/browser?keyword=${encodeURIComponent(query)}`, {
headers: { 'User-Agent': USER_AGENT },
signal: AbortSignal.timeout(6000)
});
const results = [];
let idx = 0;
while ((idx = html.indexOf('nv-anime-thumb', idx)) !== -1) {
const startHref = html.lastIndexOf('<a', idx);
if (startHref === -1) { idx += 14; continue; }
const hrefMatch = html.slice(startHref, html.indexOf('>', startHref)).match(/href=["']([^"']+)["']/i);
if (!hrefMatch) { idx += 14; continue; }
const slug = hrefMatch[1].split('/').pop();
const h3Start = html.indexOf('nv-anime-title', idx);
if (h3Start !== -1) {
const contentStart = html.indexOf('>', h3Start) + 1;
const contentEnd = html.indexOf('<', contentStart);
if (contentEnd !== -1) results.push({ slug, text: html.slice(contentStart, contentEnd).trim() });
}
idx += 14;
}
if (results.length === 0) {
const linkMatches = [...html.matchAll(/href=["']\/watch\/([^"']+)["']/gi)];
for (const match of linkMatches) {
if (!results.find(r => r.slug === match[1])) {
const linkIdx = html.indexOf(match[0]);
const titleStart = html.lastIndexOf('>', linkIdx);
const titleEnd = html.indexOf('<', linkIdx);
if (titleStart !== -1 && titleEnd !== -1 && titleEnd > linkIdx) {
const title = html.slice(titleStart + 1, Math.min(titleEnd, linkIdx + 200)).trim();
if (title.length > 2 && title.length < 100) {
results.push({ slug: match[1], text: title });
}
}
}
}
}
return results;
} catch { return []; }
}
async function extractHls(embedUrl) {
try {
const html = await fetchText(embedUrl, {
headers: {
'User-Agent': USER_AGENT,
Referer: `${BASE_URL}/`
},
signal: AbortSignal.timeout(6000)
});
const m = html.match(/const\s+src\s*=\s*["'](https?:\/\/[^"']+\.m3u8[^"']*)["']/i) ||
html.match(/file\s*:\s*["'](https?:\/\/[^"']+\.m3u8[^"']*)["']/i) ||
html.match(/["'](https?:\/\/[^"']+\/master\.m3u8[^"']*)["']/i) ||
html.match(/["'](https?:\/\/[^"']+\.m3u8[^"']*)["']/i) ||
html.match(/src:\s*["'](https?:\/\/[^"']+\.m3u8[^"']*)["']/i);
if (m) return decodeEntities(m[1]);
} catch { }
return null;
}
export async function getStream({ id, s, e, audio, sdk }) {
const tmdbApiKey = sdk?.tmdbApiKey || null;
if (!tmdbApiKey) return null;
try {
const info = await getTmdbInfo(tmdbApiKey, id, s ? 'tv' : 'movie', s);
if (!info || !info.isAnime) return null;
let seriesSlug = null;
for (const title of info.titles) {
const potentialSlug = title.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
if (!potentialSlug) continue;
try {
const checkHtml = await fetchText(`${BASE_URL}/watch/${potentialSlug}/ep-${e || 1}`, {
headers: { 'User-Agent': USER_AGENT, Referer: `${BASE_URL}/watch/${potentialSlug}` },
signal: AbortSignal.timeout(4000)
});
if (checkHtml && checkHtml.includes('nv-watch-page')) {
seriesSlug = potentialSlug;
break;
}
} catch {}
}
if (!seriesSlug) {
for (const title of info.titles) {
const results = await search(title);
const targetClean = cleanTitle(title);
const exactSlugMatch = results.find(r => {
const slugClean = cleanTitle(r.slug);
return slugClean === targetClean || r.slug === targetClean;
});
if (exactSlugMatch) {
seriesSlug = exactSlugMatch.slug;
break;
}
const sortedResults = [...results].sort((a, b) => a.slug.length - b.slug.length);
for (const r of sortedResults) {
const resultClean = cleanTitle(r.text);
const isExactMatch = resultClean === targetClean;
const isCloseMatch = r.text.toLowerCase().includes(title.toLowerCase());
if (isExactMatch || isCloseMatch) {
seriesSlug = r.slug;
break;
}
}
if (seriesSlug) break;
}
}
if (!seriesSlug && info.titles.length) {
const fSearch = await search(info.titles[0]);
const sortedResults = [...fSearch].sort((a, b) => a.slug.length - b.slug.length);
if (sortedResults.length) seriesSlug = sortedResults[0].slug;
}
if (!seriesSlug) return null;
const watchHtml = await fetchText(`${BASE_URL}/watch/${seriesSlug}/ep-${e || 1}`, {
headers: {
'User-Agent': USER_AGENT,
Referer: `${BASE_URL}/watch/${seriesSlug}`
},
signal: AbortSignal.timeout(6000)
});
const byAudio = { sub: [], dub: [] };
const iframeMatch = watchHtml.match(/<iframe[^>]*src=["']([^"']+)["'][^>]*>/i);
if (iframeMatch) {
const iframeSrc = decodeEntities(iframeMatch[1]);
const hls = await extractHls(iframeSrc);
if (hls) {
const audioType = audio === 'dub' ? 'dub' : 'sub';
byAudio[audioType].push(iframeSrc);
}
}
if (byAudio.sub.length === 0 && byAudio.dub.length === 0) {
const tabMatches = [...watchHtml.matchAll(/<button[^>]*class=["'][^"']*nv-server-tab[^"']*["'][^>]*data-id=["']([^"']+)["'][^>]*>/gi)];
const tabMap = {};
for (const match of tabMatches) {
const tabId = match[1].toLowerCase();
const tabClassMatch = match[0].match(/class=["'][^"']*(tab_\d+)[^"']/i);
if (tabClassMatch) {
const tabClass = tabClassMatch[1];
tabMap[tabClass] = tabId.includes('dub') ? 'dub' : 'sub';
}
}
const btnRegex = /<button\s+[^>]*class=["'][^"']*server-video[^"']*["'][^>]*>/gi;
const btnMatches = [...watchHtml.matchAll(btnRegex)];
for (const match of btnMatches) {
const btnHtml = match[0];
const videoMatch = btnHtml.match(/data-video=["']([^"']+)["']/i);
const tabMatch = btnHtml.match(/data-tab=["']([^"']+)["']/i);
if (videoMatch && tabMatch) {
const videoUrl = decodeEntities(videoMatch[1]);
const tabClass = tabMatch[1];
const audioType = tabMap[tabClass] || 'sub';
byAudio[audioType].push(videoUrl);
}
}
}
const audiosToTry = audio === "all" ? ["sub", "dub"] : (audio === "dub" ? ["dub", "sub"] : ["sub", "dub"]);
const allUrls = [];
for (const aud of audiosToTry) {
for (const embed of byAudio[aud]) {
const hls = await extractHls(embed);
if (hls) allUrls.push({ url: hls, type: "hls", audio: aud, server: "AniNeko", skipProxy: false });
}
if (allUrls.length) break;
}
return allUrls.length ? { allUrls } : null;
} catch { return null; }
}
export async function getSources(args) {
const stream = await getStream(args);
return stream?.allUrls ? [...new Set(stream.allUrls.map(u => u.server))] : [];
}

95
src/sources/cno.js Normal file
View File

@@ -0,0 +1,95 @@
import { fetchText } from '../utils/helpers.js';
export async function getStream({ id, s, e }) {
const isTv = s != null && e != null;
const serverIndices = Array.from({ length: 50 }, (_, i) => i + 1);
const headers = {
'Accept': 'application/json, text/plain, */*',
'Referer': 'https://cno.to/',
'Origin': 'https://cno.to',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36'
};
const promises = serverIndices.map(async (serverNum) => {
const url = isTv
? `https://cno.to/api/s/${serverNum}/tv/${id}/${s}/${e}`
: `https://cno.to/api/s/${serverNum}/movie/${id}?v=3`;
try {
const raw = await fetchText(url, { headers });
if (!raw || raw.includes('"error":')) return null;
if (raw.includes('#EXTM3U')) {
const lines = raw.split('\n');
let streamUrl = null;
let quality = 'Auto';
for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim();
if (line.startsWith('#EXT-X-STREAM-INF:')) {
const resMatch = line.match(/RESOLUTION=(\d+x\d+)/);
if (resMatch) quality = resMatch[1];
const nextLine = lines[i + 1]?.trim();
if (nextLine && !nextLine.startsWith('#')) {
streamUrl = nextLine;
break;
}
}
}
if (!streamUrl) {
for (const line of lines) {
const trimmed = line.trim();
if (trimmed && !trimmed.startsWith('#')) {
streamUrl = trimmed;
break;
}
}
}
if (streamUrl) {
if (streamUrl.startsWith('/')) {
streamUrl = `https://cno.to${streamUrl}`;
}
return {
url: streamUrl,
server: `Cno - Server ${serverNum} (${quality})`,
headers: {
Referer: 'https://cno.to/',
Origin: 'https://cno.to'
}
};
}
}
try {
const json = JSON.parse(raw);
const jsonUrl = json.url || json.file || json.stream;
if (jsonUrl && typeof jsonUrl === 'string' && jsonUrl.startsWith('http')) {
return {
url: jsonUrl,
server: `Cno - Server ${serverNum} (${json.quality || 'Auto'})`,
headers: {
Referer: 'https://cno.to/',
Origin: 'https://cno.to'
}
};
}
} catch { }
} catch { }
return null;
});
const results = await Promise.all(promises);
const allUrls = results.filter(Boolean);
if (!allUrls.length) return null;
return {
url: allUrls[0].url,
headers: allUrls[0].headers,
allUrls
};
}

View File

@@ -1,118 +1,106 @@
import { USER_AGENT, fetchJson } from '../utils/helpers.js';
import { fetchJson } from '../utils/helpers.js';
const BASE_URL = 'https://flaxmovies.xyz';
export async function getStream({ id, s, e }) {
const isTv = s != null && e != null;
const allUrls = [];
async function getWorkerUrls(embedUrl) {
const foundUrls = [];
try {
const res = await fetch(embedUrl, {
headers: {
'User-Agent': USER_AGENT,
'Referer': `${BASE_URL}/`,
'Origin': BASE_URL
},
signal: AbortSignal.timeout(8000)
});
if (res.ok) {
const html = await res.text();
const directMatch = html.match(/WORKER_URL\s*[:=]\s*["'](https?:\/\/[^"']+)["']/i);
if (directMatch && directMatch[1]) {
foundUrls.push(directMatch[1].replace(/\/+$/, ''));
}
const scriptMatches = [...html.matchAll(/<script[^>]+src=["']([^"']+)["']/gi)];
for (const sm of scriptMatches) {
let scriptUrl = sm[1];
if (scriptUrl.startsWith('//')) scriptUrl = `https:${scriptUrl}`;
else if (scriptUrl.startsWith('/')) scriptUrl = `${BASE_URL}${scriptUrl}`;
else if (!scriptUrl.startsWith('http')) scriptUrl = `${BASE_URL}/${scriptUrl}`;
if (scriptUrl.includes('jsdelivr') || scriptUrl.includes('google') || scriptUrl.includes('cloudflare')) continue;
try {
const sRes = await fetch(scriptUrl, {
headers: { 'User-Agent': USER_AGENT, 'Referer': embedUrl },
signal: AbortSignal.timeout(6000)
});
if (sRes.ok) {
const sCode = await sRes.text();
const m = sCode.match(/WORKER_URL\s*[:=]\s*["'](https?:\/\/[^"']+)["']/i);
if (m && m[1]) {
const wUrl = m[1].replace(/\/+$/, '');
if (!foundUrls.includes(wUrl)) foundUrls.push(wUrl);
}
const endpoints = [
async () => {
const url = isTv
? `https://themoviebox.uk/api/resolve?type=tv&id=${id}&season=${s}&episode=${e}`
: `https://themoviebox.uk/api/resolve?type=movie&id=${id}`;
const res = await fetchJson(url);
if (res?.success && Array.isArray(res.sources)) {
for (const item of res.sources) {
const streamUrl = item.url || item.original_url;
if (streamUrl) {
allUrls.push({
url: streamUrl,
server: `TheMovieBox - ${item.server || item.provider || 'Server'}`,
headers: {
Referer: 'https://themoviebox.uk/',
Origin: 'https://themoviebox.uk'
}
});
}
} catch { }
}
}
},
async () => {
const url = isTv
? `https://pro2.nabilekson.workers.dev/tv/${id}/${s}/${e}`
: `https://pro2.nabilekson.workers.dev/movie/${id}`;
const res = await fetchJson(url);
if (res?.success && Array.isArray(res.primarySources)) {
for (const item of res.primarySources) {
const streamUrl = item.url || item.originalUrl;
if (streamUrl) {
allUrls.push({
url: streamUrl,
server: `CineBros - ${item.name || item.quality || 'Server'}`,
headers: item.headers || {
Referer: 'https://pro2.nabilekson.workers.dev/',
Origin: 'https://pro2.nabilekson.workers.dev'
}
});
}
}
}
},
async () => {
const url = isTv
? `https://praxi.nabilekson.workers.dev/tv/${id}/${s}/${e}`
: `https://praxi.nabilekson.workers.dev/movie/${id}`;
const res = await fetchJson(url);
if (res?.success && Array.isArray(res.primarySources)) {
for (const item of res.primarySources) {
const streamUrl = item.url || item.originalUrl;
if (streamUrl) {
allUrls.push({
url: streamUrl,
server: `Praxi - ${item.name || item.providerName || 'Server'}`,
headers: item.headers || {
Referer: 'https://praxi.nabilekson.workers.dev/',
Origin: 'https://praxi.nabilekson.workers.dev'
}
});
}
}
}
},
async () => {
const url = isTv
? `https://freakyniki.elaxo.lol/?tmdb_id=${id}&season=${s}&episode=${e}`
: `https://freakyniki.elaxo.lol/?tmdb_id=${id}`;
const res = await fetchJson(url);
if (Array.isArray(res?.streams)) {
for (const item of res.streams) {
const streamUrl = item.url || item.raw_url;
if (streamUrl) {
allUrls.push({
url: streamUrl,
server: `FreakyNiki - ${item.server || item.provider || 'Server'}`,
headers: {
Referer: 'https://freakyniki.elaxo.lol/',
Origin: 'https://freakyniki.elaxo.lol'
}
});
}
}
}
}
} catch { }
];
const defaults = ['https://freakyniki.elaxo.lol', 'https://vidlove.nabilekson.workers.dev'];
for (const d of defaults) {
if (!foundUrls.includes(d)) foundUrls.push(d);
}
return foundUrls;
}
await Promise.allSettled(endpoints.map(fn => fn().catch(() => { })));
export async function getStream(args) {
const { id, s, e, server } = args;
const isTv = s != null && e != null;
const embedUrl = isTv
? `${BASE_URL}/embed/tv/${id}/${s}/${e}`
: `${BASE_URL}/embed/movie/${id}`;
if (!allUrls.length) return null;
const workerUrls = await getWorkerUrls(embedUrl);
const query = isTv
? `tmdb_id=${id}&tmdbId=${id}&season=${s}&episode=${e}`
: `tmdb_id=${id}&tmdbId=${id}`;
for (const worker of workerUrls) {
try {
const url = `${worker}/?${query}`;
const data = await fetchJson(url, {
headers: {
'User-Agent': USER_AGENT,
'Referer': `${BASE_URL}/`,
'Origin': BASE_URL,
'Accept': 'application/json, text/plain, */*'
},
signal: AbortSignal.timeout(12000)
});
if (data && Array.isArray(data.streams) && data.streams.length > 0) {
let allUrls = data.streams
.filter(st => st && typeof st.url === 'string')
.map(st => {
const isHls = (st.format || '').toLowerCase().includes('hls') || st.url.includes('.m3u8');
return {
url: st.url,
server: `FlaxMovies - ${st.provider || 'Default'}`,
quality: st.resolution || 'Auto',
type: isHls ? 'hls' : 'mp4',
headers: {
'User-Agent': USER_AGENT,
'Referer': `${BASE_URL}/`,
'Origin': BASE_URL
}
};
});
if (server && server !== 'all') {
const clean = server.toLowerCase().replace('flaxmovies - ', '');
const filtered = allUrls.filter(u => u.server.toLowerCase().includes(clean));
if (filtered.length) allUrls = filtered;
}
if (allUrls.length) {
return { allUrls };
}
}
} catch { }
}
return null;
}
export async function getSources() {
return ['FlaxMovies - Airflix', 'FlaxMovies - Xpass-VIP 1'];
return {
url: allUrls[0].url,
headers: allUrls[0].headers,
allUrls
};
}

125
src/sources/kickassanime.js Normal file
View File

@@ -0,0 +1,125 @@
import { fetchJson, USER_AGENT } from '../utils/helpers.js';
const BASE = "https://kaa.lt";
const HLS_BASE = "https://hls.krussdomi.com/manifest";
const HEADERS = { "User-Agent": USER_AGENT, Accept: "application/json" };
async function kaaSearch(query) {
const res = await fetchJson(`${BASE}/api/fsearch`, {
method: "POST",
headers: { ...HEADERS, "Content-Type": "application/json" },
body: JSON.stringify({ page: 1, query }),
}).catch(() => null);
return Array.isArray(res?.result) ? res.result : [];
}
async function kaaShowInfo(showSlug) {
return fetchJson(`${BASE}/api/show/${showSlug}`, { headers: HEADERS }).catch(() => null);
}
async function kaaEpisodePage(showSlug, ep) {
return fetchJson(`${BASE}/api/show/${showSlug}/episodes?ep=${ep}&lang=ja-JP`, { headers: HEADERS }).catch(() => null);
}
async function kaaAllEpisodes(showSlug) {
const first = await kaaEpisodePage(showSlug, 1);
if (!first) return [];
const pages = Array.isArray(first.pages) ? first.pages : [];
const all = Array.isArray(first.result) ? [...first.result] : [];
if (pages.length > 1) {
const rest = await Promise.all(
pages.slice(1).map(async (pg) => {
const startEp = pg.eps?.[0];
if (!startEp) return [];
const d = await kaaEpisodePage(showSlug, startEp);
return Array.isArray(d?.result) ? d.result : [];
})
);
for (const batch of rest) all.push(...batch);
}
return all;
}
async function kaaEpisodeServers(showSlug, fullEpSlug) {
return fetchJson(`${BASE}/api/show/${showSlug}/episode/${fullEpSlug}`, { headers: HEADERS }).catch(() => null);
}
export async function getStream({ id, s, e, tmdbApiKey, sdk }) {
try {
const apiKey = tmdbApiKey || sdk?.tmdbApiKey || process.env.TMDB_API_KEY || '45dbdd5e714da212424bda0336167447';
const isTv = !!s;
const mediaType = isTv ? 'tv' : 'movie';
const epNum = e ? Number(e) : 1;
let titles = [];
try {
const tmdbRes = await fetchJson(`https://api.themoviedb.org/3/${mediaType}/${id}?api_key=${apiKey}&append_to_response=alternative_titles`);
if (tmdbRes) {
const primary = tmdbRes.title || tmdbRes.name;
const original = tmdbRes.original_title || tmdbRes.original_name;
if (primary) titles.push(primary);
if (original && !titles.includes(original)) titles.push(original);
const alts = tmdbRes.alternative_titles?.titles || tmdbRes.alternative_titles?.results || [];
for (const alt of alts) {
const candidate = alt.title || alt.name;
if (candidate && !titles.includes(candidate)) {
titles.push(candidate);
}
}
}
} catch { }
if (!titles.length) return null;
let showSlug = null;
for (const title of titles) {
const results = await kaaSearch(title);
if (results.length) {
showSlug = results[0].slug;
break;
}
}
if (!showSlug) return null;
const showInfo = await kaaShowInfo(showSlug);
let fullEpSlug = null;
if (showInfo?.type === "movie") {
const m = (showInfo.watch_uri || "").match(/\/(ep-(\d+)-([a-f0-9]+))$/i);
if (m) fullEpSlug = m[1];
} else {
const episodes = await kaaAllEpisodes(showSlug);
const epItem = episodes.find((item) => item.episode_number === epNum);
if (epItem) fullEpSlug = `ep-${epItem.episode_number}-${epItem.slug}`;
}
if (!fullEpSlug) return null;
const episodeData = await kaaEpisodeServers(showSlug, fullEpSlug);
const servers = Array.isArray(episodeData?.servers) ? episodeData.servers : [];
const urls = [];
for (const srv of servers) {
if (!srv.src) continue;
const m = srv.src.match(/[?&]id=([^&]+)/);
if (m) {
urls.push({
url: `${HLS_BASE}/${m[1]}/master.m3u8`,
server: `KickAssAnime (${srv.name || 'Server'})`,
});
}
}
return urls.length ? { allUrls: urls } : null;
} catch (err) {
return null;
}
}
export async function getSources() {
return ['KickAssAnime'];
}

View File

@@ -1,43 +1,71 @@
import { getTmdbInfo } from '../utils/helpers.js';
export async function getStream(args) {
const { id, s, e, tmdbApiKey } = args;
const { id, s, e, tmdbApiKey, sdk } = args;
const type = (s != null && e != null) ? 'tv' : 'movie';
const apiKey = tmdbApiKey || sdk?.tmdbApiKey || process.env.TMDB_API_KEY || '45dbdd5e714da212424bda0336167447';
try {
const tmdbInfo = await getTmdbInfo(tmdbApiKey, id, type, s);
const name = tmdbInfo.titles[0] || '';
if (!name) return null;
if (type === 'tv') {
return null;
} else {
const url = `https://lmscript.xyz/v1/movies?filters[q]=${encodeURIComponent(name)}&expand=streams`;
}
const tmdbInfo = await getTmdbInfo(apiKey, id, type, s);
const titles = tmdbInfo?.titles || [];
if (!titles.length) return null;
let movie = null;
for (const title of titles) {
const url = `https://lmscript.xyz/v1/movies?filters[q]=${encodeURIComponent(title)}&expand=streams`;
const response = await fetch(url);
if (!response.ok) continue;
const data = await response.json();
const movie = data.items?.find(item => `${item.tmdb_prefix}` === `${id}` || `${item.tmdb_id}` === `${id}`);
if (!movie) return null;
const streams = movie.streams || {};
const SOURCE_ORDER = ["1080p", "720p", "480p", "360p"];
const sortedStreams = Object.keys(streams).sort((a, b) => SOURCE_ORDER.indexOf(a) - SOURCE_ORDER.indexOf(b));
if (!sortedStreams.length) return null;
const allUrls = sortedStreams.map(quality => {
const streamUrl = streams[quality];
return {
url: streamUrl,
quality,
type: streamUrl.includes('.m3u8') ? 'hls' : 'mp4',
headers: {}
};
const found = data.items?.find(item => {
const tmdbPrefixMatch = `${item.tmdb_prefix}` === `${id}`;
const tmdbIdMatch = `${item.tmdb_id}` === `${id}`;
const titleMatch = item.title?.toLowerCase() === title.toLowerCase();
return tmdbPrefixMatch || tmdbIdMatch || titleMatch;
});
return { allUrls };
if (found) {
movie = found;
break;
}
}
} catch (err) {
if (!movie) return null;
const streams = movie.streams || {};
const SOURCE_ORDER = ["1080p", "720p", "480p", "360p"];
const sortedStreams = Object.keys(streams).sort((a, b) => {
const indexA = SOURCE_ORDER.indexOf(a);
const indexB = SOURCE_ORDER.indexOf(b);
return (indexA === -1 ? 99 : indexA) - (indexB === -1 ? 99 : indexB);
});
if (!sortedStreams.length) return null;
const allUrls = sortedStreams.map(quality => {
const streamUrl = streams[quality];
return {
url: streamUrl,
quality,
server: `LMScript - ${quality}`,
type: streamUrl.includes('.m3u8') ? 'hls' : 'mp4',
headers: {
Referer: 'https://lookmovie2.to/',
Origin: 'https://lookmovie2.to'
}
};
});
return {
url: allUrls[0].url,
headers: allUrls[0].headers,
allUrls
};
} catch {
return null;
}
}

View File

@@ -1,117 +0,0 @@
import { getUA } from '../utils/helpers.js';
const BASE_URL = 'https://movienig.ht';
const PRIORITY_SERVERS = [
{ id: 'dallas', label: 'Dallas 4K' },
{ id: 'austin', label: 'Austin' },
{ id: 'helena', label: 'Helena' },
{ id: 'seattle', label: 'Seattle 4K' },
{ id: 'vixsrc-1', label: 'Newport Beach' },
{ id: 'tucson', label: 'Tucson' },
{ id: 'salem', label: 'Salem' },
];
async function resolveDetails(sdk, id, isTv) {
const apiKey = sdk?.tmdbApiKey || process.env.TMDB_API_KEY;
if (!apiKey) return null;
const type = isTv ? 'tv' : 'movie';
try {
const res = await fetch(`https://api.themoviedb.org/3/${type}/${id}?api_key=${apiKey}&append_to_response=external_ids`, {
signal: AbortSignal.timeout(6000),
});
if (!res.ok) return null;
const data = await res.json();
const title = isTv ? data.name : data.title;
const releaseDate = isTv ? data.first_air_date : data.release_date;
const year = releaseDate ? parseInt(releaseDate.split('-')[0], 10) : null;
const imdbId = data.imdb_id || data.external_ids?.imdb_id || null;
return { title, year, imdbId };
} catch {
return null;
}
}
export async function getStream({ id, s, e, sdk }) {
const isTv = Boolean(s && e);
const ua = getUA();
const details = await resolveDetails(sdk, id, isTv);
const title = details?.title || String(id);
const year = details?.year;
const imdbId = details?.imdbId;
const encTitle = encodeURIComponent(title);
const yearQuery = year ? `&year=${year}` : '';
const imdbQuery = imdbId ? `&imdbId=${imdbId}` : '';
const seenUrls = new Set();
const allUrls = [];
const defaultHeaders = {
'User-Agent': ua,
Referer: `${BASE_URL}/`,
Origin: BASE_URL,
Accept: 'text/event-stream',
};
const tasks = PRIORITY_SERVERS.map(async (server) => {
try {
const url = isTv
? `${BASE_URL}/api/stream/v1/tv/${id}/${s}/${e}?title=${encTitle}${yearQuery}${imdbQuery}&server=${server.id}&only=1`
: `${BASE_URL}/api/stream/v1/movie/${id}?title=${encTitle}${yearQuery}${imdbQuery}&server=${server.id}&only=1`;
const res = await fetch(url, {
headers: defaultHeaders,
signal: AbortSignal.timeout(6000),
});
if (!res.ok) return;
const bodyStr = await res.text();
if (bodyStr.includes('event: done')) {
const doneIdx = bodyStr.indexOf('event: done');
const dataIdx = bodyStr.indexOf('data: ', doneIdx);
if (dataIdx !== -1) {
const jsonStart = dataIdx + 6;
const jsonEnd = bodyStr.indexOf('\n', jsonStart);
const jsonText = (jsonEnd !== -1 ? bodyStr.substring(jsonStart, jsonEnd) : bodyStr.substring(jsonStart)).trim();
const data = JSON.parse(jsonText);
if (Array.isArray(data?.sources)) {
for (const src of data.sources) {
if (!src || typeof src !== 'object') continue;
const rawUrl = String(src.url || '').trim();
if (!rawUrl || seenUrls.has(rawUrl)) continue;
seenUrls.add(rawUrl);
const quality = src.quality ? String(src.quality) : 'Auto';
const titleQuality = quality !== 'Auto' ? ` (${quality})` : '';
allUrls.push({
url: rawUrl,
label: `MovieNight ${server.label}${titleQuality}`,
headers: {
'User-Agent': ua,
Referer: `${BASE_URL}/`,
},
});
}
}
}
}
} catch { }
});
await Promise.all(tasks);
if (allUrls.length === 0) return null;
return {
url: allUrls[0].url,
allUrls,
headers: allUrls[0].headers,
};
}

View File

@@ -1,282 +1,210 @@
import { getUA } from '../utils/helpers.js';
import { fetchJson, fetchText, getTmdbInfo } from '../utils/helpers.js';
const API_BASE = 'https://api.wecollege.net';
const REFERER = 'https://www.movy.bz/';
const ORIGIN = 'https://www.movy.bz';
const K = [
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174
];
const MAGIC = [109, 118, 109, 49];
const SERVERS = [
{ endpoint: 'miami', name: 'Miami', note: 'Original audio (Up to 4K)' },
{ endpoint: 'seattle', name: 'Seattle', note: 'Original audio' },
{ endpoint: 'denver', name: 'Denver', note: 'Original audio' },
{ endpoint: 'chicago', name: 'Chicago', note: 'Original audio' },
{ endpoint: 'dallas', name: 'Dallas', note: 'Original audio' },
{ endpoint: 'atlanta', name: 'Atlanta', note: 'Original audio' },
{ endpoint: 'houston', name: 'Houston', note: 'Original audio' },
{ endpoint: 'austin', name: 'Austin', note: 'Original audio' },
{ endpoint: 'boston', name: 'Boston', note: 'Original audio' },
{ endpoint: 'munich', name: 'Munich', note: 'German audio', extra: 'language=german' },
{ endpoint: 'berlin', name: 'Berlin', note: 'German audio' },
{ endpoint: 'paris', name: 'Paris', note: 'French audio' },
{ endpoint: 'delhi', name: 'Delhi', note: 'Hindi audio' },
{ endpoint: 'cancun', name: 'Cancun', note: 'Spanish audio' },
];
const isEven = e => ((e * (e + 1)) & 1) === 0;
const seedCache = new Map();
function _l(e) {
let v = e >>> 0;
v = (v ^ (v >>> 16)) >>> 0;
v = Math.imul(v, 0x85ebca6b) >>> 0;
v = (v ^ (v >>> 13)) >>> 0;
v = Math.imul(v, 0xc2b2ae35) >>> 0;
return (v ^ (v >>> 16)) >>> 0;
function scramble(e) {
e >>>= 0;
e ^= e >>> 16;
e = Math.imul(e, 0x85ebca6b) >>> 0;
e ^= e >>> 13;
e = Math.imul(e, 0xc2b2ae35) >>> 0;
return (e ^ (e >>> 16)) >>> 0;
}
function _u(e, t) {
const shift = t & 31;
if (shift === 0) return e >>> 0;
return (((e << shift) >>> 0) | (e >>> (32 - shift))) >>> 0;
function rotl(e, a) {
e >>>= 0;
a &= 31;
return a === 0 ? e >>> 0 : ((e << a) | (e >>> (32 - a))) >>> 0;
}
function _fnv1a(str) {
let t = 0x811c9dc5 >>> 0;
for (let i = 0; i < str.length; i++) {
const code = str.charCodeAt(i);
t = Math.imul((t ^ code) >>> 0, 0x1000193) >>> 0;
}
return _l(t);
}
function _initKeyState(seed, tmdbId) {
const s = new Array(61).fill(0);
const isSet = new Array(61).fill(false);
const idVal = ((tmdbId >>> 0) ^ 0x9e3779b9) >>> 0;
let r = _l((_fnv1a(seed) ^ _l(idVal)) >>> 0);
for (let e = 0; e < 8; e++) {
const t = r % 61;
r = _u((r + 0x9e3779b9) >>> 0, 7 + (7 & e));
s[t] = (r ^ _l(r)) >>> 0;
isSet[t] = true;
r = _l((r + t) >>> 0);
function initCipherState(keyStr, seedStr) {
let keyLen = keyStr.length;
if (((keyLen * (keyLen + 1)) & 1) === 1) {
const S = new Array(256);
for (let i = 0; i < 256; i++) S[i] = i;
let j = 0;
for (let i = 0; i < 256; i++) {
j = (j + S[i] + keyStr.charCodeAt(i % keyStr.length)) & 255;
const tmp = S[i];
S[i] = S[j];
S[j] = tmp;
}
let acc = 0x67452301;
for (let i = 0; i < keyStr.length; i++) {
acc = rotl((acc ^ Math.imul(keyStr.charCodeAt(i), K[15 & i])) >>> 0, 5);
}
return { S, acc: scramble(acc) };
}
const acc = _l((0xa5a5a5a5 ^ r) >>> 0);
return { s, isSet, acc };
const S = new Array(61);
let hash = 0x811c9dc5;
for (let i = 0; i < keyStr.length; i++) {
hash = Math.imul(hash ^ keyStr.charCodeAt(i), 0x1000193) >>> 0;
}
let n = scramble(scramble(hash) ^ scramble((seedStr >>> 0) ^ 0x9e3779b9)) >>> 0;
for (let i = 0; i < 8; i++) {
if (isEven(i)) {
const idx = n % 61;
n = rotl((n + 0x9e3779b9) >>> 0, 7 + (7 & i));
S[idx] = (n ^ scramble(n)) >>> 0;
n = scramble((n + idx) >>> 0);
} else {
S[i] = K[15 & i];
}
}
return {
S,
acc: scramble(0xa5a5a5a5 ^ n) >>> 0
};
}
function _nextKeystreamWord(state, t) {
const r = state.s;
let nState = state.acc;
const i = nState % 61;
const d = state.isSet[i] ? r[i] : 0;
const c = Math.imul((t + 1) >>> 0, 0x9e3779b9) >>> 0;
const a = nState;
const sVal = (d ^ c) >>> 0;
const h = state.isSet[i] ? ((a | sVal) >>> 0) : ((a ^ sVal) >>> 0);
const term1 = _u((h + nState) >>> 0, 31 & i);
const term2 = _u(nState, 31 & (i * 7));
nState = _l((((term1 ^ term2) >>> 0) + 0x9e3779b9) >>> 0);
r[i] = nState;
state.isSet[i] = true;
state.acc = nState;
return nState >>> 0;
function nextKeystreamWord(state, step) {
const S = state.S;
let acc = state.acc;
const idx = acc % 61;
const hasIdx = 0 - Number(idx in S);
const sVal = S[idx] >>> 0;
const c = Math.imul(0x9e3779b9, step + 1) >>> 0;
let b = ((acc ^ ((sVal ^ c) >>> 0)) >>> 0 | (acc & (sVal ^ c) & hasIdx) >>> 0) >>> 0;
b = (rotl((b + acc) >>> 0, 31 & idx) ^ rotl(acc, 31 & Math.imul(idx, 7))) >>> 0;
acc = scramble((b + 0x9e3779b9) >>> 0);
S[idx] = acc >>> 0;
state.acc = acc;
return acc >>> 0;
}
function _generateKeyStream(seed, tmdbId, len) {
const state = _initKeyState(seed, tmdbId);
const out = new Uint8Array(len);
let wordIdx = 0;
function decodeBase64Url(str) {
const padded = str.replace(/-/g, "+").replace(/_/g, "/").padEnd(4 * Math.ceil(str.length / 4), "=");
const bin = atob(padded);
const bytes = new Uint8Array(bin.length);
for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);
return bytes;
}
function decryptPayload(ciphertext, seed, mediaId) {
const rawBytes = decodeBase64Url(ciphertext);
const totalLen = rawBytes.length;
const state = initCipherState(seed, mediaId);
const keystream = new Uint8Array(totalLen);
let byteIdx = 0;
while (byteIdx < len) {
const word = _nextKeystreamWord(state, wordIdx++);
out[byteIdx++] = word & 0xFF;
if (byteIdx < len) out[byteIdx++] = (word >>> 8) & 0xFF;
if (byteIdx < len) out[byteIdx++] = (word >>> 16) & 0xFF;
if (byteIdx < len) out[byteIdx++] = (word >>> 24) & 0xFF;
}
return out;
}
function decrypt(cipherB64, seed, tmdbId) {
try {
let normalized = cipherB64.replace(/-/g, '+').replace(/_/g, '/');
while (normalized.length % 4 !== 0) normalized += '=';
const cipherBytes = Buffer.from(normalized, 'base64');
if (cipherBytes.length <= MAGIC.length) return null;
const ks = _generateKeyStream(seed, tmdbId, cipherBytes.length);
const decrypted = new Uint8Array(cipherBytes.length);
for (let i = 0; i < cipherBytes.length; i++) {
decrypted[i] = cipherBytes[i] ^ ks[i];
}
for (let k = 0; k < MAGIC.length; k++) {
if (decrypted[k] !== MAGIC[k]) return null;
}
return Buffer.from(decrypted.subarray(MAGIC.length)).toString('utf8');
} catch {
return null;
}
}
async function getSeed(tmdbId, ua) {
const now = Date.now();
const cached = seedCache.get(tmdbId);
if (cached && cached.expiresAt > now + 5000) {
return cached.seed;
let step = 0;
while (byteIdx < totalLen) {
const word = nextKeystreamWord(state, step++);
keystream[byteIdx++] = 255 & word;
if (byteIdx < totalLen) keystream[byteIdx++] = (word >>> 8) & 255;
if (byteIdx < totalLen) keystream[byteIdx++] = (word >>> 16) & 255;
if (byteIdx < totalLen) keystream[byteIdx++] = (word >>> 24) & 255;
}
try {
const res = await fetch(`${API_BASE}/seed?mediaId=${tmdbId}`, {
headers: {
'User-Agent': ua,
Referer: REFERER,
Origin: ORIGIN,
},
signal: AbortSignal.timeout(8000),
});
for (let i = 0; i < totalLen; i++) {
rawBytes[i] ^= keystream[i];
}
if (res.ok) {
const data = await res.json();
const seed = data?.seed ? String(data.seed) : '';
const ttlMs = typeof data?.ttlMs === 'number' ? data.ttlMs : 30000;
if (seed) {
seedCache.set(tmdbId, { seed, expiresAt: now + ttlMs });
return seed;
}
for (let i = 0; i < MAGIC.length; i++) {
if (rawBytes[i] !== MAGIC[i]) {
throw new Error("decrypt failed: bad seed or tampered payload");
}
} catch { }
}
return null;
const payload = rawBytes.subarray(MAGIC.length);
return new TextDecoder("utf-8").decode(payload);
}
async function resolveDetails(sdk, id, isTv) {
const apiKey = sdk?.tmdbApiKey || process.env.TMDB_API_KEY;
if (!apiKey) return null;
export async function getStream({ id, s, e, tmdbApiKey, sdk }) {
const isTv = s != null && e != null;
const type = isTv ? 'tv' : 'movie';
try {
const res = await fetch(`https://api.themoviedb.org/3/${type}/${id}?api_key=${apiKey}&append_to_response=external_ids`, {
signal: AbortSignal.timeout(6000),
});
if (!res.ok) return null;
const data = await res.json();
const title = isTv ? data.name : data.title;
const releaseDate = isTv ? data.first_air_date : data.release_date;
const year = releaseDate ? parseInt(releaseDate.split('-')[0], 10) : null;
const imdbId = data.imdb_id || data.external_ids?.imdb_id || null;
return { title, year, imdbId };
} catch {
return null;
}
}
const apiKey = tmdbApiKey || sdk?.tmdbApiKey || process.env.TMDB_API_KEY || '45dbdd5e714da212424bda0336167447';
function formatQuality(raw) {
const lower = String(raw).toLowerCase();
if (lower.includes('2160') || lower.includes('4k')) return '4K';
if (lower.includes('1080')) return '1080p';
if (lower.includes('720')) return '720p';
if (lower.includes('480')) return '480p';
if (lower.includes('360')) return '360p';
if (raw) return String(raw);
return 'Auto';
}
export async function getStream({ id, s, e, sdk }) {
const isTv = Boolean(s && e);
const mediaType = isTv ? 'tv' : 'movie';
const tmdbId = parseInt(id, 10);
const ua = getUA();
const details = await resolveDetails(sdk, id, isTv);
const title = details?.title || String(id);
const year = details?.year;
const imdbId = details?.imdbId;
const seed = await getSeed(tmdbId, ua);
if (!seed) return null;
const params = new URLSearchParams({
title,
mediaType,
tmdbId: String(tmdbId),
enc: '2',
seed,
});
if (year) params.set('year', String(year));
if (isTv) {
if (s) params.set('seasonId', String(s));
if (e) params.set('episodeId', String(e));
}
if (imdbId) params.set('imdbId', imdbId);
const baseQuery = params.toString();
const seenUrls = new Set();
const allUrls = [];
const defaultHeaders = {
'User-Agent': ua,
Referer: REFERER,
Origin: ORIGIN,
const headers = {
'Accept': '*/*',
'Origin': 'https://www.movy.sx',
'Referer': 'https://www.movy.sx/',
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.5 Mobile/15E148 Safari/604.1'
};
const tasks = SERVERS.map(async (server) => {
let fullUrl = `${API_BASE}/${server.endpoint}/sources?${baseQuery}`;
if (server.extra) fullUrl += `&${server.extra}`;
let title = '';
let year = '';
let imdbId = '';
try {
const info = await getTmdbInfo(apiKey, id, type, s);
title = info?.titles?.[0] || '';
year = info?.year || '';
imdbId = info?.imdbId || '';
} catch { }
if (!title) {
try {
const tmdbRes = await fetchJson(`https://api.themoviedb.org/3/${type}/${id}?api_key=${apiKey}&append_to_response=external_ids`);
title = tmdbRes?.title || tmdbRes?.name || '';
year = (tmdbRes?.release_date || tmdbRes?.first_air_date || '').split('-')[0] || '';
imdbId = tmdbRes?.imdb_id || tmdbRes?.external_ids?.imdb_id || '';
} catch { }
}
if (!title) return null;
const seedRes = await fetchJson(`https://api.wecollege.net/seed?mediaId=${id}`, { headers }).catch(() => null);
const seed = seedRes?.seed;
if (!seed) return null;
const servers = ['miami', 'boise', 'vegas', 'chicago', 'reno', 'seattle'];
const allUrls = [];
for (const srv of servers) {
const params = new URLSearchParams({
title,
mediaType: type,
year: String(year),
episodeId: String(e || 1),
seasonId: String(s || 1),
tmdbId: String(id),
imdbId: String(imdbId || ''),
enc: '2',
seed
});
const url = `https://api.wecollege.net/${srv}/sources?${params.toString()}`;
const rawEnc = await fetchText(url, { headers }).catch(() => null);
if (!rawEnc || rawEnc.length < 20 || rawEnc.startsWith('{') || rawEnc.startsWith('<')) continue;
try {
const res = await fetch(fullUrl, {
headers: defaultHeaders,
signal: AbortSignal.timeout(8000),
});
const dec = decryptPayload(rawEnc.trim(), seed, id);
const parsed = JSON.parse(dec);
if (!res.ok) return;
const encText = (await res.text()).trim();
if (!encText || encText.startsWith('<')) return;
const decJsonStr = decrypt(encText, seed, tmdbId);
if (!decJsonStr) return;
const parsed = JSON.parse(decJsonStr);
if (!Array.isArray(parsed?.sources)) return;
for (const src of parsed.sources) {
if (!src || typeof src !== 'object') continue;
const streamUrl = String(src.url || '').trim();
if (!streamUrl || seenUrls.has(streamUrl)) continue;
seenUrls.add(streamUrl);
const cleanQuality = formatQuality(src.quality || 'Auto');
const isSpecificAudio = server.note.toLowerCase().includes('audio') && !server.note.toLowerCase().includes('original');
const langLabel = isSpecificAudio ? server.note.split(' ')[0] : '';
const label = isSpecificAudio
? `${server.name} (${langLabel}) · ${cleanQuality}`
: `${server.name} · ${cleanQuality}`;
allUrls.push({
url: streamUrl,
label,
headers: {
'User-Agent': ua,
Referer: REFERER,
},
});
if (Array.isArray(parsed?.sources)) {
for (const src of parsed.sources) {
if (src?.url) {
allUrls.push({
url: src.url,
server: `Movy - ${srv.toUpperCase()} (${src.quality || 'Auto'})`,
headers: {
Referer: 'https://www.movy.sx/',
Origin: 'https://www.movy.sx'
}
});
}
}
}
} catch { }
});
await Promise.all(tasks);
if (allUrls.length) break;
}
if (allUrls.length === 0) return null;
if (!allUrls.length) return null;
return {
url: allUrls[0].url,
allUrls,
headers: allUrls[0].headers,
allUrls
};
}

56
src/sources/net79.js Normal file
View File

@@ -0,0 +1,56 @@
import { fetchJson } from '../utils/helpers.js';
export async function getStream({ id, s, e }) {
const isTv = s != null && e != null;
const url = isTv
? `https://net79.cc/api/embed-tmdb/${id}?type=tv&se=${s}&ep=${e}`
: `https://net79.cc/api/embed-tmdb/${id}?type=movie`;
const headers = {
'Accept': 'application/json',
'Referer': 'https://net79.cc/',
'Origin': 'https://net79.cc',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36'
};
const res = await fetchJson(url, { headers }).catch(() => null);
if (!res || !res.ok || res.noSource) return null;
const allUrls = [];
if (Array.isArray(res.streams) && res.streams.length > 0) {
for (const stream of res.streams) {
if (stream?.url) {
allUrls.push({
url: stream.url,
quality: stream.resolution ? `${stream.resolution}p` : 'Auto',
server: `Net79 - ${stream.resolution ? `${stream.resolution}p` : 'MP4'}`,
type: stream.url.includes('.m3u8') ? 'hls' : 'mp4',
headers: {
Referer: 'https://net79.cc/',
Origin: 'https://net79.cc'
}
});
}
}
} else if (res.mp4) {
allUrls.push({
url: res.mp4,
quality: res.resolution ? `${res.resolution}p` : 'Auto',
server: `Net79 - ${res.resolution ? `${res.resolution}p` : 'MP4'}`,
type: res.mp4.includes('.m3u8') ? 'hls' : 'mp4',
headers: {
Referer: 'https://net79.cc/',
Origin: 'https://net79.cc'
}
});
}
if (!allUrls.length) return null;
return {
url: allUrls[0].url,
headers: allUrls[0].headers,
allUrls
};
}

54
src/sources/rivo.js Normal file
View File

@@ -0,0 +1,54 @@
import { fetchJson } from '../utils/helpers.js';
export async function getStream({ id, s, e }) {
const isTv = s != null && e != null;
const separator = isTv ? '&' : '?';
const basePath = isTv
? `https://f.rivo.st/api/source/tv/${id}?s=${s}&e=${e}`
: `https://f.rivo.st/api/source/movie/${id}`;
const headers = {
'Accept': '*/*',
'Referer': 'https://f.rivo.st/',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36'
};
const serverIndices = Array.from({ length: 26 }, (_, i) => i + 1);
const responses = await Promise.allSettled(
serverIndices.map(serverNum =>
fetchJson(`${basePath}&server=${serverNum}`, { headers })
.catch(() => null)
)
);
const allUrls = [];
for (const res of responses) {
if (res.status !== 'fulfilled' || !res.value) continue;
const data = res.value;
if (Array.isArray(data.sources)) {
for (const src of data.sources) {
if (src?.url) {
allUrls.push({
url: src.url,
server: `Rivo - ${data.server || src.subProvider || 'Server'}`,
headers: {
Referer: 'https://f.rivo.st/',
Origin: 'https://f.rivo.st'
}
});
}
}
}
}
if (!allUrls.length) return null;
return {
url: allUrls[0].url,
headers: allUrls[0].headers,
allUrls
};
}

View File

@@ -1,62 +1,63 @@
import { getUA } from "../utils/helpers.js";
import { fetchJson } from '../utils/helpers.js';
const API_BASE = "https://thuktv.onrender.com/api";
function getHeaders() {
return {
"User-Agent": getUA(),
Accept: "application/json",
Referer: "https://thugtv.online/",
Origin: "https://thugtv.online",
};
}
function mapSource(source) {
if (!source || !source.url) return null;
if (source.type !== "hls" && source.type !== "mp4") return null;
if (!/^https?:\/\//.test(source.url)) return null;
return {
url: source.url,
headers: {
"User-Agent": getUA(),
Referer: "https://thugtv.online/",
Origin: "https://thugtv.online",
},
server: source.name || source.label || "ThugTV",
quality: source.quality || "1080p",
type: source.type,
skipProxy: true,
};
}
export async function getStream(args) {
const { id, s = null, e = null } = args;
export async function getStream({ id, s, e }) {
const isTv = s != null && e != null;
const path = isTv ? `/stream/tv/${id}/${s}/${e}` : `/stream/movie/${id}`;
const mainUrl = `https://api.thugtv.online/api${path}`;
const url = isTv
? `${API_BASE}/stream/tv/${id}/${s}/${e}`
: `${API_BASE}/stream/movie/${id}`;
const headers = {
'Accept': 'application/json',
'Referer': 'https://thugtv.online/',
'Origin': 'https://thugtv.online',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36'
};
try {
const res = await fetch(url, {
headers: getHeaders(),
signal: AbortSignal.timeout(15000),
});
if (!res.ok) return null;
const res = await fetchJson(mainUrl, { headers }).catch(() => null);
if (!res || !Array.isArray(res.sources) || !res.sources.length) return null;
const data = await res.json();
const sources = Array.isArray(data?.sources) ? data.sources : [];
if (!sources.length) return null;
const allUrls = [];
const allUrls = sources
.map(mapSource)
.filter(Boolean);
for (const src of res.sources) {
if (src.type === 'embed') continue;
if (!allUrls.length) return null;
if (src.url && src.url.startsWith('http')) {
allUrls.push({
url: src.url,
server: `ThugTV - ${src.name || src.label || 'Direct'} (${src.quality || 'Auto'})`,
headers: {
Referer: 'https://thugtv.online/',
Origin: 'https://thugtv.online'
}
});
continue;
}
return { allUrls };
} catch {
return null;
if (src.resolvePath) {
const cleanPath = src.resolvePath.startsWith('/') ? src.resolvePath : `/${src.resolvePath}`;
const resolveUrl = `https://thugtv.online/api${cleanPath}`;
try {
const resolved = await fetchJson(resolveUrl, { headers });
const directUrl = resolved?.source?.url;
if (directUrl && directUrl.startsWith('http')) {
allUrls.push({
url: directUrl,
server: `ThugTV - ${resolved.source.name || resolved.source.label || src.name || 'Direct'}`,
headers: {
Referer: 'https://thugtv.online/',
Origin: 'https://thugtv.online'
}
});
}
} catch { }
}
}
if (!allUrls.length) return null;
return {
url: allUrls[0].url,
headers: allUrls[0].headers,
allUrls
};
}

View File

@@ -1,147 +0,0 @@
import { USER_AGENT } from '../utils/helpers.js';
const REDIS_URL = 'https://vidnest-redis-fell-prism-rest.cloud.layerbase.dev/';
const REDIS_AUTH = 'Bearer ve8z9XSKatu74M7FjLU8eQ29';
const ORIGIN = 'https://vidgod.space';
const SERVERS = [
{
name: 'Pulsar',
key: 'pulsar',
base: 'https://vidnest-extractor.vividdubbing.workers.dev/api',
serverParam: 'prime'
},
{
name: 'Orion',
key: 'orion',
base: 'https://404-vidnest.lofiserver.workers.dev/api',
serverParam: 'gama'
},
{
name: 'Stellar',
key: 'stellar',
base: 'https://404-vidnest.lofiserver.workers.dev/api',
serverParam: 'sigma'
}
];
function buildCacheKey(serverKey, type, id, s, e) {
const parts = [serverKey, type, id];
if (type === 'tv') {
parts.push(s || '1', e || '1');
}
return parts.join(':');
}
async function getFromCache(key) {
try {
const res = await fetch(REDIS_URL, {
method: 'POST',
headers: {
'Authorization': REDIS_AUTH,
'Content-Type': 'application/json',
'Accept': '*/*',
'Origin': ORIGIN,
'Referer': `${ORIGIN}/`,
'User-Agent': USER_AGENT
},
body: JSON.stringify(['GET', key]),
signal: AbortSignal.timeout(6000)
});
if (!res.ok) return null;
const data = await res.json();
if (!data || !data.result) return null;
const parsed = typeof data.result === 'string' ? JSON.parse(data.result) : data.result;
return parsed?.streams?.length ? parsed : null;
} catch {
return null;
}
}
async function fetchFromWorker(srv, type, id, s, e) {
try {
const endpoint = type === 'movie'
? `${srv.base}/movie/${id}?server=${srv.serverParam}`
: `${srv.base}/tv/${id}/${s || '1'}/${e || '1'}?server=${srv.serverParam}`;
const res = await fetch(endpoint, {
headers: {
'Accept': 'application/json',
'Origin': ORIGIN,
'Referer': `${ORIGIN}/`,
'User-Agent': USER_AGENT
},
signal: AbortSignal.timeout(8000)
});
if (!res.ok) return null;
const data = await res.json();
return data?.streams?.length ? data : null;
} catch {
return null;
}
}
async function resolveServer(srv, type, id, s, e) {
const cacheKey = buildCacheKey(srv.key, type, id, s, e);
const cached = await getFromCache(cacheKey);
if (cached) return { server: srv.name, data: cached };
const live = await fetchFromWorker(srv, type, id, s, e);
if (live) return { server: srv.name, data: live };
return null;
}
export async function getStream(args) {
const { id, s, e, server } = args;
const isTv = s != null && e != null;
const type = isTv ? 'tv' : 'movie';
let targets = SERVERS;
if (server && server !== 'all') {
const clean = server.toLowerCase().trim();
targets = SERVERS.filter(srv => srv.name.toLowerCase() === clean || srv.key.toLowerCase() === clean);
if (!targets.length) targets = SERVERS;
}
const settled = await Promise.allSettled(
targets.map(srv => resolveServer(srv, type, id, s, e))
);
const allUrls = [];
for (const r of settled) {
if (r.status !== 'fulfilled' || !r.value) continue;
const { server: serverName, data } = r.value;
for (const item of data.streams || []) {
if (!item || !item.url) continue;
const typeStr = String(item.type || '').toLowerCase();
const urlStr = String(item.url || '').toLowerCase();
const isM3U8 =
typeStr === 'hls' ||
typeStr === 'application/vnd.apple.mpegurl' ||
typeStr === 'cloudflare' ||
typeStr === 'tiktok' ||
urlStr.includes('.m3u8') ||
urlStr.includes('.txt');
allUrls.push({
url: item.url,
server: serverName,
quality: item.quality || 'Auto',
type: isM3U8 ? 'hls' : 'mp4',
headers: item.headers || {
'User-Agent': USER_AGENT,
'Referer': `${ORIGIN}/`
}
});
}
}
return allUrls.length ? { allUrls } : null;
}

View File

@@ -1,79 +0,0 @@
import { fetchJson, fetchText, USER_AGENT } from '../utils/helpers.js';
const BASE_URL = 'https://vidnest.fun';
const API_BASE_URL = 'https://new.vidnest.fun';
const REQUEST_HEADERS = { 'User-Agent': USER_AGENT, 'Accept': 'application/json, text/javascript, */*; q=0.01', 'Accept-Language': 'en-US,en;q=0.9', 'Referer': `${BASE_URL}/`, 'Origin': BASE_URL };
const CDN_PROXY_HEADERS = { 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:137.0) Gecko/20100101 Firefox/137.0', 'accept': '*/*', 'accept-language': 'en-US,en;q=0.5', 'origin': 'https://megaplay.buzz', 'referer': 'https://megaplay.buzz/' };
export const CDN_HEADERS = [
{ pattern: /letsgocdn\d+\.shop/i, headers: { 'Referer': BASE_URL, 'Origin': BASE_URL, 'User-Agent': USER_AGENT } },
{ pattern: /cdn\.mewstream\.buzz/i, headers: { 'Referer': BASE_URL, 'Origin': BASE_URL, 'User-Agent': USER_AGENT } }
];
const ALPHABET = 'RB0fpH8ZEyVLkv7c2i6MAJ5u3IKFDxlS1NTsnGaqmXYdUrtzjwObCgQP94hoeW+/=';
const REV_MAP = new Uint8Array(256);
for (let i = 0; i < ALPHABET.length; i++) REV_MAP[ALPHABET.charCodeAt(i)] = i;
function decrypt(payload) {
const len = payload.length;
const bytes = new Uint8Array(len);
let j = 0;
for (let i = 0; i < len; i += 4) {
const c0 = REV_MAP[payload.charCodeAt(i)] ?? 64;
const c1 = REV_MAP[payload.charCodeAt(i + 1)] ?? 64;
const c2 = REV_MAP[payload.charCodeAt(i + 2)] ?? 64;
const c3 = REV_MAP[payload.charCodeAt(i + 3)] ?? 64;
bytes[j++] = (c0 << 2) | (c1 >> 4);
if (c2 !== 64) bytes[j++] = ((c1 & 15) << 4) | (c2 >> 2);
if (c3 !== 64) bytes[j++] = ((c2 & 3) << 6) | c3;
}
return JSON.parse(new TextDecoder().decode(bytes.subarray(0, j)));
}
async function tmdbToAnilist(tmdbId, season) {
try {
const data = await fetchJson(`https://api.ani.zip/mappings?tmdb_id=${tmdbId}&type=tv&season=${season || 1}`, { signal: AbortSignal.timeout(6000) });
return data?.mappings?.[0]?.anilist_id || null;
} catch { return null; }
}
const SERVER_KEYS = ['hollymoviehd', 'allmovies', 'catflix', 'purstream', 'lamda', 'vidlink', 'klikxxi'];
export async function getStream({ id, s, e, sdk, audio }) {
const tmdbApiKey = sdk?.tmdbApiKey || null;
if (!tmdbApiKey) return null;
if (s) {
const k = tmdbApiKey;
const showData = k ? await fetchJson(`https://api.themoviedb.org/3/tv/${id}?api_key=${k}`, { signal: AbortSignal.timeout(5000) }).catch(() => null) : null;
if (showData?.genres?.some(g => g.id === 16)) {
const anilistId = await tmdbToAnilist(id, s);
if (anilistId) {
try {
const json = await fetchJson(`${API_BASE_URL}/hianime/anime/${anilistId}/${e || 1}/${audio === 'dub' ? 'dub' : 'sub'}`, { headers: REQUEST_HEADERS, signal: AbortSignal.timeout(15000) });
const data = json.encrypted ? decrypt(json.data) : json.data;
const file = data?.sources?.[0]?.file;
if (file) return { url: `https://megacloud.animanga.fun/proxy?url=${encodeURIComponent(file)}&headers=${encodeURIComponent(JSON.stringify(CDN_PROXY_HEADERS))}`, headers: REQUEST_HEADERS };
} catch { }
}
}
}
if (audio === 'dub') return null;
const segment = s ? `tv/${id}/${s}/${e || 1}` : `movie/${id}`;
const settled = await Promise.allSettled(SERVER_KEYS.map(async server => {
const json = await fetchJson(`${API_BASE_URL}/${server}/${segment}`, { headers: REQUEST_HEADERS, signal: AbortSignal.timeout(10000) });
const data = json.encrypted ? decrypt(json.data) : json.data;
const file = data?.sources?.[0]?.file ?? data?.streams?.[0]?.url ?? data?.url?.[0]?.link ?? data?.data?.stream?.playlist;
if (file) return { url: file, server, headers: REQUEST_HEADERS };
throw new Error();
}));
const allUrls = [];
for (const r of settled) if (r.status === 'fulfilled') allUrls.push(r.value);
return allUrls.length ? { allUrls } : null;
}
export async function getSources(args) {
const res = await getStream(args);
return res?.allUrls ? res.allUrls.map(u => u.server) : [];
}
export const VERIFY_HEADERS = { ...REQUEST_HEADERS };

62
src/sources/vidora.js Normal file
View File

@@ -0,0 +1,62 @@
import { fetchText } from '../utils/helpers.js';
export async function getStream({ id, s, e }) {
const isTv = s != null && e != null;
const url = isTv
? `https://vidora-3l2w.onrender.com/api/sources?type=tv&id=${id}&season=${s}&episode=${e}`
: `https://vidora-3l2w.onrender.com/api/sources?type=movie&id=${id}`;
const headers = {
'Accept': 'text/event-stream',
'Referer': 'https://vidora-3l2w.onrender.com/',
'Origin': 'https://vidora-3l2w.onrender.com',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36'
};
const raw = await fetchText(url, { headers }).catch(() => null);
if (!raw) return null;
const lines = raw.split('\n');
let doneSources = null;
const streamItems = [];
for (const line of lines) {
const trimmed = line.trim();
if (!trimmed.startsWith('{') || !trimmed.endsWith('}')) continue;
try {
const parsed = JSON.parse(trimmed);
if (parsed.event === 'source' && parsed.source?.url) {
streamItems.push(parsed.source);
} else if (parsed.event === 'done' && Array.isArray(parsed.sources)) {
doneSources = parsed.sources;
}
} catch { }
}
const targetList = (doneSources && doneSources.length > 0) ? doneSources : streamItems;
if (!targetList.length) return null;
const allUrls = [];
for (const src of targetList) {
if (!src.url || !src.url.startsWith('http')) continue;
allUrls.push({
url: src.url,
server: `Vidora - ${src.label || src.server || src.providerGroup || 'Server'} (${src.quality || 'Auto'})`,
headers: src.headers || {
Referer: 'https://vidora-3l2w.onrender.com/',
Origin: 'https://vidora-3l2w.onrender.com'
}
});
}
if (!allUrls.length) return null;
return {
url: allUrls[0].url,
headers: allUrls[0].headers,
allUrls
};
}

63
src/sources/yapgrid.js Normal file
View File

@@ -0,0 +1,63 @@
import { fetchJson } from '../utils/helpers.js';
export async function getStream({ id, s, e }) {
const isTv = s != null && e != null;
const targetEmbedUrl = isTv
? `https://embedwave.cc/embed/tv/${id}/${s}/${e}`
: `https://embedwave.cc/embed/movie/${id}`;
const headers = {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Origin': 'https://yapgrid.com',
'Referer': isTv ? `https://yapgrid.com/embed/tv/${id}/${s}/${e}` : `https://yapgrid.com/embed/movie/${id}`,
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36'
};
const payload = {
url: targetEmbedUrl,
source: 'embedwave',
strictSource: true,
waitMs: 5000,
nativeHls: false,
cacheOnly: true
};
const res = await fetchJson('https://yapgrid.com/player/api/extract', {
method: 'POST',
headers,
body: JSON.stringify(payload)
}).catch(() => null);
if (!res?.success || !Array.isArray(res.found) || !res.found.length) return null;
const allUrls = [];
for (const item of res.found) {
if (!item.url) continue;
let streamUrl = item.url;
if (streamUrl.startsWith('/')) {
streamUrl = `https://yapgrid.com${streamUrl}`;
}
allUrls.push({
url: streamUrl,
server: `YapGrid - ${item.nxshaProvider || item.provider || 'Server'}`,
type: item.type === 'master' ? 'hls' : (streamUrl.includes('.m3u8') ? 'hls' : 'mp4'),
headers: {
Referer: 'https://yapgrid.com/',
Origin: 'https://yapgrid.com',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36'
}
});
}
if (!allUrls.length) return null;
return {
url: allUrls[0].url,
headers: allUrls[0].headers,
allUrls
};
}

View File

@@ -13,7 +13,7 @@ const CONFIG = {
TV_ID: "37854", // TV TMDB ID to test, for anime I recommend using a random ID like 37854
TV_SEASON: "1", // TV Season to test
TV_EPISODE: "1", // TV Episode to test
STREAM_SOURCE: "cinezo", // Source key to test
STREAM_SOURCE: "", // Source key to test
TEST_SUBTITLE: false,
TEST_DOWNLOAD: false,