Added streaming service style watch page for movies and series playback

This commit is contained in:
Trevor Mears
2026-01-06 20:01:43 -08:00
parent 3e377da67c
commit f525a6ee67
8 changed files with 1712 additions and 68 deletions
+8 -9
View File
@@ -84,13 +84,12 @@ router.get('/xtream/:sourceId', async (req, res) => {
// Proxy auth check to upstream to ensure credentials are still valid
const cached = cache.get(`xtream:${source.id}:auth`);
const cached = cache.get('xtream', source.id, 'auth', 300000);
if (cached) return res.json(cached);
const api = xtreamApi.createFromSource(source);
const data = await api.authenticate();
cache.set(`xtream:${source.id}:auth`, data, 300); // 5 min cache
cache.set('xtream', source.id, 'auth', data);
res.json(data);
} catch (err) {
res.status(502).json({ error: 'Upstream error', details: err.message });
@@ -188,13 +187,13 @@ router.get('/xtream/:sourceId/series_info', async (req, res) => {
const seriesId = req.query.series_id;
if (!seriesId) return res.status(400).send('series_id required');
const cacheKey = `xtream:${source.id}:series_info:${seriesId}`;
const cached = cache.get(cacheKey);
const cacheKey = `series_info_${seriesId}`;
const cached = cache.get('xtream', source.id, cacheKey, 3600000);
if (cached) return res.json(cached);
const api = xtreamApi.createFromSource(source);
const data = await api.getSeriesInfo(seriesId);
cache.set(cacheKey, data, 3600); // 1 hour
cache.set('xtream', source.id, cacheKey, data);
res.json(data);
} catch (err) {
res.status(502).json({ error: 'Upstream error', details: err.message });
@@ -210,13 +209,13 @@ router.get('/xtream/:sourceId/vod_info', async (req, res) => {
const vodId = req.query.vod_id;
if (!vodId) return res.status(400).send('vod_id required');
const cacheKey = `xtream:${source.id}:vod_info:${vodId}`;
const cached = cache.get(cacheKey);
const cacheKey = `vod_info_${vodId}`;
const cached = cache.get('xtream', source.id, cacheKey, 3600000);
if (cached) return res.json(cached);
const api = xtreamApi.createFromSource(source);
const data = await api.getVodInfo(vodId);
cache.set(cacheKey, data, 3600); // 1 hour
cache.set('xtream', source.id, cacheKey, data);
res.json(data);
} catch (err) {
res.status(502).json({ error: 'Upstream error', details: err.message });
+21 -15
View File
@@ -25,34 +25,40 @@ router.get('/', (req, res) => {
const args = [
'-hide_banner',
'-loglevel', 'warning',
// Low-latency startup vs Reliability trade-off
// Increased to 5MB/10s to handle slow HLS manifests and streams with large headers
'-probesize', '5000000', // 5MB
'-analyzeduration', '10000000', // 10 seconds
// Faster startup - reduced probe/analyze for quicker first bytes
'-probesize', '2000000', // 2MB (reduced from 5MB)
'-analyzeduration', '3000000', // 3 seconds (reduced from 10s)
// Error resilience: generate timestamps, discard corrupt packets
'-fflags', '+genpts+discardcorrupt',
'-fflags', '+genpts+discardcorrupt+nobuffer',
// Ignore errors in stream and continue
'-err_detect', 'ignore_err',
// Limit max demux delay to prevent buffering issues with bad timestamps
'-max_delay', '5000000',
// Limit max demux delay to prevent buffering issues
'-max_delay', '2000000',
// Reconnect settings for network drops (useful for live streams)
'-reconnect', '1',
'-reconnect_streamed', '1',
'-reconnect_delay_max', '5',
'-reconnect_delay_max', '3',
'-i', url,
// Map only first video and audio stream (avoid subtitle streams causing issues)
'-map', '0:v:0',
'-map', '0:a:0?', // ? makes audio optional if not present
// Video: passthrough (no re-encoding = fast!)
'-c:v', 'copy',
// Audio: Transcode to browser-compatible AAC with consistent parameters
// Audio: Transcode to browser-compatible AAC
'-c:a', 'aac',
'-ac', '2', // Downmix to stereo (fixes 5.1 surround issues)
'-ar', '48000',
'-b:a', '256k', // Increased for surround sound
'-af', 'aresample=48000:async=1',
// Handle timestamp discontinuities at output
'-fps_mode', 'passthrough',
'-max_muxing_queue_size', '1024',
'-b:a', '192k',
// Handle async audio/video using async filter
'-af', 'aresample=async=1:min_hard_comp=0.100000:first_pts=0',
// Timestamp handling
'-vsync', 'passthrough',
'-async', '1',
'-max_muxing_queue_size', '2048',
// Fragmented MP4 for streaming (browser-compatible)
'-f', 'mp4',
'-movflags', 'frag_keyframe+empty_moov+default_base_moof',
'-movflags', 'frag_keyframe+empty_moov+default_base_moof+faststart',
'-flush_packets', '1', // Send data immediately
'-' // Output to stdout
];