From 17f0db7ff56711ebfcac93f31ec473c50f6e5a4e Mon Sep 17 00:00:00 2001 From: Inside4ndroid Date: Fri, 26 Jul 2024 18:55:49 +0100 Subject: [PATCH] added filemoon extractor --- index.js | 39 ++++++-------- package.json | 6 +-- src/filemoon.js | 137 ++++++++++++++++++++++++++++++++++++++++++++++++ src/vidsrcto.js | 27 ++++++---- 4 files changed, 173 insertions(+), 36 deletions(-) create mode 100644 src/filemoon.js diff --git a/index.js b/index.js index 79ed815..2ba4d76 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,7 @@ import express from "express"; -import { getmovie, getserie } from "./src/vidsrcto.js"; +import { getvmovie, getvserie } from "./src/vidsrcto.js"; +import { getfmovie, getfserie } from "./src/filemoon.js"; + const port = 3000; @@ -12,7 +14,7 @@ app.get('/', (req, res) => { movie: "/vidsrc/:movieTMDBid", show: "/vidsrc/:showTMDBid?s=seasonNumber&e=episodeNumber" }, - author: "This api is developed and created by AijaZ" + author: "This api is developed and created by Inside4ndroid Studios" }); }); @@ -21,29 +23,22 @@ app.get('/vidsrc/:tmdbId', async (req, res) => { const season = req.query.s; const episode = req.query.e; - 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" - }) + try { + if (season && episode) { + const vidsrcresponse = await getvserie(id, season, episode); + const filemoonresponse = await getfserie(id, season, episode); + const combinedResponse = { ...filemoonresponse, ...vidsrcresponse }; + res.status(200).json(combinedResponse); } else { - res.status(200).json([response]); - } - } else { - const response = await getmovie(id); - - if (!response) { - res.status(404).send({ - status: 404, - return: "Oops reached rate limit of this api" - }) - } else { - res.status(200).json([response]); + const vidsrcresponse = await getvmovie(id); + const filemoonresponse = await getfmovie(id); + const combinedResponse = { ...filemoonresponse, ...vidsrcresponse }; + res.status(200).json(combinedResponse); } + } catch (error) { + console.error('Error fetching data:', error); + res.status(500).json({ error: 'Failed to fetch data' }); } - }); app.listen(port, () => { diff --git a/package.json b/package.json index 390e86b..af92bcb 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "vidsrc-api-v2", "version": "1.0.0", - "description": "An api that scrapes vidsrc.to and vidsrc.me", + "description": "An api that scrapes vidsrc (f2cloud, filemoon) plus subtitles", "main": "index.js", "type": "module", "scripts": { @@ -11,7 +11,7 @@ "author": "Inside4ndroid Studios Ltd", "license": "ISC", "dependencies": { - "express": "^4.18.2" - + "express": "^4.18.2", + "unpacker": "^1.0.1" } } diff --git a/src/filemoon.js b/src/filemoon.js new file mode 100644 index 0000000..78843e4 --- /dev/null +++ b/src/filemoon.js @@ -0,0 +1,137 @@ +import { getKeys } from './getKeys.js'; +import { unpack } from 'unpacker'; + +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]; +} + +function enc(inp) { + inp = encodeURIComponent(inp); + const e = rc4(keys[0], inp); + const out = btoa(e).replace(/\//g, '_').replace(/\+/g, '-'); + return out; +} + +function dec(inp) { + const i = atob(inp.replace(/_/g, '/').replace(/-/g, '+')); + let e = rc4(keys[3], 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 fetch(url); + resp = await resp.json(); + + let filemoon_id = resp.result[1].id; + url = `https://vidsrc.to/ajax/embed/source/${filemoon_id}?token=${encodeURIComponent(enc(filemoon_id))}`; + resp = await fetch(url); + resp = await resp.json(); + + let filemoon_url = resp.result.url; + let filemoon_url_dec = dec(filemoon_url); + const parsedUrl = new URL(filemoon_url_dec); + + const SRC_URL = parsedUrl.origin + parsedUrl.pathname; + const queryParams = parsedUrl.search.substring(1); + const params = new URLSearchParams(queryParams); + const subtitleLink = params.get('sub.info'); + + console.log(SRC_URL); + console.log(subtitleLink); + + const data = await fetch(SRC_URL); + const dataText = await data.text(); + const match = /eval\(function\(p,a,c,k,e,d\).+\)\)\)/g.exec(dataText); + + if (match) { + const packed = match[0]; + const unpacked = unpack(packed); + const hlsMatch = /sources:\[\s*{\s*file:"([^"]*)"/g.exec(unpacked); + const subs = await getSubtitles(subtitleLink); + const sources = hlsMatch[1]; + + if (!sources) { + const data = { + source: null, + subtitles: null + }; + return { filemoon: data }; + } else { + const data = { + source: sources, + subtitles: subs + }; + return { filemoon: data }; + } + } else { + console.error('Packed data not found'); + } +} + +async function getSubtitles(subtitleLink) { + if (subtitleLink) { + const subtitlesFetch = await fetch(subtitleLink); + const subtitles = await subtitlesFetch.json(); + + return subtitles.map(subtitle => ({ + file: subtitle.file, + lang: subtitle.label, + })); + } else { + console.error('Subtitle link not found in params'); + return null; + } +} + +export async function getfmovie(id) { + let resp = await fetch(`https://vidsrc.to/embed/movie/${id}`); + let respText = await resp.text(); + const match = /data-id="(.*?)"/g.exec(respText); + + if (match) { + let data_id = match[1]; + return episode(data_id); + } else { + console.error('Data ID not found'); + } +} + +export async function getfserie(id, s, e) { + let resp = await (await fetch(`https://vidsrc.to/embed/tv/${id}/${s}/${e}`)).text(); + let match = (/data-id="(.*?)"/g).exec(resp)[1]; + if (match) { + let data_id = match[1]; + return episode(data_id); + } else { + console.error('Data ID not found'); + } + } diff --git a/src/vidsrcto.js b/src/vidsrcto.js index 664d2dd..961a968 100644 --- a/src/vidsrcto.js +++ b/src/vidsrcto.js @@ -71,14 +71,19 @@ 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']; + + console.log(f2cloud_id); + url = `https://vidsrc.to/ajax/embed/source/${f2cloud_id}?token=${encodeURIComponent(enc(f2cloud_id))}`; resp = await (await fetch(url)).json(); let f2cloud_url = resp['result']['url']; let f2cloud_url_dec = dec(f2cloud_url); - const subtitles = await getSubtitles(f2cloud_url_dec); + const subs = await getSubtitles(f2cloud_url_dec); url = new URL(f2cloud_url_dec); let embed_id = url.pathname.split("/")[2]; @@ -91,24 +96,24 @@ async function episode(data_id) { playlist = JSON.parse(playlist); } - const source = playlist.sources?.[0]?.file; + const sources = playlist.sources?.[0]?.file; - if (!source) { + if (!sources) { const data = { - file: null, - sub: subtitles + source: null, + subtitles: null }; - return { data: data }; + return { vidsrc: data }; } else { const data = { - file: source, - sub: subtitles + source: sources, + subtitles: subs }; - return { data: data }; + return { vidsrc: data }; } } -export async function getmovie(id) { +export async function getvmovie(id) { let resp = await (await fetch(`https://vidsrc.to/embed/movie/${id}`)).text(); const match = (/data-id="(.*?)"/g).exec(resp); @@ -116,7 +121,7 @@ export async function getmovie(id) { return episode(data_id); } -export async function getserie(id, s, e) { +export async function getvserie(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);