mirror of
https://gitlab.com/vyla-entertainment/sdk.git
synced 2026-11-13 13:47:25 -05:00
Updated sources
This commit is contained in:
@@ -23,7 +23,7 @@ This project is developed and maintained by [Vyla Entertainment](https://github.
|
||||
|
||||
Original work and core authorship:
|
||||
|
||||
[@endoverdosing](https://github.com/EndOverdosing)
|
||||
[@alwaysmesmerizingyou](https://gitlab.com/alwaysmesmerizingyou)
|
||||
|
||||
[@GavinGoGaming](https://github.com/GavinGoGaming)
|
||||
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
"email": "gavin@klash.dev"
|
||||
},
|
||||
{
|
||||
"name": "EndOverdosing"
|
||||
"name": "alwaysmesmerizingyou",
|
||||
"email": "alwaysmesmerizingyou@gmail.com"
|
||||
}
|
||||
],
|
||||
"license": "CC-BY-NC-4.0",
|
||||
|
||||
@@ -6,17 +6,6 @@
|
||||
// Fshare/Fsonic Tested: 155 ( they don't have Michael )
|
||||
|
||||
export const SOURCES = [
|
||||
{
|
||||
key: '1embed',
|
||||
label: '1Embed',
|
||||
sourceFile: '1embed',
|
||||
proxyParam: '1e',
|
||||
timeout: 20000,
|
||||
jitter: 500,
|
||||
retries: 2,
|
||||
multiUrl: true,
|
||||
},
|
||||
|
||||
{
|
||||
key: '123anime',
|
||||
label: '123Anime',
|
||||
@@ -145,6 +134,18 @@ export const SOURCES = [
|
||||
},],
|
||||
},
|
||||
|
||||
{
|
||||
key: 'frame',
|
||||
label: 'Frame',
|
||||
sourceFile: 'frame',
|
||||
proxyParam: 'fr',
|
||||
timeout: 25000,
|
||||
jitter: 500,
|
||||
retries: 2,
|
||||
multiUrl: true,
|
||||
skipProxy: true,
|
||||
},
|
||||
|
||||
{
|
||||
key: 'fsharetv',
|
||||
label: 'FShareTV',
|
||||
@@ -252,30 +253,15 @@ export const SOURCES = [
|
||||
},
|
||||
|
||||
{
|
||||
key: 'opstream',
|
||||
label: 'OpStream',
|
||||
sourceFile: 'opstream',
|
||||
proxyParam: 'op',
|
||||
timeout: 35000,
|
||||
key: 'peestream',
|
||||
label: 'PeeStream',
|
||||
sourceFile: 'peestream',
|
||||
proxyParam: 'pee',
|
||||
timeout: 25000,
|
||||
jitter: 500,
|
||||
retries: 2,
|
||||
multiUrl: true,
|
||||
skipProxy: true,
|
||||
verifyHeaders: {
|
||||
Referer: 'https://opstream.fun/',
|
||||
Origin: 'https://opstream.fun',
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
key: 'peachify',
|
||||
label: 'Peachify',
|
||||
sourceFile: 'peachify',
|
||||
proxyParam: 'pf',
|
||||
timeout: 30000,
|
||||
jitter: 500,
|
||||
retries: 1,
|
||||
multiUrl: true
|
||||
},
|
||||
|
||||
{
|
||||
@@ -408,7 +394,8 @@ export const SOURCES = [
|
||||
jitter: 500,
|
||||
retries: 2,
|
||||
multiUrl: true,
|
||||
skipProxy: true
|
||||
skipProxy: true,
|
||||
disabled: true, // Temporarily disabled because their site is down, embed website does not load
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
import { USER_AGENT } from '../utils/helpers.js';
|
||||
|
||||
const BASE_URL = "https://1embed.cc";
|
||||
|
||||
const PROVIDERS = [
|
||||
{ id: "BUKE", name: "1Embed - BUKE" },
|
||||
{ id: "YOTA", name: "1Embed - YOTA" },
|
||||
{ id: "NEPT", name: "1Embed - NEPT" },
|
||||
{ id: "AZURE", name: "1Embed - AZURE" },
|
||||
{ id: "YORU", name: "1Embed - YORU" }
|
||||
];
|
||||
|
||||
let cachedToken = null;
|
||||
let tokenExpiresAt = 0;
|
||||
|
||||
async function getToken() {
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
if (cachedToken && tokenExpiresAt > now + 10) return cachedToken;
|
||||
try {
|
||||
const res = await fetch(`${BASE_URL}/api/token`, { headers: { "Referer": `${BASE_URL}/`, "User-Agent": USER_AGENT, "Accept": "application/json" }, signal: AbortSignal.timeout(10000) });
|
||||
if (!res.ok) return null;
|
||||
const data = await res.json();
|
||||
cachedToken = data.token;
|
||||
tokenExpiresAt = data.expiresAt;
|
||||
return cachedToken;
|
||||
} catch { return null; }
|
||||
}
|
||||
|
||||
function decodeBsUrl(url) {
|
||||
if (!url) return url;
|
||||
let target = url;
|
||||
if (url.includes('d=')) {
|
||||
try {
|
||||
const parts = url.split(/[?&]/);
|
||||
for (const part of parts) {
|
||||
if (part.startsWith('d=')) {
|
||||
target = decodeURIComponent(part.substring(2));
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch { }
|
||||
}
|
||||
if (target.startsWith('bs_')) {
|
||||
try {
|
||||
const decoded = Buffer.from(target.slice(3), 'base64').toString('utf-8');
|
||||
const reversed = decoded.split('').reverse().join('');
|
||||
if (reversed.startsWith('http')) return reversed;
|
||||
} catch { }
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
async function fetchProviderStream(provider, type, id, s, e, token) {
|
||||
try {
|
||||
const referer = type === "movie" ? `${BASE_URL}/embed/movie/${id}` : `${BASE_URL}/embed/tv/${id}/${s}/${e}`;
|
||||
const payload = { type, id: String(id), provider: provider.id };
|
||||
if (type === "tv") { payload.season = Number(s); payload.episode = Number(e); }
|
||||
const res = await fetch(`${BASE_URL}/api/sources`, {
|
||||
method: "POST",
|
||||
headers: { "User-Agent": USER_AGENT, "Referer": referer, "Origin": BASE_URL, "Accept": "application/json", "Content-Type": "application/json", "x-bcine-key": token, "X-Requested-With": "XMLHttpRequest" },
|
||||
body: JSON.stringify(payload),
|
||||
signal: AbortSignal.timeout(15000)
|
||||
});
|
||||
if (!res.ok) return null;
|
||||
const data = await res.json();
|
||||
|
||||
let rawSources = [];
|
||||
if (Array.isArray(data.servers)) rawSources.push(...data.servers);
|
||||
if (data.providerResults?.[provider.id]?.sources) rawSources.push(...data.providerResults[provider.id].sources);
|
||||
if (data.providerResults?.[provider.id] && Array.isArray(data.providerResults[provider.id])) rawSources.push(...data.providerResults[provider.id]);
|
||||
|
||||
if (!rawSources.length) return null;
|
||||
const results = [];
|
||||
for (const src of rawSources) {
|
||||
if (typeof src !== 'object' || !src) continue;
|
||||
let rawUrl = src.url || src.file || src.link;
|
||||
if (typeof rawUrl !== 'string') continue;
|
||||
let streamUrl = decodeBsUrl(rawUrl);
|
||||
if (streamUrl.startsWith('/')) streamUrl = `${BASE_URL}${streamUrl}`;
|
||||
const isWorker = streamUrl.includes('omena-puu') || streamUrl.includes('nocach') || streamUrl.includes('?p=');
|
||||
const isHls = src.type === 'hls' || src.type === 'm3u8' || streamUrl.includes('m3u8') || isWorker;
|
||||
if (isHls && !streamUrl.includes('m3u8')) streamUrl += streamUrl.includes('?') ? '&format=.m3u8' : '?format=.m3u8';
|
||||
const srcHeaders = (src.headers && typeof src.headers === 'object') ? src.headers : {};
|
||||
results.push({
|
||||
url: streamUrl,
|
||||
server: provider.name,
|
||||
quality: src.quality || src.label || src.title || "Auto",
|
||||
type: isHls ? "hls" : "mp4",
|
||||
headers: { "User-Agent": USER_AGENT, "Referer": referer, "Origin": BASE_URL, ...srcHeaders },
|
||||
});
|
||||
}
|
||||
return results;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getStream(args) {
|
||||
const { id, s, e, server } = args;
|
||||
const type = s != null && e != null ? "tv" : "movie";
|
||||
const token = await getToken();
|
||||
if (!token) return null;
|
||||
let targets = PROVIDERS;
|
||||
if (server && server !== 'all') {
|
||||
const cleanName = server.replace('1Embed - ', '').toLowerCase();
|
||||
targets = PROVIDERS.filter(p => p.id.toLowerCase() === cleanName || p.name.toLowerCase().includes(cleanName));
|
||||
if (!targets.length) targets = PROVIDERS;
|
||||
}
|
||||
const settled = await Promise.allSettled(targets.map(p => fetchProviderStream(p, type, id, s, e, token)));
|
||||
const allUrls = [];
|
||||
for (const r of settled) if (r.status === 'fulfilled' && r.value) allUrls.push(...r.value);
|
||||
return allUrls.length ? { allUrls } : null;
|
||||
}
|
||||
|
||||
export async function getSources() { return PROVIDERS.map(p => p.name); }
|
||||
@@ -1,26 +1,118 @@
|
||||
import { fetchJson } from '../utils/helpers.js';
|
||||
import { USER_AGENT, fetchJson } from '../utils/helpers.js';
|
||||
|
||||
export async function getStream({ id, s, e }) {
|
||||
const BASE_URL = 'https://flaxmovies.xyz';
|
||||
|
||||
async function getWorkerUrls(embedUrl) {
|
||||
const foundUrls = [];
|
||||
try {
|
||||
const url = s && e
|
||||
? `https://sparkling-mode-a1ed.fillatame.workers.dev/?tmdb_id=${id}&season=${s}&episode=${e}`
|
||||
: `https://sparkling-mode-a1ed.fillatame.workers.dev/?tmdb_id=${id}`;
|
||||
const data = await fetchJson(url, { signal: AbortSignal.timeout(15000) });
|
||||
if (!data || !Array.isArray(data.streams)) return null;
|
||||
const allUrls = [];
|
||||
for (const stream of data.streams) {
|
||||
const streamUrl = stream.url || stream.link;
|
||||
if (!streamUrl) continue;
|
||||
const isHls = stream.format === 'HLS' || stream.isM3U8 === true || streamUrl.includes('m3u8');
|
||||
allUrls.push({
|
||||
url: streamUrl,
|
||||
server: stream.provider || 'FlaxMovies',
|
||||
quality: stream.resolution || 'Auto',
|
||||
type: isHls ? 'hls' : 'mp4'
|
||||
});
|
||||
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);
|
||||
}
|
||||
}
|
||||
} catch { }
|
||||
}
|
||||
}
|
||||
return allUrls.length ? { allUrls } : null;
|
||||
} catch {
|
||||
return null;
|
||||
} 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;
|
||||
}
|
||||
|
||||
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}`;
|
||||
|
||||
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'];
|
||||
}
|
||||
88
src/sources/frame.js
Normal file
88
src/sources/frame.js
Normal file
@@ -0,0 +1,88 @@
|
||||
import { USER_AGENT, fetchJson } from '../utils/helpers.js';
|
||||
|
||||
const BASE_URL = 'https://api.peestream.in';
|
||||
|
||||
const PROVIDERS = [
|
||||
{ id: 'vaplayer', name: 'FRAME Zephyr' },
|
||||
{ id: 'castle', name: 'FRAME Atlas' },
|
||||
{ id: 'hera', name: 'FRAME Luna' },
|
||||
{ id: 'multivid', name: 'FRAME Volt' },
|
||||
{ id: 'netmirror', name: 'FRAME Echo' },
|
||||
{ id: 'vidsuper-castle', name: 'FRAME Rift' },
|
||||
{ id: 'vidsuper-vixsrc', name: 'FRAME Quill' }
|
||||
];
|
||||
|
||||
async function fetchProviderStreams(provider, type, id, s, e) {
|
||||
try {
|
||||
const queryParams = new URLSearchParams({
|
||||
q: String(id),
|
||||
type,
|
||||
provider: provider.id
|
||||
});
|
||||
if (type === 'tv') {
|
||||
queryParams.set('season', String(s));
|
||||
queryParams.set('episode', String(e));
|
||||
}
|
||||
|
||||
const url = `${BASE_URL}/api/search?${queryParams.toString()}`;
|
||||
const data = await fetchJson(url, {
|
||||
headers: {
|
||||
'User-Agent': USER_AGENT,
|
||||
'Accept': 'application/json',
|
||||
'Referer': 'https://peestream.in/'
|
||||
},
|
||||
signal: AbortSignal.timeout(12000)
|
||||
});
|
||||
|
||||
if (!data || !Array.isArray(data.results) || !data.results.length) return [];
|
||||
|
||||
const streams = [];
|
||||
for (const result of data.results) {
|
||||
for (const stream of result.streams || []) {
|
||||
if (!stream || !stream.url) continue;
|
||||
streams.push({
|
||||
url: stream.url,
|
||||
server: provider.name,
|
||||
quality: stream.quality || 'Auto',
|
||||
type: stream.type === 'm3u8' || stream.url.includes('.m3u8') ? 'hls' : 'mp4',
|
||||
headers: stream.headers || {
|
||||
'User-Agent': USER_AGENT
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return streams;
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export async function getStream(args) {
|
||||
const { id, s, e, server } = args;
|
||||
const isTv = s != null && e != null;
|
||||
const type = isTv ? 'tv' : 'movie';
|
||||
|
||||
let targets = PROVIDERS;
|
||||
if (server && server !== 'all') {
|
||||
const clean = server.toLowerCase().replace('frame ', '').trim();
|
||||
targets = PROVIDERS.filter(p => p.id.toLowerCase() === clean || p.name.toLowerCase().includes(clean));
|
||||
if (!targets.length) targets = PROVIDERS;
|
||||
}
|
||||
|
||||
const settled = await Promise.allSettled(
|
||||
targets.map(p => fetchProviderStreams(p, type, id, s, e))
|
||||
);
|
||||
|
||||
const allUrls = [];
|
||||
for (const r of settled) {
|
||||
if (r.status === 'fulfilled' && Array.isArray(r.value)) {
|
||||
allUrls.push(...r.value);
|
||||
}
|
||||
}
|
||||
|
||||
return allUrls.length ? { allUrls } : null;
|
||||
}
|
||||
|
||||
export async function getSources() {
|
||||
return PROVIDERS.map(p => p.name);
|
||||
}
|
||||
@@ -76,13 +76,29 @@ export async function getStream(args) {
|
||||
const reqMatch = html.match(/window\.__REQUEST_TOKEN__\s*=\s*"([^"]+)"/);
|
||||
if (!reqMatch) return null;
|
||||
const requestToken = reqMatch[1];
|
||||
const initRes = await fetch(`${BASE_URL}/api/playback-init`, { method: "POST", headers: { "Content-Type": "application/json", ...PROXY_HEADERS, Referer: pageUrl }, body: JSON.stringify({ mediaId: Number(id), mediaType, requestToken }), signal: AbortSignal.timeout(10000) });
|
||||
const cookies = pageRes.headers.getSetCookie();
|
||||
const cookieHeader = cookies.map(c => c.split(';')[0]).join('; ');
|
||||
|
||||
const initHeaders = { "Content-Type": "application/json", ...PROXY_HEADERS, Referer: pageUrl };
|
||||
if (cookieHeader) initHeaders.Cookie = cookieHeader;
|
||||
|
||||
const initRes = await fetch(`${BASE_URL}/api/playback-init`, {
|
||||
method: "POST",
|
||||
headers: initHeaders,
|
||||
body: JSON.stringify({ mediaId: Number(id), mediaType, requestToken }),
|
||||
signal: AbortSignal.timeout(10000)
|
||||
});
|
||||
const initData = await initRes.json();
|
||||
let streamToken = initData.token;
|
||||
if (initData.requiresPow && initData.pow) {
|
||||
const powRes = solvePoW(initData.pow.challenge, initData.pow.difficulty);
|
||||
if (powRes.found) {
|
||||
const solveRes = await fetch(`${BASE_URL}/api/playback-init`, { method: "POST", headers: { "Content-Type": "application/json", ...PROXY_HEADERS, Referer: pageUrl }, body: JSON.stringify({ mediaId: Number(id), mediaType, requestToken, pow: { challengeId: initData.pow.challengeId, nonce: powRes.nonce } }), signal: AbortSignal.timeout(10000) });
|
||||
const solveRes = await fetch(`${BASE_URL}/api/playback-init`, {
|
||||
method: "POST",
|
||||
headers: initHeaders,
|
||||
body: JSON.stringify({ mediaId: Number(id), mediaType, requestToken, pow: { challengeId: initData.pow.challengeId, nonce: powRes.nonce } }),
|
||||
signal: AbortSignal.timeout(10000)
|
||||
});
|
||||
streamToken = (await solveRes.json()).token;
|
||||
}
|
||||
}
|
||||
@@ -96,8 +112,28 @@ export async function getStream(args) {
|
||||
const allUrls = [];
|
||||
for (const srv of targets) {
|
||||
try {
|
||||
const streamUrl = `${BASE_URL}/api/stream?mediaId=${id}&mediaType=${mediaType}&tv_slug=${tvSlug}&source=${srv.id}&apikey=mptv_sk_a8f29c4e7b3d1f&requestToken=${requestToken}&token=${streamToken}`;
|
||||
const streamRes = await fetch(streamUrl, { headers: { ...PROXY_HEADERS, Referer: pageUrl }, signal: AbortSignal.timeout(8000) });
|
||||
const encryptRes = await fetch(`${BASE_URL}/api/encrypt`, {
|
||||
method: "POST",
|
||||
headers: initHeaders,
|
||||
body: JSON.stringify({
|
||||
data: {
|
||||
mediaId: Number(id),
|
||||
mediaType,
|
||||
tv_slug: tvSlug,
|
||||
source: srv.id,
|
||||
apikey: "mptv_sk_a8f29c4e7b3d1f"
|
||||
}
|
||||
}),
|
||||
signal: AbortSignal.timeout(8000)
|
||||
});
|
||||
const encryptData = await encryptRes.json();
|
||||
if (!encryptData.encrypted) continue;
|
||||
|
||||
const streamUrl = `${BASE_URL}/api/stream-encrypted?data=${encodeURIComponent(encryptData.encrypted)}&requestToken=${encodeURIComponent(requestToken)}&token=${encodeURIComponent(streamToken)}`;
|
||||
const streamRes = await fetch(streamUrl, {
|
||||
headers: { ...PROXY_HEADERS, Referer: pageUrl, ...(cookieHeader ? { Cookie: cookieHeader } : {}) },
|
||||
signal: AbortSignal.timeout(8000)
|
||||
});
|
||||
const streamData = await streamRes.json();
|
||||
if (streamData.success && streamData.data?.stream_url) {
|
||||
let fileUrl = streamData.data.stream_url;
|
||||
|
||||
@@ -13,15 +13,7 @@ const HEADERS = {
|
||||
};
|
||||
|
||||
const SERVERS = [
|
||||
'pseudo',
|
||||
'lynx',
|
||||
'tik',
|
||||
'ipcloud',
|
||||
'v4:English',
|
||||
'turkce',
|
||||
'v5:Hindi',
|
||||
'v4:Hindi',
|
||||
'v6:Hindi'
|
||||
'hindiv3'
|
||||
];
|
||||
|
||||
const TIMEOUT_MS = 15000;
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
import { fetchJson, fetchText, getTmdbInfo, tmdbToAnilist } from '../utils/helpers.js';
|
||||
|
||||
const BASE_URL = "https://nekowatch.xyz";
|
||||
const ANIME_JS_URL = `${BASE_URL}/js/anime.js?v=v263_fast_player_start`;
|
||||
const FALLBACK_ORDER = ['kiwi', 'ally', 'bee', 'arc', 'anikoto', 'jet', 'bonk', 'moo', 'hop', 'pulsar', 'pahe'];
|
||||
|
||||
let providerOrderCache = null, providerOrderTs = 0;
|
||||
|
||||
async function getProviderOrder() {
|
||||
const now = Date.now();
|
||||
if (providerOrderCache && now - providerOrderTs < 1800000) return providerOrderCache;
|
||||
try {
|
||||
const js = await fetchText(ANIME_JS_URL, { signal: AbortSignal.timeout(6000) });
|
||||
const match = js.match(/PROVIDER_ORDER\s*=\s*\[([\s\S]*?)\]/);
|
||||
if (match) {
|
||||
const order = [...match[1].matchAll(/['"]([a-z0-9_]+)['"]/gi)].map(m => m[1]);
|
||||
if (order.length) { providerOrderCache = order; providerOrderTs = now; return order; }
|
||||
}
|
||||
} catch { }
|
||||
return providerOrderCache || FALLBACK_ORDER;
|
||||
}
|
||||
|
||||
export async function getStream({ id, s, e, audio }) {
|
||||
try {
|
||||
const info = await getTmdbInfo(id, s ? 'tv' : 'movie', s);
|
||||
if (!info || !info.isAnime) return null;
|
||||
const anilistId = await tmdbToAnilist(id, s ? 'tv' : 'movie', s, info.titles, info.year);
|
||||
if (!anilistId) return null;
|
||||
const ep = e || 1;
|
||||
const audiosToTry = audio === "all" ? ["sub", "dub"] : (audio === "dub" ? ["dub", "sub"] : ["sub", "dub"]);
|
||||
const providerOrder = await getProviderOrder();
|
||||
for (const aud of audiosToTry) {
|
||||
const results = await Promise.allSettled(providerOrder.map(p => fetchJson(`${BASE_URL}/api/anime/watch/${p}/${anilistId}/${aud}/${p}-${ep}`, { signal: AbortSignal.timeout(8000) })));
|
||||
const allUrls = [], seenActual = new Set();
|
||||
for (const r of results) {
|
||||
if (r.status !== 'fulfilled' || !r.value) continue;
|
||||
const data = r.value;
|
||||
let streams = [], actual = data.attempted?.actualProvider || data.provider;
|
||||
if (Array.isArray(data.streams)) streams = data.streams;
|
||||
else {
|
||||
for (const key of Object.keys(data)) if (data[key] && Array.isArray(data[key].streams)) { streams.push(...data[key].streams); if (!actual) actual = data[key].provider || key; }
|
||||
}
|
||||
if (!streams.length) continue;
|
||||
if (actual) { if (seenActual.has(actual)) continue; seenActual.add(actual); }
|
||||
const valid = streams.filter(st => st.type === "hls" && st.url).sort((a, b) => (b.priority || 0) - (a.priority || 0));
|
||||
for (const st of valid) allUrls.push({ url: st.url, type: "hls", audio: aud, server: st.server ? `NekoWatch-${st.server}` : "NekoWatch", headers: st.referer ? { Referer: st.referer } : undefined, skipProxy: /\.workers\.dev(\/|$)/i.test(st.url) });
|
||||
}
|
||||
if (allUrls.length) return { allUrls };
|
||||
}
|
||||
return 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))] : [];
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
import { getTmdbInfo, USER_AGENT } from '../utils/helpers.js';
|
||||
|
||||
const BASE_URL = 'https://opstream.fun';
|
||||
const API_BASE = `${BASE_URL}/api/resolve`;
|
||||
|
||||
const API_HEADERS = {
|
||||
'User-Agent': USER_AGENT,
|
||||
'Accept': 'application/x-ndjson; charset=utf-8',
|
||||
'Referer': `${BASE_URL}/`,
|
||||
'Origin': BASE_URL,
|
||||
};
|
||||
|
||||
const STREAM_HEADERS = {
|
||||
'User-Agent': USER_AGENT,
|
||||
'Referer': `${BASE_URL}/`,
|
||||
'Origin': BASE_URL,
|
||||
};
|
||||
|
||||
export async function getStream({ id, s, e }) {
|
||||
try {
|
||||
const isTv = s != null && e != null;
|
||||
const info = await getTmdbInfo(id, isTv ? 'tv' : 'movie');
|
||||
|
||||
if (!info?.titles?.length || !info.imdbId) return null;
|
||||
|
||||
const params = new URLSearchParams({
|
||||
type: isTv ? 'tv' : 'movie',
|
||||
tmdbId: id,
|
||||
title: info.titles[0],
|
||||
year: info.year || '',
|
||||
imdbId: info.imdbId,
|
||||
progress: '1'
|
||||
});
|
||||
|
||||
if (isTv) {
|
||||
params.append('season', s);
|
||||
params.append('episode', e);
|
||||
}
|
||||
|
||||
const res = await fetch(`${API_BASE}?${params}`, {
|
||||
headers: API_HEADERS,
|
||||
signal: AbortSignal.timeout(15000)
|
||||
});
|
||||
|
||||
if (!res.ok) return null;
|
||||
|
||||
const text = await res.text();
|
||||
const lines = text.split('\n');
|
||||
|
||||
let streamData = null;
|
||||
for (const line of lines) {
|
||||
const trimmed = line.trim();
|
||||
if (!trimmed) continue;
|
||||
try {
|
||||
const json = JSON.parse(trimmed);
|
||||
if (json.t === 'done' && json.data) {
|
||||
streamData = json.data;
|
||||
break;
|
||||
}
|
||||
} catch { continue; }
|
||||
}
|
||||
|
||||
if (!streamData) return null;
|
||||
|
||||
const subtitles = (streamData.captions || []).map(c => ({
|
||||
url: c.url.startsWith('http') ? c.url : `${BASE_URL}${c.url}`,
|
||||
lang: c.label || 'Unknown'
|
||||
}));
|
||||
|
||||
const allUrls = [];
|
||||
|
||||
if (streamData.qualities && streamData.qualities.length > 0) {
|
||||
for (const q of streamData.qualities) {
|
||||
let finalUrl = q.url;
|
||||
if (!finalUrl.startsWith('http')) {
|
||||
finalUrl = `${BASE_URL}${finalUrl}`;
|
||||
}
|
||||
|
||||
const isDash = finalUrl.includes('.mpd');
|
||||
const isHls = finalUrl.includes('.m3u8');
|
||||
|
||||
allUrls.push({
|
||||
url: finalUrl,
|
||||
server: `OpStream - ${q.label}`,
|
||||
type: isDash ? 'dash' : (isHls ? 'hls' : 'mp4'),
|
||||
headers: STREAM_HEADERS,
|
||||
subtitles,
|
||||
skipProxy: true
|
||||
});
|
||||
}
|
||||
} else if (streamData.url) {
|
||||
let finalUrl = streamData.url;
|
||||
if (finalUrl.includes('?u=')) {
|
||||
try {
|
||||
const uParam = new URL(finalUrl, BASE_URL).searchParams.get('u');
|
||||
if (uParam) finalUrl = Buffer.from(uParam, 'base64').toString('utf8');
|
||||
} catch {}
|
||||
} else if (!finalUrl.startsWith('http')) {
|
||||
finalUrl = `${BASE_URL}${finalUrl}`;
|
||||
}
|
||||
|
||||
const isDash = streamData.kind === 'dash' || finalUrl.includes('.mpd');
|
||||
const isHls = streamData.kind === 'hls' || finalUrl.includes('.m3u8');
|
||||
|
||||
allUrls.push({
|
||||
url: finalUrl,
|
||||
server: 'OpStream',
|
||||
type: isDash ? 'dash' : (isHls ? 'hls' : 'mp4'),
|
||||
headers: STREAM_HEADERS,
|
||||
subtitles,
|
||||
skipProxy: true
|
||||
});
|
||||
}
|
||||
|
||||
if (allUrls.length === 0) return null;
|
||||
|
||||
return { allUrls };
|
||||
} catch { return null; }
|
||||
}
|
||||
|
||||
export async function getSources() {
|
||||
return ['OpStream'];
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
import { fetchJson, USER_AGENT } from '../utils/helpers.js';
|
||||
|
||||
const DEC_API = "https://enc-dec.app/api/dec-peachify";
|
||||
const ORIGIN = "https://peachify.top";
|
||||
const HEADERS = { "User-Agent": USER_AGENT, "Origin": ORIGIN, "Referer": `${ORIGIN}/` };
|
||||
|
||||
const SERVERS = [
|
||||
{ label: "Wolf", path: "air", api: "https://usa.eat-peach.sbs" },
|
||||
{ label: "Spider", path: "holly", api: "https://usa.eat-peach.sbs" },
|
||||
{ label: "Iron", path: "moviebox", api: "https://uwu.eat-peach.sbs" },
|
||||
{ label: "Multi", path: "multi", api: "https://usa.eat-peach.sbs" },
|
||||
{ label: "Dark", path: "net", api: "https://uwu.eat-peach.sbs" },
|
||||
];
|
||||
|
||||
export async function getStream({ id, s, e, server }) {
|
||||
try {
|
||||
const isTv = s != null && e != null;
|
||||
const segment = isTv ? `tv/${id}/${s}/${e}` : `movie/${id}`;
|
||||
|
||||
let targets = SERVERS;
|
||||
if (server && server !== 'all') {
|
||||
const cleanName = server.replace('Peachify - ', '');
|
||||
targets = SERVERS.filter(srv => srv.label === cleanName);
|
||||
if (!targets.length) targets = SERVERS;
|
||||
}
|
||||
|
||||
const settled = await Promise.allSettled(targets.map(async srv => {
|
||||
const url = `${srv.api}/${srv.path}/${segment}`;
|
||||
const rawData = await fetchJson(url, { headers: HEADERS, signal: AbortSignal.timeout(10000) });
|
||||
|
||||
if (!rawData?.data) throw new Error();
|
||||
|
||||
const decJson = await fetchJson(DEC_API, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ text: rawData.data }),
|
||||
signal: AbortSignal.timeout(10000)
|
||||
});
|
||||
|
||||
if (decJson.status !== 200 || !decJson.result) throw new Error();
|
||||
const res = decJson.result;
|
||||
|
||||
const sources = Array.isArray(res) ? res : (res.sources || [res]);
|
||||
const subs = res.subtitles || [];
|
||||
|
||||
return sources.map(source => {
|
||||
const streamUrl = source.url || source.file || source.link;
|
||||
if (!streamUrl) return null;
|
||||
|
||||
return {
|
||||
url: streamUrl,
|
||||
server: `Peachify - ${srv.label}`,
|
||||
quality: source.quality || "Auto",
|
||||
type: streamUrl.includes('.m3u8') ? 'hls' : 'mp4',
|
||||
headers: { ...HEADERS, "Origin": ORIGIN },
|
||||
subtitles: subs.map(sub => ({
|
||||
url: sub.url || sub.file,
|
||||
lang: sub.language || sub.lang || sub.label || 'Unknown'
|
||||
})),
|
||||
skipProxy: false,
|
||||
skipVerify: true,
|
||||
skipHlsCheck: true
|
||||
};
|
||||
}).filter(Boolean);
|
||||
}));
|
||||
|
||||
const allUrls = [];
|
||||
for (const r of settled) {
|
||||
if (r.status === 'fulfilled' && r.value) allUrls.push(...r.value);
|
||||
}
|
||||
|
||||
return allUrls.length ? { allUrls } : null;
|
||||
} catch { return null; }
|
||||
}
|
||||
|
||||
export async function getSources() {
|
||||
return SERVERS.map(s => `Peachify - ${s.label}`);
|
||||
}
|
||||
122
src/sources/peestream.js
Normal file
122
src/sources/peestream.js
Normal file
@@ -0,0 +1,122 @@
|
||||
import { USER_AGENT, fetchJson, getTmdbInfo } from '../utils/helpers.js';
|
||||
|
||||
const BASE_URL = 'https://providers.peestream.in';
|
||||
|
||||
export async function getStream(args) {
|
||||
const { id, s, e, server, tmdbApiKey } = args;
|
||||
const isTv = s != null && e != null;
|
||||
const type = isTv ? 'tv' : 'movie';
|
||||
|
||||
let title = args.title || '';
|
||||
let releaseYear = args.year || '';
|
||||
let imdbId = args.imdbId || '';
|
||||
|
||||
if (!title) {
|
||||
try {
|
||||
const info = await getTmdbInfo(id, type, s, tmdbApiKey);
|
||||
if (info) {
|
||||
title = info.title || '';
|
||||
if (info.year) releaseYear = info.year;
|
||||
if (info.imdbId) imdbId = info.imdbId;
|
||||
}
|
||||
} catch { }
|
||||
}
|
||||
|
||||
const queryParams = new URLSearchParams({
|
||||
type,
|
||||
tmdbId: String(id),
|
||||
title: title || String(id)
|
||||
});
|
||||
if (releaseYear) queryParams.set('releaseYear', String(releaseYear));
|
||||
if (imdbId) queryParams.set('imdbId', String(imdbId));
|
||||
if (isTv) {
|
||||
queryParams.set('season', String(s));
|
||||
queryParams.set('episode', String(e));
|
||||
}
|
||||
|
||||
const allUrls = [];
|
||||
|
||||
try {
|
||||
const scrapeUrl = `${BASE_URL}/scrape?${queryParams.toString()}`;
|
||||
const res = await fetch(scrapeUrl, {
|
||||
headers: {
|
||||
'User-Agent': USER_AGENT,
|
||||
'Accept': 'text/event-stream',
|
||||
'Referer': `${BASE_URL}/`
|
||||
},
|
||||
signal: AbortSignal.timeout(15000)
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
const text = await res.text();
|
||||
const events = text.split('\n\n');
|
||||
for (const ev of events) {
|
||||
if (ev.includes('event: completed')) {
|
||||
const dataMatch = ev.match(/data:\s*(.+)/);
|
||||
if (dataMatch) {
|
||||
try {
|
||||
const parsed = JSON.parse(dataMatch[1].trim());
|
||||
const stream = parsed.stream;
|
||||
const streamUrl = stream?.playlist || stream?.url || stream?.file;
|
||||
if (streamUrl) {
|
||||
allUrls.push({
|
||||
url: streamUrl,
|
||||
server: `PeeStream - ${parsed.sourceId || 'Poseidon'}`,
|
||||
quality: 'Auto',
|
||||
type: stream.type === 'hls' || streamUrl.includes('.m3u8') ? 'hls' : 'mp4',
|
||||
headers: stream.headers || {
|
||||
'User-Agent': USER_AGENT
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch { }
|
||||
|
||||
if (!allUrls.length) {
|
||||
try {
|
||||
const searchUrl = `${BASE_URL}/api/search?q=${encodeURIComponent(title || id)}&type=${type}&tmdbId=${id}${isTv ? `&season=${s}&episode=${e}` : ''}`;
|
||||
const data = await fetchJson(searchUrl, {
|
||||
headers: {
|
||||
'User-Agent': USER_AGENT,
|
||||
'Accept': 'application/json',
|
||||
'Referer': `${BASE_URL}/`
|
||||
},
|
||||
signal: AbortSignal.timeout(10000)
|
||||
});
|
||||
|
||||
if (data && Array.isArray(data.results)) {
|
||||
for (const result of data.results) {
|
||||
const pName = result.providerName || result.provider || 'PeeStream';
|
||||
for (const stream of result.streams || []) {
|
||||
if (!stream || !stream.url) continue;
|
||||
allUrls.push({
|
||||
url: stream.url,
|
||||
server: `PeeStream - ${stream.name || pName}`,
|
||||
quality: stream.quality || 'Auto',
|
||||
type: stream.type === 'm3u8' || stream.url.includes('.m3u8') ? 'hls' : 'mp4',
|
||||
headers: stream.headers || {
|
||||
'User-Agent': USER_AGENT
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch { }
|
||||
}
|
||||
|
||||
if (server && server !== 'all') {
|
||||
const clean = server.toLowerCase().replace('peestream - ', '');
|
||||
const filtered = allUrls.filter(u => u.server.toLowerCase().includes(clean));
|
||||
if (filtered.length) return { allUrls: filtered };
|
||||
}
|
||||
|
||||
return allUrls.length ? { allUrls } : null;
|
||||
}
|
||||
|
||||
export async function getSources() {
|
||||
return ['PeeStream - vaplayer'];
|
||||
}
|
||||
@@ -18,7 +18,7 @@ const CONFIG = {
|
||||
|
||||
TEST_SUBTITLE: false, // Toggle subtitle testing
|
||||
TEST_DOWNLOAD: false, // Toggle download testing
|
||||
TEST_MOVIE: false, // Toggle movie testing
|
||||
TEST_MOVIE: true, // Toggle movie testing
|
||||
TEST_TV: true // Toggle TV testing
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user