added cinesu & pstream, removed vidrift & vidsync

This commit is contained in:
2026-08-25 15:21:15 -07:00
parent 280cc0c3dd
commit e70dff6604
5 changed files with 247 additions and 157 deletions

View File

@@ -115,6 +115,26 @@ export const SOURCES = [
skipProxy: true,
},
{
key: 'cinesu',
label: 'CineSu',
sourceFile: 'cinesu',
proxyParam: 'cs',
timeout: 25000,
jitter: 500,
retries: 2,
multiUrl: true,
skipProxy: true,
skipVerify: true,
cdnHeaders: [{
pattern: /glendale-plumbing\.com/i,
headers: {
Referer: 'https://cine.su/',
Origin: 'https://cine.su',
},
}],
},
{
key: 'flaxmovies',
label: 'FlaxMovies',
@@ -303,6 +323,17 @@ export const SOURCES = [
disabled: false, // Enabled, but only returns streams when a manifest is provided
},
{
key: 'pstream',
label: 'PStream',
sourceFile: 'pstream',
proxyParam: 'ps',
timeout: 35000,
jitter: 500,
retries: 1,
multiUrl: true,
},
{
key: 'nova',
label: 'Nova',
@@ -456,19 +487,6 @@ export const SOURCES = [
skipProxy: true,
},
{
key: 'vidrift',
label: 'VidRift',
sourceFile: 'vidrift',
proxyParam: 'vr',
timeout: 20000,
jitter: 500,
retries: 2,
multiUrl: true,
skipProxy: true,
disabled: true, // Temporarily disabled because their site is down, embed website does not load
},
{
key: 'vidrock',
label: 'VidRock',
@@ -489,17 +507,6 @@ export const SOURCES = [
},],
},
{
key: 'vidsync',
label: 'VidSync',
sourceFile: 'vidsync',
proxyParam: 'vs',
timeout: 20000,
retries: 2,
multiUrl: true,
disabled: true, // Temporarily disabled due to enc dec issues
},
{
key: 'vidup',
label: 'VidUp',

148
src/sources/cinesu.js Normal file
View File

@@ -0,0 +1,148 @@
import { USER_AGENT } from '../utils/helpers.js';
const BASE_DOMAIN = 'https://glendale-plumbing.com';
const REFERER = 'https://cine.su/';
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-_';
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 generateMasterUrl(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 `${BASE_DOMAIN}/c/v1/${iD(i)}/master.m3u8`;
}
function parsePlaylist(m3u8Text) {
const qualities = [];
const subtitles = [];
const subRegex = /#EXT-X-MEDIA:TYPE=SUBTITLES,[^\n]*NAME="([^"]*)"[^\n]*LANGUAGE="([^"]*)"[^\n]*URI="([^"]*)"/g;
let match;
while ((match = subRegex.exec(m3u8Text)) !== null) {
let uri = match[3];
if (uri.startsWith('/')) uri = `${BASE_DOMAIN}${uri}`;
subtitles.push({ label: match[1], language: match[2], file: uri, type: 'vtt' });
}
const streamRegex = /#EXT-X-STREAM-INF:[^\n]*RESOLUTION=\d+x(\d+)[^\n]*\r?\n([^\n#][^\n]*)/g;
while ((match = streamRegex.exec(m3u8Text)) !== null) {
let streamPath = match[2].trim();
if (streamPath.startsWith('/')) streamPath = `${BASE_DOMAIN}${streamPath}`;
qualities.push({ quality: `${match[1]}p`, url: streamPath });
}
return { qualities, subtitles };
}
export async function getStream(args) {
const { id, s, e, clientIP } = args;
const rawMasterUrl = generateMasterUrl(id, s, e);
const headers = {
'User-Agent': USER_AGENT,
'Referer': REFERER,
'Origin': 'https://cine.su',
...(clientIP && { 'X-Forwarded-For': clientIP })
};
try {
const res = await fetch(rawMasterUrl, {
headers,
signal: AbortSignal.timeout(5000)
});
if (res.ok) {
const text = await res.text();
const { qualities, subtitles } = parsePlaylist(text);
if (qualities.length) {
const allUrls = [{
url: rawMasterUrl,
server: 'CineSu (Auto)',
quality: 'Auto',
type: 'hls',
headers,
subtitles: subtitles.length ? subtitles : undefined
}];
for (const q of qualities) {
allUrls.push({
url: q.url,
server: `CineSu (${q.quality})`,
quality: q.quality,
type: 'hls',
headers,
subtitles: subtitles.length ? subtitles : undefined
});
}
return { allUrls };
}
}
} catch { }
return {
allUrls: [{
url: rawMasterUrl,
server: 'CineSu',
quality: 'Auto',
type: 'hls',
headers
}]
};
}

68
src/sources/pstream.js Normal file
View File

@@ -0,0 +1,68 @@
import { USER_AGENT } from '../utils/helpers.js';
async function safeImport(moduleName) {
try {
return await import(`./${moduleName}.js`);
} catch {
return null;
}
}
export async function getStream(args) {
const { id, s, e, clientIP, sdk } = args;
const isTv = s != null && e != null;
const type = isTv ? 'tv' : 'movie';
const allUrls = [];
const providers = [
{ name: 'CineSu', key: 'cinesu', fnName: 'fetchCineSuStreams' },
{ name: 'ZxcStream', key: 'zxcstream', fnName: 'fetchZxcStreams' },
{ name: 'VidFast', key: 'vidfast', fnName: 'fetchVidFastStreams' },
{ name: '1Embed', key: '1embed', fnName: 'fetchOneEmbedStreams' },
];
await Promise.all(
providers.map(async ({ name, key, fnName }) => {
try {
let streamResult = null;
if (sdk) {
try {
streamResult = await sdk.getStream(key, id, s, e, clientIP);
} catch { }
}
if (!streamResult) {
const mod = await safeImport(key);
if (mod) {
if (typeof mod.getStream === 'function') {
streamResult = await mod.getStream(args);
} else if (typeof mod[fnName] === 'function') {
const raw = await mod[fnName]({ type, id, season: s, episode: e });
if (Array.isArray(raw)) {
streamResult = { allUrls: raw };
}
}
}
}
if (!streamResult) return;
const streams = streamResult.allUrls || (streamResult.url ? [streamResult] : []);
for (const item of streams) {
allUrls.push({
url: item.url,
server: `FlyStream (${item.source || item.server || name})`,
quality: item.quality || 'Auto',
type: item.type || item.format || (item.url?.includes('.m3u8') ? 'hls' : 'mp4'),
headers: item.headers || {
'User-Agent': USER_AGENT,
},
});
}
} catch { }
})
);
return allUrls.length ? { allUrls } : null;
}

View File

@@ -1,95 +0,0 @@
'use strict';
const BASE = "https://embed.vidrift.in";
const DEFAULT_HEADERS = {
"Accept": "*/*",
"Accept-Language": "en-US,en;q=0.9",
"Cache-Control": "no-cache",
"Pragma": "no-cache",
"Referer": `${BASE}/`,
"Origin": BASE,
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36 Edg/150.0.0.0"
};
function decodeStreams(encodedStr) {
try {
const base64Str = encodedStr.replace(/-/g, '+').replace(/_/g, '/');
const binaryStr = typeof atob === 'function'
? atob(base64Str)
: Buffer.from(base64Str, 'base64').toString('binary');
const bytes = new Uint8Array(binaryStr.length);
for (let i = 0; i < binaryStr.length; i++) {
bytes[i] = binaryStr.charCodeAt(i);
}
const key = bytes[0];
let decodedStr = '';
for (let i = 1; i < bytes.length; i++) {
decodedStr += String.fromCharCode(bytes[i] ^ key);
}
return JSON.parse(decodedStr);
} catch (e) {
return [];
}
}
export async function getStream(args) {
const { id, s, e, server: serverName } = args;
const type = s != null && e != null ? "tv" : "movie";
const endpoint = type === "tv"
? `${BASE}/embed/tv/${id}/${s}/${e}`
: `${BASE}/embed/movie/${id}`;
try {
const res = await fetch(endpoint, {
headers: {
...DEFAULT_HEADERS,
"Accept": "text/html",
},
signal: AbortSignal.timeout(10000)
});
if (!res.ok) return null;
const html = await res.text();
const match = html.match(/var\s+_s\s*=\s*['"]([^'"]+)['"]/);
if (!match) return null;
const encodedStreams = match[1];
const streamsData = decodeStreams(encodedStreams);
if (!Array.isArray(streamsData) || !streamsData.length) {
return null;
}
const allUrls = streamsData.map((stream, index) => ({
url: stream.url.startsWith("http")
? stream.url
: `${BASE}${stream.url}`,
server: stream.label || `VidRift - Server ${index + 1}`,
type: "hls",
headers: DEFAULT_HEADERS
}));
if (serverName && serverName !== "all") {
const filtered = allUrls.filter(x => x.server === serverName);
return filtered.length ? { allUrls: filtered } : null;
}
return { allUrls };
} catch (err) {
return null;
}
}
export async function getSources(args) {
const res = await getStream(args);
if (!res?.allUrls) return ["VidRift - Server 1"];
return res.allUrls.map(x => x.server);
}

View File

@@ -1,38 +0,0 @@
import { fetchJson, fetchText, USER_AGENT, getTmdbInfo } from '../utils/helpers.js';
const API_BASE = "https://enc-dec.app/api";
const DOMAIN = "https://vidsync.xyz";
const HEADERS = { "Accept": "*/*", "Origin": DOMAIN, "Referer": `${DOMAIN}/`, "User-Agent": USER_AGENT, "X-Requested-With": "XMLHttpRequest" };
const SERVERS = ["cinevault", "cinedub", "cinebox", "cineflix", "cinevip", "cinecloud", "cine4k"];
export async function getStream({ id, s, e, server }) {
try {
const isTv = s != null && e != null;
const type = isTv ? 'tv' : 'movie';
const info = await getTmdbInfo(id, type);
if (!info?.titles?.length) return null;
const turnstileJson = await fetchJson(`${API_BASE}/enc-vidsync`, { signal: AbortSignal.timeout(5000) });
if (turnstileJson.status !== 200 || !turnstileJson.result?.token) return null;
const turnstileToken = turnstileJson.result.token;
let targets = SERVERS;
if (server && server !== 'all') {
const clean = server.replace('VidSync - ', '');
targets = SERVERS.includes(clean) ? [clean] : SERVERS;
}
const encTitle = encodeURIComponent(info.titles[0]).replace(/%20/g, '+');
const results = await Promise.allSettled(targets.map(async srv => {
let url = `${DOMAIN}/api/stream/fetch?title=${encTitle}&type=${type}&releaseYear=${info.year}&mediaId=${id}&serverName=${srv}`;
if (isTv) url += `&season=${s}&episode=${e}`;
const encryptedText = await fetchText(url, { headers: { ...HEADERS, "X-Cf-Turnstile": turnstileToken }, signal: AbortSignal.timeout(10000) });
const decJson = await fetchJson(`${API_BASE}/dec-vidsync`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ text: encryptedText, id: String(id) }), signal: AbortSignal.timeout(10000) });
if (decJson.status !== 200 || !decJson.result) throw new Error();
const streams = Array.isArray(decJson.result) ? decJson.result : [decJson.result];
return streams.map(st => ({ url: st.url || st.file, server: `VidSync - ${srv}`, quality: st.quality || "Auto", type: (st.url || st.file || "").includes(".m3u8") ? "hls" : "mp4", headers: { ...HEADERS, "Origin": DOMAIN }, skipProxy: false, skipVerify: true, skipHlsCheck: true }));
}));
const allUrls = [];
for (const r of results) if (r.status === 'fulfilled') allUrls.push(...r.value);
return allUrls.length ? { allUrls } : null;
} catch { return null; }
}
export async function getSources() { return SERVERS.map(s => `VidSync - ${s}`); }