diff --git a/.docs/content/1.get-started/4.changelog.md b/.docs/content/1.get-started/4.changelog.md index 71af2b0..1408225 100644 --- a/.docs/content/1.get-started/4.changelog.md +++ b/.docs/content/1.get-started/4.changelog.md @@ -2,6 +2,17 @@ title: 'Changelog' --- +# Version 2.2.5 +- Add Primewire provider +- Improve VidSrcTo search results +- Fixed Filemoon embeds +- Fixed febbox +- Disabled non-working providers +- Reordered providers in ranking + +# Version 2.2.4 +- Hotfix for HDRezka provider + # Version 2.2.3 - Fix VidSrcTo - Add HDRezka provider diff --git a/.eslintrc.js b/.eslintrc.js index 927283f..7fa3236 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -19,10 +19,11 @@ module.exports = { plugins: ['@typescript-eslint', 'import', 'prettier'], rules: { 'no-plusplus': 'off', + 'class-methods-use-this': 'off', 'no-bitwise': 'off', 'no-underscore-dangle': 'off', '@typescript-eslint/no-explicit-any': 'off', - 'no-console': 'off', + 'no-console': ['error', { allow: ['warn', 'error'] }], '@typescript-eslint/no-this-alias': 'off', 'import/prefer-default-export': 'off', '@typescript-eslint/no-empty-function': 'off', diff --git a/package-lock.json b/package-lock.json index 08b65f9..a75feca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@movie-web/providers", - "version": "2.2.3", + "version": "2.2.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@movie-web/providers", - "version": "2.2.3", + "version": "2.2.5", "license": "MIT", "dependencies": { "cheerio": "^1.0.0-rc.12", diff --git a/package.json b/package.json index b90b41c..4c41a1a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@movie-web/providers", - "version": "2.2.4", + "version": "2.2.5", "description": "Package that contains all the providers of movie-web", "main": "./lib/index.umd.js", "types": "./lib/index.d.ts", diff --git a/src/dev-cli/logging.ts b/src/dev-cli/logging.ts index 1a519f4..5495f1d 100644 --- a/src/dev-cli/logging.ts +++ b/src/dev-cli/logging.ts @@ -1,5 +1,7 @@ import { inspect } from 'node:util'; export function logDeepObject(object: Record) { + // This is the dev cli, so we can use console.log + // eslint-disable-next-line no-console console.log(inspect(object, { showHidden: false, depth: null, colors: true })); } diff --git a/src/dev-cli/scraper.ts b/src/dev-cli/scraper.ts index 39f75f6..bc32af2 100644 --- a/src/dev-cli/scraper.ts +++ b/src/dev-cli/scraper.ts @@ -41,6 +41,8 @@ async function runBrowserScraping( args: ['--no-sandbox', '--disable-setuid-sandbox'], }); const page = await browser.newPage(); + // This is the dev cli, so we can use console.log + // eslint-disable-next-line no-console page.on('console', (message) => console.log(`${message.type().slice(0, 3).toUpperCase()} ${message.text()}`)); await page.goto(server.resolvedUrls.local[0]); await page.waitForFunction('!!window.scrape', { timeout: 5000 }); diff --git a/src/providers/all.ts b/src/providers/all.ts index 7725558..d20e14b 100644 --- a/src/providers/all.ts +++ b/src/providers/all.ts @@ -23,12 +23,16 @@ import { fileMoonScraper } from './embeds/filemoon'; import { ridooScraper } from './embeds/ridoo'; import { smashyStreamDScraper } from './embeds/smashystream/dued'; import { smashyStreamFScraper } from './embeds/smashystream/video1'; +import { streamtapeScraper } from './embeds/streamtape'; +import { streamvidScraper } from './embeds/streamvid'; import { vidCloudScraper } from './embeds/vidcloud'; import { vidplayScraper } from './embeds/vidplay'; +import { voeScraper } from './embeds/voe'; import { wootlyScraper } from './embeds/wootly'; import { goojaraScraper } from './sources/goojara'; import { hdRezkaScraper } from './sources/hdrezka'; import { nepuScraper } from './sources/nepu'; +import { primewireScraper } from './sources/primewire'; import { ridooMoviesScraper } from './sources/ridomovies'; import { smashyStreamScraper } from './sources/smashystream'; import { vidSrcToScraper } from './sources/vidsrcto'; @@ -50,6 +54,7 @@ export function gatherAllSources(): Array { nepuScraper, goojaraScraper, hdRezkaScraper, + primewireScraper, ]; } @@ -74,5 +79,8 @@ export function gatherAllEmbeds(): Array { vidplayScraper, wootlyScraper, doodScraper, + streamvidScraper, + voeScraper, + streamtapeScraper, ]; } diff --git a/src/providers/embeds/dood.ts b/src/providers/embeds/dood.ts index 3f0a371..5dd8def 100644 --- a/src/providers/embeds/dood.ts +++ b/src/providers/embeds/dood.ts @@ -3,15 +3,20 @@ import { customAlphabet } from 'nanoid'; import { makeEmbed } from '@/providers/base'; const nanoid = customAlphabet('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', 10); +const baseUrl = 'https://d000d.com'; export const doodScraper = makeEmbed({ id: 'dood', name: 'dood', rank: 173, async scrape(ctx) { - const baseUrl = 'https://d0000d.com'; + let url = ctx.url; + if (ctx.url.includes('primewire')) { + const request = await ctx.proxiedFetcher.full(ctx.url); + url = request.finalUrl; + } - const id = ctx.url.split('/d/')[1] || ctx.url.split('/e/')[1]; + const id = url.split('/d/')[1] || url.split('/e/')[1]; const doodData = await ctx.proxiedFetcher(`/e/${id}`, { method: 'GET', @@ -46,7 +51,7 @@ export const doodScraper = makeEmbed({ }, }, headers: { - Referer: 'https://d0000d.com/', + Referer: baseUrl, }, }, ], diff --git a/src/providers/embeds/febbox/qualities.ts b/src/providers/embeds/febbox/qualities.ts index b55dd72..032db79 100644 --- a/src/providers/embeds/febbox/qualities.ts +++ b/src/providers/embeds/febbox/qualities.ts @@ -20,6 +20,21 @@ function mapToQuality(quality: FebboxQuality): FebboxQuality | null { }; } +function removeBadUrlParams(url: string): string { + const urlObject = new URL(url); + + const urlSearchParams = new URLSearchParams(urlObject.search); + + const keysToKeep = ['KEY1', 'KEY2']; + for (const key of Array.from(urlSearchParams.keys())) { + if (!keysToKeep.includes(key)) { + urlSearchParams.delete(key); + } + } + + return `${urlObject.origin}${urlObject.pathname}?${urlSearchParams.toString()}`; +} + export async function getStreamQualities(ctx: ScrapeContext, apiQuery: object) { const mediaRes: { list: FebboxQuality[] } = (await sendRequest(ctx, apiQuery)).data; @@ -32,7 +47,7 @@ export async function getStreamQualities(ctx: ScrapeContext, apiQuery: object) { if (foundQuality) { qualities[quality] = { type: 'mp4', - url: foundQuality.path, + url: removeBadUrlParams(foundQuality.path), }; } }); diff --git a/src/providers/embeds/filemoon/index.ts b/src/providers/embeds/filemoon/index.ts index 3f8a2f3..ad9358c 100644 --- a/src/providers/embeds/filemoon/index.ts +++ b/src/providers/embeds/filemoon/index.ts @@ -19,7 +19,7 @@ export const fileMoonScraper = makeEmbed({ referer: ctx.url, }, }); - const evalCode = evalCodeRegex.exec(embedRes); + const evalCode = embedRes.match(evalCodeRegex); if (!evalCode) throw new Error('Failed to find eval code'); const unpacked = unpack(evalCode[1]); const file = fileRegex.exec(unpacked); diff --git a/src/providers/embeds/streamtape.ts b/src/providers/embeds/streamtape.ts new file mode 100644 index 0000000..db345b0 --- /dev/null +++ b/src/providers/embeds/streamtape.ts @@ -0,0 +1,39 @@ +import { flags } from '@/entrypoint/utils/targets'; +import { makeEmbed } from '@/providers/base'; + +export const streamtapeScraper = makeEmbed({ + id: 'streamtape', + name: 'Streamtape', + rank: 160, + async scrape(ctx) { + const embed = await ctx.proxiedFetcher(ctx.url); + + const match = embed.match(/robotlink'\).innerHTML = (.*)'/); + if (!match) throw new Error('No match found'); + + const [fh, sh] = match?.[1]?.split("+ ('") ?? []; + if (!fh || !sh) throw new Error('No match found'); + + const url = `https:${fh?.replace(/'/g, '').trim()}${sh?.substring(3).trim()}`; + + return { + stream: [ + { + id: 'primary', + type: 'file', + flags: [flags.CORS_ALLOWED, flags.IP_LOCKED], + captions: [], + qualities: { + unknown: { + type: 'mp4', + url, + }, + }, + headers: { + Referer: 'https://streamtape.com', + }, + }, + ], + }; + }, +}); diff --git a/src/providers/embeds/streamvid.ts b/src/providers/embeds/streamvid.ts new file mode 100644 index 0000000..ceb8810 --- /dev/null +++ b/src/providers/embeds/streamvid.ts @@ -0,0 +1,36 @@ +import * as unpacker from 'unpacker'; + +import { flags } from '@/entrypoint/utils/targets'; +import { makeEmbed } from '@/providers/base'; + +const packedRegex = /(eval\(function\(p,a,c,k,e,d\).*\)\)\))/; +const linkRegex = /src:"(https:\/\/[^"]+)"/; + +export const streamvidScraper = makeEmbed({ + id: 'streamvid', + name: 'Streamvid', + rank: 215, + async scrape(ctx) { + // Example url: https://streamvid.net/fu1jaf96vofx + const streamRes = await ctx.proxiedFetcher(ctx.url); + const packed = streamRes.match(packedRegex); + + if (!packed) throw new Error('streamvid packed not found'); + + const unpacked = unpacker.unpack(packed[1]); + const link = unpacked.match(linkRegex); + + if (!link) throw new Error('streamvid link not found'); + return { + stream: [ + { + type: 'hls', + id: 'primary', + playlist: link[1], + flags: [flags.CORS_ALLOWED], + captions: [], + }, + ], + }; + }, +}); diff --git a/src/providers/embeds/voe.ts b/src/providers/embeds/voe.ts new file mode 100644 index 0000000..7d92a3a --- /dev/null +++ b/src/providers/embeds/voe.ts @@ -0,0 +1,33 @@ +import { flags } from '@/entrypoint/utils/targets'; +import { makeEmbed } from '@/providers/base'; + +const linkRegex = /'hls': ?'(http.*?)',/; + +export const voeScraper = makeEmbed({ + id: 'voe', + name: 'voe.sx', + rank: 180, + async scrape(ctx) { + const embed = await ctx.proxiedFetcher(ctx.url); + + const playerSrc = embed.match(linkRegex) ?? []; + + const streamUrl = playerSrc[1]; + if (!streamUrl) throw new Error('Stream url not found in embed code'); + + return { + stream: [ + { + type: 'hls', + id: 'primary', + playlist: streamUrl, + flags: [flags.CORS_ALLOWED], + captions: [], + headers: { + Referer: 'https://voe.sx', + }, + }, + ], + }; + }, +}); diff --git a/src/providers/sources/flixhq/index.ts b/src/providers/sources/flixhq/index.ts index 45064ac..d0a2898 100644 --- a/src/providers/sources/flixhq/index.ts +++ b/src/providers/sources/flixhq/index.ts @@ -9,8 +9,9 @@ import { NotFoundError } from '@/utils/errors'; export const flixhqScraper = makeSourcerer({ id: 'flixhq', name: 'FlixHQ', - rank: 100, + rank: 61, flags: [flags.CORS_ALLOWED], + disabled: true, async scrapeMovie(ctx) { const id = await getFlixhqId(ctx, ctx.media); if (!id) throw new NotFoundError('no search results match'); diff --git a/src/providers/sources/gomovies/index.ts b/src/providers/sources/gomovies/index.ts index 9bec3e0..b57e6be 100644 --- a/src/providers/sources/gomovies/index.ts +++ b/src/providers/sources/gomovies/index.ts @@ -12,8 +12,9 @@ export const gomoviesBase = `https://gomovies.sx`; export const goMoviesScraper = makeSourcerer({ id: 'gomovies', name: 'GOmovies', - rank: 110, + rank: 60, flags: [flags.CORS_ALLOWED], + disabled: true, async scrapeShow(ctx) { const search = await ctx.proxiedFetcher(`/ajax/search`, { method: 'POST', diff --git a/src/providers/sources/goojara/index.ts b/src/providers/sources/goojara/index.ts index 402c0b6..7e2955a 100644 --- a/src/providers/sources/goojara/index.ts +++ b/src/providers/sources/goojara/index.ts @@ -22,8 +22,9 @@ async function universalScraper(ctx: ShowScrapeContext | MovieScrapeContext): Pr export const goojaraScraper = makeSourcerer({ id: 'goojara', name: 'Goojara', - rank: 225, + rank: 70, flags: [], + disabled: true, scrapeShow: universalScraper, scrapeMovie: universalScraper, }); diff --git a/src/providers/sources/hdrezka/index.ts b/src/providers/sources/hdrezka/index.ts index f3de725..2628589 100644 --- a/src/providers/sources/hdrezka/index.ts +++ b/src/providers/sources/hdrezka/index.ts @@ -120,7 +120,7 @@ const universalScraper = async (ctx: ShowScrapeContext | MovieScrapeContext): Pr export const hdRezkaScraper = makeSourcerer({ id: 'hdrezka', name: 'HDRezka', - rank: 195, + rank: 120, flags: [flags.CORS_ALLOWED, flags.IP_LOCKED], scrapeShow: universalScraper, scrapeMovie: universalScraper, diff --git a/src/providers/sources/kissasian/index.ts b/src/providers/sources/kissasian/index.ts index 4b8032b..1474bcc 100644 --- a/src/providers/sources/kissasian/index.ts +++ b/src/providers/sources/kissasian/index.ts @@ -12,7 +12,7 @@ import { search } from './search'; export const kissAsianScraper = makeSourcerer({ id: 'kissasian', name: 'KissAsian', - rank: 130, + rank: 40, flags: [flags.CORS_ALLOWED], disabled: true, diff --git a/src/providers/sources/lookmovie/index.ts b/src/providers/sources/lookmovie/index.ts index db24196..80f816d 100644 --- a/src/providers/sources/lookmovie/index.ts +++ b/src/providers/sources/lookmovie/index.ts @@ -33,7 +33,7 @@ export const lookmovieScraper = makeSourcerer({ id: 'lookmovie', name: 'LookMovie', disabled: true, - rank: 700, + rank: 50, flags: [flags.IP_LOCKED], scrapeShow: universalScraper, scrapeMovie: universalScraper, diff --git a/src/providers/sources/nepu/index.ts b/src/providers/sources/nepu/index.ts index 576c789..0e19721 100644 --- a/src/providers/sources/nepu/index.ts +++ b/src/providers/sources/nepu/index.ts @@ -76,8 +76,9 @@ const universalScraper = async (ctx: MovieScrapeContext | ShowScrapeContext) => export const nepuScraper = makeSourcerer({ id: 'nepu', name: 'Nepu', - rank: 111, + rank: 80, flags: [], + disabled: true, scrapeMovie: universalScraper, scrapeShow: universalScraper, }); diff --git a/src/providers/sources/primewire/common.ts b/src/providers/sources/primewire/common.ts new file mode 100644 index 0000000..20d4c59 --- /dev/null +++ b/src/providers/sources/primewire/common.ts @@ -0,0 +1,2 @@ +export const primewireBase = 'https://www.primewire.tf'; +export const primewireApiKey = atob('bHpRUHNYU0tjRw=='); diff --git a/src/providers/sources/primewire/decryption/README.md b/src/providers/sources/primewire/decryption/README.md new file mode 100644 index 0000000..1d6ba2f --- /dev/null +++ b/src/providers/sources/primewire/decryption/README.md @@ -0,0 +1,7 @@ +# Maintaining decryption + +This folder contains the decryption logic for the primewire provider + +The code in `blowfish.ts` is a de-obfuscated version of the original code that is used to decrypt the video links. You can find original the code [in this JavaScript file](https://www.primewire.tf/js/app-21205005105979fb964d17bf03570023.js?vsn=d]) by searching for the keyword "sBox0". + +The code is minified, so use prettier to deobfuscate it. In the case that the URL changes, you can find it used in the [primewire homepage](https://www.primewire.tf/). diff --git a/src/providers/sources/primewire/decryption/blowfish.ts b/src/providers/sources/primewire/decryption/blowfish.ts new file mode 100644 index 0000000..cae97f4 --- /dev/null +++ b/src/providers/sources/primewire/decryption/blowfish.ts @@ -0,0 +1,263 @@ +import { pArray, sBox0, sBox1, sBox2, sBox3 } from './constants'; + +class Blowfish { + sBox0: number[]; + + sBox1: number[]; + + sBox2: number[]; + + sBox3: number[]; + + pArray: number[]; + + keyStr: string; + + iv: string; + + constructor(t: string) { + this.sBox0 = sBox0.slice(); + this.sBox1 = sBox1.slice(); + this.sBox2 = sBox2.slice(); + this.sBox3 = sBox3.slice(); + this.pArray = pArray.slice(); + this.keyStr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; + this.iv = 'abc12345'; + this.generateSubkeys(t); + } + + encrypt(e: string) { + const root = this.utf8Decode(e); + let encrypted = ''; + const blockSize = 8; + const paddingChar = '\0'; + const numBlocks = Math.ceil(e.length / blockSize); + + for (let i = 0; i < numBlocks; i++) { + let block = root.substr(blockSize * i, blockSize); + + if (block.length < blockSize) { + block += paddingChar.repeat(blockSize - block.length); + } + + let [left, right] = this.split64by32(block); + [left, right] = this.encipher(left, right); + + encrypted += this.num2block32(left) + this.num2block32(right); + } + + return encrypted; + } + + decrypt(input: string) { + const numBlocks = Math.ceil(input.length / 8); + let decrypted = ''; + for (let i = 0; i < numBlocks; i++) { + const block = input.substr(8 * i, 8); + if (block.length < 8) { + throw new Error('Invalid block size'); + } + const [left, right] = this.split64by32(block); + const [decipheredLeft, decipheredRight] = this.decipher(left, right); + decrypted += this.num2block32(decipheredLeft) + this.num2block32(decipheredRight); + } + return this.utf8Encode(decrypted); + } + + substitute(value: number) { + const t = value >>> 24; + const n = (value << 8) >>> 24; + const r = (value << 16) >>> 24; + const i = (value << 24) >>> 24; + let result = this.addMod32(this.sBox0[t], this.sBox1[n]); + result = this.xor(result, this.sBox2[r]); + result = this.addMod32(result, this.sBox3[i]); + return result; + } + + /* eslint-disable */ + encipher(plaintext: number, key: number) { + for (var temp, round = 0; round < 16; round++) { + temp = plaintext = this.xor(plaintext, this.pArray[round]); + plaintext = key = this.xor(this.substitute(plaintext), key); + key = temp; + } + temp = plaintext; + plaintext = key; + key = temp; + key = this.xor(key, this.pArray[16]); + + return [(plaintext = this.xor(plaintext, this.pArray[17])), key]; + } + /* eslint-enable */ + + decipher(left: number, right: number) { + let n; + let e = left; + let t = right; + n = this.xor(e, this.pArray[17]); + e = this.xor(t, this.pArray[16]); + t = n; + for (let r = 15; r >= 0; r--) { + n = e; + e = t; + t = n; + t = this.xor(this.substitute(e), t); + e = this.xor(e, this.pArray[r]); + } + return [e, t]; + } + + generateSubkeys(key: string) { + let temp; + let keyIndex = 0; + let pIndex = 0; + for (let i = 0; i < 18; i++) { + temp = 0; + for (let j = 0; j < 4; j++) { + temp = this.fixNegative((temp << 8) | key.charCodeAt(keyIndex)); + keyIndex = (keyIndex + 1) % key.length; + } + this.pArray[pIndex] = this.xor(this.pArray[pIndex], temp); + pIndex++; + } + let tempSubkey = [0, 0]; + for (let i = 0; i < 18; i += 2) { + tempSubkey = this.encipher(tempSubkey[0], tempSubkey[1]); + this.pArray[i] = tempSubkey[0]; + this.pArray[i + 1] = tempSubkey[1]; + } + for (let i = 0; i < 256; i += 2) { + tempSubkey = this.encipher(tempSubkey[0], tempSubkey[1]); + this.sBox0[i] = tempSubkey[0]; + this.sBox0[i + 1] = tempSubkey[1]; + } + for (let i = 0; i < 256; i += 2) { + tempSubkey = this.encipher(tempSubkey[0], tempSubkey[1]); + this.sBox1[i] = tempSubkey[0]; + this.sBox1[i + 1] = tempSubkey[1]; + } + for (let i = 0; i < 256; i += 2) { + tempSubkey = this.encipher(tempSubkey[0], tempSubkey[1]); + this.sBox2[i] = tempSubkey[0]; + this.sBox2[i + 1] = tempSubkey[1]; + } + for (let i = 0; i < 256; i += 2) { + tempSubkey = this.encipher(tempSubkey[0], tempSubkey[1]); + this.sBox3[i] = tempSubkey[0]; + this.sBox3[i + 1] = tempSubkey[1]; + } + } + + block32toNum(e: string) { + return this.fixNegative( + (e.charCodeAt(0) << 24) | (e.charCodeAt(1) << 16) | (e.charCodeAt(2) << 8) | e.charCodeAt(3), + ); + } + + num2block32(e: number) { + return ( + String.fromCharCode(e >>> 24) + + String.fromCharCode((e << 8) >>> 24) + + String.fromCharCode((e << 16) >>> 24) + + String.fromCharCode((e << 24) >>> 24) + ); + } + + xor(e: number, t: number) { + return this.fixNegative(e ^ t); + } + + addMod32(e: number, t: number) { + return this.fixNegative((e + t) | 0); + } + + fixNegative(e: number) { + return e >>> 0; + } + + split64by32(e: string) { + const t = e.substring(0, 4); + const n = e.substring(4, 8); + return [this.block32toNum(t), this.block32toNum(n)]; + } + + utf8Decode(input: string) { + let decoded = ''; + for (let i = 0; i < input.length; i++) { + const charCode = input.charCodeAt(i); + if (charCode < 128) { + decoded += String.fromCharCode(charCode); + } else if (charCode > 127 && charCode < 2048) { + const firstCharCode = (charCode >> 6) | 192; + const secondCharCode = (63 & charCode) | 128; + decoded += String.fromCharCode(firstCharCode, secondCharCode); + } else { + const firstCharCode = (charCode >> 12) | 224; + const secondCharCode = ((charCode >> 6) & 63) | 128; + const thirdCharCode = (63 & charCode) | 128; + decoded += String.fromCharCode(firstCharCode, secondCharCode, thirdCharCode); + } + } + return decoded; + } + + utf8Encode(input: string) { + let encoded = ''; + let charCode; + for (let i = 0; i < input.length; i++) { + charCode = input.charCodeAt(i); + if (charCode < 128) { + encoded += String.fromCharCode(charCode); + } else if (charCode > 191 && charCode < 224) { + const secondCharCode = input.charCodeAt(i + 1); + encoded += String.fromCharCode(((31 & charCode) << 6) | (63 & secondCharCode)); + i += 1; + } else { + const secondCharCode = input.charCodeAt(i + 1); + const thirdCharCode = input.charCodeAt(i + 2); + encoded += String.fromCharCode(((15 & charCode) << 12) | ((63 & secondCharCode) << 6) | (63 & thirdCharCode)); + i += 2; + } + } + return encoded; + } + + base64(e: string) { + let t; + let n; + let r; + let i; + let o; + let a; + let s = ''; + let l = 0; + const root = e.replace(/[^A-Za-z0-9\\+\\/=]/g, ''); + while (l < root.length) { + t = (this.keyStr.indexOf(root.charAt(l++)) << 2) | ((i = this.keyStr.indexOf(root.charAt(l++))) >> 4); + n = ((15 & i) << 4) | ((o = this.keyStr.indexOf(root.charAt(l++))) >> 2); + r = ((3 & o) << 6) | (a = this.keyStr.indexOf(root.charAt(l++))); + s += String.fromCharCode(t); + if (o !== 64) { + s += String.fromCharCode(n); + } + if (a !== 64) { + s += String.fromCharCode(r); + } + } + return s; + } +} + +export function getLinks(encryptedInput: string) { + const key = encryptedInput.slice(-10); + const data = encryptedInput.slice(0, -10); + const cipher = new Blowfish(key); + const decryptedData = cipher.decrypt(cipher.base64(data)).match(/.{1,5}/g); + + if (!decryptedData) { + throw new Error('No links found'); + } else { + return decryptedData; + } +} diff --git a/src/providers/sources/primewire/decryption/constants.ts b/src/providers/sources/primewire/decryption/constants.ts new file mode 100644 index 0000000..91ab2c5 --- /dev/null +++ b/src/providers/sources/primewire/decryption/constants.ts @@ -0,0 +1,116 @@ +export const pArray = [ + 608135816, 2242054355, 320440878, 57701188, 2752067618, 698298832, 137296536, 3964562569, 1160258022, 953160567, + 3193202383, 887688300, 3232508343, 3380367581, 1065670069, 3041331479, 2450970073, 2306472731, +]; +export const sBox0 = [ + 3509652390, 2564797868, 805139163, 3491422135, 3101798381, 1780907670, 3128725573, 4046225305, 614570311, 3012652279, + 134345442, 2240740374, 1667834072, 1901547113, 2757295779, 4103290238, 227898511, 1921955416, 1904987480, 2182433518, + 2069144605, 3260701109, 2620446009, 720527379, 3318853667, 677414384, 3393288472, 3101374703, 2390351024, 1614419982, + 1822297739, 2954791486, 3608508353, 3174124327, 2024746970, 1432378464, 3864339955, 2857741204, 1464375394, + 1676153920, 1439316330, 715854006, 3033291828, 289532110, 2706671279, 2087905683, 3018724369, 1668267050, 732546397, + 1947742710, 3462151702, 2609353502, 2950085171, 1814351708, 2050118529, 680887927, 999245976, 1800124847, 3300911131, + 1713906067, 1641548236, 4213287313, 1216130144, 1575780402, 4018429277, 3917837745, 3693486850, 3949271944, 596196993, + 3549867205, 258830323, 2213823033, 772490370, 2760122372, 1774776394, 2652871518, 566650946, 4142492826, 1728879713, + 2882767088, 1783734482, 3629395816, 2517608232, 2874225571, 1861159788, 326777828, 3124490320, 2130389656, 2716951837, + 967770486, 1724537150, 2185432712, 2364442137, 1164943284, 2105845187, 998989502, 3765401048, 2244026483, 1075463327, + 1455516326, 1322494562, 910128902, 469688178, 1117454909, 936433444, 3490320968, 3675253459, 1240580251, 122909385, + 2157517691, 634681816, 4142456567, 3825094682, 3061402683, 2540495037, 79693498, 3249098678, 1084186820, 1583128258, + 426386531, 1761308591, 1047286709, 322548459, 995290223, 1845252383, 2603652396, 3431023940, 2942221577, 3202600964, + 3727903485, 1712269319, 422464435, 3234572375, 1170764815, 3523960633, 3117677531, 1434042557, 442511882, 3600875718, + 1076654713, 1738483198, 4213154764, 2393238008, 3677496056, 1014306527, 4251020053, 793779912, 2902807211, 842905082, + 4246964064, 1395751752, 1040244610, 2656851899, 3396308128, 445077038, 3742853595, 3577915638, 679411651, 2892444358, + 2354009459, 1767581616, 3150600392, 3791627101, 3102740896, 284835224, 4246832056, 1258075500, 768725851, 2589189241, + 3069724005, 3532540348, 1274779536, 3789419226, 2764799539, 1660621633, 3471099624, 4011903706, 913787905, 3497959166, + 737222580, 2514213453, 2928710040, 3937242737, 1804850592, 3499020752, 2949064160, 2386320175, 2390070455, 2415321851, + 4061277028, 2290661394, 2416832540, 1336762016, 1754252060, 3520065937, 3014181293, 791618072, 3188594551, 3933548030, + 2332172193, 3852520463, 3043980520, 413987798, 3465142937, 3030929376, 4245938359, 2093235073, 3534596313, 375366246, + 2157278981, 2479649556, 555357303, 3870105701, 2008414854, 3344188149, 4221384143, 3956125452, 2067696032, 3594591187, + 2921233993, 2428461, 544322398, 577241275, 1471733935, 610547355, 4027169054, 1432588573, 1507829418, 2025931657, + 3646575487, 545086370, 48609733, 2200306550, 1653985193, 298326376, 1316178497, 3007786442, 2064951626, 458293330, + 2589141269, 3591329599, 3164325604, 727753846, 2179363840, 146436021, 1461446943, 4069977195, 705550613, 3059967265, + 3887724982, 4281599278, 3313849956, 1404054877, 2845806497, 146425753, 1854211946, +]; +export const sBox1 = [ + 1266315497, 3048417604, 3681880366, 3289982499, 290971e4, 1235738493, 2632868024, 2414719590, 3970600049, 1771706367, + 1449415276, 3266420449, 422970021, 1963543593, 2690192192, 3826793022, 1062508698, 1531092325, 1804592342, 2583117782, + 2714934279, 4024971509, 1294809318, 4028980673, 1289560198, 2221992742, 1669523910, 35572830, 157838143, 1052438473, + 1016535060, 1802137761, 1753167236, 1386275462, 3080475397, 2857371447, 1040679964, 2145300060, 2390574316, + 1461121720, 2956646967, 4031777805, 4028374788, 33600511, 2920084762, 1018524850, 629373528, 3691585981, 3515945977, + 2091462646, 2486323059, 586499841, 988145025, 935516892, 3367335476, 2599673255, 2839830854, 265290510, 3972581182, + 2759138881, 3795373465, 1005194799, 847297441, 406762289, 1314163512, 1332590856, 1866599683, 4127851711, 750260880, + 613907577, 1450815602, 3165620655, 3734664991, 3650291728, 3012275730, 3704569646, 1427272223, 778793252, 1343938022, + 2676280711, 2052605720, 1946737175, 3164576444, 3914038668, 3967478842, 3682934266, 1661551462, 3294938066, + 4011595847, 840292616, 3712170807, 616741398, 312560963, 711312465, 1351876610, 322626781, 1910503582, 271666773, + 2175563734, 1594956187, 70604529, 3617834859, 1007753275, 1495573769, 4069517037, 2549218298, 2663038764, 504708206, + 2263041392, 3941167025, 2249088522, 1514023603, 1998579484, 1312622330, 694541497, 2582060303, 2151582166, 1382467621, + 776784248, 2618340202, 3323268794, 2497899128, 2784771155, 503983604, 4076293799, 907881277, 423175695, 432175456, + 1378068232, 4145222326, 3954048622, 3938656102, 3820766613, 2793130115, 2977904593, 26017576, 3274890735, 3194772133, + 1700274565, 1756076034, 4006520079, 3677328699, 720338349, 1533947780, 354530856, 688349552, 3973924725, 1637815568, + 332179504, 3949051286, 53804574, 2852348879, 3044236432, 1282449977, 3583942155, 3416972820, 4006381244, 1617046695, + 2628476075, 3002303598, 1686838959, 431878346, 2686675385, 1700445008, 1080580658, 1009431731, 832498133, 3223435511, + 2605976345, 2271191193, 2516031870, 1648197032, 4164389018, 2548247927, 300782431, 375919233, 238389289, 3353747414, + 2531188641, 2019080857, 1475708069, 455242339, 2609103871, 448939670, 3451063019, 1395535956, 2413381860, 1841049896, + 1491858159, 885456874, 4264095073, 4001119347, 1565136089, 3898914787, 1108368660, 540939232, 1173283510, 2745871338, + 3681308437, 4207628240, 3343053890, 4016749493, 1699691293, 1103962373, 3625875870, 2256883143, 3830138730, + 1031889488, 3479347698, 1535977030, 4236805024, 3251091107, 2132092099, 1774941330, 1199868427, 1452454533, 157007616, + 2904115357, 342012276, 595725824, 1480756522, 206960106, 497939518, 591360097, 863170706, 2375253569, 3596610801, + 1814182875, 2094937945, 3421402208, 1082520231, 3463918190, 2785509508, 435703966, 3908032597, 1641649973, 2842273706, + 3305899714, 1510255612, 2148256476, 2655287854, 3276092548, 4258621189, 236887753, 3681803219, 274041037, 1734335097, + 3815195456, 3317970021, 1899903192, 1026095262, 4050517792, 356393447, 2410691914, 3873677099, 3682840055, +]; +export const sBox2 = [ + 3913112168, 2491498743, 4132185628, 2489919796, 1091903735, 1979897079, 3170134830, 3567386728, 3557303409, 857797738, + 1136121015, 1342202287, 507115054, 2535736646, 337727348, 3213592640, 1301675037, 2528481711, 1895095763, 1721773893, + 3216771564, 62756741, 2142006736, 835421444, 2531993523, 1442658625, 3659876326, 2882144922, 676362277, 1392781812, + 170690266, 3921047035, 1759253602, 3611846912, 1745797284, 664899054, 1329594018, 3901205900, 3045908486, 2062866102, + 2865634940, 3543621612, 3464012697, 1080764994, 553557557, 3656615353, 3996768171, 991055499, 499776247, 1265440854, + 648242737, 3940784050, 980351604, 3713745714, 1749149687, 3396870395, 4211799374, 3640570775, 1161844396, 3125318951, + 1431517754, 545492359, 4268468663, 3499529547, 1437099964, 2702547544, 3433638243, 2581715763, 2787789398, 1060185593, + 1593081372, 2418618748, 4260947970, 69676912, 2159744348, 86519011, 2512459080, 3838209314, 1220612927, 3339683548, + 133810670, 1090789135, 1078426020, 1569222167, 845107691, 3583754449, 4072456591, 1091646820, 628848692, 1613405280, + 3757631651, 526609435, 236106946, 48312990, 2942717905, 3402727701, 1797494240, 859738849, 992217954, 4005476642, + 2243076622, 3870952857, 3732016268, 765654824, 3490871365, 2511836413, 1685915746, 3888969200, 1414112111, 2273134842, + 3281911079, 4080962846, 172450625, 2569994100, 980381355, 4109958455, 2819808352, 2716589560, 2568741196, 3681446669, + 3329971472, 1835478071, 660984891, 3704678404, 4045999559, 3422617507, 3040415634, 1762651403, 1719377915, 3470491036, + 2693910283, 3642056355, 3138596744, 1364962596, 2073328063, 1983633131, 926494387, 3423689081, 2150032023, 4096667949, + 1749200295, 3328846651, 309677260, 2016342300, 1779581495, 3079819751, 111262694, 1274766160, 443224088, 298511866, + 1025883608, 3806446537, 1145181785, 168956806, 3641502830, 3584813610, 1689216846, 3666258015, 3200248200, 1692713982, + 2646376535, 4042768518, 1618508792, 1610833997, 3523052358, 4130873264, 2001055236, 3610705100, 2202168115, + 4028541809, 2961195399, 1006657119, 2006996926, 3186142756, 1430667929, 3210227297, 1314452623, 4074634658, + 4101304120, 2273951170, 1399257539, 3367210612, 3027628629, 1190975929, 2062231137, 2333990788, 2221543033, + 2438960610, 1181637006, 548689776, 2362791313, 3372408396, 3104550113, 3145860560, 296247880, 1970579870, 3078560182, + 3769228297, 1714227617, 3291629107, 3898220290, 166772364, 1251581989, 493813264, 448347421, 195405023, 2709975567, + 677966185, 3703036547, 1463355134, 2715995803, 1338867538, 1343315457, 2802222074, 2684532164, 233230375, 2599980071, + 2000651841, 3277868038, 1638401717, 4028070440, 3237316320, 6314154, 819756386, 300326615, 590932579, 1405279636, + 3267499572, 3150704214, 2428286686, 3959192993, 3461946742, 1862657033, 1266418056, 963775037, 2089974820, 2263052895, + 1917689273, 448879540, 3550394620, 3981727096, 150775221, 3627908307, 1303187396, 508620638, 2975983352, 2726630617, + 1817252668, 1876281319, 1457606340, 908771278, 3720792119, 3617206836, 2455994898, 1729034894, 1080033504, +]; +export const sBox3 = [ + 976866871, 3556439503, 2881648439, 1522871579, 1555064734, 1336096578, 3548522304, 2579274686, 3574697629, 3205460757, + 3593280638, 3338716283, 3079412587, 564236357, 2993598910, 1781952180, 1464380207, 3163844217, 3332601554, 1699332808, + 1393555694, 1183702653, 3581086237, 1288719814, 691649499, 2847557200, 2895455976, 3193889540, 2717570544, 1781354906, + 1676643554, 2592534050, 3230253752, 1126444790, 2770207658, 2633158820, 2210423226, 2615765581, 2414155088, + 3127139286, 673620729, 2805611233, 1269405062, 4015350505, 3341807571, 4149409754, 1057255273, 2012875353, 2162469141, + 2276492801, 2601117357, 993977747, 3918593370, 2654263191, 753973209, 36408145, 2530585658, 25011837, 3520020182, + 2088578344, 530523599, 2918365339, 1524020338, 1518925132, 3760827505, 3759777254, 1202760957, 3985898139, 3906192525, + 674977740, 4174734889, 2031300136, 2019492241, 3983892565, 4153806404, 3822280332, 352677332, 2297720250, 60907813, + 90501309, 3286998549, 1016092578, 2535922412, 2839152426, 457141659, 509813237, 4120667899, 652014361, 1966332200, + 2975202805, 55981186, 2327461051, 676427537, 3255491064, 2882294119, 3433927263, 1307055953, 942726286, 933058658, + 2468411793, 3933900994, 4215176142, 1361170020, 2001714738, 2830558078, 3274259782, 1222529897, 1679025792, + 2729314320, 3714953764, 1770335741, 151462246, 3013232138, 1682292957, 1483529935, 471910574, 1539241949, 458788160, + 3436315007, 1807016891, 3718408830, 978976581, 1043663428, 3165965781, 1927990952, 4200891579, 2372276910, 3208408903, + 3533431907, 1412390302, 2931980059, 4132332400, 1947078029, 3881505623, 4168226417, 2941484381, 1077988104, + 1320477388, 886195818, 18198404, 3786409e3, 2509781533, 112762804, 3463356488, 1866414978, 891333506, 18488651, + 661792760, 1628790961, 3885187036, 3141171499, 876946877, 2693282273, 1372485963, 791857591, 2686433993, 3759982718, + 3167212022, 3472953795, 2716379847, 445679433, 3561995674, 3504004811, 3574258232, 54117162, 3331405415, 2381918588, + 3769707343, 4154350007, 1140177722, 4074052095, 668550556, 3214352940, 367459370, 261225585, 2610173221, 4209349473, + 3468074219, 3265815641, 314222801, 3066103646, 3808782860, 282218597, 3406013506, 3773591054, 379116347, 1285071038, + 846784868, 2669647154, 3771962079, 3550491691, 2305946142, 453669953, 1268987020, 3317592352, 3279303384, 3744833421, + 2610507566, 3859509063, 266596637, 3847019092, 517658769, 3462560207, 3443424879, 370717030, 4247526661, 2224018117, + 4143653529, 4112773975, 2788324899, 2477274417, 1456262402, 2901442914, 1517677493, 1846949527, 2295493580, + 3734397586, 2176403920, 1280348187, 1908823572, 3871786941, 846861322, 1172426758, 3287448474, 3383383037, 1655181056, + 3139813346, 901632758, 1897031941, 2986607138, 3066810236, 3447102507, 1393639104, 373351379, 950779232, 625454576, + 3124240540, 4148612726, 2007998917, 544563296, 2244738638, 2330496472, 2058025392, 1291430526, 424198748, 50039436, + 29584100, 3605783033, 2429876329, 2791104160, 1057563949, 3255363231, 3075367218, 3463963227, 1469046755, 985887462, +]; diff --git a/src/providers/sources/primewire/index.ts b/src/providers/sources/primewire/index.ts new file mode 100644 index 0000000..dca155a --- /dev/null +++ b/src/providers/sources/primewire/index.ts @@ -0,0 +1,118 @@ +import { load } from 'cheerio'; + +import { flags } from '@/entrypoint/utils/targets'; +import { makeSourcerer } from '@/providers/base'; +import { ScrapeContext } from '@/utils/context'; +import { NotFoundError } from '@/utils/errors'; + +import { primewireApiKey, primewireBase } from './common'; +import { getLinks } from './decryption/blowfish'; + +async function search(ctx: ScrapeContext, imdbId: string) { + const searchResult = await ctx.proxiedFetcher<{ + id: string; + }>('/api/v1/show/', { + baseUrl: primewireBase, + query: { + key: primewireApiKey, + imdb_id: imdbId, + }, + }); + + return searchResult.id; +} + +async function getStreams(title: string) { + const titlePage = load(title); + const userData = titlePage('#user-data').attr('v'); + if (!userData) throw new NotFoundError('No user data found'); + + const links = getLinks(userData); + + const embeds = []; + + if (!links) throw new NotFoundError('No links found'); + + for (const link in links) { + if (link.includes(link)) { + const element = titlePage(`.propper-link[link_version='${link}']`); + const sourceName = element.parent().parent().parent().find('.version-host').text().trim(); + let embedId; + switch (sourceName) { + case 'mixdrop.co': + embedId = 'mixdrop'; + break; + case 'voe.sx': + embedId = 'voe'; + break; + case 'upstream.to': + embedId = 'upstream'; + break; + case 'streamvid.net': + embedId = 'streamvid'; + break; + case 'dood.watch': + embedId = 'dood'; + break; + default: + embedId = null; + } + if (!embedId) continue; + embeds.push({ + url: `${primewireBase}/links/go/${links[link]}`, + embedId, + }); + } + } + + return embeds; +} + +export const primewireScraper = makeSourcerer({ + id: 'primewire', + name: 'Primewire', + rank: 110, + flags: [flags.CORS_ALLOWED], + async scrapeMovie(ctx) { + if (!ctx.media.imdbId) throw new Error('No imdbId provided'); + const searchResult = await search(ctx, ctx.media.imdbId); + + const title = await ctx.proxiedFetcher(`movie/${searchResult}`, { + baseUrl: primewireBase, + }); + + const embeds = await getStreams(title); + + return { + embeds, + }; + }, + async scrapeShow(ctx) { + if (!ctx.media.imdbId) throw new Error('No imdbId provided'); + const searchResult = await search(ctx, ctx.media.imdbId); + + const season = await ctx.proxiedFetcher(`tv/${searchResult}`, { + baseUrl: primewireBase, + }); + + const seasonPage = load(season); + + const episodeLink = seasonPage(`.show_season[data-id='${ctx.media.season.number}'] > div > a`) + .toArray() + .find((link) => { + return link.attribs.href.includes(`-episode-${ctx.media.episode.number}`); + })?.attribs.href; + + if (!episodeLink) throw new NotFoundError('No episode links found'); + + const title = await ctx.proxiedFetcher(episodeLink, { + baseUrl: primewireBase, + }); + + const embeds = await getStreams(title); + + return { + embeds, + }; + }, +}); diff --git a/src/providers/sources/remotestream.ts b/src/providers/sources/remotestream.ts index ab884ab..15b07df 100644 --- a/src/providers/sources/remotestream.ts +++ b/src/providers/sources/remotestream.ts @@ -11,7 +11,7 @@ export const remotestreamScraper = makeSourcerer({ id: 'remotestream', name: 'Remote Stream', disabled: true, - rank: 55, + rank: 20, flags: [flags.CORS_ALLOWED], async scrapeShow(ctx) { const seasonNumber = ctx.media.season.number; diff --git a/src/providers/sources/ridomovies/index.ts b/src/providers/sources/ridomovies/index.ts index fba442a..1fa7659 100644 --- a/src/providers/sources/ridomovies/index.ts +++ b/src/providers/sources/ridomovies/index.ts @@ -68,7 +68,7 @@ const universalScraper = async (ctx: MovieScrapeContext | ShowScrapeContext) => export const ridooMoviesScraper = makeSourcerer({ id: 'ridomovies', name: 'RidoMovies', - rank: 105, + rank: 100, flags: [flags.CORS_ALLOWED], scrapeMovie: universalScraper, scrapeShow: universalScraper, diff --git a/src/providers/sources/showbox/index.ts b/src/providers/sources/showbox/index.ts index ce3eceb..c22979a 100644 --- a/src/providers/sources/showbox/index.ts +++ b/src/providers/sources/showbox/index.ts @@ -41,7 +41,7 @@ async function comboScraper(ctx: ShowScrapeContext | MovieScrapeContext): Promis export const showboxScraper = makeSourcerer({ id: 'showbox', name: 'Showbox', - rank: 400, + rank: 150, flags: [flags.CORS_ALLOWED, flags.CF_BLOCKED], scrapeShow: comboScraper, scrapeMovie: comboScraper, diff --git a/src/providers/sources/smashystream/index.ts b/src/providers/sources/smashystream/index.ts index 62102ea..f4a47d0 100644 --- a/src/providers/sources/smashystream/index.ts +++ b/src/providers/sources/smashystream/index.ts @@ -57,8 +57,9 @@ const universalScraper = async (ctx: ShowScrapeContext | MovieScrapeContext): Pr export const smashyStreamScraper = makeSourcerer({ id: 'smashystream', name: 'SmashyStream', - rank: 70, + rank: 30, flags: [flags.CORS_ALLOWED], + disabled: true, scrapeMovie: universalScraper, scrapeShow: universalScraper, }); diff --git a/src/providers/sources/vidsrc/index.ts b/src/providers/sources/vidsrc/index.ts index 6331a05..d6b15a8 100644 --- a/src/providers/sources/vidsrc/index.ts +++ b/src/providers/sources/vidsrc/index.ts @@ -6,7 +6,7 @@ import { scrapeShow } from '@/providers/sources/vidsrc/scrape-show'; export const vidsrcScraper = makeSourcerer({ id: 'vidsrc', name: 'VidSrc', - rank: 120, + rank: 90, flags: [flags.CORS_ALLOWED], scrapeMovie, scrapeShow, diff --git a/src/providers/sources/vidsrcto/index.ts b/src/providers/sources/vidsrcto/index.ts index c6c9fb5..d5f72e2 100644 --- a/src/providers/sources/vidsrcto/index.ts +++ b/src/providers/sources/vidsrcto/index.ts @@ -11,11 +11,11 @@ const vidSrcToBase = 'https://vidsrc.to'; const referer = `${vidSrcToBase}/`; const universalScraper = async (ctx: ShowScrapeContext | MovieScrapeContext): Promise => { - const imdbId = ctx.media.imdbId; + const mediaId = ctx.media.imdbId ?? ctx.media.tmdbId; const url = ctx.media.type === 'movie' - ? `/embed/movie/${imdbId}` - : `/embed/tv/${imdbId}/${ctx.media.season.number}/${ctx.media.episode.number}`; + ? `/embed/movie/${mediaId}` + : `/embed/tv/${mediaId}/${ctx.media.season.number}/${ctx.media.episode.number}`; const mainPage = await ctx.proxiedFetcher(url, { baseUrl: vidSrcToBase, headers: { @@ -79,5 +79,5 @@ export const vidSrcToScraper = makeSourcerer({ scrapeMovie: universalScraper, scrapeShow: universalScraper, flags: [flags.CORS_ALLOWED], - rank: 300, + rank: 130, }); diff --git a/src/providers/sources/zoechip/index.ts b/src/providers/sources/zoechip/index.ts index ee7f3a6..6a991a0 100644 --- a/src/providers/sources/zoechip/index.ts +++ b/src/providers/sources/zoechip/index.ts @@ -6,8 +6,9 @@ import { scrapeShow } from '@/providers/sources/zoechip/scrape-show'; export const zoechipScraper = makeSourcerer({ id: 'zoechip', name: 'ZoeChip', - rank: 200, + rank: 62, flags: [flags.CORS_ALLOWED], + disabled: true, scrapeMovie, scrapeShow, });