├── .env.example ├── .gitignore ├── LICENSE ├── README.md ├── api └── index.ts ├── config ├── axois.ts ├── server.ts └── vidstream.ts ├── controllers ├── homePage.controller.ts ├── index.ts ├── movieDetails.controller.ts ├── movieEpisodeServers.controller.ts ├── movieEpisodeSources.controller.ts ├── movieEpisodes.controller.ts ├── movieSeasons.controller.ts └── search.controller.ts ├── index.ts ├── middleware └── cors.ts ├── package.json ├── parsers ├── index.ts └── rabbitstream.ts ├── public ├── bg.png ├── index.css └── index.html ├── routes └── index.ts ├── types ├── common.ts └── controllers │ ├── homePage.ts │ ├── movieDetails.ts │ ├── movieEpisodeServers.ts │ ├── movieEpisodeSources.ts │ ├── movieEpisodes.ts │ └── search.ts ├── utils └── extractMovieTvSeriesItem.ts └── vercel.json /.env.example: -------------------------------------------------------------------------------- 1 | PORT=4001 2 | 3 | # Origins as a string 4 | CORS_ORIGIN="*" -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json 3 | 4 | .env -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Koert Weber 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vidstream API 2 | This project includes: 3 | - A scraper for the website "vidstream.to" 4 | - A parser for the stream provider "rabbitstream.net" 5 | 6 | > [!IMPORTANT] 7 | > 8 | > 1. This API is just an unofficial api for [vidstream.to](https://vidstream.to) and is in no other way officially related to the same. 9 | > 2. The content that this api provides is not mine, nor is it hosted by me. These belong to their respective owners. This api just demonstrates how to build an api that scrapes websites and uses their content. 10 | 11 | ## Contents 12 | 13 | - [Installation](#installation) 14 | - [3rd Party Hosting](#3rd-party-hosting) 15 | - [API Documentation](#api-documentation) 16 | - [GET Home Page](#get-home-page) 17 | - [GET Search](#get-search) 18 | - [GET Movie Details](#get-movie-details) 19 | - [GET Movie Seasons](#get-movie-seasons) 20 | - [GET Movie Episodes](#get-movie-episodes) 21 | - [GET Movie Episode Servers](#get-movie-episode-servers) 22 | - [Contributors](#contributors) 23 | - [Special Thanks](#thanks-to-) 24 | 25 | ## Installation 26 | To get this project on your local machine run the following command. 27 | ``` 28 | git clone https://github.com/WBRK-dev/vidstream-api.git 29 | ``` 30 | After that go into the new directory and install all the dependencies. 31 | ``` 32 | npm install 33 | ``` 34 | When you are done with installing run the serve command. 35 | ``` 36 | npm start 37 | ``` 38 | Now the api is accessible through `localhost:4030`. 39 | 40 | ## 3rd Party Hosting 41 | 42 | [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/WBRK-dev/vidstream-api) 43 | 44 | ## API Documentation 45 | 46 |
47 | 48 | 49 | ### `GET` Home Page 50 | 51 | 52 | 53 | ``` 54 | /home 55 | ``` 56 | ```javascript 57 | { 58 | spotlight: [ 59 | { 60 | id: string, 61 | title: string, 62 | banner: string, 63 | poster: string, 64 | rating: string, 65 | year: string, 66 | }, 67 | { ... } 68 | ], 69 | trending: { 70 | movies: [ 71 | { 72 | id: string, 73 | title: string, 74 | poster: string, 75 | stats: { 76 | duration: string, 77 | rating: string, 78 | year: string, 79 | } 80 | }, 81 | { ... } 82 | ], 83 | tvSeries: [ 84 | { 85 | id: string, 86 | title: string, 87 | poster: string, 88 | stats: { 89 | seasons: string, 90 | rating: string, 91 | } 92 | }, 93 | { ... } 94 | ] 95 | }, 96 | latestMovies: [ 97 | { 98 | id: string, 99 | title: string, 100 | poster: string, 101 | stats: { 102 | duration: string, 103 | rating: string, 104 | year: string, 105 | } 106 | }, 107 | { ... } 108 | ], 109 | latestTvSeries: [ 110 | { 111 | id: string, 112 | title: string, 113 | poster: string, 114 | stats: { 115 | seasons: string, 116 | rating: string, 117 | } 118 | }, 119 | { ... } 120 | ] 121 | } 122 | ``` 123 | 124 |
125 | 126 |
127 | 128 | 129 | 130 | ### `GET` Search 131 | 132 | 133 | 134 | ``` 135 | /search?q={searchQuery}&page={pageIndex} 136 | ``` 137 | | Parameter | Type | Description | Required? | Default | 138 | | :------------------: | :----: | :-----------------------------------: | :-------: | :-----: | 139 | | `searchQuery` | string | The search string. E.g. "family guy". | Yes | -- | 140 | | `pageIndex` | number | The index of the page. | No | 1 | 141 | ```javascript 142 | { 143 | items: [ 144 | { 145 | id: string, 146 | title: string, 147 | poster: string, 148 | stats: { 149 | duration: string, 150 | rating: string, 151 | year: string, 152 | } 153 | }, 154 | { 155 | id: string, 156 | title: string, 157 | poster: string, 158 | stats: { 159 | seasons: string, 160 | rating: string, 161 | } 162 | }, 163 | { ... } 164 | ], 165 | pagination: { 166 | current: number, 167 | total: number, 168 | } 169 | } 170 | ``` 171 | 172 |
173 | 174 |
175 | 176 | 177 | 178 | ### `GET` Movie Details 179 | 180 | 181 | 182 | ``` 183 | /movie/{movieId} 184 | ``` 185 | 186 | | Parameter | Type | Description | Required? | Default | 187 | | :----------------: | :----: | :---------------------------------: | :-------: | :-----: | 188 | | `movieId` | string | The movie id given in e.g. `/home`. | Yes | -- | 189 | 190 |

episodeId is only available when type is equal to movie and only has one episode.

191 | 192 | ```javascript 193 | { 194 | title: string, 195 | description: string, 196 | type: "movie" | "tvSeries", 197 | stats: { name: string, value: string | string[] }[], 198 | episodeId?: string, 199 | related: [ 200 | { 201 | id: string, 202 | title: string, 203 | poster: string, 204 | stats: { 205 | seasons: string, 206 | rating: string, 207 | } 208 | }, 209 | { 210 | id: string, 211 | title: string, 212 | poster: string, 213 | stats: { 214 | year: string, 215 | duration: string, 216 | rating: string, 217 | } 218 | }, 219 | { ... } 220 | ] 221 | } 222 | ``` 223 | 224 |
225 | 226 |
227 | 228 | 229 | 230 | ### `GET` Movie Seasons 231 | 232 | 233 | 234 | ``` 235 | /movie/{movieId}/seasons 236 | ``` 237 | | Parameter | Type | Description | Required? | Default | 238 | | :----------------: | :----: | :---------------------------------: | :-------: | :-----: | 239 | | `movieId` | string | The movie id given in e.g. `/home`. | Yes | -- | 240 | ```javascript 241 | { 242 | seasons: [ 243 | { 244 | id: string, 245 | number: number, 246 | }, 247 | { ... } 248 | ] 249 | } 250 | ``` 251 | 252 |
253 | 254 |
255 | 256 | 257 | 258 | ### `GET` Movie Episodes 259 | 260 | 261 | 262 | ``` 263 | /movie/{movieId}/episodes?seasonId={seasonId} 264 | ``` 265 | | Parameter | Type | Description | Required? | Default | 266 | | :-----------------: | :----: | :------------------------------------------------: | :-------: | :-----: | 267 | | `movieId` | string | The movie id given in e.g. `/home`. | Yes | -- | 268 | | `seasonId` | string | The season id given in `/movie/{movieId}/seasons`. | Yes | -- | 269 | ```javascript 270 | { 271 | episodes: [ 272 | { 273 | id: string, 274 | number: number, 275 | title: string, 276 | }, 277 | { ... } 278 | ] 279 | } 280 | ``` 281 | 282 |
283 | 284 |
285 | 286 | 287 | 288 | ### `GET` Movie Episode Servers 289 | 290 | 291 | 292 | ``` 293 | /movie/{movieId}/servers?episodeId={episodeId} 294 | ``` 295 | | Parameter | Type | Description | Required? | Default | 296 | | :------------------: | :----: | :-------------------------------------------------------: | :-------: | :-----: | 297 | | `movieId` | string | The movie id given in e.g. `/home`. | Yes | -- | 298 | | `episodeId` | string | The episode id given in e.g. `/movie/{movieId}/episodes`. | Yes | -- | 299 | ```javascript 300 | { 301 | servers: [ 302 | { 303 | id: string, 304 | name: string, 305 | }, 306 | { ... } 307 | ] 308 | } 309 | ``` 310 | 311 |
312 | 313 |
314 | 315 | 316 | 317 | ### `GET` Movie Episode Sources 318 | 319 | 320 | 321 | ``` 322 | /movie/{movieId}/sources?serverId={serverId} 323 | ``` 324 | | Parameter | Type | Description | Required? | Default | 325 | | :-----------------: | :----: | :------------------------------------------------: | :-------: | :-----: | 326 | | `movieId` | string | The movie id given in e.g. `/home`. | Yes | -- | 327 | | `serverId` | string | The server id given in `/movie/{movieId}/servers`. | Yes | -- | 328 | ```javascript 329 | { 330 | sources: [ 331 | { 332 | src: string, 333 | type: string, 334 | }, 335 | { ... } 336 | ], 337 | tracks: [ 338 | { 339 | file: string, 340 | label: string, 341 | kind: string, 342 | default?: string, 343 | }, 344 | { ... } 345 | ] 346 | } 347 | ``` 348 | 349 |
350 | 351 | ## Contributors 352 | None 353 | 354 | ## Thanks to ... 355 | - [ghoshRitesh12/aniwatch-api](https://github.com/ghoshRitesh12/aniwatch-api) for providing a codebase structure I used in this project. 356 | -------------------------------------------------------------------------------- /api/index.ts: -------------------------------------------------------------------------------- 1 | import app from "../index"; 2 | 3 | export default app; -------------------------------------------------------------------------------- /config/axois.ts: -------------------------------------------------------------------------------- 1 | import axios, { AxiosError, type AxiosRequestConfig } from "axios"; 2 | 3 | export const SRC_BASE_URL = "https://vidstream.to"; 4 | export const SRC_HOME_URL = `${SRC_BASE_URL}/home`; 5 | export const SRC_AJAX_URL = `${SRC_BASE_URL}/ajax`; 6 | 7 | export const ACCEPT_HEADER = "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"; 8 | export const USER_AGENT_HEADER = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4692.71 Safari/537.36"; 9 | export const ACCEPT_ENCODING_HEADER = "gzip, deflate, br"; 10 | 11 | const clientConfig: AxiosRequestConfig = { 12 | timeout: 10000, 13 | baseURL: SRC_BASE_URL, 14 | headers: { 15 | Accept: ACCEPT_HEADER, 16 | "User-Agent": USER_AGENT_HEADER, 17 | "Accept-Encoding": ACCEPT_ENCODING_HEADER, 18 | }, 19 | }; 20 | 21 | const client = axios.create(clientConfig); 22 | 23 | export { client, AxiosError }; -------------------------------------------------------------------------------- /config/server.ts: -------------------------------------------------------------------------------- 1 | import dotenv from 'dotenv'; 2 | dotenv.config(); 3 | 4 | export const PORT = process.env.PORT || 4030; -------------------------------------------------------------------------------- /config/vidstream.ts: -------------------------------------------------------------------------------- 1 | export const ALLOWED_EPISODE_SERVERS = ["upcloud", "vidcloud"]; 2 | 3 | export const SERVERS = { 4 | RABBITSTREAM: "rabbitstream.net", 5 | }; -------------------------------------------------------------------------------- /controllers/homePage.controller.ts: -------------------------------------------------------------------------------- 1 | import { HomePageResponse } from "../types/controllers/homePage"; 2 | 3 | import { Request, Response } from "express"; 4 | import { SRC_HOME_URL, USER_AGENT_HEADER, ACCEPT_ENCODING_HEADER, ACCEPT_HEADER } from "../config/axois"; 5 | import axios from "axios"; 6 | import { load, CheerioAPI } from "cheerio"; 7 | import { extractMovie, extractTvSeries } from "../utils/extractMovieTvSeriesItem"; 8 | 9 | // GET /home 10 | export default async function (req: Request, res: Response) { 11 | const response: HomePageResponse = { 12 | spotlight: [], 13 | trending: { 14 | movies: [], 15 | tvSeries: [], 16 | }, 17 | latestMovies: [], 18 | latestTvSeries: [], 19 | }; 20 | 21 | const mainPage = await axios.get(SRC_HOME_URL as string, { 22 | headers: { 23 | "User-Agent": USER_AGENT_HEADER, 24 | "Accept-Encoding": ACCEPT_ENCODING_HEADER, 25 | Accept: ACCEPT_HEADER, 26 | }, 27 | }); 28 | 29 | const $: CheerioAPI = load(mainPage.data); 30 | 31 | $(".swiper-wrapper .swiper-slide").each((_, el) => { 32 | let id = $(el).find("a").attr("href")?.split("-") || ""; 33 | id = id[id.length - 1]; 34 | 35 | response.spotlight.push({ 36 | id: id as string, 37 | title: $(el).find(".item-content .movie-name").text(), 38 | banner: $(el).find("a img").attr("src") as string, 39 | poster: $(el).find(".item-content img").attr("src") as string, 40 | rating: $(el).find(".item-content .is-rated")?.text()?.trim(), 41 | year: $(el).find(".item-content .dot")?.next()?.text()?.trim(), 42 | }); 43 | }); 44 | 45 | $("#trending-movies .item").each((_, el) => { 46 | response.trending.movies.push(extractMovie($, el)); 47 | }); 48 | 49 | $("#trending-series .item").each((_, el) => { 50 | response.trending.tvSeries.push(extractTvSeries($, el)); 51 | }); 52 | 53 | $(".section-row.section-last").each((i, el) => { 54 | if (i === 0) { 55 | $(el).find(".item").each((_, el) => { 56 | response.latestMovies.push(extractMovie($, el)); 57 | }); 58 | } else if (i === 1) { 59 | $(el).find(".item").each((_, el) => { 60 | response.latestTvSeries.push(extractTvSeries($, el)); 61 | }); 62 | } 63 | }); 64 | 65 | res.send(response); 66 | 67 | } -------------------------------------------------------------------------------- /controllers/index.ts: -------------------------------------------------------------------------------- 1 | import getHomeController from './homePage.controller'; 2 | import getSearchController from './search.controller'; 3 | import getMovieDetailsController from './movieDetails.controller'; 4 | import getMovieSeasonsController from './movieSeasons.controller'; 5 | import getMovieEpisodesController from './movieEpisodes.controller'; 6 | import getMovieEpisodeServersController from './movieEpisodeServers.controller'; 7 | import getMovieEpisodeSourcesController from './movieEpisodeSources.controller'; 8 | 9 | export { 10 | getHomeController, 11 | getSearchController, 12 | getMovieDetailsController, 13 | getMovieSeasonsController, 14 | getMovieEpisodesController, 15 | getMovieEpisodeServersController, 16 | getMovieEpisodeSourcesController 17 | }; -------------------------------------------------------------------------------- /controllers/movieDetails.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from "express"; 2 | import { MovieDetailsResponse } from "../types/controllers/movieDetails"; 3 | 4 | import { USER_AGENT_HEADER, ACCEPT_ENCODING_HEADER, ACCEPT_HEADER, SRC_AJAX_URL, SRC_BASE_URL } from "../config/axois"; 5 | import axios from "axios"; 6 | import { load, CheerioAPI } from "cheerio"; 7 | import { extractDetect } from "../utils/extractMovieTvSeriesItem"; 8 | 9 | // GET /movie/:id 10 | export default async function (req: Request, res: Response) { 11 | const response: MovieDetailsResponse = { 12 | title: "", 13 | description: "", 14 | type: "", 15 | stats: [], 16 | related: [], 17 | }; 18 | 19 | const axiosResponse = await axios.get(`${SRC_BASE_URL}/watch-movie/watch-${req.params.id}` as string, { 20 | headers: { 21 | "Alt-Used": "vidstream.to", 22 | "Host": "vidstream.to", 23 | "Referer": `https://vidstream.to/watch-movie/watch-${req.params.id}`, 24 | "User-Agent": USER_AGENT_HEADER, 25 | "Accept-Encoding": ACCEPT_ENCODING_HEADER, 26 | Accept: ACCEPT_HEADER, 27 | }, 28 | }); 29 | 30 | const $: CheerioAPI = load(axiosResponse.data); 31 | 32 | response.title = $(".movie-detail h3.movie-name").text(); 33 | response.description = $(".movie-detail .is-description .dropdown-menu .dropdown-text").text().trim(); 34 | 35 | $(".movie-detail .is-sub > div").each((_, el) => { 36 | const stat: any = { 37 | name: $(el).find(".name").text(), 38 | value: [] 39 | }; 40 | 41 | const anchorTags = $(el).find(".value a"); 42 | if (anchorTags.length) { 43 | anchorTags.each((_, anchor) => { 44 | stat.value.push($(anchor).text()); 45 | }); 46 | } else { 47 | stat.value = $(el).find(".value").text().trim(); 48 | } 49 | 50 | response.stats.push(stat); 51 | }); 52 | 53 | $(".section-related .item").each((_, el) => { 54 | response.related.push(extractDetect($, el)); 55 | }); 56 | 57 | const axiosResponseLines = axiosResponse.data.split("\n"); 58 | for (let i = axiosResponseLines.length - 1; i > 0; i--) { 59 | if (axiosResponseLines[i].includes("const movie = {")) { 60 | response.type = (axiosResponseLines[i + 2].includes("type: '1'")) ? "movie" : "tvSeries"; 61 | if (response.type === "movie") response.episodeId = axiosResponseLines[i + 4].split("'")[1]; 62 | } 63 | } 64 | 65 | res.send(response); 66 | } -------------------------------------------------------------------------------- /controllers/movieEpisodeServers.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from "express"; 2 | import { Server } from "../types/controllers/movieEpisodeServers"; 3 | import { ALLOWED_EPISODE_SERVERS } from "../config/vidstream"; 4 | 5 | import { USER_AGENT_HEADER, ACCEPT_ENCODING_HEADER, ACCEPT_HEADER, SRC_AJAX_URL } from "../config/axois"; 6 | import axios from "axios"; 7 | import { load, CheerioAPI } from "cheerio"; 8 | 9 | // GET /movie/:id/servers?episodeId=string 10 | export default async function (req: Request, res: Response) { 11 | const response = { 12 | servers: [] as Server[], 13 | }; 14 | 15 | const serverAjaxResponse = await axios.get(`${SRC_AJAX_URL}/movie/episode/servers/${req.query.episodeId}` as string, { 16 | headers: { 17 | "Alt-Used": "vidstream.to", 18 | "Host": "vidstream.to", 19 | "Referer": `https://vidstream.to/series/${req.params.id}/${req.query.episodeId}`, 20 | "User-Agent": USER_AGENT_HEADER, 21 | "Accept-Encoding": ACCEPT_ENCODING_HEADER, 22 | Accept: ACCEPT_HEADER, 23 | }, 24 | }); 25 | 26 | const $: CheerioAPI = load(serverAjaxResponse.data); 27 | 28 | $(".dropdown-menu .dropdown-item").each((_, el) => { 29 | if (ALLOWED_EPISODE_SERVERS.filter(obj => obj === $(el).text().trim().toLowerCase())?.length === 0) return; 30 | response.servers.push({ 31 | id: $(el).attr("data-id") as string, 32 | name: $(el).text().trim(), 33 | }); 34 | }); 35 | 36 | res.send(response); 37 | } -------------------------------------------------------------------------------- /controllers/movieEpisodeSources.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from "express"; 2 | import { EpisodeServerResponse } from "../types/controllers/movieEpisodeSources"; 3 | import { SERVERS } from "../config/vidstream"; 4 | 5 | import { USER_AGENT_HEADER, ACCEPT_ENCODING_HEADER, ACCEPT_HEADER, SRC_AJAX_URL } from "../config/axois"; 6 | import axios from "axios"; 7 | import { 8 | RabbitStream 9 | } from "../parsers/index"; 10 | 11 | // GET /movie/:id/sources?episodeId=string&serverId=string 12 | export default async function (req: any, res: Response) { 13 | const serverAjaxResponse = await axios.get(`${SRC_AJAX_URL}/movie/episode/server/sources/${req.query.serverId}` as string, { 14 | headers: { 15 | "Alt-Used": "vidstream.to", 16 | "Host": "vidstream.to", 17 | "Referer": `https://vidstream.to/series/${req.params.id}/${req.query.episodeId}`, 18 | "User-Agent": USER_AGENT_HEADER, 19 | "Accept-Encoding": ACCEPT_ENCODING_HEADER, 20 | Accept: ACCEPT_HEADER, 21 | }, 22 | }); 23 | 24 | res.send(await RabbitStream(serverAjaxResponse.data.data.link, "https://vidstream.to")); 25 | } 26 | -------------------------------------------------------------------------------- /controllers/movieEpisodes.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from "express"; 2 | import { Episode } from "../types/controllers/movieEpisodes"; 3 | 4 | import { SRC_HOME_URL, USER_AGENT_HEADER, ACCEPT_ENCODING_HEADER, ACCEPT_HEADER, SRC_AJAX_URL } from "../config/axois"; 5 | import axios from "axios"; 6 | import { load, CheerioAPI } from "cheerio"; 7 | 8 | // GET /movie/:id/episodes?seasonId=string 9 | export default async function (req: Request, res: Response) { 10 | const response = { 11 | episodes: [] as Episode[], 12 | }; 13 | 14 | const serverAjaxResponse = await axios.get(`${SRC_AJAX_URL}/movie/season/episodes/${req.query.seasonId}` as string, { 15 | headers: { 16 | "Alt-Used": "vidstream.to", 17 | "Host": "vidstream.to", 18 | "Referer": `https://vidstream.to/series/${req.params.id}`, 19 | "User-Agent": USER_AGENT_HEADER, 20 | "Accept-Encoding": ACCEPT_ENCODING_HEADER, 21 | Accept: ACCEPT_HEADER, 22 | }, 23 | }); 24 | 25 | let $: CheerioAPI = load(serverAjaxResponse.data); 26 | 27 | $(".item a").each((_, el) => { 28 | response.episodes.push({ 29 | id: $(el).attr("data-id") as string, 30 | number: parseInt($(el).find("strong").text().trim().split(" ")[1]), 31 | title: $(el).text().trim().split(" ").slice(2).join(" "), 32 | }); 33 | }); 34 | 35 | res.send(response); 36 | } -------------------------------------------------------------------------------- /controllers/movieSeasons.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from "express"; 2 | import { Episode, Season } from "../types/controllers/movieEpisodes"; 3 | 4 | import { SRC_HOME_URL, USER_AGENT_HEADER, ACCEPT_ENCODING_HEADER, ACCEPT_HEADER, SRC_AJAX_URL } from "../config/axois"; 5 | import axios from "axios"; 6 | import { load, CheerioAPI } from "cheerio"; 7 | 8 | // GET /movie/:id/seasons 9 | export default async function (req: Request, res: Response) { 10 | const response = { 11 | seasons: [] as Season[], 12 | }; 13 | 14 | const serverAjaxResponse = await axios.get(`${SRC_AJAX_URL}/movie/seasons/${req.params.id}` as string, { 15 | headers: { 16 | "Alt-Used": "vidstream.to", 17 | "Host": "vidstream.to", 18 | "Referer": `https://vidstream.to/series/${req.params.id}`, 19 | "User-Agent": USER_AGENT_HEADER, 20 | "Accept-Encoding": ACCEPT_ENCODING_HEADER, 21 | Accept: ACCEPT_HEADER, 22 | }, 23 | }); 24 | 25 | let $: CheerioAPI = load(serverAjaxResponse.data); 26 | 27 | $(".dropdown-menu .dropdown-item").each((_, el) => { 28 | response.seasons.push({ 29 | id: $(el).attr("data-id") as string, 30 | number: parseInt($(el).text().split(" ")[1]), 31 | }); 32 | }); 33 | 34 | res.send(response); 35 | } -------------------------------------------------------------------------------- /controllers/search.controller.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from "express"; 2 | import { SearchResponse } from "../types/controllers/search"; 3 | 4 | import { SRC_BASE_URL, USER_AGENT_HEADER, ACCEPT_ENCODING_HEADER, ACCEPT_HEADER } from "../config/axois"; 5 | import axios from "axios"; 6 | import { load, CheerioAPI } from "cheerio"; 7 | import { extractDetect } from "../utils/extractMovieTvSeriesItem"; 8 | 9 | // GET /search?q=string&page=number 10 | export default async function (req: Request, res: Response) { 11 | const response: SearchResponse = { 12 | items: [], 13 | pagination: { 14 | current: parseInt((req.query.page || 1) as string), 15 | total: 1, 16 | }, 17 | }; 18 | 19 | const serverAjaxResponse = await axios.get(`${SRC_BASE_URL}/search?keyword=${req.query.q}&page=${req.query.page || 1}` as string, { 20 | headers: { 21 | "Alt-Used": "vidstream.to", 22 | "Host": "vidstream.to", 23 | "Referer": `https://vidstream.to/series/${req.params.id}`, 24 | "User-Agent": USER_AGENT_HEADER, 25 | "Accept-Encoding": ACCEPT_ENCODING_HEADER, 26 | Accept: ACCEPT_HEADER, 27 | }, 28 | }); 29 | 30 | let $: CheerioAPI = load(serverAjaxResponse.data); 31 | 32 | $(".item").each((_, el) => { 33 | response.items.push(extractDetect($, el)); 34 | }); 35 | 36 | response.pagination.total = parseInt($("ul.pagination .page-item:last-of-type a").attr("href")?.split("page=")[1] || "1"); 37 | 38 | res.send(response); 39 | } -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | 3 | import { PORT } from './config/server'; 4 | 5 | import cors from './middleware/cors'; 6 | import routes from './routes'; 7 | 8 | const app = express(); 9 | 10 | app.use(cors); 11 | routes(app); 12 | 13 | app.use("/", express.static("public")); 14 | 15 | app.listen(PORT, () => console.log(`Server listening on port ${PORT}`)); 16 | 17 | export default app; -------------------------------------------------------------------------------- /middleware/cors.ts: -------------------------------------------------------------------------------- 1 | import { NextFunction, Request, Response } from "express"; 2 | import { config } from "dotenv"; 3 | config(); 4 | 5 | export default (req: Request, res: Response, next: NextFunction) => { 6 | res.header("Access-Control-Allow-Origin", process.env.CORS_ORIGIN || "*"); 7 | res.header("Access-Control-Allow-Methods", "GET"); 8 | next(); 9 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vidstreamapi", 3 | "version": "0.2.0", 4 | "main": "index.ts", 5 | "scripts": { 6 | "start": "tsx .", 7 | "dev": "tsx watch ." 8 | }, 9 | "keywords": [], 10 | "author": "WBR_K", 11 | "license": "ISC", 12 | "description": "", 13 | "dependencies": { 14 | "axios": "^1.7.4", 15 | "cheerio": "^1.0.0", 16 | "crypto-js": "^4.2.0", 17 | "dotenv": "^16.4.5", 18 | "express": "^4.19.2", 19 | "image-pixels": "^2.2.2", 20 | "tsx": "^4.17.0", 21 | "typescript": "^5.5.4" 22 | }, 23 | "devDependencies": { 24 | "@types/express": "^4.17.21", 25 | "@types/node": "^20.11.5" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /parsers/index.ts: -------------------------------------------------------------------------------- 1 | import RabbitStream from './rabbitstream'; 2 | 3 | export { 4 | RabbitStream 5 | } -------------------------------------------------------------------------------- /parsers/rabbitstream.ts: -------------------------------------------------------------------------------- 1 | import util from 'util'; 2 | import pixels from 'image-pixels'; 3 | import cryptoJs from 'crypto-js'; 4 | const user_agent = "Mozilla/5.0 (X11; Linux x86_64; rv:133.0) Gecko/20100101 Firefox/133.0"; 5 | import { webcrypto } from 'crypto' 6 | const crypto = webcrypto as unknown as Crypto 7 | 8 | export default async function (main_arg_embed_url: string, main_arg_site: string) { 9 | 10 | let wasm: any; 11 | let arr = new Array(128).fill(void 0); 12 | let dateNow = Date.now(); 13 | let content: string = ""; 14 | let referrer = ""; 15 | 16 | function isDetached(buffer: ArrayBuffer): boolean { 17 | if (buffer.byteLength === 0) { 18 | const formatted = util.format(buffer) 19 | return formatted.includes('detached') 20 | } 21 | return false 22 | } 23 | 24 | const dataURL = "data:image/png;base64,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JDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAtwCFrnv+pEcAAQQQECJAoQsZJDEQQAABBHQLUOi65096BBBAAAEhAhS6kEESAwEEEEBAt8D/ATY93seMmImHAAAAAElFTkSuQmCC"; 25 | 26 | const meta = { 27 | content: content 28 | } 29 | 30 | const image_data = { 31 | height: 50, 32 | width: 65, 33 | data: new Uint8ClampedArray(), 34 | } 35 | 36 | interface fakeLocalStorage { 37 | [key: string]: string | Function 38 | setItem: Function 39 | } 40 | 41 | interface fakeWindow { 42 | localStorage: fakeLocalStorage 43 | [key: string]: any 44 | } 45 | 46 | const canvas = { 47 | baseUrl: "", 48 | width: 0, 49 | height: 0, 50 | style: { 51 | style: { 52 | display: "inline", 53 | }, 54 | }, 55 | context2d: {}, 56 | } 57 | 58 | const fake_window: fakeWindow = { 59 | localStorage: { 60 | setItem: function(item: string, value: string) { 61 | fake_window.localStorage[item] = value; 62 | } 63 | }, 64 | navigator: { 65 | webdriver: false, 66 | userAgent: user_agent, 67 | }, 68 | length: 0, 69 | document: { 70 | cookie: "", 71 | }, 72 | origin: "", 73 | location: { 74 | href: "", 75 | origin: "", 76 | }, 77 | performance: { 78 | timeOrigin: dateNow, 79 | }, 80 | xrax: '', 81 | c: false, 82 | G: '', 83 | z: function(a: number) { 84 | return [(4278190080 & a) >> 24, 85 | (16711680 & a) >> 16, 86 | (65280 & a) >> 8, 87 | 255 & a]; 88 | }, 89 | crypto: crypto, 90 | msCrypto: crypto, 91 | browser_version: 1676800512 92 | }; 93 | 94 | const nodeList = { 95 | image: { 96 | src: "", 97 | height: 50, 98 | width: 65, 99 | complete: true, 100 | }, 101 | context2d: {}, 102 | length: 1, 103 | } 104 | 105 | function get(index: number) { 106 | return arr[index]; 107 | } 108 | 109 | arr.push(void 0, null, true, false); 110 | 111 | let size = 0; 112 | let memoryBuff: Uint8Array | null; 113 | 114 | function getMemBuff(): Uint8Array { 115 | return memoryBuff = null !== memoryBuff && 0 !== memoryBuff.byteLength ? memoryBuff : new Uint8Array(wasm.memory.buffer); 116 | } 117 | 118 | const encoder = new TextEncoder(); 119 | const encode = function(text: string, array: Uint8Array) { 120 | return encoder.encodeInto(text, array) 121 | } 122 | 123 | function parse(text: string, func: Function, func2: Function) { 124 | if (void 0 === func2) { 125 | var encoded = encoder.encode(text); 126 | const parsedIndex = func(encoded.length, 1) >>> 0; 127 | return getMemBuff().subarray(parsedIndex, parsedIndex + encoded.length).set(encoded), size = encoded.length, parsedIndex; 128 | } 129 | let len = text.length; 130 | let parsedLen = func(len, 1) >>> 0; 131 | var new_arr = getMemBuff(); 132 | let i = 0; 133 | for (; i < len; i++) { 134 | var char = text.charCodeAt(i); 135 | if (127 < char) { 136 | break; 137 | } 138 | new_arr[parsedLen + i] = char; 139 | } 140 | return i !== len && (0 !== i && (text = text.slice(i)), parsedLen = func2(parsedLen, len, len = i + 3 * text.length, 1) >>> 0, encoded = getMemBuff().subarray(parsedLen + i, parsedLen + len), i += encode(text, encoded).written, parsedLen = func2(parsedLen, len, i, 1) >>> 0), size = i, parsedLen; 141 | } 142 | 143 | 144 | let dataView: DataView | null; 145 | 146 | function isNull(test: any) { 147 | return null == test; 148 | } 149 | 150 | function getDataView() { 151 | return dataView = dataView === null || isDetached(dataView.buffer) || dataView.buffer !== wasm.memory.buffer ? new DataView(wasm.memory.buffer) : dataView; 152 | } 153 | 154 | let pointer = arr.length; 155 | 156 | function shift(QP: number) { 157 | QP < 132 || (arr[QP] = pointer, pointer = QP); 158 | } 159 | 160 | function shiftGet(QP: number) { 161 | var Qn = get(QP); 162 | return shift(QP), Qn; 163 | } 164 | 165 | const decoder = new TextDecoder("utf-8", { 166 | fatal: true, 167 | ignoreBOM: true, 168 | }); 169 | 170 | function decodeSub(index: number, offset: number) { 171 | return index >>>= 0, decoder.decode(getMemBuff().subarray(index, index + offset)); 172 | } 173 | 174 | function addToStack(item: any) { 175 | pointer === arr.length && arr.push(arr.length + 1); 176 | var Qn = pointer; 177 | return pointer = arr[Qn], arr[Qn] = item, Qn; 178 | } 179 | 180 | function args(QP: any, Qn: number, QT: number, func: Function) { 181 | const Qx = { 182 | 'a': QP, 183 | 'b': Qn, 184 | 'cnt': 1, 185 | 'dtor': QT 186 | } 187 | return QP = (...Qw: any) => { 188 | Qx.cnt++; 189 | try { 190 | return func(Qx.a, Qx.b, ...Qw); 191 | } finally { 192 | 0 == --Qx.cnt && (wasm.__wbindgen_export_2.get(Qx.dtor)(Qx.a, Qx.b), Qx.a = 0); 193 | } 194 | }, (QP.original = Qx, QP); 195 | } 196 | 197 | function export3(QP: any, Qn: any) { 198 | return shiftGet(wasm.__wbindgen_export_3(QP, Qn)); 199 | } 200 | 201 | function export4(Qy: any, QO: any, QX: any) { 202 | wasm.__wbindgen_export_4(Qy, QO, addToStack(QX)); 203 | } 204 | 205 | function export5(QP: any, Qn: any) { 206 | wasm.__wbindgen_export_5(QP, Qn); 207 | } 208 | 209 | function applyToWindow(func: Function, args: ArrayLike) { 210 | try { 211 | return func.apply(fake_window, args); 212 | } catch (error) { 213 | wasm.__wbindgen_export_6(addToStack(error)); 214 | } 215 | } 216 | 217 | function Qj(QP: ArrayLike, Qn: any) { 218 | return Qn = Qn(+QP.length, 1) >>> 0, (getMemBuff().set(QP, Qn), size = QP.length, Qn); 219 | } 220 | 221 | async function QN(QP: Response, Qn: WebAssembly.Imports) { 222 | let QT: ArrayBuffer, Qt: any; 223 | return 'function' == typeof Response && QP instanceof Response ? (QT = await QP.arrayBuffer(), Qt = await WebAssembly.instantiate(QT, Qn), Object.assign(Qt, { 'bytes': QT })) : (Qt = await WebAssembly.instantiate(QP, Qn)) instanceof WebAssembly.Instance ? { 224 | 'instance': Qt, 225 | 'module': QP 226 | } : Qt; 227 | } 228 | 229 | function initWasm() { 230 | const wasmObj = { 231 | 'wbg': { 232 | '__wbindgen_is_function': function(index: number) { 233 | return typeof get(index) == "function"; 234 | }, 235 | '__wbindgen_is_string': function(index: number) { 236 | return typeof get(index) == "string"; 237 | }, 238 | '__wbindgen_is_object': function(index: number) { 239 | let object = get(index); 240 | return typeof object == "object" && object !== null; 241 | }, 242 | '__wbindgen_number_get': function(offset: number, index: number) { 243 | let number = get(index); 244 | getDataView().setFloat64(offset + 8, isNull(number) ? 0 : number, true); 245 | getDataView().setInt32(offset, isNull(number) ? 0 : 1, true); 246 | 247 | }, 248 | '__wbindgen_string_get': function(offset: number, index: number) { 249 | let str = get(index); 250 | let val = parse(str, wasm.__wbindgen_export_0, wasm.__wbindgen_export_1); 251 | getDataView().setInt32(offset + 4, size, true); 252 | getDataView().setInt32(offset, val, true); 253 | }, 254 | '__wbindgen_object_drop_ref': function(index: number) { 255 | shiftGet(index); 256 | }, 257 | '__wbindgen_cb_drop': function(index: number) { 258 | let org = shiftGet(index).original; 259 | return 1 == org.cnt-- && !(org.a = 0); 260 | 261 | }, 262 | '__wbindgen_string_new': function(index: number, offset: number) { 263 | return addToStack(decodeSub(index, offset)); 264 | }, 265 | '__wbindgen_is_null': function(index: number) { 266 | return null === get(index); 267 | }, 268 | '__wbindgen_is_undefined': function(index: number) { 269 | return void 0 === get(index); 270 | }, 271 | '__wbindgen_boolean_get': function(index: number) { 272 | let bool = get(index); 273 | return 'boolean' == typeof bool ? bool ? 1 : 0 : 2; 274 | }, 275 | '__wbg_instanceof_CanvasRenderingContext2d_4ec30ddd3f29f8f9': function() { 276 | return true; 277 | }, 278 | '__wbg_subarray_adc418253d76e2f1': function(index: number, num1: number, num2: number) { 279 | return addToStack(get(index).subarray(num1 >>> 0, num2 >>> 0)); 280 | }, 281 | '__wbg_randomFillSync_5c9c955aa56b6049': function() { }, 282 | '__wbg_getRandomValues_3aa56aa6edec874c': function() { 283 | return applyToWindow(function(index1: number, index2: number) { 284 | get(index1).getRandomValues(get(index2)); 285 | }, arguments); 286 | }, 287 | '__wbg_msCrypto_eb05e62b530a1508': function(index: number) { 288 | return addToStack(get(index).msCrypto); 289 | }, 290 | '__wbg_toString_6eb7c1f755c00453': function(index: number) { 291 | let fakestr = "[object Storage]"; 292 | return addToStack(fakestr); 293 | }, 294 | '__wbg_toString_139023ab33acec36': function(index: number) { 295 | return addToStack(get(index).toString()); 296 | }, 297 | '__wbg_require_cca90b1a94a0255b': function() { 298 | return applyToWindow(function() { 299 | return addToStack(module.require); 300 | }, arguments); 301 | }, 302 | '__wbg_crypto_1d1f22824a6a080c': function(index: number) { 303 | return addToStack(get(index).crypto); 304 | }, 305 | '__wbg_process_4a72847cc503995b': function(index: number) { 306 | return addToStack(get(index).process); 307 | }, 308 | '__wbg_versions_f686565e586dd935': function(index: number) { 309 | return addToStack(get(index).versions); 310 | }, 311 | '__wbg_node_104a2ff8d6ea03a2': function(index: number) { 312 | return addToStack(get(index).node); 313 | }, 314 | '__wbg_localStorage_3d538af21ea07fcc': function() { 315 | return applyToWindow(function(index: number) { 316 | let data = fake_window.localStorage; 317 | if (isNull(data)) { 318 | return 0; 319 | } else { 320 | return addToStack(data); 321 | } 322 | }, arguments); 323 | }, 324 | '__wbg_setfillStyle_59f426135f52910f': function() { }, 325 | '__wbg_setshadowBlur_229c56539d02f401': function() { }, 326 | '__wbg_setshadowColor_340d5290cdc4ae9d': function() { }, 327 | '__wbg_setfont_16d6e31e06a420a5': function() { }, 328 | '__wbg_settextBaseline_c3266d3bd4a6695c': function() { }, 329 | '__wbg_drawImage_cb13768a1bdc04bd': function() { }, 330 | '__wbg_getImageData_66269d289f37d3c7': function() { 331 | return applyToWindow(function() { 332 | return addToStack(image_data); 333 | }, arguments); 334 | }, 335 | '__wbg_rect_2fa1df87ef638738': function() { }, 336 | '__wbg_fillRect_4dd28e628381d240': function() { }, 337 | '__wbg_fillText_07e5da9e41652f20': function() { }, 338 | '__wbg_setProperty_5144ddce66bbde41': function() { }, 339 | '__wbg_createElement_03cf347ddad1c8c0': function() { 340 | return applyToWindow(function(index, decodeIndex: number, decodeIndexOffset: number) { 341 | return addToStack(canvas); 342 | }, arguments); 343 | }, 344 | '__wbg_querySelector_118a0639aa1f51cd': function() { 345 | return applyToWindow(function(index: number, decodeIndex: number, decodeOffset: number) { 346 | return addToStack(meta); 347 | }, arguments); 348 | }, 349 | '__wbg_querySelectorAll_50c79cd4f7573825': function() { 350 | return applyToWindow(function() { 351 | return addToStack(nodeList); 352 | }, arguments); 353 | }, 354 | '__wbg_getAttribute_706ae88bd37410fa': function(offset: number, index: number, decodeIndex: number, decodeOffset: number) { 355 | let attr = meta.content; 356 | let todo = isNull(attr) ? 0 : parse(attr, wasm.__wbindgen_export_0, wasm.__wbindgen_export_1); 357 | getDataView().setInt32(offset + 4, size, true); 358 | getDataView().setInt32(offset, todo, true); 359 | }, 360 | '__wbg_target_6795373f170fd786': function(index: number) { 361 | let target = get(index).target 362 | return isNull(target) ? 0 : addToStack(target); 363 | }, 364 | '__wbg_addEventListener_f984e99465a6a7f4': function() { }, 365 | '__wbg_instanceof_HtmlCanvasElement_1e81f71f630e46bc': function() { 366 | return true; 367 | }, 368 | '__wbg_setwidth_233645b297bb3318': function(index: number, set: number) { 369 | get(index).width = set >>> 0; 370 | }, 371 | '__wbg_setheight_fcb491cf54e3527c': function(index: number, set: number) { 372 | get(index).height = set >>> 0; 373 | }, 374 | '__wbg_getContext_dfc91ab0837db1d1': function() { 375 | return applyToWindow(function(index: number) { 376 | return addToStack(get(index).context2d); 377 | }, arguments); 378 | }, 379 | '__wbg_toDataURL_97b108dd1a4b7454': function() { 380 | return applyToWindow(function(offset: number, index: number) { 381 | let _dataUrl = parse(dataURL, wasm.__wbindgen_export_0, wasm.__wbindgen_export_1); 382 | getDataView().setInt32(offset + 4, size, true); 383 | getDataView().setInt32(offset, _dataUrl, true); 384 | }, arguments); 385 | }, 386 | '__wbg_instanceof_HtmlDocument_1100f8a983ca79f9': function() { 387 | return true; 388 | }, 389 | '__wbg_style_ca229e3326b3c3fb': function(index: number) { 390 | addToStack(get(index).style); 391 | }, 392 | '__wbg_instanceof_HtmlImageElement_9c82d4e3651a8533': function() { 393 | return true; 394 | }, 395 | '__wbg_src_87a0e38af6229364': function(offset: number, index: number) { 396 | let _src = parse(get(index).src, wasm.__wbindgen_export_0, wasm.__wbindgen_export_1); 397 | getDataView().setInt32(offset + 4, size, true); 398 | getDataView().setInt32(offset, _src, true); 399 | }, 400 | '__wbg_width_e1a38bdd483e1283': function(index: number) { 401 | return get(index).width; 402 | }, 403 | '__wbg_height_e4cc2294187313c9': function(index: number) { 404 | return get(index).height; 405 | }, 406 | '__wbg_complete_1162c2697406af11': function(index: number) { 407 | return get(index).complete; 408 | }, 409 | '__wbg_data_d34dc554f90b8652': function(offset: number, index: number) { 410 | var _data = Qj(get(index).data, wasm.__wbindgen_export_0); 411 | getDataView().setInt32(offset + 4, size, true); 412 | getDataView().setInt32(offset, _data, true); 413 | }, 414 | '__wbg_origin_305402044aa148ce': function() { 415 | return applyToWindow(function(offset: number, index: number) { 416 | let _origin = parse(get(index).origin, wasm.__wbindgen_export_0, wasm.__wbindgen_export_1); 417 | getDataView().setInt32(offset + 4, size, true); 418 | getDataView().setInt32(offset, _origin, true); 419 | }, arguments) 420 | }, 421 | '__wbg_length_8a9352f7b7360c37': function(index: number) { 422 | return get(index).length; 423 | }, 424 | '__wbg_get_c30ae0782d86747f': function(index: number) { 425 | let _image = get(index).image; 426 | return isNull(_image) ? 0 : addToStack(_image); 427 | }, 428 | '__wbg_timeOrigin_f462952854d802ec': function(index: number) { 429 | return get(index).timeOrigin; 430 | }, 431 | '__wbg_instanceof_Window_cee7a886d55e7df5': function() { 432 | return true 433 | }, 434 | '__wbg_document_eb7fd66bde3ee213': function(index: number) { 435 | let _document = get(index).document; 436 | return isNull(_document) ? 0 : addToStack(_document); 437 | }, 438 | '__wbg_location_b17760ac7977a47a': function(index: number) { 439 | return addToStack(get(index).location); 440 | }, 441 | '__wbg_performance_4ca1873776fdb3d2': function(index: number) { 442 | let _performance = get(index).performance; 443 | return isNull(_performance) ? 0 : addToStack(_performance); 444 | }, 445 | '__wbg_origin_e1f8acdeb3a39a2b': function(offset: number, index: number) { 446 | let _origin = parse(get(index).origin, wasm.__wbindgen_export_0, wasm.__wbindgen_export_1); 447 | getDataView().setInt32(offset + 4, size, true); 448 | getDataView().setInt32(offset, _origin, true); 449 | }, 450 | '__wbg_get_8986951b1ee310e0': function(index: number, decode1: number, decode2: number) { 451 | let data = get(index)[decodeSub(decode1, decode2)]; 452 | return isNull(data) ? 0 : addToStack(data); 453 | }, 454 | '__wbg_setTimeout_6ed7182ebad5d297': function() { 455 | return applyToWindow(function() { 456 | return 7; 457 | }, arguments) 458 | }, 459 | '__wbg_self_05040bd9523805b9': function() { 460 | return applyToWindow(function() { 461 | return addToStack(fake_window); 462 | }, arguments); 463 | }, 464 | '__wbg_window_adc720039f2cb14f': function() { 465 | return applyToWindow(function() { 466 | return addToStack(fake_window); 467 | }, arguments); 468 | }, 469 | '__wbg_globalThis_622105db80c1457d': function() { 470 | return applyToWindow(function() { 471 | return addToStack(fake_window); 472 | }, arguments) 473 | }, 474 | '__wbg_global_f56b013ed9bcf359': function() { 475 | return applyToWindow(function() { 476 | return addToStack(fake_window); 477 | }, arguments) 478 | }, 479 | '__wbg_newnoargs_cfecb3965268594c': function(index: number, offset: number) { 480 | return addToStack(new Function(decodeSub(index, offset))); 481 | }, 482 | '__wbindgen_object_clone_ref': function(index: number) { 483 | return addToStack(get(index)); 484 | }, 485 | '__wbg_eval_c824e170787ad184': function() { 486 | return applyToWindow(function(index: number, offset: number) { 487 | let fake_str = "fake_" + decodeSub(index, offset); 488 | let ev = eval(fake_str); 489 | return addToStack(ev); 490 | }, arguments) 491 | }, 492 | '__wbg_call_3f093dd26d5569f8': function() { 493 | return applyToWindow(function(index: number, index2: number) { 494 | return addToStack(get(index).call(get(index2))); 495 | }, arguments); 496 | }, 497 | '__wbg_call_67f2111acd2dfdb6': function() { 498 | return applyToWindow(function(index: number, index2: number, index3: number) { 499 | return addToStack(get(index).call(get(index2), get(index3))); 500 | }, arguments); 501 | }, 502 | '__wbg_set_961700853a212a39': function() { 503 | return applyToWindow(function(index: number, index2: number, index3: number) { 504 | return Reflect.set(get(index), get(index2), get(index3)); 505 | }, arguments); 506 | }, 507 | '__wbg_buffer_b914fb8b50ebbc3e': function(index: number) { 508 | return addToStack(get(index).buffer); 509 | }, 510 | '__wbg_newwithbyteoffsetandlength_0de9ee56e9f6ee6e': function(index: number, val: number, val2: number) { 511 | return addToStack(new Uint8Array(get(index), val >>> 0, val2 >>> 0)); 512 | }, 513 | '__wbg_newwithlength_0d03cef43b68a530': function(length: number) { 514 | return addToStack(new Uint8Array(length >>> 0)); 515 | }, 516 | '__wbg_new_b1f2d6842d615181': function(index: number) { 517 | return addToStack(new Uint8Array(get(index))); 518 | }, 519 | '__wbg_buffer_67e624f5a0ab2319': function(index: number) { 520 | return addToStack(get(index).buffer); 521 | }, 522 | '__wbg_length_21c4b0ae73cba59d': function(index: number) { 523 | return get(index).length; 524 | }, 525 | '__wbg_set_7d988c98e6ced92d': function(index: number, index2: number, val: number) { 526 | get(index).set(get(index2), val >>> 0); 527 | }, 528 | '__wbindgen_debug_string': function() { }, 529 | '__wbindgen_throw': function(index: number, offset: number) { 530 | throw new Error(decodeSub(index, offset)); 531 | }, 532 | '__wbindgen_memory': function() { 533 | return addToStack(wasm.memory); 534 | }, 535 | '__wbindgen_closure_wrapper117': function(Qn: any, QT: any) { 536 | return addToStack(args(Qn, QT, 2, export3)); 537 | }, 538 | '__wbindgen_closure_wrapper119': function(Qn: any, QT: any) { 539 | return addToStack(args(Qn, QT, 2, export4)); 540 | }, 541 | '__wbindgen_closure_wrapper121': function(Qn: any, QT: any) { 542 | return addToStack(args(Qn, QT, 2, export5)); 543 | }, 544 | '__wbindgen_closure_wrapper123': function(Qn: any, QT: any) { 545 | let test = addToStack(args(Qn, QT, 9, export4)); 546 | return test 547 | }, 548 | } 549 | } 550 | return wasmObj; 551 | } 552 | 553 | function assignWasm(resp: any) { 554 | wasm = resp.exports; 555 | dataView = null, memoryBuff = null, wasm; 556 | } 557 | 558 | function QZ(QP: any) { 559 | let Qn: any; 560 | return void 0 !== wasm ? wasm : (Qn = initWasm(), QP instanceof WebAssembly.Module || (QP = new WebAssembly.Module(QP)), assignWasm(new WebAssembly.Instance(QP, Qn))); 561 | } 562 | 563 | 564 | async function loadWasm(url: any) { 565 | let mod: any, buffer: any; 566 | return void 0 !== wasm ? wasm : (mod = initWasm(), { 567 | instance: url, 568 | module: mod, 569 | bytes: buffer 570 | } = (url = fetch(url), void 0, await QN(await url, mod)), assignWasm(url), buffer); 571 | } 572 | 573 | const grootLoader = { 574 | groot: function() { 575 | wasm.groot(); 576 | } 577 | } 578 | 579 | let wasmLoader = Object.assign(loadWasm, { 'initSync': QZ }, grootLoader); 580 | 581 | const V = async (url: string) => { 582 | let Q0 = await wasmLoader(url); 583 | fake_window.bytes = Q0; 584 | try { 585 | wasmLoader.groot(); 586 | } catch (error) { 587 | console.log("error: ", error); 588 | } 589 | fake_window.jwt_plugin(Q0); 590 | return fake_window.navigate(); 591 | } 592 | 593 | const getMeta = async (url: string) => { 594 | let resp = await fetch(url, { 595 | "headers": { 596 | "UserAgent": user_agent, 597 | "Referrer": referrer, 598 | } 599 | }); 600 | let txt = await resp.text(); 601 | let regx = /name="j_crt" content="[A-Za-z0-9]*/g 602 | let match = txt.match(regx)?.[0]; 603 | if (!match) 604 | throw new Error("j_crt not found"); 605 | let content = match.slice(match.lastIndexOf('"') + 1) 606 | meta.content = content + "=="; 607 | } 608 | 609 | const i = (a: Uint8Array, P: Array) => { 610 | try { 611 | for (let Q0 = 0; Q0 < a.length; Q0++) { 612 | a[Q0] = a[Q0] ^ P[Q0 % P.length]; 613 | } 614 | } catch (Q1) { 615 | return null; 616 | } 617 | }; 618 | 619 | 620 | const M = (a: any, P: any) => { 621 | try { 622 | var Q0 = cryptoJs.AES.decrypt(a, P); 623 | return JSON.parse(Q0.toString(cryptoJs.enc.Utf8)); 624 | } catch (Q1) { 625 | var Q0 = cryptoJs.AES.decrypt(a, P); 626 | } 627 | return []; 628 | }; 629 | 630 | function z(a: any) { 631 | return [(a & 4278190080) >> 24, (a & 16711680) >> 16, (a & 65280) >> 8, a & 255]; 632 | } 633 | 634 | return await (async function(embed_url: string, site: string) { 635 | referrer = site; 636 | let xrax = embed_url.split("/").pop()?.split("?").shift(); //thanks itzzzme 637 | 638 | let regx = /https:\/\/[a-zA-Z0-9.]*/; 639 | let base_url = embed_url?.match(regx)?.[0] || "https://flixhq.to"; 640 | 641 | nodeList.image.src = base_url + "/images/image.png?v=0.0.9"; 642 | let data = new Uint8ClampedArray((await pixels(nodeList.image.src)).data); 643 | image_data.data = data; 644 | let test = embed_url.split('/'); 645 | 646 | let browser_version = 1676800512 647 | canvas.baseUrl = base_url; 648 | fake_window.origin = base_url; 649 | fake_window.location.origin = base_url; 650 | fake_window.location.href = embed_url; 651 | fake_window.xrax = xrax; 652 | fake_window.G = xrax; 653 | 654 | await getMeta(embed_url); 655 | 656 | let Q5 = await V(base_url + "/images/loading.png?v=0.0.9"); 657 | 658 | let getSourcesUrl = ""; 659 | 660 | if (base_url.includes("mega")) { 661 | getSourcesUrl = base_url + "/" + test[3] + "/ajax/" + test[4] + "/getSources?id=" + fake_window.pid + "&v=" + fake_window.localStorage.kversion + "&h=" + fake_window.localStorage.kid + "&b=" + browser_version; 662 | } else { 663 | getSourcesUrl = base_url + "/ajax/" + test[3] + "/" + test[4] + "/getSources?id=" + fake_window.pid + "&v=" + fake_window.localStorage.kversion + "&h=" + fake_window.localStorage.kid + "&b=" + browser_version; 664 | } 665 | let resp_json = await (await fetch(getSourcesUrl, { 666 | "headers": { 667 | "User-Agent": user_agent, 668 | "Referrer": embed_url, 669 | "X-Requested-With": "XMLHttpRequest", 670 | }, 671 | "method": "GET", 672 | "mode": "cors" 673 | })).json(); 674 | 675 | let Q3 = fake_window.localStorage.kversion; 676 | let Q1 = z(Q3); 677 | Q5 = new Uint8Array(Q5); 678 | let Q8: any; 679 | Q8 = resp_json.t != 0 ? (i(Q5, Q1), Q5) : (Q8 = resp_json.k, i(Q8, Q1), Q8); 680 | 681 | 682 | let str = btoa(String.fromCharCode.apply(null, new Uint8Array(Q8))); 683 | var real = M(resp_json.sources, str); 684 | 685 | resp_json.sources = real; 686 | 687 | return resp_json; 688 | })(main_arg_embed_url, main_arg_site); 689 | 690 | } 691 | 692 | // main("https://zizicoi.online/v2/embed-4/QM4U19kPqLWl?autoPlay=0", "https://vidstream.to"); //change this value to the embed_url you want 693 | //the second arguments is the original site you want to extract from, this is needed so it can be used as the referrer -------------------------------------------------------------------------------- /public/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WBRK-dev/vidstream-api/970c844b8f573c59ed285db5a940d25b0c55cff1/public/bg.png -------------------------------------------------------------------------------- /public/index.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap'); 2 | 3 | body { 4 | font-family: "Inter", sans-serif; 5 | font-optical-sizing: auto; 6 | font-weight: 400; 7 | font-style: normal; 8 | 9 | background: url("./bg.png"); 10 | color: #fff; 11 | } 12 | 13 | * { 14 | margin: 0; 15 | } 16 | 17 | .main { 18 | display: flex; 19 | flex-direction: column; 20 | align-items: center; 21 | 22 | padding: 2rem .5rem; 23 | } 24 | 25 | .links { 26 | display: flex; 27 | gap: .5rem; 28 | 29 | margin-top: 1rem; 30 | } 31 | 32 | .links a { 33 | color: #1135ff; 34 | text-decoration: none; 35 | } 36 | 37 | .links span { 38 | color: #aaaaaa; 39 | } -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Vidstream API 7 | 8 | 9 | 10 |
11 |

Vidstream API

12 | 17 |
18 | 19 | -------------------------------------------------------------------------------- /routes/index.ts: -------------------------------------------------------------------------------- 1 | import { NextFunction, Request, Response, type Express } from 'express'; 2 | 3 | import { 4 | getHomeController, 5 | getSearchController, 6 | getMovieDetailsController, 7 | getMovieEpisodesController, 8 | getMovieSeasonsController, 9 | getMovieEpisodeServersController, 10 | getMovieEpisodeSourcesController 11 | } from "../controllers/index"; 12 | 13 | export default (app: Express) => { 14 | 15 | app.use(verboseMessageOnHit); 16 | 17 | app.get('/home', (req: Request, res: Response) => catchExceptions(req, res, getHomeController)); 18 | 19 | app.get("/search", (req: Request, res: Response) => catchExceptions(req, res, getSearchController)); 20 | 21 | app.get("/movie/:id", (req: Request, res: Response) => catchExceptions(req, res, getMovieDetailsController)); 22 | 23 | app.get("/movie/:id/seasons", (req: Request, res: Response) => catchExceptions(req, res, getMovieSeasonsController)); 24 | app.get("/movie/:id/episodes", (req: Request, res: Response) => catchExceptions(req, res, getMovieEpisodesController)); 25 | 26 | app.get("/movie/:id/servers", (req: Request, res: Response) => catchExceptions(req, res, getMovieEpisodeServersController)); 27 | app.get("/movie/:id/sources", (req: Request, res: Response) => catchExceptions(req, res, getMovieEpisodeSourcesController)); 28 | 29 | } 30 | 31 | async function catchExceptions(req: Request, res: Response, fn: (req: Request, res: Response) => any) { 32 | try { 33 | await fn(req, res); 34 | } catch (error) { 35 | console.log("\x1b[31m%s\x1b[0m", error); 36 | res.status(500).json({ kind: error.name, error: error.message }); 37 | } 38 | } 39 | 40 | function verboseMessageOnHit(req: Request, _: Response, next: NextFunction) { 41 | const date = new Date(); 42 | console.log(`${String(date.getDate()).padStart(2, "0")}-${String(date.getMonth()).padStart(2, "0")}-${String(date.getFullYear()).padStart(2, "0")} ${String(date.getHours()).padStart(2, "0")}:${String(date.getMinutes()).padStart(2, "0")}:${String(date.getSeconds()).padStart(2, "0")}, ${req.ip} ${req.url}`); 43 | next(); 44 | } -------------------------------------------------------------------------------- /types/common.ts: -------------------------------------------------------------------------------- 1 | export interface MovieItem { 2 | id: string; 3 | title: string; 4 | poster: string; 5 | stats: MovieItemStats; 6 | } 7 | 8 | interface MovieItemStats { 9 | year: string; 10 | duration: string; 11 | rating: string; 12 | } 13 | 14 | export interface TvSeriesItem { 15 | id: string; 16 | title: string; 17 | poster: string; 18 | stats: TvSeriesItemStats; 19 | } 20 | 21 | interface TvSeriesItemStats { 22 | seasons: string; 23 | rating: string; 24 | } -------------------------------------------------------------------------------- /types/controllers/homePage.ts: -------------------------------------------------------------------------------- 1 | import { MovieItem, TvSeriesItem } from "../common"; 2 | 3 | export interface HomePageResponse { 4 | spotlight: SpotlightItem[]; 5 | trending: { 6 | movies: MovieItem[]; 7 | tvSeries: TvSeriesItem[]; 8 | }; 9 | latestMovies: MovieItem[]; 10 | latestTvSeries: TvSeriesItem[]; 11 | } 12 | 13 | export interface SpotlightItem { 14 | id: string; 15 | title: string; 16 | banner: string; 17 | poster: string; 18 | rating?: string; 19 | year: string; 20 | } -------------------------------------------------------------------------------- /types/controllers/movieDetails.ts: -------------------------------------------------------------------------------- 1 | import { MovieItem, TvSeriesItem } from "../common"; 2 | 3 | export interface MovieDetailsResponse { 4 | title: string; 5 | description: string; 6 | type: string; 7 | stats: {name: string, value: string | string[]}[]; 8 | related: (MovieItem | TvSeriesItem)[]; 9 | episodeId?: string; 10 | } -------------------------------------------------------------------------------- /types/controllers/movieEpisodeServers.ts: -------------------------------------------------------------------------------- 1 | export interface Server { 2 | id: string; 3 | name: string; 4 | } -------------------------------------------------------------------------------- /types/controllers/movieEpisodeSources.ts: -------------------------------------------------------------------------------- 1 | export interface EpisodeServerResponse { 2 | 3 | } 4 | 5 | -------------------------------------------------------------------------------- /types/controllers/movieEpisodes.ts: -------------------------------------------------------------------------------- 1 | export interface Season { 2 | id: string; 3 | number: number; 4 | } 5 | 6 | export interface Episode { 7 | id: string; 8 | number: number; 9 | title: string; 10 | } -------------------------------------------------------------------------------- /types/controllers/search.ts: -------------------------------------------------------------------------------- 1 | import { MovieItem, TvSeriesItem } from "../common"; 2 | 3 | export interface SearchResponse { 4 | items: (MovieItem | TvSeriesItem)[]; 5 | pagination: Pagination; 6 | } 7 | 8 | export interface Pagination { 9 | current: number; 10 | total: number; 11 | } -------------------------------------------------------------------------------- /utils/extractMovieTvSeriesItem.ts: -------------------------------------------------------------------------------- 1 | import { Cheerio, CheerioAPI } from "cheerio"; 2 | import { MovieItem, TvSeriesItem } from "../types/common"; 3 | 4 | export function extractDetect($: CheerioAPI, el): MovieItem | TvSeriesItem { 5 | 6 | if ($(el).find(".info-split .badge-type").html()) { 7 | return extractTvSeries($, el); 8 | } else { 9 | return extractMovie($, el); 10 | } 11 | 12 | } 13 | 14 | export function extractMovie($: CheerioAPI, el): MovieItem { 15 | return { 16 | id: $(el).find("a").attr("href")?.split("-")?.pop() as string, 17 | title: $(el).find(".movie-name").text() as string, 18 | poster: $(el).find(".movie-thumbnail img").attr("src") as string, 19 | stats: { 20 | year: $(el).find(".info-split").children().eq(0).text(), 21 | duration: $(el).find(".info-split").children().eq(2).text(), 22 | rating: $(el).find(".info-split .is-rated").text().trim(), 23 | } 24 | }; 25 | } 26 | 27 | export function extractTvSeries($: CheerioAPI, el): TvSeriesItem { 28 | return { 29 | id: $(el).find("a").attr("href")?.split("-")?.pop() as string, 30 | title: $(el).find(".movie-name").text() as string, 31 | poster: $(el).find(".movie-thumbnail img").attr("src") as string, 32 | stats: { 33 | seasons: $(el).find(".info-split").children().eq(1).text(), 34 | rating: $(el).find(".info-split .is-rated").text().trim(), 35 | } 36 | }; 37 | } -------------------------------------------------------------------------------- /vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "rewrites": [ 3 | { 4 | "source": "(.*)", 5 | "destination": "/api" 6 | } 7 | ] 8 | } 9 | --------------------------------------------------------------------------------