|
1 | 1 | const axios = require('axios'); |
2 | 2 |
|
| 3 | +// Confirmed AES-CBC decoder for VidZee encrypted link format. |
| 4 | +// Format (after initial atob in site script): ivBase64:cipherBase64 |
| 5 | +// ivBase64 -> CryptoJS Base64 parsed to WordArray (IV) |
| 6 | +// Key: UTF-8 bytes of "qrincywincyspider" padded with nulls to 32 bytes (AES-256-CBC) |
| 7 | +// Cipher: base64 ciphertext, PKCS7 padding, mode CBC. |
| 8 | +let cryptoJs; // lazy load to avoid cost if not needed |
| 9 | +function decodeVidZeeToken(token, debug) { |
| 10 | + try { |
| 11 | + if (typeof token !== 'string') return null; |
| 12 | + if (/^https?:\/\//i.test(token)) return null; // already a URL |
| 13 | + // Expect pattern base64:base64 (iv:cipher) |
| 14 | + const raw = Buffer.from(token, 'base64').toString('utf8'); |
| 15 | + if (!raw.includes(':')) return null; |
| 16 | + const [ivB64, cipherB64] = raw.split(':'); |
| 17 | + if (!ivB64 || !cipherB64) return null; |
| 18 | + if (!cryptoJs) cryptoJs = require('crypto-js'); |
| 19 | + const iv = cryptoJs.enc.Base64.parse(ivB64.trim()); |
| 20 | + const keyStr = 'qrincywincyspider'; |
| 21 | + // Pad key with nulls to 32 bytes |
| 22 | + const keyUtf8 = cryptoJs.enc.Utf8.parse(keyStr.padEnd(32, '\0')); |
| 23 | + const decrypted = cryptoJs.AES.decrypt(cipherB64.trim(), keyUtf8, { |
| 24 | + iv, |
| 25 | + mode: cryptoJs.mode.CBC, |
| 26 | + padding: cryptoJs.pad.Pkcs7 |
| 27 | + }).toString(cryptoJs.enc.Utf8); |
| 28 | + if (!decrypted) return null; |
| 29 | + if (!/^https?:\/\//i.test(decrypted)) { |
| 30 | + if (debug) console.log('[VidZee] decrypted but not a URL', decrypted.slice(0,80)); |
| 31 | + return null; |
| 32 | + } |
| 33 | + if (debug) console.log('[VidZee] AES decoded token', { tokenSnippet: token.slice(0, 24)+'...', url: decrypted.slice(0,120) }); |
| 34 | + return decrypted.trim(); |
| 35 | + } catch (e) { |
| 36 | + if (debug) console.log('[VidZee] AES decode error', e.message); |
| 37 | + return null; |
| 38 | + } |
| 39 | +} |
| 40 | + |
3 | 41 | // Removed unused parseArgs helper |
4 | 42 |
|
5 | 43 | const getVidZeeStreams = async (tmdbId, mediaType, seasonNum, episodeNum) => { |
@@ -73,20 +111,23 @@ const getVidZeeStreams = async (tmdbId, mediaType, seasonNum, episodeNum) => { |
73 | 111 | } |
74 | 112 |
|
75 | 113 | const streams = apiSources.map(sourceItem => { |
76 | | - // Prefer sourceItem.name as label, fallback to sourceItem.type, then 'VidZee Stream' |
77 | 114 | const label = sourceItem.name || sourceItem.type || 'VidZee'; |
78 | | - // Ensure quality has 'p' if it's a resolution, or keep it as is |
79 | 115 | let quality = String(label).match(/^\d+$/) ? `${label}p` : label; |
80 | | - // Normalize non-numeric or ambiguous quality labels to a baseline so they survive minQuality filter |
81 | 116 | if (!/(\d{3,4})p/.test(quality.toLowerCase())) { |
82 | 117 | quality = '720p'; |
83 | 118 | } |
84 | 119 | const language = sourceItem.language || sourceItem.lang; |
85 | | - |
| 120 | + let rawLink = sourceItem.link; |
| 121 | + const debug = process.env.VIDZEE_DEBUG === '1'; |
| 122 | + const decoded = decodeVidZeeToken(rawLink, debug); |
| 123 | + if (decoded && /^https?:\/\//i.test(decoded)) { |
| 124 | + if (debug) console.log('[VidZee] decoded link', { beforeSample: rawLink.slice(0,40)+'...', after: decoded.slice(0,80) }); |
| 125 | + rawLink = decoded; |
| 126 | + } |
86 | 127 | return { |
87 | 128 | name: `VidZee Server${sr} - ${quality} - ${language}`, |
88 | 129 | title: `VidZee Server${sr} - ${quality} - ${language}`, |
89 | | - url: sourceItem.link, // Use sourceItem.link for the URL |
| 130 | + url: rawLink, |
90 | 131 | quality: quality, |
91 | 132 | provider: "VidZee", |
92 | 133 | headers: { |
|
0 commit comments