From 5e0a3880564fdc0d668895a71fe2a5b4e7044ada Mon Sep 17 00:00:00 2001 From: alwaysmesmerizingyou Date: Sun, 11 Oct 2026 16:04:29 -0700 Subject: [PATCH] added 3eyeddraven, cinemaarmy, movenerds, thugtv, vidy --- package-lock.json | 441 +++++++++++++++++++++++++++++++++++++- package.json | 3 + src/config.js | 103 ++++++++- src/sources/3eyedraven.js | 136 ++++++++++++ src/sources/cinemaarmy.js | 181 ++++++++++++++++ src/sources/movenerds.js | 125 +++++++++++ src/sources/rivestream.js | 2 - src/sources/thugtv.js | 62 ++++++ src/sources/vidy.js | 187 ++++++++++++++++ test/index.js | 2 +- 10 files changed, 1230 insertions(+), 12 deletions(-) create mode 100644 src/sources/3eyedraven.js create mode 100644 src/sources/cinemaarmy.js create mode 100644 src/sources/movenerds.js create mode 100644 src/sources/thugtv.js create mode 100644 src/sources/vidy.js diff --git a/package-lock.json b/package-lock.json index 493a2ee..7070afe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,17 +1,353 @@ { "name": "@vyla-entertainment/sdk", - "version": "1.2.0", + "version": "1.15.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@vyla-entertainment/sdk", - "version": "1.2.0", + "version": "1.15.0", "license": "CC-BY-NC-4.0", + "dependencies": { + "node-tls-client": "^2.1.0" + }, "devDependencies": { "dotenv": "^17.4.2" } }, + "node_modules/@napi-rs/nice": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice/-/nice-1.1.1.tgz", + "integrity": "sha512-xJIPs+bYuc9ASBl+cvGsKbGrJmS6fAKaSZCnT0lhahT5rhA2VVy9/EcIgd2JhtEuFOJNx7UHNn/qiTPTY4nrQw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "optionalDependencies": { + "@napi-rs/nice-android-arm-eabi": "1.1.1", + "@napi-rs/nice-android-arm64": "1.1.1", + "@napi-rs/nice-darwin-arm64": "1.1.1", + "@napi-rs/nice-darwin-x64": "1.1.1", + "@napi-rs/nice-freebsd-x64": "1.1.1", + "@napi-rs/nice-linux-arm-gnueabihf": "1.1.1", + "@napi-rs/nice-linux-arm64-gnu": "1.1.1", + "@napi-rs/nice-linux-arm64-musl": "1.1.1", + "@napi-rs/nice-linux-ppc64-gnu": "1.1.1", + "@napi-rs/nice-linux-riscv64-gnu": "1.1.1", + "@napi-rs/nice-linux-s390x-gnu": "1.1.1", + "@napi-rs/nice-linux-x64-gnu": "1.1.1", + "@napi-rs/nice-linux-x64-musl": "1.1.1", + "@napi-rs/nice-openharmony-arm64": "1.1.1", + "@napi-rs/nice-win32-arm64-msvc": "1.1.1", + "@napi-rs/nice-win32-ia32-msvc": "1.1.1", + "@napi-rs/nice-win32-x64-msvc": "1.1.1" + } + }, + "node_modules/@napi-rs/nice-android-arm-eabi": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-android-arm-eabi/-/nice-android-arm-eabi-1.1.1.tgz", + "integrity": "sha512-kjirL3N6TnRPv5iuHw36wnucNqXAO46dzK9oPb0wj076R5Xm8PfUVA9nAFB5ZNMmfJQJVKACAPd/Z2KYMppthw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-android-arm64": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-android-arm64/-/nice-android-arm64-1.1.1.tgz", + "integrity": "sha512-blG0i7dXgbInN5urONoUCNf+DUEAavRffrO7fZSeoRMJc5qD+BJeNcpr54msPF6qfDD6kzs9AQJogZvT2KD5nw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-darwin-arm64": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-darwin-arm64/-/nice-darwin-arm64-1.1.1.tgz", + "integrity": "sha512-s/E7w45NaLqTGuOjC2p96pct4jRfo61xb9bU1unM/MJ/RFkKlJyJDx7OJI/O0ll/hrfpqKopuAFDV8yo0hfT7A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-darwin-x64": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-darwin-x64/-/nice-darwin-x64-1.1.1.tgz", + "integrity": "sha512-dGoEBnVpsdcC+oHHmW1LRK5eiyzLwdgNQq3BmZIav+9/5WTZwBYX7r5ZkQC07Nxd3KHOCkgbHSh4wPkH1N1LiQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-freebsd-x64": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-freebsd-x64/-/nice-freebsd-x64-1.1.1.tgz", + "integrity": "sha512-kHv4kEHAylMYmlNwcQcDtXjklYp4FCf0b05E+0h6nDHsZ+F0bDe04U/tXNOqrx5CmIAth4vwfkjjUmp4c4JktQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-arm-gnueabihf": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm-gnueabihf/-/nice-linux-arm-gnueabihf-1.1.1.tgz", + "integrity": "sha512-E1t7K0efyKXZDoZg1LzCOLxgolxV58HCkaEkEvIYQx12ht2pa8hoBo+4OB3qh7e+QiBlp1SRf+voWUZFxyhyqg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-arm64-gnu": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm64-gnu/-/nice-linux-arm64-gnu-1.1.1.tgz", + "integrity": "sha512-CIKLA12DTIZlmTaaKhQP88R3Xao+gyJxNWEn04wZwC2wmRapNnxCUZkVwggInMJvtVElA+D4ZzOU5sX4jV+SmQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-arm64-musl": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm64-musl/-/nice-linux-arm64-musl-1.1.1.tgz", + "integrity": "sha512-+2Rzdb3nTIYZ0YJF43qf2twhqOCkiSrHx2Pg6DJaCPYhhaxbLcdlV8hCRMHghQ+EtZQWGNcS2xF4KxBhSGeutg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-ppc64-gnu": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-ppc64-gnu/-/nice-linux-ppc64-gnu-1.1.1.tgz", + "integrity": "sha512-4FS8oc0GeHpwvv4tKciKkw3Y4jKsL7FRhaOeiPei0X9T4Jd619wHNe4xCLmN2EMgZoeGg+Q7GY7BsvwKpL22Tg==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-riscv64-gnu": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-riscv64-gnu/-/nice-linux-riscv64-gnu-1.1.1.tgz", + "integrity": "sha512-HU0nw9uD4FO/oGCCk409tCi5IzIZpH2agE6nN4fqpwVlCn5BOq0MS1dXGjXaG17JaAvrlpV5ZeyZwSon10XOXw==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-s390x-gnu": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-s390x-gnu/-/nice-linux-s390x-gnu-1.1.1.tgz", + "integrity": "sha512-2YqKJWWl24EwrX0DzCQgPLKQBxYDdBxOHot1KWEq7aY2uYeX+Uvtv4I8xFVVygJDgf6/92h9N3Y43WPx8+PAgQ==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-x64-gnu": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-x64-gnu/-/nice-linux-x64-gnu-1.1.1.tgz", + "integrity": "sha512-/gaNz3R92t+dcrfCw/96pDopcmec7oCcAQ3l/M+Zxr82KT4DljD37CpgrnXV+pJC263JkW572pdbP3hP+KjcIg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-x64-musl": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-x64-musl/-/nice-linux-x64-musl-1.1.1.tgz", + "integrity": "sha512-xScCGnyj/oppsNPMnevsBe3pvNaoK7FGvMjT35riz9YdhB2WtTG47ZlbxtOLpjeO9SqqQ2J2igCmz6IJOD5JYw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-openharmony-arm64": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-openharmony-arm64/-/nice-openharmony-arm64-1.1.1.tgz", + "integrity": "sha512-6uJPRVwVCLDeoOaNyeiW0gp2kFIM4r7PL2MczdZQHkFi9gVlgm+Vn+V6nTWRcu856mJ2WjYJiumEajfSm7arPQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-win32-arm64-msvc": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-arm64-msvc/-/nice-win32-arm64-msvc-1.1.1.tgz", + "integrity": "sha512-uoTb4eAvM5B2aj/z8j+Nv8OttPf2m+HVx3UjA5jcFxASvNhQriyCQF1OB1lHL43ZhW+VwZlgvjmP5qF3+59atA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-win32-ia32-msvc": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-ia32-msvc/-/nice-win32-ia32-msvc-1.1.1.tgz", + "integrity": "sha512-CNQqlQT9MwuCsg1Vd/oKXiuH+TcsSPJmlAFc5frFyX/KkOh0UpBLEj7aoY656d5UKZQMQFP7vJNa1DNUNORvug==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-win32-x64-msvc": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-x64-msvc/-/nice-win32-x64-msvc-1.1.1.tgz", + "integrity": "sha512-vB+4G/jBQCAh0jelMTY3+kgFy00Hlx2f2/1zjMoH821IbplbWZOkLiTYXQkygNTzQJTq5cvwBDgn2ppHD+bglQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-parser": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz", + "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==", + "license": "MIT", + "dependencies": { + "cookie": "0.7.2", + "cookie-signature": "1.0.6" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "license": "MIT" + }, "node_modules/dotenv": { "version": "17.4.2", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.2.tgz", @@ -24,6 +360,107 @@ "funding": { "url": "https://dotenvx.com" } + }, + "node_modules/koffi": { + "version": "2.16.3", + "resolved": "https://registry.npmjs.org/koffi/-/koffi-2.16.3.tgz", + "integrity": "sha512-E9y1AsgYGlaxMhcZzHr8y96QF2U5XzA12GGVAfbWqIubTwPNMXQarfBzePNXHe0xtIEtNd6ifAv3GAKYGUeBAQ==", + "hasInstallScript": true, + "license": "MIT", + "funding": { + "url": "https://liberapay.com/Koromix" + } + }, + "node_modules/node-tls-client": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/node-tls-client/-/node-tls-client-2.1.0.tgz", + "integrity": "sha512-b1Y2WFdQ7BJXa5CT6qRafCFeaNiTsZP+UFqNy+8hrHExlPHe3ccEtFkKt3WDGzGIfNvb4Ws9EEQmzFS9Jztisg==", + "license": "GPL-3.0", + "dependencies": { + "cookie-parser": "^1.4.7", + "koffi": "^2.8.9", + "piscina": "^5.1.1", + "tough-cookie": "^4.1.4" + } + }, + "node_modules/piscina": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/piscina/-/piscina-5.3.2.tgz", + "integrity": "sha512-vv7l/mM7B9WgrhDaudIqv1x6v4LOBRYYmpWGdWushhpbCmVHp11sUB2v4hj1CAeTGXVxuZFz6xOU1RalAiPKfQ==", + "license": "MIT", + "engines": { + "node": ">=20.x" + }, + "optionalDependencies": { + "@napi-rs/nice": "^1.0.4" + } + }, + "node_modules/psl": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", + "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "funding": { + "url": "https://github.com/sponsors/lupomontero" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "license": "MIT" + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "license": "MIT" + }, + "node_modules/tough-cookie": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "license": "BSD-3-Clause", + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "license": "MIT", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } } } } diff --git a/package.json b/package.json index a044292..4bbde1b 100644 --- a/package.json +++ b/package.json @@ -29,5 +29,8 @@ }, "publishConfig": { "access": "public" + }, + "dependencies": { + "node-tls-client": "^2.1.0" } } \ No newline at end of file diff --git a/src/config.js b/src/config.js index b0a5414..4bb0ef6 100644 --- a/src/config.js +++ b/src/config.js @@ -1,11 +1,23 @@ -// Currently active sources reported on 6:18:22 PM PST 9/13/2026 - -// None Anime Tested: 936075 -// Anime Tested: 37854 - -// Fshare/Fsonic Tested: 155 ( they don't have Michael ) - export const SOURCES = [ + { + key: '3eyedraven', + label: '3eyedraven', + sourceFile: '3eyedraven', + proxyParam: '3er', + timeout: 25000, + jitter: 500, + retries: 2, + multiUrl: false, + skipProxy: true, + cdnHeaders: [{ + pattern: /3eyedraven\.watch/i, + headers: { + Referer: 'https://3eyedraven.watch/', + Origin: 'https://3eyedraven.watch', + }, + }], + }, + { key: '123anime', label: '123Anime', @@ -106,6 +118,25 @@ export const SOURCES = [ }], }, + { + key: 'cinemaarmy', + label: 'CinemaArmy', + sourceFile: 'cinemaarmy', + proxyParam: 'ca', + timeout: 30000, + jitter: 500, + retries: 2, + multiUrl: true, + skipProxy: true, + cdnHeaders: [{ + pattern: /cinema\.army/i, + headers: { + Referer: 'https://cinema.army/', + Origin: 'https://cinema.army', + }, + }], + }, + { key: 'dulo', label: 'Dulo', @@ -191,6 +222,25 @@ export const SOURCES = [ }, }, + { + key: 'movenerds', + label: 'MoverNerds', + sourceFile: 'movenerds', + proxyParam: 'mnr', + timeout: 30000, + jitter: 500, + retries: 2, + multiUrl: true, + skipProxy: true, + cdnHeaders: [{ + pattern: /movenerds\.space/i, + headers: { + Referer: 'https://movenerds.space/', + Origin: 'https://movenerds.space', + }, + }], + }, + { key: 'movienight', label: 'MovieNight', @@ -293,6 +343,26 @@ export const SOURCES = [ }, }], }, + + { + key: 'thugtv', + label: 'ThugTV', + sourceFile: 'thugtv', + proxyParam: 'tv', + timeout: 30000, + jitter: 500, + retries: 2, + multiUrl: true, + skipProxy: true, + cdnHeaders: [{ + pattern: /thugtv\.online|thuktv\.onrender\.com/i, + headers: { + Referer: 'https://thugtv.online/', + Origin: 'https://thugtv.online', + }, + }], + }, + { key: 'vidapi', label: 'VidAPI', @@ -426,6 +496,25 @@ export const SOURCES = [ multiUrl: true, }, + { + key: 'vidy', + label: 'Vidy', + sourceFile: 'vidy', + proxyParam: 'vy', + timeout: 25000, + jitter: 500, + retries: 2, + multiUrl: true, + skipProxy: true, + cdnHeaders: [{ + pattern: /wecollege\.net|vidy\.st/i, + headers: { + Referer: 'https://www.vidy.st/', + Origin: 'https://www.vidy.st', + }, + }], + }, + { key: 'vidzee', label: 'VidZee', diff --git a/src/sources/3eyedraven.js b/src/sources/3eyedraven.js new file mode 100644 index 0000000..1c6f08a --- /dev/null +++ b/src/sources/3eyedraven.js @@ -0,0 +1,136 @@ +import { getUA } from "../utils/helpers.js"; + +const BASE_URL = "https://3eyedraven.watch"; +const API_BASE = "https://3eyedraven.watch/api"; + +function getHeaders() { + return { + "User-Agent": getUA(), + Accept: "application/json", + Referer: `${BASE_URL}/`, + Origin: BASE_URL, + }; +} + +function resolveApiKey(args) { + return ( + args?.tmdbApiKey || + args?.sdk?.tmdbApiKey || + process.env.TMDB_API_KEY || + null + ); +} + +async function getTitle(id, isTv, apiKey) { + if (!apiKey) return null; + const type = isTv ? "tv" : "movie"; + try { + const res = await fetch( + `https://api.themoviedb.org/3/${type}/${id}?api_key=${apiKey}`, + { signal: AbortSignal.timeout(5000) } + ); + if (!res.ok) return null; + const data = await res.json(); + return isTv ? data.name : data.title; + } catch { + return null; + } +} + +function slugify(text) { + return text + .toLowerCase() + .normalize("NFKD") + .replace(/[\u0300-\u036f]/g, "") + .replace(/[^a-z0-9]+/g, "-") + .replace(/^-|-$/g, ""); +} + +function absolute(url) { + if (!url) return ""; + if (/^https?:\/\//.test(url)) return url; + return `${BASE_URL}${url.startsWith("/") ? "" : "/"}${url}`; +} + +export async function getStream(args) { + const { id, s = null, e = null } = args; + const isTv = s != null && e != null; + + const apiKey = resolveApiKey(args); + const title = await getTitle(id, isTv, apiKey); + + if (!title) { + if (process.env.DEBUG_CAUSE) { + console.error( + `[3eyedraven] Could not resolve title for ${isTv ? "tv" : "movie"} ${id} ` + + `(tmdbApiKey ${apiKey ? "present" : "MISSING"})` + ); + } + return null; + } + + const identifier = `${slugify(title)}-${id}`; + + const url = isTv + ? `${API_BASE}/stream/${identifier}?season=${s}&episode=${e}` + : `${API_BASE}/stream/${identifier}`; + + try { + const res = await fetch(url, { + headers: getHeaders(), + signal: AbortSignal.timeout(15000), + }); + + if (!res.ok) { + if (process.env.DEBUG_CAUSE) { + console.error(`[3eyedraven] HTTP ${res.status} for ${url}`); + } + return null; + } + + const data = await res.json(); + const stream = data?.stream; + if (!stream?.url) { + if (process.env.DEBUG_CAUSE) { + console.error(`[3eyedraven] No stream.url in response for ${url}`); + } + return null; + } + + const streamUrl = absolute(stream.url); + if (!/^https?:\/\//.test(streamUrl)) return null; + + const subtitles = (stream.subtitles || []) + .map(sub => { + const subUrl = absolute(sub.url); + if (!subUrl) return null; + return { + label: sub.label || sub.language || "Subtitle", + file: subUrl, + type: "vtt", + source: "3eyedraven", + }; + }) + .filter(Boolean); + + return { + url: streamUrl, + headers: { + "User-Agent": getUA(), + Referer: `${BASE_URL}/`, + Origin: BASE_URL, + }, + server: "3eyedraven", + quality: "1080p", + type: stream.format === "hls" ? "hls" : "mp4", + subtitles, + skipProxy: true, + }; + } catch (err) { + if (process.env.DEBUG_CAUSE) { + console.error(`[3eyedraven] fetch error for ${url}:`, err.message); + if (err.cause) console.error(" cause:", err.cause.code, err.cause.message); + } + return null; + } +} \ No newline at end of file diff --git a/src/sources/cinemaarmy.js b/src/sources/cinemaarmy.js new file mode 100644 index 0000000..b50f621 --- /dev/null +++ b/src/sources/cinemaarmy.js @@ -0,0 +1,181 @@ +import crypto from "crypto"; +import { Session, ClientIdentifier, initTLS, destroyTLS } from "node-tls-client"; + +const BASE_URL = "https://cinema.army"; +const API_BASE = "https://cinema.army/api"; + +const XOR_KEY = "f16a6b92fcd6eed3e9476297f893f2505c32f79bc6efef9b6c95496aa5914548"; +const HMAC_KEY = "a2cfc9eaa2a3690b5d4f6b5f958dbdeae52a097283d375bd253ee7c7062524f5"; + +const UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"; + +const SERVERS = [ + { key: "cookiebakers", method: "POST", label: "CookieBakers" }, + { key: "tcloud", method: "GET", label: "TCloud" }, + { key: "ipcloud", method: "GET", label: "IPcloud" }, + { key: "dcloud", method: "GET", label: "Dcloud" }, +]; + +let tlsInitialized = false; +let session = null; + +async function ensureSession() { + if (!tlsInitialized) { + await initTLS(); + tlsInitialized = true; + } + if (!session) { + session = new Session({ + clientIdentifier: ClientIdentifier.chrome_131, + timeout: 15000, + insecureSkipVerify: false, + }); + } + return session; +} + +function getHeaders(token, id, isTv, s, e) { + const referer = isTv + ? `${BASE_URL}/watch?id=${id}&type=tv&season=${s}&episode=${e}` + : `${BASE_URL}/watch?id=${id}&type=movie`; + + return { + "User-Agent": UA, + "Accept": "application/json", + "Accept-Language": "en-US,en;q=0.9", + "Accept-Encoding": "gzip, deflate, br", + "Cache-Control": "no-cache", + "Pragma": "no-cache", + "Referer": referer, + "Origin": BASE_URL, + "Sec-Ch-Ua": '"Chromium";v="131", "Not_A Brand";v="24"', + "Sec-Ch-Ua-Mobile": "?0", + "Sec-Ch-Ua-Platform": '"Windows"', + "Sec-Fetch-Dest": "empty", + "Sec-Fetch-Mode": "cors", + "Sec-Fetch-Site": "same-origin", + "Priority": "u=1, i", + "X-Stream-Token": token, + }; +} + +function generateToken(id) { + try { + const minute = Math.floor(Date.now() / 1000 / 60); + return crypto + .createHmac("sha256", HMAC_KEY) + .update(`${id}:${minute}`) + .digest("hex"); + } catch { + return ""; + } +} + +function decryptStreamUrl(encoded) { + if (!encoded) return ""; + if (encoded.startsWith("http://") || encoded.startsWith("https://")) return encoded; + try { + const decoded = Buffer.from(encoded, "base64").toString("binary"); + let out = ""; + for (let i = 0; i < decoded.length; i++) { + out += String.fromCharCode( + decoded.charCodeAt(i) ^ XOR_KEY.charCodeAt(i % XOR_KEY.length) + ); + } + return out; + } catch { + return encoded; + } +} + +export async function getStream(args) { + const { id, s = null, e = null } = args; + const isTv = s != null && e != null; + + const token = generateToken(id); + const headers = getHeaders(token, id, isTv, s, e); + + const sess = await ensureSession(); + + const requests = SERVERS.map(server => { + const path = isTv + ? `/${server.key}/tv/${id}/${s}/${e}` + : `/${server.key}/movie/${id}`; + return { url: `${API_BASE}${path}`, server }; + }); + + const results = await Promise.allSettled( + requests.map(async ({ url, server }) => { + try { + const res = server.method === "POST" + ? await sess.post(url, { headers }) + : await sess.get(url, { headers }); + + if (res.status !== 200) { + if (process.env.DEBUG_CAUSE) { + console.error(`[CinemaArmy ${server.key}] HTTP ${res.status}`); + } + return null; + } + + const text = await res.text(); + let data; + try { + data = JSON.parse(text); + } catch { + return null; + } + + const encrypted = data?.stream?.url; + if (!encrypted) return null; + + const streamUrl = decryptStreamUrl(encrypted); + if (!streamUrl || !/^https?:\/\//.test(streamUrl)) return null; + + return { + url: streamUrl, + headers: { + "User-Agent": UA, + Referer: `${BASE_URL}/`, + Origin: BASE_URL, + }, + server: `CinemaArmy (${server.label})`, + quality: "1080p", + type: streamUrl.includes(".m3u8") ? "hls" : "mp4", + }; + } catch (err) { + if (process.env.DEBUG_CAUSE) { + console.error(`[CinemaArmy ${server.key}] ${err.message}`); + if (err.cause) { + console.error(` cause.code: ${err.cause.code || "n/a"}`); + console.error(` cause.message: ${err.cause.message || err.cause}`); + } + } + return null; + } + }) + ); + + const allUrls = results + .filter(r => r.status === "fulfilled" && r.value) + .map(r => r.value); + + if (!allUrls.length) return null; + + return { allUrls }; +} + +export async function cleanup() { + if (session) { + try { + await session.close(); + } catch { } + session = null; + } + if (tlsInitialized) { + try { + await destroyTLS(); + } catch { } + tlsInitialized = false; + } +} \ No newline at end of file diff --git a/src/sources/movenerds.js b/src/sources/movenerds.js new file mode 100644 index 0000000..38a6ee0 --- /dev/null +++ b/src/sources/movenerds.js @@ -0,0 +1,125 @@ +import { getUA } from "../utils/helpers.js"; + +const API_BASE = "https://api.movenerds.space/api"; + +function getHeaders() { + return { + "User-Agent": getUA(), + Accept: "application/json", + Referer: "https://movenerds.space/", + Origin: "https://movenerds.space", + }; +} + +function resolveApiKey(args) { + return ( + args?.tmdbApiKey || + args?.sdk?.tmdbApiKey || + process.env.TMDB_API_KEY || + null + ); +} + +async function getMeta(id, isTv, apiKey) { + if (!apiKey) return null; + const type = isTv ? "tv" : "movie"; + try { + const res = await fetch( + `https://api.themoviedb.org/3/${type}/${id}?api_key=${apiKey}`, + { signal: AbortSignal.timeout(5000) } + ); + if (!res.ok) return null; + const data = await res.json(); + const title = isTv ? data.name : data.title; + const date = isTv ? data.first_air_date : data.release_date; + const year = date ? parseInt(date.slice(0, 4), 10) : null; + return { title, year }; + } catch { + return null; + } +} + +function mapSource(source) { + if (!source || !source.url) return null; + if (source.type !== "hls" && source.type !== "mp4") return null; + if (!/^https?:\/\//.test(source.url)) return null; + + return { + url: source.url, + headers: { + "User-Agent": source.headers?.["User-Agent"] || getUA(), + Referer: source.headers?.Referer || "https://movenerds.space/", + Origin: source.headers?.Origin || "https://movenerds.space", + }, + server: source.name || source.providerName || "MoverNerds", + quality: source.quality || "1080p", + type: source.type, + skipProxy: true, + }; +} + +export async function getStream(args) { + const { id, s = null, e = null } = args; + const isTv = s != null && e != null; + + const apiKey = resolveApiKey(args); + const meta = await getMeta(id, isTv, apiKey); + + if (!meta?.title) { + if (process.env.DEBUG_CAUSE) { + console.error( + `[movenerds] Could not resolve title for ${isTv ? "tv" : "movie"} ${id} ` + + `(tmdbApiKey ${apiKey ? "present" : "MISSING"})` + ); + } + return null; + } + + const params = new URLSearchParams({ + tmdbId: String(id), + type: isTv ? "tv" : "movie", + title: meta.title, + }); + if (meta.year) params.set("year", String(meta.year)); + if (isTv) { + params.set("season", String(s)); + params.set("episode", String(e)); + params.set("language", "en"); + } + + const url = `${API_BASE}/stream?${params.toString()}`; + + try { + const res = await fetch(url, { + headers: getHeaders(), + signal: AbortSignal.timeout(20000), + }); + + if (!res.ok) { + if (process.env.DEBUG_CAUSE) { + console.error(`[movenerds] HTTP ${res.status} for ${url}`); + } + return null; + } + + const data = await res.json(); + const primary = Array.isArray(data?.primarySources) ? data.primarySources : []; + if (!primary.length) { + if (process.env.DEBUG_CAUSE) { + console.error(`[movenerds] No primarySources in response for ${url}`); + } + return null; + } + + const allUrls = primary.map(mapSource).filter(Boolean); + if (!allUrls.length) return null; + + return { allUrls }; + } catch (err) { + if (process.env.DEBUG_CAUSE) { + console.error(`[movenerds] fetch error for ${url}:`, err.message); + if (err.cause) console.error(" cause:", err.cause.code, err.cause.message); + } + return null; + } +} \ No newline at end of file diff --git a/src/sources/rivestream.js b/src/sources/rivestream.js index d4ebf25..e388c62 100644 --- a/src/sources/rivestream.js +++ b/src/sources/rivestream.js @@ -1,5 +1,3 @@ -// im being salty - import { USER_AGENT, fetchJson } from '../utils/helpers.js'; const C_ARRAY = [ diff --git a/src/sources/thugtv.js b/src/sources/thugtv.js new file mode 100644 index 0000000..9fefae1 --- /dev/null +++ b/src/sources/thugtv.js @@ -0,0 +1,62 @@ +import { getUA } from "../utils/helpers.js"; + +const API_BASE = "https://thuktv.onrender.com/api"; + +function getHeaders() { + return { + "User-Agent": getUA(), + Accept: "application/json", + Referer: "https://thugtv.online/", + Origin: "https://thugtv.online", + }; +} + +function mapSource(source) { + if (!source || !source.url) return null; + if (source.type !== "hls" && source.type !== "mp4") return null; + if (!/^https?:\/\//.test(source.url)) return null; + + return { + url: source.url, + headers: { + "User-Agent": getUA(), + Referer: "https://thugtv.online/", + Origin: "https://thugtv.online", + }, + server: source.name || source.label || "ThugTV", + quality: source.quality || "1080p", + type: source.type, + skipProxy: true, + }; +} + +export async function getStream(args) { + const { id, s = null, e = null } = args; + const isTv = s != null && e != null; + + const url = isTv + ? `${API_BASE}/stream/tv/${id}/${s}/${e}` + : `${API_BASE}/stream/movie/${id}`; + + try { + const res = await fetch(url, { + headers: getHeaders(), + signal: AbortSignal.timeout(15000), + }); + if (!res.ok) return null; + + const data = await res.json(); + const sources = Array.isArray(data?.sources) ? data.sources : []; + if (!sources.length) return null; + + const allUrls = sources + .map(mapSource) + .filter(Boolean); + + if (!allUrls.length) return null; + + return { allUrls }; + } catch { + return null; + } +} \ No newline at end of file diff --git a/src/sources/vidy.js b/src/sources/vidy.js new file mode 100644 index 0000000..fbe309c --- /dev/null +++ b/src/sources/vidy.js @@ -0,0 +1,187 @@ +import { getUA } from "../utils/helpers.js"; + +const API_URL = "https://api.wecollege.net"; +const SERVERS = ["miami", "atlanta", "seattle", "denver", "dallas", "tampa"]; +const MAGIC = [109, 118, 109, 49]; + +function imul(a, b) { + return Math.imul(a | 0, b | 0) >>> 0; +} + +function pFn(e) { + e = e >>> 0; + e = (e ^ (e >>> 16)) >>> 0; + e = imul(e, 2246822507); + e = (e ^ (e >>> 13)) >>> 0; + e = imul(e, 3266489909); + e = (e ^ (e >>> 16)) >>> 0; + return e; +} + +function gFn(e, t) { + e = e >>> 0; + t = t & 31; + if (t === 0) return e; + return ((e << t) | (e >>> (32 - t))) >>> 0; +} + +function decryptVidy(ciphertext, seedStr, mediaId) { + let padded = ciphertext.replace(/-/g, "+").replace(/_/g, "/"); + while (padded.length % 4) padded += "="; + + const raw = new Uint8Array(Buffer.from(padded, "base64")); + const length = raw.length; + + let tFnv = 2166136261; + for (let i = 0; i < seedStr.length; i++) { + tFnv = imul((tFnv ^ seedStr.charCodeAt(i)) >>> 0, 16777619); + } + const fnvRes = pFn(tFnv); + + const innerArg = (((mediaId >>> 0) ^ 2654435769) >>> 0); + const inner = pFn(innerArg); + let n = pFn(((fnvRes ^ inner) >>> 0)); + + const S = {}; + for (let e = 0; e < 8; e++) { + const t = n % 61; + n = gFn((n + 2654435769) >>> 0, 7 + (7 & e)); + S[t] = ((n ^ pFn(n)) >>> 0); + n = pFn((n + t) >>> 0); + } + + let acc = pFn(((2779096485 ^ n) >>> 0)); + const keystream = new Uint8Array(length); + let sCnt = 0; + let idx = 0; + + while (idx < length) { + const sVal = acc; + const o = sVal % 61; + const hasO = Object.prototype.hasOwnProperty.call(S, o); + const iVal = hasO ? -1 : 0; + const d = hasO ? S[o] : 0; + const nTerm = ((d ^ imul(2654435769, sCnt + 1)) >>> 0); + sCnt += 1; + const aTerm = sVal; + let lVal = (((aTerm ^ nTerm) | (aTerm & nTerm & iVal)) >>> 0); + const term1 = gFn((lVal + sVal) >>> 0, 31 & o); + const term2 = gFn(sVal, (31 & imul(o, 7)) >>> 0); + lVal = ((term1 ^ term2) >>> 0); + const newS = pFn((lVal + 2654435769) >>> 0); + S[o] = newS; + acc = newS; + const tVal = newS; + + keystream[idx++] = tVal & 0xFF; + if (idx < length) keystream[idx++] = (tVal >>> 8) & 0xFF; + if (idx < length) keystream[idx++] = (tVal >>> 16) & 0xFF; + if (idx < length) keystream[idx++] = (tVal >>> 24) & 0xFF; + } + + for (let i = 0; i < length; i++) { + raw[i] ^= keystream[i]; + } + + for (let i = 0; i < MAGIC.length; i++) { + if (raw[i] !== MAGIC[i]) { + throw new Error("Vidy decrypt failed: magic header mismatch"); + } + } + + return new TextDecoder().decode(raw.slice(MAGIC.length)); +} + +export async function getStream(args) { + const { id, s = null, e = null } = args; + const isTv = s != null && e != null; + const mediaId = Number(id); + + const headers = { + "User-Agent": getUA(), + Referer: "https://www.vidy.st/", + Origin: "https://www.vidy.st", + }; + + try { + const seedUrl = new URL(`${API_URL}/seed`); + seedUrl.searchParams.set("mediaId", String(id)); + + const seedRes = await fetch(seedUrl.toString(), { + headers, + signal: AbortSignal.timeout(6000), + }); + if (!seedRes.ok) return null; + + const seedData = await seedRes.json(); + const seed = seedData?.seed; + if (!seed) return null; + + for (const server of SERVERS) { + try { + const srcUrl = new URL(`${API_URL}/${server}/sources`); + srcUrl.searchParams.set("mediaType", isTv ? "tv" : "movie"); + srcUrl.searchParams.set("tmdbId", String(id)); + srcUrl.searchParams.set("enc", "2"); + srcUrl.searchParams.set("seed", seed); + if (isTv) { + srcUrl.searchParams.set("seasonId", String(s)); + srcUrl.searchParams.set("episodeId", String(e)); + } + + const res = await fetch(srcUrl.toString(), { + headers, + signal: AbortSignal.timeout(6000), + }); + if (!res.ok) continue; + + const text = await res.text(); + + let data; + try { + data = JSON.parse(decryptVidy(text, seed, mediaId)); + } catch { + continue; + } + + const sources = data?.sources || []; + const playlist = data?.playlist || ""; + if (!sources.length && !playlist) continue; + + let streamUrl = playlist; + let quality = "1080p"; + + for (const src of sources) { + if (src?.quality === "1080p" && src?.url) { + streamUrl = src.url; + quality = "1080p"; + break; + } + } + if (!streamUrl && sources.length) { + streamUrl = sources[0]?.url || ""; + quality = sources[0]?.quality || ""; + } + if (!streamUrl) continue; + + return { + url: streamUrl, + headers: { + "User-Agent": getUA(), + Referer: "https://www.vidy.st/", + Origin: "https://www.vidy.st", + }, + server: "Vidy", + quality, + type: streamUrl.includes(".m3u8") ? "hls" : "mp4", + }; + } catch { + continue; + } + } + + return null; + } catch { + return null; + } +} diff --git a/test/index.js b/test/index.js index 7946b74..db0f1cf 100644 --- a/test/index.js +++ b/test/index.js @@ -189,4 +189,4 @@ if (CONFIG.TEST_TV) { } } else { console.log("\nSkipped Stream Test"); -} +} \ No newline at end of file