added cinecat, cinesrc, cinezo, hexa, hyper, and yastream

This commit is contained in:
2026-10-20 14:38:36 -07:00
parent 4b255c7ee0
commit ec9f259c4a
8 changed files with 701 additions and 5 deletions

View File

@@ -118,6 +118,25 @@ 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',
@@ -137,6 +156,43 @@ export const SOURCES = [
}],
},
{
key: 'cinesrc',
label: 'CineSrc',
sourceFile: 'cinesrc',
proxyParam: 'cs',
timeout: 35000,
jitter: 500,
retries: 2,
multiUrl: true,
skipProxy: true,
cdnHeaders: [{
pattern: /cinesrc\.st/i,
headers: {
Referer: 'https://cinesrc.st/',
Origin: 'https://cinesrc.st',
},
}],
},
{
key: 'cinezo',
label: 'Cinezo',
sourceFile: 'cinezo',
proxyParam: 'czo',
timeout: 25000,
jitter: 500,
retries: 2,
multiUrl: true,
cdnHeaders: [{
pattern: /yenime\.net|flikhub\.net|hakunaymatata\.com/i,
headers: {
Referer: 'https://player.cinezo.live/',
Origin: 'https://player.cinezo.live',
},
}],
},
{
key: 'dulo',
label: 'Dulo',
@@ -187,6 +243,43 @@ export const SOURCES = [
skipProxy: true,
},
{
key: 'hexa',
label: 'Hexa',
sourceFile: 'hexa',
proxyParam: 'hx',
timeout: 30000,
jitter: 500,
retries: 2,
multiUrl: true,
skipProxy: true,
cdnHeaders: [{
pattern: /hexa\.su|flixer\.su/i,
headers: {
Referer: 'https://hexa.su/',
Origin: 'https://hexa.su',
},
}],
},
{
key: 'hyper',
label: 'Hyper',
sourceFile: 'hyper',
proxyParam: 'hyp',
timeout: 25000,
jitter: 500,
retries: 2,
multiUrl: true,
cdnHeaders: [{
pattern: /hakunaymatata\.com/i,
headers: {
Referer: 'https://mzfi.me/',
Origin: 'https://mzfi.me',
},
}],
},
{
key: 'kisskh',
label: 'KissKH',
@@ -588,6 +681,17 @@ export const SOURCES = [
}],
},
{
key: 'yastream',
label: 'YaStream',
sourceFile: 'yastream',
proxyParam: 'ya',
timeout: 25000,
jitter: 500,
retries: 2,
multiUrl: true,
},
];
export const HEALTH_PROBE_ID = '155';

19
src/sources/cinecat.js Normal file
View File

@@ -0,0 +1,19 @@
import { fetchJson } from '../utils/helpers.js';
export async function getStream({ id, s, e }) {
const isTv = s != null && e != null;
const url = isTv
? `https://zcdn.bluevelvet.space/cdn?tmdb=${id}&type=tv&season=${s}&episode=${e}`
: `https://zcdn.bluevelvet.space/cdn?tmdb=${id}&type=movie`;
const res = await fetchJson(url);
if (!res || !res.ready || !res.url) return null;
return {
url: res.url,
headers: {
Referer: 'https://zcdn.bluevelvet.space/',
Origin: 'https://zcdn.bluevelvet.space',
}
};
}

398
src/sources/cinesrc.js Normal file
View File

@@ -0,0 +1,398 @@
import { fetchJson, fetchText } from '../utils/helpers.js';
const API = "https://enc-dec.app/api";
async function solveStage1(challenge1, seed) {
const K = [
0x46C6C721, 0x4230C311, 0x18223A1D, 0x56389BA7,
0xD4A87103, 0x07CB9E2F, 0x73085F9D, 0xD2396A1F,
0x23AE5ADD, 0x4646C48F, 0x0F4E0903, 0x808F5F31,
0x953D4E07, 0x7E238FFF, 0x634595DF, 0xCB5E4301,
];
const SCHEDULES = [
[0, 7, 6, 3, 2, 1, 5, 4, 0, 2, 4, 6, 5, 1, 7, 3],
[5, 2, 1, 3, 0, 4, 6, 7, 4, 1, 0, 7, 3, 6, 5, 2],
[4, 0, 2, 1, 5, 7, 6, 3, 2, 6, 0, 7, 4, 3, 5, 1],
[5, 4, 7, 0, 2, 3, 1, 6, 5, 1, 4, 0, 7, 2, 3, 6],
[4, 7, 0, 2, 1, 6, 5, 3, 6, 1, 3, 2, 0, 7, 4, 5],
[0, 7, 2, 6, 3, 4, 5, 1, 6, 5, 0, 2, 7, 1, 4, 3],
[0, 4, 5, 3, 1, 7, 2, 6, 3, 7, 5, 6, 1, 0, 2, 4],
[7, 6, 0, 4, 2, 5, 1, 3, 7, 5, 3, 4, 2, 6, 0, 1],
];
const PERMUTATIONS = [
[3, 5, 1, 0, 2, 4],
[2, 0, 4, 5, 3, 1],
[1, 4, 5, 3, 0, 2],
[2, 0, 1, 5, 4, 3],
[4, 2, 1, 0, 3, 5],
[5, 0, 2, 1, 3, 4],
[4, 5, 1, 0, 3, 2],
[4, 0, 3, 5, 1, 2],
];
const u32 = x => x >>> 0;
const rotl = (x, n) => u32((x << (n & 31)) | (x >>> (32 - (n & 31))));
const imul = (a, b) => {
const ah = (a >>> 16) & 0xffff;
const al = a & 0xffff;
const bh = (b >>> 16) & 0xffff;
const bl = b & 0xffff;
return u32((ah * bl + al * bh) << 16) + al * bl;
};
const b64dec = s => {
const bin = atob(s.replace(/-/g, '+').replace(/_/g, '/'));
const bytes = new Uint8Array(bin.length);
for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);
return bytes;
};
const c = b64dec(challenge1.w);
const view = new DataView(c.buffer);
const mode = c[21];
const diffVal = c[22];
const var_ = c[23];
const rnd = c[24];
const m_b = c[25];
const s_i = c[26];
const p_i = c[27];
const w0 = view.getUint32(0, true);
const w1 = view.getUint32(4, true);
const w2 = view.getUint32(8, true);
const w3 = view.getUint32(12, true);
const w = [w0, w1, w2, w3];
const size = 1 << m_b;
const s = new Uint32Array(size);
let x = u32(w[var_ & 3] ^ K[s_i] ^ imul(K[8 + s_i], var_ + 1));
let y = u32(w[(var_ + 1) & 3] ^ K[15 - s_i]);
for (let i = 0; i < size; i++) {
x = u32(w[(i + s_i) & 3] + x + imul(K[(i + var_) & 15], i + 1));
x = u32(x ^ (x << 13));
x = u32(x ^ (x >>> 17));
x = u32(x ^ (x << 5));
y = rotl(imul(K[(i + 9) & 15], i + 3) ^ y ^ x, i + var_ + s_i);
s[i] = u32(imul(K[(i + 5) & 15], i ^ var_) ^ rotl(w[(i + var_) & 3], i + s_i) ^ x ^ y);
}
const seedBytes = new Uint8Array(seed.match(/.{1,2}/g).map(byte => parseInt(byte, 16)));
const msg = new Uint8Array(88);
msg.set(c.slice(0, 16), 24);
const cfg = m_b ^ 0x6B29;
msg[48] = 4;
msg[49] = mode;
msg[50] = var_;
msg[51] = s_i;
msg[52] = p_i;
msg[53] = rnd;
msg[54] = cfg & 0xff;
msg[55] = cfg >> 8;
msg.set(seedBytes, 56);
const mask = size - 1;
const sch = SCHEDULES[s_i];
const perm = PERMUTATIONS[p_i];
let n = 0n;
while (true) {
const lo = Number(n & 0xffffffffn);
const hi = Number(n >> 32n);
let ma = u32(w[0] ^ K[var_] ^ lo);
let mb = u32(rotl(lo, var_ + 7) ^ w[2]);
let mc = u32(w[1] ^ K[8 + s_i] ^ hi);
let md = u32(rotl(lo ^ hi, s_i + 11) ^ w[3]);
let me = u32(K[s_i + var_] ^ lo ^ hi);
let mf = u32(K[15 - var_] ^ w[s_i & 3]);
for (let r = 0; r < rnd; r++) {
const i1 = u32(rotl(mb, r + 1) ^ imul(K[r & 15], mf) ^ me ^ ma) & mask;
const v1 = s[i1];
const i2 = u32(rotl(me, var_ + r + 3) ^ imul(K[(r + 7) & 15], md) ^ mc ^ v1) & mask;
const op = sch[r];
if (op === 0) {
ma = rotl(u32(ma + md + v1), 5);
mc = u32(imul(mc ^ mf ^ ma, 1187432225) + mb);
} else if (op === 1) {
mb = rotl(mc ^ me ^ mb ^ v1, 11);
md = u32(imul(mb ^ ma, 1110491921) + md);
} else if (op === 2) {
mc = rotl(u32(mc + mb + v1), 17);
ma = u32(imul(md ^ mf ^ mc, 404896285) ^ ma);
} else if (op === 3) {
md = rotl(ma ^ md ^ v1, 23);
mb = u32(imul(mc ^ me ^ md, 1446550439) + mb);
} else if (op === 4) {
ma = u32(imul(ma ^ me ^ v1, -727158525) + rotl(mb, 7));
md = u32(rotl(u32(mc + mf + ma), 13) ^ md);
} else if (op === 5) {
mc = u32(imul(u32(mc + mf + v1), 130784815) ^ rotl(md, 9));
mb = u32(rotl(ma ^ me ^ mc, 19) + mb);
} else if (op === 6) {
mb = u32(imul(mb ^ mf ^ v1, 1929928605) + rotl(ma, 3));
mc = u32(rotl(u32(md + me + mb), 15) ^ mc);
} else {
md = u32(imul(u32(md + me + v1), -767989217) ^ rotl(mc, 21));
ma = u32(rotl(mb ^ mf ^ md, 27) + ma);
}
const v2 = s[i2];
if (((mf ^ r ^ v1) & 1) === 0) {
mf = rotl(u32(v2 + mf + ma), r + 5);
me = u32(imul(K[8 + op], v2 ^ me ^ mf) + md);
} else {
me = rotl(v2 ^ me ^ mb, r + 9);
mf = u32(imul(K[(op + 11) & 15], u32(v2 + mf + me)) ^ mc);
}
}
const mArr = [ma, mc, mb, md, mf, me];
const v = perm.map(idx => mArr[idx]);
const msgView = new DataView(msg.buffer);
msgView.setUint32(0, v[0], true);
msgView.setUint32(4, v[1], true);
msgView.setUint32(8, v[2], true);
msgView.setUint32(12, v[3], true);
msgView.setUint32(16, v[4], true);
msgView.setUint32(20, v[5], true);
msgView.setBigUint64(40, n, true);
const hashBuf = await crypto.subtle.digest('SHA-256', msg);
const hashBytes = new Uint8Array(hashBuf);
const b = Math.floor(diffVal / 8);
const rBits = diffVal % 8;
let ok = true;
for (let i = 0; i < b; i++) {
if (hashBytes[i] !== 0) {
ok = false;
break;
}
}
if (ok && (rBits === 0 || (hashBytes[b] >> (8 - rBits)) === 0)) {
return `m4.${n.toString(16)}`;
}
n++;
}
}
async function solveStage2(data) {
const target = data.pack[0].split('').reverse().join('');
const salt = data.pack[3].split('').reverse().join('');
const r = data.pack[4].split('').reverse().join('');
const b64decStr = s => decodeURIComponent(escape(atob(s.replace(/-/g, '+').replace(/_/g, '/'))));
const body = b64decStr(r.split('.')[1]);
const payload = b64decStr(body.split('.')[1]);
const difficulty = JSON.parse(payload).d;
const width = Math.floor((difficulty + 3) / 4);
const limit = 1 << difficulty;
for (let counter = 0; counter < limit; counter++) {
const key = counter.toString(16).padStart(width, '0');
const msgUint8 = new TextEncoder().encode(salt + key);
const hashBuffer = await crypto.subtle.digest('SHA-256', msgUint8);
const hashArray = Array.from(new Uint8Array(hashBuffer));
const digest = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
if (digest === target) {
return key;
}
}
throw new Error('no solution found');
}
export async function getStream({ id, s, e }) {
const isTv = s != null && e != null;
const type = isTv ? 'tv' : 'movie';
const url = isTv
? `https://cinesrc.st/embed/tv/${id}?s=${s}&e=${e}`
: `https://cinesrc.st/embed/movie/${id}`;
const headers = {
"Origin": "https://cinesrc.st",
"Referer": "https://cinesrc.st/",
"Content-Type": "text/plain;charset=UTF-8",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.37 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.37"
};
const fields = [type, id, s ? String(s) : null, e ? String(e) : null];
const encoded = btoa(JSON.stringify(fields).replace(/ /g, '')).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
const bootstrapHeaders = { ...headers, "x-cs-q": encoded };
const bootstrap = await fetchJson("https://cinesrc.st/api/c/bootstrap", {
method: "POST",
headers: bootstrapHeaders,
body: "{}"
});
const cookies = {
"x-cs-b": "20261014-a",
"x-cs-q": encoded,
"x-cs-r": bootstrap.r,
"x-cs-p": bootstrap.p,
};
const challenge1 = await fetchJson("https://cinesrc.st/api/c/issue", {
headers: { ...headers, ...cookies }
});
const payload1 = {
data: {
url,
user_agent: headers["User-Agent"],
challenge_data: challenge1
},
type: "seed"
};
const seedRes = await fetchJson(`${API}/enc-cinesrc`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload1)
});
const seedData = seedRes.result;
const solution1 = await solveStage1(challenge1, seedData.seed);
const challenge2 = await fetchJson("https://cinesrc.st/api/c/stage2/issue", {
headers: { ...headers, ...cookies }
});
const solution2 = await solveStage2(challenge2);
const payload2 = {
data: {
url,
user_agent: headers["User-Agent"],
challenge_data: {
stage1: { challenge: challenge1, solution: solution1 },
stage2: { challenge: challenge2, solution: solution2 }
},
state: seedData.state
},
type: "encrypt"
};
const encryptRes = await fetchJson(`${API}/enc-cinesrc`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload2)
});
const encData = encryptRes.result;
const token = `${encData.token}::c3::${cookies["x-cs-r"]}`;
const nextActions = encData.headers;
const providersText = await fetchText(url, {
method: "POST",
headers: { ...headers, ...cookies, "Next-Action": nextActions.getProviderList },
body: JSON.stringify([])
});
const lines = providersText.split('\n');
let providers = null;
for (const rawLine of lines) {
const colonIndex = rawLine.indexOf(':');
if (colonIndex !== -1) {
const candidate = rawLine.slice(colonIndex + 1).trim();
if (candidate.startsWith('[') && candidate.endsWith(']')) {
try {
providers = JSON.parse(candidate);
break;
} catch { }
}
}
}
if (!Array.isArray(providers) || providers.length === 0) return null;
const allUrls = [];
for (const providerObj of providers) {
const provider = providerObj.id;
const streamPayload = [
id,
isTv ? "show" : type,
isTv ? String(s) : "$undefined",
isTv ? String(e) : "$undefined",
token,
provider
];
let streamRes;
try {
streamRes = await fetchText(url, {
method: "POST",
headers: { ...headers, ...cookies, "Next-Action": nextActions.getStream },
body: JSON.stringify(streamPayload)
});
} catch {
continue;
}
if (!streamRes || streamRes.includes('"status":500') || streamRes.includes('invalid_challenge') || streamRes.length < 10) continue;
let decryptRes;
try {
decryptRes = await fetchJson(`${API}/dec-cinesrc`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ text: streamRes, state: encData.state })
});
} catch {
continue;
}
if (decryptRes && decryptRes.status === 200 && decryptRes.result) {
let resultData = decryptRes.result;
if (typeof resultData === 'string') {
try {
resultData = JSON.parse(resultData);
} catch { }
}
let streamUrl = null;
if (Array.isArray(resultData?.url)) {
streamUrl = resultData.url[0]?.url;
} else if (typeof resultData?.url === 'string') {
streamUrl = resultData.url;
} else if (resultData?.file) {
streamUrl = resultData.file;
} else if (Array.isArray(resultData?.sources)) {
streamUrl = resultData.sources[0]?.url;
}
if (streamUrl) {
if (streamUrl.startsWith('/')) {
streamUrl = `https://cinesrc.st${streamUrl}`;
}
allUrls.push({
url: streamUrl,
server: `CineSrc - ${providerObj.name || provider}`,
headers: resultData.headers || {
Referer: "https://cinesrc.st/",
Origin: "https://cinesrc.st"
}
});
break;
}
}
}
if (!allUrls.length) return null;
return {
url: allUrls[0].url,
headers: allUrls[0].headers,
allUrls
};
}

55
src/sources/cinezo.js Normal file
View File

@@ -0,0 +1,55 @@
import { fetchJson } from '../utils/helpers.js';
export async function getStream({ id, s, e }) {
const isTv = s != null && e != null;
const referer = isTv
? `https://player.cinezo.live/embed/tv/${id}?s=${s}&e=${e}`
: `https://player.cinezo.live/embed/movie/${id}`;
const headers = {
'Accept': '*/*',
'Origin': 'https://player.cinezo.live',
'Referer': referer,
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/155.0.0.0 Safari/537.36 Edg/155.0.0.0'
};
const hosts = [
'https://proxy.yenime.net',
'https://proxy1.flikhub.net'
];
const sources = ['zendaya', 'berlin', 'jennifer', 'scarlet'];
const allUrls = [];
for (const host of hosts) {
for (const source of sources) {
const url = isTv
? `${host}/tv?id=${id}&season=${s}&episode=${e}&mode=json&sources=${source}&hevc=1`
: `${host}/movie?id=${id}&mode=json&sources=${source}&hevc=1`;
try {
const res = await fetchJson(url, { headers });
const streamUrl = res?.source?.url;
if (streamUrl) {
allUrls.push({
url: streamUrl,
server: `Cinezo - ${res.source.label || source}`,
headers: {
Referer: 'https://player.cinezo.live/',
Origin: 'https://player.cinezo.live'
}
});
}
} catch { }
}
if (allUrls.length) break;
}
if (!allUrls.length) return null;
return {
url: allUrls[0].url,
headers: allUrls[0].headers,
allUrls
};
}

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

@@ -0,0 +1,54 @@
import { fetchJson, fetchText } from '../utils/helpers.js';
const API = "https://enc-dec.app/api";
function getRandomHex(size = 32) {
const array = new Uint8Array(size);
crypto.getRandomValues(array);
return Array.from(array).map(b => b.toString(16).padStart(2, '0')).join('');
}
export async function getStream({ id, s, e }) {
const isTv = s != null && e != null;
const key = getRandomHex(32);
const headers = {
"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",
"Referer": "https://hexa.su/",
"Accept": "text/plain",
"X-Fingerprint-Lite": "e9136c41504646444",
"X-Api-Key": key
};
const encHexaRes = await fetchJson(`${API}/enc-hexa`);
if (!encHexaRes || encHexaRes.status !== 200) return null;
headers["X-Cap-Token"] = encHexaRes.result.token;
const url = isTv
? `https://theemoviedb.hexa.su/api/tmdb/tv/${id}/season/${s}/episode/${e}/images`
: `https://theemoviedb.hexa.su/api/tmdb/movie/${id}/images`;
const encrypted = await fetchText(url, { headers });
if (!encrypted) return null;
const decRes = await fetchJson(`${API}/dec-hexa`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ text: encrypted, key })
});
if (!decRes || decRes.status !== 200 || !decRes.result) return null;
const data = decRes.result;
const streamUrl = data.url || data.file || (Array.isArray(data.sources) ? data.sources[0]?.url : null);
if (!streamUrl) return null;
return {
url: streamUrl,
headers: {
Referer: "https://hexa.su/",
Origin: "https://hexa.su"
}
};
}

36
src/sources/hyper.js Normal file
View File

@@ -0,0 +1,36 @@
import { fetchJson } from '../utils/helpers.js';
export async function getStream({ id, s, e }) {
const isTv = s != null && e != null;
const path = isTv
? `/local/stream/series/tmdb:${id}:${s}:${e}.json`
: `/local/stream/movie/tmdb:${id}.json`;
const url = `https://hyper.stravo.site${path}`;
const res = await fetchJson(url);
if (!res || !Array.isArray(res.streams)) return null;
const validStreams = res.streams.filter(item => {
if (!item.url || item.externalUrl) return false;
if (typeof item.title === 'string' && item.title.includes('🔒')) return false;
return true;
});
if (!validStreams.length) return null;
const allUrls = validStreams.map(item => ({
url: item.url,
server: `Hyper - ${item.title?.replace(/[^\x20-\x7E]+/g, '').trim() || 'Server'}`,
headers: item.behaviorHints?.proxyHeaders?.request || {
Referer: 'https://mzfi.me/',
Accept: '*/*'
},
skipProxy: item.behaviorHints?.notWebReady === false
}));
return {
url: allUrls[0].url,
headers: allUrls[0].headers,
allUrls
};
}

30
src/sources/yastream.js Normal file
View File

@@ -0,0 +1,30 @@
import { fetchJson } from '../utils/helpers.js';
export async function getStream({ id, s, e }) {
const isTv = s != null && e != null;
const path = isTv
? `/stream/series/tmdb:${id}:${s}:${e}.json`
: `/stream/movie/tmdb:${id}.json`;
const url = `https://yastream.tamthai.de${path}`;
const res = await fetchJson(url);
if (!res || !Array.isArray(res.streams) || !res.streams.length) return null;
const allUrls = res.streams.map(item => {
if (!item.url) return null;
return {
url: item.url,
server: `YaStream - ${item.name?.replace(/\n/g, ' ') || 'Server'}`,
headers: item.behaviorHints?.proxyHeaders?.request || undefined,
skipProxy: item.behaviorHints?.notWebReady === false
};
}).filter(Boolean);
if (!allUrls.length) return null;
return {
url: allUrls[0].url,
headers: allUrls[0].headers,
allUrls
};
}

View File

@@ -1,7 +1,7 @@
// usage: npm test
// Configure the variables below to customize your testing environment
import fs from "fs";
import fs, { truncate } from "fs";
import path from "path";
import VylaSDK from "../src/sdk.js";
import dotenv from "dotenv";
@@ -13,13 +13,13 @@ 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: "atsu", // Source key to test
STREAM_SOURCE: "cinezo", // Source key to test
TEST_SUBTITLE: false,
TEST_DOWNLOAD: false,
TEST_MOVIE: false,
TEST_TV: false,
TEST_MANGA: true,
TEST_MOVIE: true,
TEST_TV: true,
TEST_MANGA: false,
MANGA_QUERY: "Steel Ball Run",
EXTRA_SDK_PARAMS: {