diff --git a/src/config.js b/src/config.js index 684f9b3..d9fe3b6 100644 --- a/src/config.js +++ b/src/config.js @@ -56,24 +56,6 @@ export const SOURCES = [ }], }, - { - key: 'bcine', - label: 'Bcine', - sourceFile: 'bcine', - proxyParam: 'bc', - timeout: 25000, - jitter: 500, - retries: 2, - multiUrl: true, - cdnHeaders: [{ - pattern: /1embed\.cc|videasy\.to/i, - headers: { - Referer: 'https://bcine.ru/', - Origin: 'https://bcine.ru', - }, - }], - }, - { key: 'cinemaarmy', label: 'CinemaArmy', @@ -361,14 +343,6 @@ export const SOURCES = [ jitter: 500, retries: 2, multiUrl: true, - skipProxy: true, - cdnHeaders: [{ - pattern: /hakunaymatata\.com/i, - headers: { - Referer: 'https://net79.cc/', - Origin: 'https://net79.cc', - }, - }], }, { @@ -493,7 +467,16 @@ export const SOURCES = [ jitter: 500, retries: 1, multiUrl: true, + skipProxy: true, + cdnHeaders: [{ + pattern: /vidcore\.io/i, + headers: { + Referer: 'https://vidcore.io/', + Origin: 'https://vidcore.io', + }, + }], }, + { key: 'vidfast', label: 'VidFast', @@ -505,25 +488,6 @@ export const SOURCES = [ multiUrl: true, }, - { - 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', - }, - }], - }, - { key: 'vidlink', label: 'Vidlink', @@ -532,7 +496,6 @@ export const SOURCES = [ timeout: 20000, jitter: 500, retries: 2, - skipProxy: true, multiUrl: true, }, diff --git a/src/sources/bcine.js b/src/sources/bcine.js deleted file mode 100644 index 709e33c..0000000 --- a/src/sources/bcine.js +++ /dev/null @@ -1,202 +0,0 @@ -import { USER_AGENT } from '../utils/helpers.js'; - -const _sInit = [23, 27, 30, 31, 86, 28, 19, 86, 9, 18, 19, 21, 26, 27, 86, 120, 114, 120, 116, 86, 9, 18, 19, 21, 26, 27, 122, 89, 25, 23]; -const _vParam = `_v=${(_sInit[15] ^ 0x5a)}${(_sInit[16] ^ 0x5a)}${(_sInit[17] ^ 0x5a)}${(_sInit[18] ^ 0x5a)}`; - -const nD = '4860ac8bfddb'; -const aD = '224eff10e662e9635c9f671cf46351dcd69af42b1edd56f5e5fa21751f44b9c8'; -const Ls = [17, 91, 203, 44, 8, 177, 62, 239, 119, 3, 154, 81, 28, 210, 101, 7]; -const wa = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'; - -const SERVERS = [ - { id: 'NIGHT', endpoint: '/server/night' }, - { id: 'EMP', endpoint: '/server/emp' }, - { id: 'MAIN', endpoint: '/server/vidsrc' } -]; - -const CORS_PROXY = 'https://eve.pstream.cfd'; -let cachedToken = null; -let tokenExpiry = 0; - -function ab(e) { - let t = e >>> 0; - t ^= t >>> 16; - t = Math.imul(t, 2146121005) >>> 0; - t ^= t >>> 15; - t = Math.imul(t, 2221713035) >>> 0; - return (t ^ (t >>> 16)) >>> 0; -} - -function sD(e) { - const t = new TextEncoder().encode(aD); - const r = Math.max(32, Math.min(128, e + 17)); - const n = new Uint8Array(r); - let a = 2166136261; - for (let s = 0; s < r; s += 1) { - a ^= t[s % t.length] ?? s; - a = ab((a + Ls[s % Ls.length] + ((2654435761 * s) >>> 0)) >>> 0); - n[s] = a & 255; - } - return n; -} - -function iD(e) { - let t = ''; - for (let r = 0; r < e.length; r += 3) { - const n = e[r]; - const a = e[r + 1]; - const s = e[r + 2]; - t += wa[n >>> 2]; - t += wa[((3 & n) << 4) | ((a ?? 0) >>> 4)]; - if (a === undefined) break; - t += wa[((15 & a) << 2) | ((s ?? 0) >>> 6)]; - if (s === undefined) break; - t += wa[63 & s]; - } - return t; -} - -function generateDirectHlsUrl(id, s, e) { - const isTv = s != null && e != null; - const tmdbId = Math.floor(Number(id)); - const season = isTv ? Math.floor(Number(s || 1)) : 0; - const episode = isTv ? Math.floor(Number(e || 1)) : 0; - - const str = `${nD}:${isTv ? 's' : 'm'}:${tmdbId}:${season}:${episode}`; - const a = new TextEncoder().encode(str); - const sArr = sD(a.length); - const i = new Uint8Array(a.length + 2); - i[0] = a.length & 255; - i[1] = (a.length >>> 8) & 255; - let o = (2654435769 ^ a.length) >>> 0; - for (let l = 0; l < a.length; l += 1) { - o = ab((o + sArr[l % sArr.length] + Ls[l % Ls.length] + l) >>> 0); - i[l + 2] = (a[l] ^ (255 & o)) ^ sArr[(7 * l + 3) % sArr.length]; - } - - return `https://glendale-plumbing.com/c/v1/${iD(i)}/master.m3u8`; -} - -async function fetchInternalToken() { - if (cachedToken && Date.now() < tokenExpiry) return cachedToken; - try { - const res = await fetch('https://1embed.cc/api/token', { - headers: { - 'User-Agent': USER_AGENT, - 'Referer': 'https://1embed.cc/', - 'Accept': 'application/json, text/plain, */*' - }, - signal: AbortSignal.timeout(4000) - }); - if (res.ok) { - const data = await res.json(); - if (data?.token) { - cachedToken = data.token; - tokenExpiry = Date.now() + 1800 * 1000; - return cachedToken; - } - } - } catch { } - - try { - const proxiedUrl = `${CORS_PROXY}/?destination=${encodeURIComponent('https://1embed.cc/api/token')}`; - const res = await fetch(proxiedUrl, { - headers: { - 'X-Referer': 'https://1embed.cc/', - 'X-User-Agent': USER_AGENT - }, - signal: AbortSignal.timeout(4000) - }); - if (res.ok) { - const data = await res.json(); - if (data?.token) { - cachedToken = data.token; - tokenExpiry = Date.now() + 1800 * 1000; - return cachedToken; - } - } - } catch { } - - return ''; -} - -export async function getStream(args) { - const { id, s, e, clientIP } = args; - const isTv = s != null && e != null; - const isMovie = !isTv; - const allUrls = []; - - const directHlsUrl = generateDirectHlsUrl(id, s, e); - const masterUrlWithMark = `${directHlsUrl}?${_vParam}`; - - allUrls.push({ - url: masterUrlWithMark, - server: 'Bcine (Direct Master)', - quality: 'Auto', - type: 'hls', - headers: { - 'User-Agent': USER_AGENT, - 'Referer': 'https://bcine.ru/', - 'Origin': 'https://bcine.ru', - ...(clientIP && { 'X-Forwarded-For': clientIP }) - } - }); - - const token = await fetchInternalToken(); - - await Promise.allSettled( - SERVERS.map(async (server) => { - const query = isTv - ? `id=${id}?type=tv&s=${s || 1}&e=${e || 1}` - : `id=${id}?type=movie`; - - const stParam = token ? `&_st=${token}` : ''; - const targetUrl = `https://1embed.cc${server.endpoint}/${query}${stParam}`; - - const headers = { - 'User-Agent': USER_AGENT, - 'Referer': 'https://bcine.ru/', - 'Accept': 'application/json, text/plain, */*', - ...(token && { 'X-Stream-Token': token }), - ...(clientIP && { 'X-Forwarded-For': clientIP }) - }; - - let res = await fetch(targetUrl, { headers, signal: AbortSignal.timeout(5000) }).catch(() => null); - if (!res || !res.ok) { - const proxiedUrl = `${CORS_PROXY}/?destination=${encodeURIComponent(targetUrl)}`; - res = await fetch(proxiedUrl, { - headers: { - 'X-Referer': 'https://bcine.ru/', - 'X-User-Agent': USER_AGENT - }, - signal: AbortSignal.timeout(5000) - }).catch(() => null); - } - - if (res && res.ok) { - try { - const data = await res.json(); - if (data && data.success !== false) { - const streamUrl = data.streams?.proxy_m3u8 || data.streams?.raw_m3u8 || data.streams?.m3u8 || data.streamUrl; - if (streamUrl && typeof streamUrl === 'string') { - const urlWithMark = streamUrl.includes('?') ? `${streamUrl}&${_vParam}` : `${streamUrl}?${_vParam}`; - allUrls.push({ - url: urlWithMark, - server: `Bcine (${data.selectedSource || data.provider || server.id})`, - quality: 'Auto', - type: streamUrl.includes('.m3u8') ? 'hls' : 'mp4', - headers: { - 'User-Agent': USER_AGENT, - 'Referer': 'https://bcine.ru/', - 'Origin': 'https://bcine.ru' - } - }); - } - } - } catch { } - } - }) - ); - - return allUrls.length ? { allUrls } : null; -} \ No newline at end of file diff --git a/src/sources/vidlink.js b/src/sources/vidlink.js index 6a74d3a..3a00283 100644 --- a/src/sources/vidlink.js +++ b/src/sources/vidlink.js @@ -7,22 +7,70 @@ export async function getStream({ id, s, e }) { try { const encData = await fetchJson(`https://enc-dec.app/api/enc-vidlink?text=${id}`, { signal: AbortSignal.timeout(6000) }); if (encData?.status !== 200 || !encData?.result) return null; - const data = await fetchJson(s ? `${BASE}/api/b/tv/${encData.result}/${s}/${e || 1}` : `${BASE}/api/b/movie/${encData.result}`, { headers: HEADERS, signal: AbortSignal.timeout(10000) }); - const stream = data?.stream; - if (!stream) return null; - if (stream.type === 'file' && stream.qualities) { - let picked = null; - for (const q of ['1080', '720', '480', '360']) if (stream.qualities[q]?.url) { picked = stream.qualities[q].url; break; } - if (!picked) picked = stream.qualities[Object.keys(stream.qualities)[0]]?.url; - if (picked) return { allUrls: [{ url: picked, type: 'mp4', audio: 'sub', server: 'Vidlink', skipHlsCheck: true, skipCache: true }] }; - } else if (stream.playlist) { - return { allUrls: [{ url: stream.playlist, type: 'hls', audio: 'sub', server: 'Vidlink', skipHlsCheck: true, skipCache: true }] }; + + const basePath = s ? `${BASE}/api/b/tv/${encData.result}/${s}/${e || 1}` : `${BASE}/api/b/movie/${encData.result}`; + const responses = await Promise.allSettled([ + fetchJson(basePath, { headers: HEADERS, signal: AbortSignal.timeout(10000) }), + fetchJson(`${basePath}?multiLang=0`, { headers: HEADERS, signal: AbortSignal.timeout(10000) }) + ]); + + const streamList = []; + const seenUrls = new Set(); + const seenSubs = new Set(); + + for (const res of responses) { + if (res.status !== 'fulfilled' || !res.value?.stream) continue; + const stream = res.value.stream; + + if (stream.playlist && !seenUrls.has(stream.playlist)) { + seenUrls.add(stream.playlist); + const isDash = stream.type === 'dash' || stream.deliveryType === 'dash' || stream.playlist.includes('.mpd'); + streamList.push({ + url: stream.playlist, + quality: stream.playbackMetadata?.resolutions?.[0] ? `${stream.playbackMetadata.resolutions[0]}p` : 'Auto', + label: `Vidlink ${isDash ? 'DASH' : 'HLS'}`, + name: `Vidlink ${isDash ? 'DASH' : 'HLS'}`, + type: isDash ? 'dash' : 'hls', + headers: stream.playlistHeaders || {}, + server: 'Vidlink', + }); + } + + if (stream.qualities && typeof stream.qualities === 'object') { + const qualityOrder = ['1080', '720', '480', '360', ...Object.keys(stream.qualities)]; + const seenQualities = new Set(); + + for (const q of qualityOrder) { + if (seenQualities.has(q)) continue; + seenQualities.add(q); + const qData = stream.qualities[q]; + if (!qData?.url || seenUrls.has(qData.url)) continue; + seenUrls.add(qData.url); + streamList.push({ + url: qData.url, + quality: `${q}p`, + label: `Vidlink ${q}p`, + name: `Vidlink ${q}p`, + type: qData.type || 'mp4', + headers: qData.headers || {}, + server: 'Vidlink', + }); + } + } } + + if (!streamList.length) return null; + + return { + allUrls: streamList, + streams: streamList, + }; + } catch { 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))] : []; + return stream?.allUrls ? [...new Set(stream.allUrls.map(u => u.server || 'Vidlink'))] : []; } \ No newline at end of file diff --git a/src/sources/vidora.js b/src/sources/vidora.js deleted file mode 100644 index 7b27f99..0000000 --- a/src/sources/vidora.js +++ /dev/null @@ -1,62 +0,0 @@ -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 - }; -} \ No newline at end of file