'use strict'; const express = require('express'); const fetch = require('node-fetch'); const app = express(); const PORT = process.env.PORT || 7860; // ── Source links ───────────────────────────────────────────────── const SOURCE_1 = 'https://opensubtitlesv3-pro.dexter21767.com/eyJsYW5ncyI6WyJlbmdsaXNoIiwiYXJhYmljIl0sInNvdXJjZSI6ImFsbCIsImFpVHJhbnNsYXRlZCI6ZmFsc2UsImF1dG9BZGp1c3RtZW50Ijp0cnVlfQ=='; const SOURCE_2 = 'https://subsource.strem.top/c2tfMjhhMmE2ZmY4NzdhZjE1NzUxN2Q3NTgzZTQ2MmU2NmE0MmQyYjZlOGQ2NWJlN2QzNmI1YmFlOTYwMWRkYTM4Zi9hcmFiaWMsZW5nbGlzaC9oaUluY2x1ZGUvdHlwZToxLDIv'; const SOURCE_3 = 'https://wyzie.vanseleem.workers.dev'; const FETCH_TIMEOUT_MS = 12_000; const MAX_PER_LANG = 1; // max subtitles per language per source (1 = 6 total, 2 = 12 total, etc.) // ── CORS ─────────────────────────────────────────────────────── app.use((req, res, next) => { res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); if (req.method === 'OPTIONS') return res.sendStatus(200); next(); }); // ── Manifest ──────────────────────────────────────────────────── app.get('/manifest.json', (req, res) => res.json({ id: 'org.van.vansubs.pro', name: 'VanSubs+', description: 'Human-Verified Arabic & English Subtitles. Anti-AI.', version: '23.0.0', resources: ['subtitles'], types: ['movie', 'series'], idPrefixes: ['tt'], catalogs: [] })); // ── AbortController-based timeout ───────────────────────────── async function fetchWithTimeout(url, ms) { const controller = new AbortController(); const timer = setTimeout(() => controller.abort(), ms); try { const resp = await fetch(url, { signal: controller.signal }); return resp; } finally { clearTimeout(timer); } } // ── Language normalizer ──────────────────────────────────────── function normalizeLang(lang) { if (!lang) return null; const l = lang.toLowerCase().trim(); if (['en','eng','english','en-us','en-gb'].includes(l)) return 'english'; if (['ar','ara','arabic','ar-ae','ar-sa'].includes(l)) return 'arabic'; return null; } // ── Pick top MAX_PER_LANG Arabic and English from a source ──── function topTwoFromSource(subs) { if (!Array.isArray(subs) || subs.length === 0) return { arabic: [], english: [] }; const arabic = [], english = []; for (const sub of subs) { if (!sub.url) continue; const lang = normalizeLang(sub.lang); if (lang === 'arabic' && arabic.length < MAX_PER_LANG) arabic.push(sub); if (lang === 'english' && english.length < MAX_PER_LANG) english.push(sub); if (arabic.length >= MAX_PER_LANG && english.length >= MAX_PER_LANG) break; } return { arabic, english }; } // ── Subtitle endpoint (parallel fetch + merge) ──────────────── app.get('/subtitles/:type/:id/:extra?.json', async (req, res) => { const { type, id } = req.params; const extra = req.params.extra ? `/${req.params.extra}` : ''; const urls = [ `${SOURCE_1}/subtitles/${type}/${id}${extra}.json`, `${SOURCE_2}/subtitles/${type}/${id}${extra}.json`, `${SOURCE_3}/subtitles/${type}/${id}${extra}.json` ]; const fetchJson = async (url) => { try { const resp = await fetchWithTimeout(url, FETCH_TIMEOUT_MS); if (!resp.ok) { console.log(`[van] ${url} → HTTP ${resp.status}`); return null; } const data = await resp.json(); const subs = Array.isArray(data) ? data : (data?.subtitles || data?.data || []); console.log(`[van] ${url.split('?')[0]} → ${subs.length} subtitles`); return subs; } catch (err) { const reason = err.name === 'AbortError' ? 'timed out' : err.message; console.log(`[van] ${url.split('?')[0]} failed: ${reason}`); return null; } }; const allResults = await Promise.all(urls.map(u => fetchJson(u))); const seenAr = new Set(); const seenEn = new Set(); const mergedAr = []; const mergedEn = []; for (let i = 0; i < allResults.length; i++) { const subs = allResults[i]; if (!subs) continue; const { arabic, english } = topTwoFromSource(subs); for (const sub of arabic) { if (!seenAr.has(sub.url)) { seenAr.add(sub.url); mergedAr.push(sub); console.log(`[van] source ${i + 1} → Arabic : ${sub.url}`); } } for (const sub of english) { if (!seenEn.has(sub.url)) { seenEn.add(sub.url); mergedEn.push(sub); console.log(`[van] source ${i + 1} → English : ${sub.url}`); } } } const merged = [...mergedAr, ...mergedEn]; console.log(`[van] Final: ${merged.length} subtitles (AR:${mergedAr.length} EN:${mergedEn.length})`); res.json({ subtitles: merged }); }); // ── Landing page ────────────────────────────────────────────── app.get('/', (req, res) => { const manifestUrl = `${req.protocol}://${req.get('host')}/manifest.json`; const installUrl = 'stremio://vanseleem-vansubs.hf.space/manifest.json'; res.type('html').send(`
Human-Verified Arabic & English Subtitles. Anti-AI.