mirror of
https://github.com/Inside4ndroid/TMDB-Embed-API.git
synced 2026-10-22 09:35:11 +00:00
FIXED :)
This commit is contained in:
@@ -1,5 +1,3 @@
|
||||
# [NOT WORKING - NEEDS WORK]
|
||||
|
||||
### USAGE
|
||||
- base url:
|
||||
https://your-vercel-domain
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import express from "express";
|
||||
import { getVidsrcSourcesId, getVidsrctoStreams } from "./src/vidsrcto.js";
|
||||
import { port } from "./src/constants.js";
|
||||
import { getmovie, getserie } from "./src/vidsrcto.js";
|
||||
|
||||
|
||||
const app = express()
|
||||
|
||||
@@ -20,18 +21,29 @@ app.get('/vidsrc/:tmdbId', async (req, res) => {
|
||||
const season = req.query.s;
|
||||
const episode = req.query.e;
|
||||
|
||||
const sourcesId = await getVidsrcSourcesId(id, season, episode);
|
||||
const response = await getVidsrctoStreams(sourcesId);
|
||||
const responseData = response.data;
|
||||
if (responseData.file === null) {
|
||||
res.status(404).send({
|
||||
status: 404,
|
||||
return: "Oops reached rate limit of this api"
|
||||
})
|
||||
if (season && episode) {
|
||||
const response = await getserie(id, season, episode);
|
||||
if (!response) {
|
||||
res.status(404).send({
|
||||
status: 404,
|
||||
return: "Oops reached rate limit of this api"
|
||||
})
|
||||
} else {
|
||||
res.status(200).json([response]);
|
||||
}
|
||||
} else {
|
||||
const response = await getmovie(id);
|
||||
|
||||
res.status(200).json([response]);
|
||||
if (!response) {
|
||||
res.status(404).send({
|
||||
status: 404,
|
||||
return: "Oops reached rate limit of this api"
|
||||
})
|
||||
} else {
|
||||
res.status(200).json([response]);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
app.listen(port, () => {
|
||||
|
||||
Generated
+91
@@ -13,6 +13,7 @@
|
||||
"express": "^4.18.2",
|
||||
"html-entities": "^2.4.0",
|
||||
"jsdom": "^24.0.0",
|
||||
"node-fetch": "^3.3.2",
|
||||
"random-useragent": "^0.5.0"
|
||||
}
|
||||
},
|
||||
@@ -235,6 +236,15 @@
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/data-uri-to-buffer": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
|
||||
"integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 12"
|
||||
}
|
||||
},
|
||||
"node_modules/data-urls": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz",
|
||||
@@ -427,6 +437,29 @@
|
||||
"node": ">= 0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/fetch-blob": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
|
||||
"integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/jimmywarting"
|
||||
},
|
||||
{
|
||||
"type": "paypal",
|
||||
"url": "https://paypal.me/jimmywarting"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"node-domexception": "^1.0.0",
|
||||
"web-streams-polyfill": "^3.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.20 || >= 14.13"
|
||||
}
|
||||
},
|
||||
"node_modules/finalhandler": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
|
||||
@@ -457,6 +490,18 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/formdata-polyfill": {
|
||||
"version": "4.0.10",
|
||||
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
|
||||
"integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fetch-blob": "^3.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/forwarded": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
||||
@@ -812,6 +857,43 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/node-domexception": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
|
||||
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/jimmywarting"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://paypal.me/jimmywarting"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/node-fetch": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz",
|
||||
"integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"data-uri-to-buffer": "^4.0.0",
|
||||
"fetch-blob": "^3.1.4",
|
||||
"formdata-polyfill": "^4.0.10"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/node-fetch"
|
||||
}
|
||||
},
|
||||
"node_modules/nth-check": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
|
||||
@@ -1197,6 +1279,15 @@
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/web-streams-polyfill": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
|
||||
"integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/webidl-conversions": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
"express": "^4.18.2",
|
||||
"html-entities": "^2.4.0",
|
||||
"jsdom": "^24.0.0",
|
||||
"node-fetch": "^3.3.2",
|
||||
"random-useragent": "^0.5.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import fetch from "node-fetch";
|
||||
|
||||
export const getKeys = async () => {
|
||||
const url = "https://raw.githubusercontent.com/Ciarands/vidsrc-keys/main/keys.json";
|
||||
|
||||
try {
|
||||
const response = await fetch(url);
|
||||
if (response.status !== 200) {
|
||||
console.error("Failed to fetch decryption keys:", response.statusText);
|
||||
return null;
|
||||
}
|
||||
|
||||
const keys = await response.json();
|
||||
|
||||
if (keys && keys.encrypt && keys.decrypt) {
|
||||
const allKeys = [...keys.encrypt, ...keys.decrypt];
|
||||
console.log("All keys:", allKeys);
|
||||
return allKeys;
|
||||
} else {
|
||||
console.error("Keys object is empty or malformed");
|
||||
return null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error:", error.message);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
+119
-187
@@ -1,113 +1,132 @@
|
||||
import { load } from "cheerio";
|
||||
import { decode } from "html-entities";
|
||||
import randomUserAgent from 'random-useragent';
|
||||
|
||||
import { vidsrcBase, vidplayBase } from "./constants.js";
|
||||
|
||||
export async function getVidsrctoStreams(sourcesId) {
|
||||
|
||||
let vidplay;
|
||||
|
||||
const sources = await getVidsrcSources(sourcesId);
|
||||
|
||||
console.log(sources);
|
||||
|
||||
try {
|
||||
vidplay = sources.result.find((v) => v.title.toLowerCase() === 'f2cloud');
|
||||
} catch (error) {
|
||||
return { data: sources };
|
||||
}
|
||||
|
||||
|
||||
if (!vidplay) {
|
||||
const data = {
|
||||
file: sources
|
||||
};
|
||||
return { data: data };
|
||||
}
|
||||
|
||||
const vidplayLink = await getVidsrcSourceDetails(vidplay.id);
|
||||
|
||||
const key = await encodeId(vidplayLink.split('/e/')[1].split('?')[0]);
|
||||
const dataFutoken = await getFutoken(key, vidplayLink);
|
||||
|
||||
const subtitles = await getSubtitles(vidplayLink);
|
||||
|
||||
const response = await (await fetch(`https://vidplay.online/mediainfo/${dataFutoken}?${vidplayLink.split('?')[1]}&autostart=true`, {
|
||||
headers: {
|
||||
"Origin": generateRandomIp(),
|
||||
"Referer": vidplayLink,
|
||||
"Host": "vidplay.online",
|
||||
"User-Agent": randomUserAgent.getRandom()
|
||||
}
|
||||
})).json();
|
||||
|
||||
const result = response.result;
|
||||
const source = result.sources?.[0]?.file;
|
||||
|
||||
if (!source) {
|
||||
const data = {
|
||||
file: null,
|
||||
sub: subtitles
|
||||
};
|
||||
return { data: data };
|
||||
} else {
|
||||
const data = {
|
||||
file: source,
|
||||
sub: subtitles
|
||||
};
|
||||
return { data: data };
|
||||
}
|
||||
|
||||
import fetch from 'node-fetch';
|
||||
import { getKeys } from './getKeys.js';
|
||||
|
||||
const keys = await getKeys();
|
||||
|
||||
function rc4(key, inp) {
|
||||
let e = [];
|
||||
e[4] = [];
|
||||
e[3] = 0;
|
||||
let i = 0;
|
||||
e[8] = "";
|
||||
for (i = 0; i < 256; i++) {
|
||||
e[4][i] = i;
|
||||
}
|
||||
for (i = 0; i < 256; i++) {
|
||||
e[3] = (e[3] + e[4][i] + key.charCodeAt(i % key.length)) % 256;
|
||||
e[2] = e[4][i];
|
||||
e[4][i] = e[4][e[3]];
|
||||
e[4][e[3]] = e[2];
|
||||
}
|
||||
i = 0;
|
||||
e[3] = 0;
|
||||
let j = 0;
|
||||
for (j = 0; j < inp.length; j++) {
|
||||
i = (i + 1) % 256;
|
||||
e[3] = (e[3] + e[4][i]) % 256;
|
||||
e[2] = e[4][i];
|
||||
e[4][i] = e[4][e[3]];
|
||||
e[4][e[3]] = e[2];
|
||||
e[8] += String.fromCharCode(inp.charCodeAt(j) ^ e[4][(e[4][i] + e[4][e[3]]) % 256]);
|
||||
}
|
||||
return e[8];
|
||||
}
|
||||
|
||||
export async function getVidsrcSourcesId(tmdbId, seasonNumber, episodeNumber) {
|
||||
const type = seasonNumber && episodeNumber ? "tv" : "movie";
|
||||
const url = `${vidsrcBase}/embed/${type}/${tmdbId}${type === "tv" ? `/${seasonNumber}/${episodeNumber}` : ''}`
|
||||
try {
|
||||
const data = await (await fetch(url)).text();
|
||||
|
||||
const doc = load(data);
|
||||
const sourcesCode = doc('a[data-id]').attr('data-id');
|
||||
|
||||
return sourcesCode;
|
||||
} catch (err) {
|
||||
return;
|
||||
}
|
||||
function enc(inp) {
|
||||
inp = encodeURIComponent(inp);
|
||||
const e = rc4(keys[0], inp);
|
||||
const out = btoa(e).replace(/\//g, "_").replace(/\+/g, '-');
|
||||
return out;
|
||||
}
|
||||
|
||||
async function getVidsrcSources(sourceId) {
|
||||
try {
|
||||
const response = await fetch(`${vidsrcBase}/ajax/embed/episode/${sourceId}/sources`);
|
||||
|
||||
if (!response.ok) {
|
||||
return 'No Sources Found';
|
||||
}
|
||||
|
||||
const contentType = response.headers.get("content-type");
|
||||
if (contentType && contentType.includes("application/json")) {
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} else {
|
||||
const text = await response.text();
|
||||
console.error("Unexpected content type or response:", text);
|
||||
return 'Unexpected content type or response';
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error fetching or parsing JSON:", error);
|
||||
return null; // or handle the error as needed
|
||||
}
|
||||
function embed_enc(inp) {
|
||||
inp = encodeURIComponent(inp);
|
||||
const e = rc4(keys[1], inp);
|
||||
const out = btoa(e).replace(/\//g, "_").replace(/\+/g, '-');
|
||||
return out;
|
||||
}
|
||||
|
||||
async function getVidsrcSourceDetails(sourceId) {
|
||||
const data = await (await fetch(`${vidsrcBase}/ajax/embed/source/${sourceId}`)).json();
|
||||
function h_enc(inp) {
|
||||
inp = encodeURIComponent(inp);
|
||||
const e = rc4(keys[2], inp);
|
||||
const out = btoa(e).replace(/\//g, "_").replace(/\+/g, '-');
|
||||
return out;
|
||||
}
|
||||
|
||||
const encryptedUrl = data.result.url;
|
||||
const decryptedUrl = decryptSourceUrl(encryptedUrl);
|
||||
return decodeURIComponent(decryptedUrl);
|
||||
function dec(inp) {
|
||||
const i = atob((inp).replace(/_/g, "/").replace(/-/g, "+"));
|
||||
let e = rc4(keys[3], i);
|
||||
e = decodeURIComponent(e);
|
||||
return e;
|
||||
}
|
||||
|
||||
function embed_dec(inp) {
|
||||
const i = atob((inp).replace(/_/g, "/").replace(/-/g, "+"));
|
||||
let e = rc4(keys[4], i);
|
||||
e = decodeURIComponent(e);
|
||||
return e;
|
||||
}
|
||||
|
||||
async function episode(data_id) {
|
||||
let url = `https://vidsrc.to/ajax/embed/episode/${data_id}/sources?token=${encodeURIComponent(enc(data_id))}`;
|
||||
let resp = await (await fetch(url)).json();
|
||||
|
||||
console.log(resp);
|
||||
|
||||
let f2cloud_id = resp['result'][0]['id'];
|
||||
url = `https://vidsrc.to/ajax/embed/source/${f2cloud_id}?token=${encodeURIComponent(enc(f2cloud_id))}`;
|
||||
resp = await (await fetch(url)).json();
|
||||
|
||||
console.log(resp);
|
||||
|
||||
let f2cloud_url = resp['result']['url'];
|
||||
let f2cloud_url_dec = dec(f2cloud_url);
|
||||
|
||||
console.log(f2cloud_url_dec);
|
||||
|
||||
const subtitles = await getSubtitles(f2cloud_url_dec);
|
||||
|
||||
url = new URL(f2cloud_url_dec);
|
||||
let embed_id = url.pathname.split("/")[2];
|
||||
let h = h_enc(embed_id);
|
||||
let mediainfo_url = `https://vid2v11.site/mediainfo/${embed_enc(embed_id)}${url.search}&ads=0&h=${encodeURIComponent(h)}`;
|
||||
resp = await (await fetch(mediainfo_url)).json();
|
||||
|
||||
console.log(resp);
|
||||
|
||||
let playlist = embed_dec(resp['result']);
|
||||
if (typeof playlist === 'string') {
|
||||
playlist = JSON.parse(playlist);
|
||||
}
|
||||
|
||||
const source = playlist.sources?.[0]?.file;
|
||||
|
||||
if (!source) {
|
||||
const data = {
|
||||
file: null,
|
||||
sub: subtitles
|
||||
};
|
||||
return { data: data };
|
||||
} else {
|
||||
const data = {
|
||||
file: source,
|
||||
sub: subtitles
|
||||
};
|
||||
return { data: data };
|
||||
}
|
||||
}
|
||||
|
||||
export async function getmovie(id) {
|
||||
let resp = await (await fetch(`https://vidsrc.to/embed/movie/${id}`)).text();
|
||||
let data_id = (/data-id="(.*?)"/g).exec(resp)[1];
|
||||
return episode(data_id);
|
||||
}
|
||||
|
||||
export async function getserie(id, s, e) {
|
||||
let resp = await (await fetch(`https://vidsrc.to/embed/tv/${id}/${s}/${e}`)).text();
|
||||
let data_id = (/data-id="(.*?)"/g).exec(resp)[1];
|
||||
return episode(data_id);
|
||||
}
|
||||
|
||||
async function getSubtitles(vidplayLink) {
|
||||
@@ -123,90 +142,3 @@ async function getSubtitles(vidplayLink) {
|
||||
return subtitles;
|
||||
}
|
||||
}
|
||||
|
||||
function adecode(str) {
|
||||
const keyBytes = Buffer.from('WXrUARXb1aDLaZjI', 'utf-8');
|
||||
let j = 0;
|
||||
const s = Buffer.from(Array(256).fill().map((_, i) => i));
|
||||
|
||||
for (let i = 0; i < 256; i++) {
|
||||
j = (j + s[i] + keyBytes[i % keyBytes.length]) & 0xff;
|
||||
[s[i], s[j]] = [s[j], s[i]];
|
||||
}
|
||||
|
||||
const decoded = Buffer.alloc(str.length);
|
||||
let i = 0;
|
||||
let k = 0;
|
||||
|
||||
for (let index = 0; index < str.length; index++) {
|
||||
i = (i + 1) & 0xff;
|
||||
k = (k + s[i]) & 0xff;
|
||||
[s[i], s[k]] = [s[k], s[i]];
|
||||
const t = (s[i] + s[k]) & 0xff;
|
||||
decoded[index] = str[index] ^ s[t];
|
||||
}
|
||||
|
||||
return decoded;
|
||||
}
|
||||
|
||||
function decodeBase64UrlSafe(s) {
|
||||
const standardizedInput = s.replace('_', '/').replace('-', '+');
|
||||
const binaryData = Buffer.from(standardizedInput, 'base64');
|
||||
|
||||
return Buffer.from(binaryData);
|
||||
}
|
||||
|
||||
function decryptSourceUrl(sourceUrl) {
|
||||
const encoded = decodeBase64UrlSafe(sourceUrl);
|
||||
const decoded = adecode(encoded);
|
||||
|
||||
const decodedText = decoded.toString('utf-8');
|
||||
return decode(decodedText);
|
||||
}
|
||||
|
||||
async function encodeId(v_id) {
|
||||
const resp = await (await fetch('https://raw.githubusercontent.com/Inside4ndroid/vidkey-js/main/keys.json')).json();
|
||||
const [key1, key2] = resp;
|
||||
const decoded_id = keyPermutation(key1, v_id).toString('latin1');
|
||||
const encoded_result = keyPermutation(key2, decoded_id).toString('latin1');
|
||||
const encoded_base64 = btoa(encoded_result);
|
||||
return encoded_base64.replace('/', '_');
|
||||
}
|
||||
|
||||
async function getFutoken(key, url) {
|
||||
const response = await (await fetch(`${vidplayBase}/futoken`, { headers: { "Referer": `${url}/` } })).text();
|
||||
const fuKey = response.match(/var\s+k\s*=\s*'([^']+)'/)[1];
|
||||
const fuToken = `${fuKey},${Array.from({ length: key.length }, (_, i) => (fuKey.charCodeAt(i % fuKey.length) + key.charCodeAt(i)).toString()).join(',')}`;
|
||||
return fuToken;
|
||||
}
|
||||
|
||||
const generateRandomIp = () => {
|
||||
return (Math.floor(Math.random() * 255) + 1) + "." + (Math.floor(Math.random() * 255)) + "." + (Math.floor(Math.random() * 255)) + "." + (Math.floor(Math.random() * 255));
|
||||
}
|
||||
|
||||
function keyPermutation(key, data) {
|
||||
var state = Array.from(Array(256).keys());
|
||||
var index_1 = 0;
|
||||
for (var i = 0; i < 256; i++) {
|
||||
index_1 = ((index_1 + state[i]) + key.charCodeAt(i % key.length)) % 256;
|
||||
var temp = state[i];
|
||||
state[i] = state[index_1];
|
||||
state[index_1] = temp;
|
||||
}
|
||||
var index_1 = 0;
|
||||
var index_2 = 0;
|
||||
var final_key = '';
|
||||
for (var char = 0; char < data.length; char++) {
|
||||
index_1 = (index_1 + 1) % 256;
|
||||
index_2 = (index_2 + state[index_1]) % 256;
|
||||
var temp = state[index_1];
|
||||
state[index_1] = state[index_2];
|
||||
state[index_2] = temp;
|
||||
if (typeof data[char] === 'string') {
|
||||
final_key += String.fromCharCode(data[char].charCodeAt(0) ^ state[(state[index_1] + state[index_2]) % 256]);
|
||||
} else if (typeof data[char] === 'number') {
|
||||
final_key += String.fromCharCode(data[char] ^ state[(state[index_1] + state[index_2]) % 256]);
|
||||
}
|
||||
}
|
||||
return final_key;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user