// video-stream.js const express = require('express'); const fs = require('fs'); const path = require('path'); const readline = require('readline'); const mime = require('mime-types'); // npm install mime-types const app = express(); const PORT = 4002; // CORS headers for Watchparty / browsers 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', 'Range, Origin, X-Requested-With, Content-Type, Accept'); next(); }); // Ask user for video directory const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); rl.question('Enter full path to video directory: ', (videoDir) => { videoDir = path.resolve(videoDir.trim()); if (!fs.existsSync(videoDir)) { console.error('❌ Directory does not exist!'); process.exit(1); } const allowedExtensions = ['.mp4', '.mkv', '.webm', '.mov', '.avi', '.flv', '.ogg']; const videos = fs.readdirSync(videoDir).filter(f => allowedExtensions.includes(path.extname(f).toLowerCase()) ); if (videos.length === 0) { console.error('❌ No video files found!'); process.exit(1); } // Generate random serial numbers const serialMap = {}; videos.forEach(video => { const serial = Math.random().toString(36).slice(2, 10).toUpperCase(); serialMap[serial] = video; // VPS domain URL (change if needed) console.log(`🎥 ${video} -> Serial URL: https://stream.arulbalaji.xyz/videos/serial/${serial}`); }); // Serve video by serial app.get('/videos/serial/:code', (req, res) => { const code = req.params.code; const filename = serialMap[code]; if (!filename) return res.status(404).send('Invalid or expired serial'); const filePath = path.join(videoDir, filename); if (!fs.existsSync(filePath)) return res.status(404).send('File missing'); const stat = fs.statSync(filePath); const fileSize = stat.size; const range = req.headers.range; const contentType = mime.lookup(path.extname(filename)) || 'application/octet-stream'; if (range) { const parts = range.replace(/bytes=/, "").split("-"); const start = parseInt(parts[0], 10); const end = parts[1] ? parseInt(parts[1], 10) : fileSize - 1; const chunkSize = (end - start) + 1; const file = fs.createReadStream(filePath, { start, end }); res.writeHead(206, { 'Content-Range': `bytes ${start}-${end}/${fileSize}`, 'Accept-Ranges': 'bytes', 'Content-Length': chunkSize, 'Content-Type': contentType }); file.pipe(res); } else { res.writeHead(200, { 'Content-Length': fileSize, 'Content-Type': contentType, 'Accept-Ranges': 'bytes' }); fs.createReadStream(filePath).pipe(res); } }); app.listen(PORT, '127.0.0.1', () => { console.log(`✅ Serial video server running on 127.0.0.1:${PORT}`); }); rl.close(); });