/** * YumeZone — HLS Proxy Server (Node.js + Express) * Deployable to: Hugging Face Spaces, Vercel, Railway, Heroku, etc. */ import express from 'express'; import fetch from 'node-fetch'; const app = express(); const PORT = process.env.PORT || 3000; /* ─── CDN rule table ─────────────────────────────────────────────────────── */ const CDN_RULES = [ { test: h => h.endsWith('.otakuu.se') || h === 'otakuu.se', referer: 'https://animex.one/', origin: 'https://animex.one', secSite: 'cross-site' }, { test: h => h === 'vibeplayer.site' || h.endsWith('.vibeplayer.site'), referer: 'https://vibeplayer.site/', origin: 'https://vibeplayer.site', secSite: 'same-origin' }, { test: h => h.endsWith('.mofl.pro') || h === 'mofl.pro', referer: 'https://kem.clvd.xyz/', origin: 'https://kem.clvd.xyz', secSite: 'cross-site' }, { test: h => h.endsWith('.vidhosters.com') || h === 'vidhosters.com', referer: 'https://kem.clvd.xyz/', origin: 'https://kem.clvd.xyz', secSite: 'cross-site' }, { test: h => h.endsWith('.burntburst45.store') || h === 'burntburst45.store', referer: null, origin: 'https://play2.echovideo.ru', secSite: 'cross-site' }, { test: h => h.endsWith('.streamzone1.site') || h === 'streamzone1.site', referer: 'https://megaplay.buzz/', origin: 'https://megaplay.buzz', secSite: 'cross-site' }, { test: h => h.endsWith('.zencloudz.cc') || h === 'zencloudz.cc', referer: 'https://aniwave.at/', origin: 'https://aniwave.at', secSite: 'cross-site' }, { test: h => h.endsWith('.cinewave2.site') || h === 'cinewave2.site', referer: 'https://megaplay.buzz/', origin: 'https://megaplay.buzz', secSite: 'cross-site' }, { test: h => h.endsWith('.watching.onl') || h === 'watching.onl', referer: 'https://vidwish.live/', origin: 'https://vidwish.live', secSite: 'cross-site' }, { test: h => h.endsWith('.krussdomi.com') || h === 'krussdomi.com', referer: 'https://krussdomi.com/', origin: 'https://krussdomi.com', secSite: 'same-origin' }, { test: h => h.endsWith('.owocdn.top') || h === 'owocdn.top', referer: 'https://kwik.cx/', origin: 'https://kwik.cx', secSite: 'cross-site' }, { test: h => h.endsWith('.anime-dunya.com') || h === 'anime-dunya.com', referer: 'https://anime-dunya.com/', origin: 'https://anime-dunya.com', secSite: 'same-origin' }, { test: h => h.startsWith('rrr.'), referer: 'https://megaup.nl/', origin: 'https://megaup.nl', secSite: 'cross-site' }, { test: h => h === 'megaup.nl' || h.endsWith('.megaup.nl') || h === 'hub26link.site' || h.endsWith('.hub26link.site'), referer: 'https://megaup.nl/', origin: 'https://megaup.nl', secSite: 'cross-site' }, { test: h => h.endsWith('.mewstream.buzz') || h === 'mewstream.buzz', referer: 'https://megaplay.buzz/', origin: 'https://megaplay.buzz', secSite: 'cross-site' }, { test: h => h.endsWith('.vid-cdn.xyz') || h === 'vid-cdn.xyz', referer: 'https://anizone.to/', origin: 'https://anizone.to', secSite: 'cross-site' }, ]; /* ─── Base64url helpers ──────────────────────────────────────────────────── */ function b64uEncode(str) { return Buffer.from(str).toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''); } function b64uDecode(b64u) { try { const b64 = b64u.replace(/-/g, '+').replace(/_/g, '/'); const padded = b64 + '='.repeat((4 - b64.length % 4) % 4); return Buffer.from(padded, 'base64').toString(); } catch { return null; } } function encodePayload(url, referer) { return b64uEncode(url + '\0' + (referer || '')); } function decodePayload(b64u) { const plain = b64uDecode(b64u); if (!plain) return null; const idx = plain.indexOf('\0'); if (idx === -1) return { url: plain, ref: null }; return { url: plain.slice(0, idx), ref: plain.slice(idx + 1) || null }; } /* ─── Browser impersonation headers ─────────────────────────────────────── */ function browserHeaders(referer, origin, secSite) { return { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36', 'Accept': '*/*', 'Accept-Language': 'en-US,en;q=0.9', 'Accept-Encoding': 'gzip, deflate, br', 'Referer': referer, 'Origin': origin, 'Sec-Fetch-Dest': 'empty', 'Sec-Fetch-Mode': 'cors', 'Sec-Fetch-Site': secSite || 'cross-site', 'Sec-CH-UA': '"Chromium";v="124", "Google Chrome";v="124", "Not-A.Brand";v="99"', 'Sec-CH-UA-Mobile': '?0', 'Sec-CH-UA-Platform': '"Windows"', 'Connection': 'keep-alive', 'Cache-Control': 'no-cache', 'Pragma': 'no-cache', }; } /* ─── CORS headers ───────────────────────────────────────────────────────── */ function corsHeaders() { return { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, HEAD, OPTIONS', 'Access-Control-Allow-Headers': 'Range, Content-Type', 'Access-Control-Expose-Headers': 'Content-Length, Content-Range, Content-Type, Accept-Ranges', 'Accept-Ranges': 'bytes', }; } /* ─── Resolve relative URL against base ─────────────────────────────────── */ function resolveUrl(rel, base) { if (/^https?:\/\//i.test(rel)) return rel; try { return new URL(rel, base).href; } catch { return rel; } } /* ─── Rewrite M3U8: all segment/key URIs → /p/ ───────────────── */ function rewriteM3u8(text, baseUrl, referer, serverBase) { const lines = text.split('\n'); return lines.map(raw => { const line = raw.trim(); if (line.startsWith('#') && line.includes('URI="')) { return line.replace(/URI="([^"]+)"/g, (_, uri) => { const abs = resolveUrl(uri, baseUrl); return `URI="${serverBase}/p/${encodePayload(abs, referer)}"`; }); } if (line && !line.startsWith('#')) { const abs = resolveUrl(line, baseUrl); return `${serverBase}/p/${encodePayload(abs, referer)}`; } return raw; }).join('\n'); } /* ─── Core proxy logic ───────────────────────────────────────────────────── */ async function proxyTarget(targetUrl, refParam, request, serverBase) { let parsedTarget; try { parsedTarget = new URL(targetUrl); if (parsedTarget.protocol !== 'https:') throw new Error('https only'); } catch { return { status: 400, json: { error: 'Invalid target URL — must be https' } }; } const targetHost = parsedTarget.hostname.toLowerCase(); const overrideReferer = refParam || null; const rule = CDN_RULES.find(r => r.test(targetHost)); let effectiveReferer, effectiveOrigin, effectiveSecSite; if (rule) { effectiveReferer = overrideReferer || rule.referer || `https://${targetHost}/`; effectiveOrigin = rule.origin || `https://${targetHost}`; effectiveSecSite = rule.secSite || 'cross-site'; } else if (overrideReferer) { try { const refUrl = new URL(overrideReferer); effectiveReferer = overrideReferer; effectiveOrigin = refUrl.origin; effectiveSecSite = 'cross-site'; } catch { effectiveReferer = overrideReferer; effectiveOrigin = `https://${targetHost}`; effectiveSecSite = 'cross-site'; } } else { return { status: 403, json: { error: `Host not in CDN_RULES and no referer provided: ${targetHost}` } }; } const headers = browserHeaders(effectiveReferer, effectiveOrigin, effectiveSecSite); const rangeHeader = request.headers.get ? request.headers.get('Range') : request.headers.range; if (rangeHeader) headers['Range'] = rangeHeader; let upstreamResp; try { upstreamResp = await fetch(targetUrl, { method: request.method === 'HEAD' ? 'HEAD' : 'GET', headers, redirect: 'follow', }); } catch (err) { return { status: 502, json: { error: 'Upstream fetch failed', detail: String(err) } }; } if (!upstreamResp.ok && upstreamResp.status !== 206) { return { status: upstreamResp.status, json: { error: 'Upstream error', status: upstreamResp.status } }; } const contentType = (upstreamResp.headers.get('content-type') || '').toLowerCase(); const isM3u8 = contentType.includes('mpegurl') || contentType.includes('x-mpegurl') || targetUrl.split('?')[0].endsWith('.m3u8'); if (request.method === 'HEAD') { return { status: upstreamResp.status, headers: { 'Content-Type': upstreamResp.headers.get('content-type') || 'application/octet-stream', ...corsHeaders(), }, body: null }; } if (isM3u8) { const text = await upstreamResp.text(); const rewritten = rewriteM3u8(text, targetUrl, effectiveReferer, serverBase); return { status: upstreamResp.status, headers: { 'Content-Type': 'application/vnd.apple.mpegurl', 'Cache-Control': 'no-cache', ...corsHeaders(), }, body: rewritten }; } const passHeaders = { 'Content-Type': upstreamResp.headers.get('content-type') || 'application/octet-stream', 'Cache-Control': 'public, max-age=86400, immutable', ...corsHeaders(), }; const cl = upstreamResp.headers.get('content-length'); if (cl) passHeaders['Content-Length'] = cl; const cr = upstreamResp.headers.get('content-range'); if (cr) passHeaders['Content-Range'] = cr; return { status: upstreamResp.status, headers: passHeaders, body: upstreamResp.body }; } /* ─── Express Routes ───────────────────────────────────────────────────────── */ app.use((req, res, next) => { Object.assign(res.corsHeaders = {}, corsHeaders()); res.set(res.corsHeaders); if (req.method === 'OPTIONS') return res.status(204).end(); next(); }); app.get('/health', (req, res) => { res.json({ ok: true, worker: 'yumezone-nodejs', ts: Date.now() }); }); app.get('/p/:b64u', async (req, res) => { const decoded = decodePayload(req.params.b64u); if (!decoded) return res.status(400).json({ error: 'Invalid payload' }); const serverBase = `${req.protocol}://${req.get('host')}`; const result = await proxyTarget(decoded.url, decoded.ref, req, serverBase); if (result.json) return res.status(result.status).set(result.headers || {}).json(result.json); res.status(result.status).set(result.headers || {}); if (result.body) res.send(result.body); else res.end(); }); app.get('/proxy', async (req, res) => { const targetRaw = req.query.url; if (!targetRaw) return res.status(400).json({ error: 'Missing ?url= parameter' }); let targetUrl; try { targetUrl = decodeURIComponent(targetRaw); } catch { return res.status(400).json({ error: 'Bad URL encoding' }); } const refParam = req.query.ref; const serverBase = `${req.protocol}://${req.get('host')}`; const result = await proxyTarget(targetUrl, refParam, req, serverBase); if (result.json) return res.status(result.status).set(result.headers || {}).json(result.json); res.status(result.status).set(result.headers || {}); if (result.body) res.send(result.body); else res.end(); }); app.use((req, res) => res.status(404).send('Not found')); app.listen(PORT, '0.0.0.0', () => { console.log(`🚀 YumeZone Proxy running on http://0.0.0.0:${PORT}`); });