Added Cinejoy, cleaned up README

This commit is contained in:
endoverdosing
2026-07-29 23:18:25 -07:00
parent 482055836a
commit 902326bdcd
7 changed files with 282 additions and 75 deletions

1
.env.example Normal file
View File

@@ -0,0 +1 @@
TMDB_API_KEY=your_tmdb_api_key_here

View File

@@ -11,36 +11,8 @@ npm install @vyla-entertainment/sdk
pnpm install @vyla-entertainment/sdk
```
## Usage
Start by creating a VylaSDK element with your TMDB api key.
```js
import VylaSDK from "@vyla-entertainment/sdk";
View our full documentation at https://docs.vyla.cc/sdk
const sdk = new VylaSDK({
tmdbApiKey: ...
});
```
You can now use this to access Vyla's content.
```js
// Get sources
// Args: excludeDisabled
sdk.getSources(false); -> {key, timeout, label, ...}[]
// Get subtitles
// Args: tmdb id, season?, episode?
await sdk.getSubtitles('0000000', null, null); -> {label, file, type, source}[]
// Probe & test a source
// Args: source key
await sdk.probeSource('nebula'); -> {ok, ms}
// Probe & test all sources
await sdk.probeAllSources(); -> {'key': {ok, ms}, ...}
// Get HLS streams
// Args: source key, tmdb id, season?, episode?, clientIP?
await sdk.getStream('nebula', '0000000', null, null, null) -> {allUrls: {url, quality, label?, server?, type?, skipProxy?, skipVerify?, headers? ...}[]}
```
# [DMCA](https://docs.vyla.cc/misc/dmca)

View File

@@ -1,3 +1,10 @@
// Currently active sources reported on 7:47:50 PM PST 7/6/2026
// None Anime Tested: 936075
// Anime Tested: 37854
// Fshare/Fsonic Tested: 155 ( they don't have Michael )
export const SOURCES = [
{
key: '1embed',
@@ -107,6 +114,18 @@ export const SOURCES = [
},
},
{
key: 'cinejoy',
label: 'Cinejoy',
sourceFile: 'cinejoy',
proxyParam: 'cj',
timeout: 30000,
jitter: 500,
retries: 1,
multiUrl: true,
skipProxy: true,
},
{
key: 'flaxmovies',
label: 'FlaxMovies',
@@ -512,6 +531,7 @@ export const SOURCES = [
retries: 2,
skipProxy: true,
},
];
export const HEALTH_PROBE_ID = '155';

View File

@@ -1,8 +1,12 @@
import { getDownloads, getDownloadsTv } from "./sources/trendimovies.js";
export async function fetchDownloads(id, s = null, e = null) {
const res = s != null && e != null
? await getDownloadsTv(id, s, e)
: await getDownloads(id);
return res;
try {
const res = s != null && e != null
? await getDownloadsTv(id, s, e)
: await getDownloads(id);
return res || [];
} catch {
return [];
}
}

210
src/sources/cinejoy.js Normal file
View File

@@ -0,0 +1,210 @@
import crypto from "crypto";
export const ID = "cinejoy";
export const DOMAIN = "https://cinejoy.to";
export const API_DOMAIN = "https://api.shegu.st";
export const ENC_DEC_API = "https://enc-dec.app/api";
export const RETURN_ALL_URLS = true;
const UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36";
function countLeadingZeroBits(buffer) {
let count = 0;
for (const value of buffer) {
if (value === 0) {
count += 8;
continue;
}
count += 8 - value.toString(2).length;
break;
}
return count;
}
function solveChallenge(data) {
const saltStr = `pow2-salt|${data.s}|${data.b}`;
const salt = crypto.createHash("sha256").update(saltStr).digest();
const n = data.n;
const r = data.r;
const p = data.p;
const d = data.d;
const b = data.b;
const s = data.s;
for (let counter = 0; counter < 1000000; counter++) {
const payload = `pow2|${b}|${s}|${counter}`;
const result = crypto.scryptSync(payload, salt, 32, {
N: n,
r: r,
p: p,
maxmem: 128 * n * r * 2
});
if (countLeadingZeroBits(result) >= d) {
const solved = { ...data, c: counter };
return Buffer.from(JSON.stringify(solved)).toString("base64");
}
}
throw new Error("Challenge solve failed");
}
async function getTmdbMetadata(tmdbId, isTv) {
const apiKey = process.env.TMDB_API_KEY;
if (!apiKey) return null;
const type = isTv ? "tv" : "movie";
try {
const url = `https://api.themoviedb.org/3/${type}/${tmdbId}?api_key=${apiKey}&append_to_response=external_ids`;
const res = await fetch(url, { signal: AbortSignal.timeout(10000) });
if (!res.ok) return null;
const data = await res.json();
const title = isTv ? data.name : data.title;
const year = isTv ? (data.first_air_date ? data.first_air_date.split("-")[0] : "") : (data.release_date ? data.release_date.split("-")[0] : "");
const imdbId = data.external_ids?.imdb_id || "";
return { title, year, imdbId };
} catch (err) {
return null;
}
}
export async function getStream({ id, s, e, clientIP }) {
try {
const isTv = s && e && s !== "null" && e !== "null";
const meta = await getTmdbMetadata(id, isTv);
if (!meta) return null;
let servers = [];
try {
const serverRes = await fetch(`${API_DOMAIN}/servers`, {
headers: {
"Origin": DOMAIN,
"Referer": DOMAIN + "/",
"User-Agent": UA
},
signal: AbortSignal.timeout(15000)
});
if (serverRes.ok) {
const data = await serverRes.json();
servers = data.servers || [];
}
} catch (err) { }
const activeServers = servers
.filter(srv => srv.status === "ok" && srv.name)
.map(srv => srv.name);
if (activeServers.length === 0) return null;
const allUrls = [];
for (const serverName of activeServers) {
try {
const queryParts = [
`title=${encodeURIComponent(meta.title)}`,
`type=${isTv ? "series" : "movie"}`,
`year=${meta.year}`,
`imdb=${meta.imdbId}`,
`tmdb=${id}`,
`server=${serverName}`
];
if (isTv) {
queryParts.push(`season=${s}`, `episode=${e}`);
}
const plainUrl = `${API_DOMAIN}/?${queryParts.join("&")}`;
const encRes = await fetch(`${ENC_DEC_API}/enc-cinejoy?url=${encodeURIComponent(plainUrl)}`, {
headers: { "User-Agent": UA },
signal: AbortSignal.timeout(15000)
});
if (!encRes.ok) continue;
const encData = await encRes.json();
if (encData.status !== 200) continue;
const rid = encData.result;
const challengeRes = await fetch(`${API_DOMAIN}/challenge?rid=${rid}`, {
headers: {
"Origin": DOMAIN,
"Referer": DOMAIN + "/",
"User-Agent": UA
},
signal: AbortSignal.timeout(30000)
});
if (!challengeRes.ok) continue;
const challengeData = await challengeRes.json();
const solved = solveChallenge(challengeData);
const streamRes = await fetch(`${API_DOMAIN}/${rid}`, {
headers: {
"Origin": DOMAIN,
"Referer": DOMAIN + "/",
"User-Agent": UA,
"X-At": solved
},
signal: AbortSignal.timeout(30000)
});
if (!streamRes.ok) continue;
const encryptedText = await streamRes.text();
const decRes = await fetch(`${ENC_DEC_API}/dec-cinejoy`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"User-Agent": UA
},
body: JSON.stringify({ text: encryptedText }),
signal: AbortSignal.timeout(15000)
});
if (!decRes.ok) continue;
const decData = await decRes.json();
if (decData.status !== 200) continue;
let decryptedText = decData.result;
if (!decryptedText) continue;
if (typeof decryptedText === "string") {
try {
decryptedText = JSON.parse(decryptedText);
} catch (e) { }
}
let streamUrl = "";
if (decryptedText && typeof decryptedText === "object") {
if (decryptedText.stream && decryptedText.stream[0]) {
streamUrl = decryptedText.stream[0].playlist || decryptedText.stream[0].url || "";
} else {
streamUrl = decryptedText.url || decryptedText.playlist || decryptedText.file || "";
}
} else if (typeof decryptedText === "string") {
streamUrl = decryptedText.trim();
}
if (streamUrl.startsWith("http")) {
allUrls.push({
url: streamUrl,
headers: {
"User-Agent": UA,
"Referer": DOMAIN + "/",
"Origin": DOMAIN
},
server: serverName,
quality: "Auto",
type: "hls",
skipVerify: true,
skipProxy: true
});
}
} catch (err) { }
}
if (allUrls.length > 0) {
return { ...allUrls[0], allUrls };
}
} catch (err) { }
return null;
}

View File

@@ -1,54 +1,54 @@
import { HEALTH_PROBE_ID } from "../src/config.js";
// usage: npm test
// comment out the tests you don't want to run
import VylaSDK from "../src/sdk.js";
import dotenv from "dotenv";
dotenv.config();
const BACKROOMS = '1083381';
const sdk = new VylaSDK({
tmdbApiKey: process.env.TMDB_API_KEY
});
// 'true' is excludeDisabled
const sources = sdk.getSources(true);
const source = sources.find(s => s.key === 'goated');
try {
const movieSubs = await sdk.getSubtitles("155");
console.log("Movie Subtitles Count:", movieSubs.length);
console.log("First Subtitle Sample:", movieSubs[0] || "None");
} catch (err) {
console.error("Movie Subtitles Error:", err.message);
}
const result = await sdk.getStream(source.key, BACKROOMS);
console.log("Result:", result);
try {
const movieDownloads = await sdk.getDownloads("155");
console.log("Movie Downloads:", movieDownloads);
} catch (err) {
console.error("Movie Downloads Error:", err.message);
}
// const tests = {
// probeAll(log) {
// const sources = sdk.getSources(true);
// let index = 0;
try {
const tvDownloads = await sdk.getDownloads("1399", "1", "1");
console.log("TV Downloads:", tvDownloads);
} catch (err) {
console.error("TV Downloads Error:", err.message);
}
// Promise.all(sources.map(async (source, idx) => {
// try {
// const result = await sdk.probeSource(source.key);
// index++;
// log(`(${index}/${sources.length}) source #${idx + 1} (${source.label}):`, result);
// } catch (err) {
// log(`Error probing source ${source.key}:`, err);
// }
// })).then(() => {
// log("All sources probed.");
// });
// },
// tryHealthProbe(log) {
// const sources = sdk.getSources(true);
// let index = 0;
try {
const movieResult = await sdk.getStream("cinejoy", "155");
console.log("Movie Stream:", movieResult);
} catch (err) {
console.error("Movie Stream Error:", err.message);
}
// Promise.all(sources.map(async (source, idx) => {
// try {
// const result = await sdk.getStream(source.key, HEALTH_PROBE_ID, null, null, null);
// index++;
// log(`(${index}/${sources.length}) source #${idx + 1} (${source.label}):`, result?.allUrls?.[0]?.url || result?.url || "no sources :(");
// } catch (err) {
// log(`Error probing source ${source.key}:`, err);
// }
// })).then(() => {
// log("All sources probed.");
// });
// }
// }
try {
const tvResult = await sdk.getStream("cinejoy", "1399", "1", "1");
console.log("TV Stream:", tvResult);
} catch (err) {
console.error("TV Stream Error:", err.message);
}
// tests.tryHealthProbe(console.log);
try {
const tvSubs = await sdk.getSubtitles("1399", "1", "1");
console.log("TV Subtitles Count:", tvSubs.length);
console.log("First TV Subtitle Sample:", tvSubs[0] || "None");
} catch (err) {
console.error("TV Subtitles Error:", err.message);
}