mirror of
https://github.com/Inside4ndroid/TMDB-Embed-API.git
synced 2026-10-22 09:35:11 +00:00
first commit
This commit is contained in:
@@ -0,0 +1,25 @@
|
|||||||
|
### USAGE
|
||||||
|
- base url:
|
||||||
|
https://vidsrc-api-two.app
|
||||||
|
|
||||||
|
- endpoint:
|
||||||
|
- `/vidsrc/{db_id}`
|
||||||
|
|
||||||
|
- parameters:
|
||||||
|
- `s` - season (series only)
|
||||||
|
- `e` - episodes (series only)
|
||||||
|
|
||||||
|
### EXAMPLE MOVIE
|
||||||
|
```
|
||||||
|
https://vidsrc-api-two.app/vidsrc/916224
|
||||||
|
```
|
||||||
|
|
||||||
|
### EXAMPLE SHOW
|
||||||
|
```
|
||||||
|
https://vidsrc-api-two.app/vidsrc/1429?s=1&e=1
|
||||||
|
```
|
||||||
|
|
||||||
|
### Vercel
|
||||||
|
Host your own instance of vidsrc-api on Vercel using the button below.
|
||||||
|
|
||||||
|
[](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FAijazmakerb%2Fvidsrc-api)
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import express from "express";
|
||||||
|
import { getVidsrcSourcesId, getVidsrctoStreams } from "./src/vidsrcto.js";
|
||||||
|
import { port } from "./src/constants.js";
|
||||||
|
|
||||||
|
const app = express()
|
||||||
|
|
||||||
|
app.get('/', (req, res) => {
|
||||||
|
res.status(200).json({
|
||||||
|
intro: "Welcome to the unofficial vidsrc provider: check the provider website @ https://vidsrc.to/ ",
|
||||||
|
routes: {
|
||||||
|
movie: "/vidsrc/:movieTMDBid",
|
||||||
|
show: "/vidsrc/:showTMDBid?s=seasonNumber&e=episodeNumber"
|
||||||
|
},
|
||||||
|
author: "This api is developed and created by AijaZ"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get('/vidsrc/:tmdbId', async (req, res) => {
|
||||||
|
const id = req.params.tmdbId;
|
||||||
|
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"
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
|
||||||
|
res.status(200).json([response]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.listen(port, () => {
|
||||||
|
console.log(`Example app listening on port http://localhost:${port}`);
|
||||||
|
});
|
||||||
Generated
+1284
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"name": "vidsrc-api-v2",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "An api that scrapes vidsrc.to and vidsrc.me",
|
||||||
|
"main": "index.js",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
|
"main": "node --watch index.js"
|
||||||
|
},
|
||||||
|
"author": "Inside4ndroid Studios Ltd",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"cheerio": "^1.0.0-rc.12",
|
||||||
|
"express": "^4.18.2",
|
||||||
|
"html-entities": "^2.4.0",
|
||||||
|
"jsdom": "^24.0.0",
|
||||||
|
"random-useragent": "^0.5.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export const vidsrcBase = 'https://vidsrc.to'
|
||||||
|
export const vidplayBase = 'https://vidplay.online';
|
||||||
|
export const port = 3000;
|
||||||
+186
@@ -0,0 +1,186 @@
|
|||||||
|
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) {
|
||||||
|
const sources = await getVidsrcSources(sourcesId);
|
||||||
|
|
||||||
|
console.log(sources);
|
||||||
|
|
||||||
|
const vidplay = sources.result.find((v) => v.title.toLowerCase() === 'f2cloud');
|
||||||
|
|
||||||
|
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 };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getVidsrcSources(sourceId) {
|
||||||
|
const data = await (await fetch(`${vidsrcBase}/ajax/embed/episode/${sourceId}/sources`)).json();
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getVidsrcSourceDetails(sourceId) {
|
||||||
|
const data = await (await fetch(`${vidsrcBase}/ajax/embed/source/${sourceId}`)).json();
|
||||||
|
|
||||||
|
const encryptedUrl = data.result.url;
|
||||||
|
const decryptedUrl = decryptSourceUrl(encryptedUrl);
|
||||||
|
return decodeURIComponent(decryptedUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getSubtitles(vidplayLink) {
|
||||||
|
if (vidplayLink.includes('sub.info=')) {
|
||||||
|
const subtitleLink = vidplayLink.split('?sub.info=')[1].split('&')[0];
|
||||||
|
const subtitlesFetch = await (await fetch(decodeURIComponent(subtitleLink))).json();
|
||||||
|
|
||||||
|
const subtitles = subtitlesFetch.map(subtitle => ({
|
||||||
|
file: subtitle.file,
|
||||||
|
lang: subtitle.label
|
||||||
|
}));
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"version": 2,
|
||||||
|
"builds": [
|
||||||
|
{
|
||||||
|
"src": "index.js",
|
||||||
|
"use": "@vercel/node"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"routes": [
|
||||||
|
{
|
||||||
|
"src": "/(.*)",
|
||||||
|
"dest": "index.js",
|
||||||
|
"headers": {
|
||||||
|
"Access-Control-Allow-Origin": "*"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"installCommand": "npm install"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user