mirror of
https://gitlab.com/vyla-entertainment/sdk.git
synced 2026-11-13 21:57:27 -05:00
181 lines
5.5 KiB
JavaScript
181 lines
5.5 KiB
JavaScript
import crypto from "crypto";
|
|
import { Session, ClientIdentifier, initTLS, destroyTLS } from "node-tls-client";
|
|
|
|
const BASE_URL = "https://cinema.army";
|
|
const API_BASE = "https://cinema.army/api";
|
|
|
|
const XOR_KEY = "f16a6b92fcd6eed3e9476297f893f2505c32f79bc6efef9b6c95496aa5914548";
|
|
const HMAC_KEY = "a2cfc9eaa2a3690b5d4f6b5f958dbdeae52a097283d375bd253ee7c7062524f5";
|
|
|
|
const UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36";
|
|
|
|
const SERVERS = [
|
|
{ key: "cookiebakers", method: "POST", label: "CookieBakers" },
|
|
{ key: "tcloud", method: "GET", label: "TCloud" },
|
|
{ key: "ipcloud", method: "GET", label: "IPcloud" },
|
|
{ key: "dcloud", method: "GET", label: "Dcloud" },
|
|
];
|
|
|
|
let tlsInitialized = false;
|
|
let session = null;
|
|
|
|
async function ensureSession() {
|
|
if (!tlsInitialized) {
|
|
await initTLS();
|
|
tlsInitialized = true;
|
|
}
|
|
if (!session) {
|
|
session = new Session({
|
|
clientIdentifier: ClientIdentifier.chrome_131,
|
|
timeout: 15000,
|
|
insecureSkipVerify: false,
|
|
});
|
|
}
|
|
return session;
|
|
}
|
|
|
|
function getHeaders(token, id, isTv, s, e) {
|
|
const referer = isTv
|
|
? `${BASE_URL}/watch?id=${id}&type=tv&season=${s}&episode=${e}`
|
|
: `${BASE_URL}/watch?id=${id}&type=movie`;
|
|
|
|
return {
|
|
"User-Agent": UA,
|
|
"Accept": "application/json",
|
|
"Accept-Language": "en-US,en;q=0.9",
|
|
"Accept-Encoding": "gzip, deflate, br",
|
|
"Cache-Control": "no-cache",
|
|
"Pragma": "no-cache",
|
|
"Referer": referer,
|
|
"Origin": BASE_URL,
|
|
"Sec-Ch-Ua": '"Chromium";v="131", "Not_A Brand";v="24"',
|
|
"Sec-Ch-Ua-Mobile": "?0",
|
|
"Sec-Ch-Ua-Platform": '"Windows"',
|
|
"Sec-Fetch-Dest": "empty",
|
|
"Sec-Fetch-Mode": "cors",
|
|
"Sec-Fetch-Site": "same-origin",
|
|
"Priority": "u=1, i",
|
|
"X-Stream-Token": token,
|
|
};
|
|
}
|
|
|
|
function generateToken(id) {
|
|
try {
|
|
const minute = Math.floor(Date.now() / 1000 / 60);
|
|
return crypto
|
|
.createHmac("sha256", HMAC_KEY)
|
|
.update(`${id}:${minute}`)
|
|
.digest("hex");
|
|
} catch {
|
|
return "";
|
|
}
|
|
}
|
|
|
|
function decryptStreamUrl(encoded) {
|
|
if (!encoded) return "";
|
|
if (encoded.startsWith("http://") || encoded.startsWith("https://")) return encoded;
|
|
try {
|
|
const decoded = Buffer.from(encoded, "base64").toString("binary");
|
|
let out = "";
|
|
for (let i = 0; i < decoded.length; i++) {
|
|
out += String.fromCharCode(
|
|
decoded.charCodeAt(i) ^ XOR_KEY.charCodeAt(i % XOR_KEY.length)
|
|
);
|
|
}
|
|
return out;
|
|
} catch {
|
|
return encoded;
|
|
}
|
|
}
|
|
|
|
export async function getStream(args) {
|
|
const { id, s = null, e = null } = args;
|
|
const isTv = s != null && e != null;
|
|
|
|
const token = generateToken(id);
|
|
const headers = getHeaders(token, id, isTv, s, e);
|
|
|
|
const sess = await ensureSession();
|
|
|
|
const requests = SERVERS.map(server => {
|
|
const path = isTv
|
|
? `/${server.key}/tv/${id}/${s}/${e}`
|
|
: `/${server.key}/movie/${id}`;
|
|
return { url: `${API_BASE}${path}`, server };
|
|
});
|
|
|
|
const results = await Promise.allSettled(
|
|
requests.map(async ({ url, server }) => {
|
|
try {
|
|
const res = server.method === "POST"
|
|
? await sess.post(url, { headers })
|
|
: await sess.get(url, { headers });
|
|
|
|
if (res.status !== 200) {
|
|
if (process.env.DEBUG_CAUSE) {
|
|
console.error(`[CinemaArmy ${server.key}] HTTP ${res.status}`);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
const text = await res.text();
|
|
let data;
|
|
try {
|
|
data = JSON.parse(text);
|
|
} catch {
|
|
return null;
|
|
}
|
|
|
|
const encrypted = data?.stream?.url;
|
|
if (!encrypted) return null;
|
|
|
|
const streamUrl = decryptStreamUrl(encrypted);
|
|
if (!streamUrl || !/^https?:\/\//.test(streamUrl)) return null;
|
|
|
|
return {
|
|
url: streamUrl,
|
|
headers: {
|
|
"User-Agent": UA,
|
|
Referer: `${BASE_URL}/`,
|
|
Origin: BASE_URL,
|
|
},
|
|
server: `CinemaArmy (${server.label})`,
|
|
quality: "1080p",
|
|
type: streamUrl.includes(".m3u8") ? "hls" : "mp4",
|
|
};
|
|
} catch (err) {
|
|
if (process.env.DEBUG_CAUSE) {
|
|
console.error(`[CinemaArmy ${server.key}] ${err.message}`);
|
|
if (err.cause) {
|
|
console.error(` cause.code: ${err.cause.code || "n/a"}`);
|
|
console.error(` cause.message: ${err.cause.message || err.cause}`);
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
})
|
|
);
|
|
|
|
const allUrls = results
|
|
.filter(r => r.status === "fulfilled" && r.value)
|
|
.map(r => r.value);
|
|
|
|
if (!allUrls.length) return null;
|
|
|
|
return { allUrls };
|
|
}
|
|
|
|
export async function cleanup() {
|
|
if (session) {
|
|
try {
|
|
await session.close();
|
|
} catch { }
|
|
session = null;
|
|
}
|
|
if (tlsInitialized) {
|
|
try {
|
|
await destroyTLS();
|
|
} catch { }
|
|
tlsInitialized = false;
|
|
}
|
|
} |