Instancia transcoder t1: cambios locales

This commit is contained in:
Joaquin
2026-07-13 15:19:13 +00:00
parent 0e26a90dae
commit bb189a472e
27 changed files with 2161 additions and 496 deletions
+45 -18
View File
@@ -12,6 +12,35 @@ const https = require('https');
const { spawn } = require('child_process');
const ffmpegPath = require('ffmpeg-static');
const { Readable } = require('stream');
const auth = require('../auth');
const { canUserAccessSource } = require('../sourceAccess');
// Rutas que el navegador pide sin Bearer (HLS, <img>, etc.) deben quedar fuera de JWT
router.use((req, res, next) => {
const p = req.path || (req.url || '').split('?')[0];
if (p === '/stream' || p === '/image') return next();
return auth.requireAuth(req, res, next);
});
router.param('sourceId', async (req, res, next, id) => {
try {
const sid = parseInt(id, 10);
if (Number.isNaN(sid)) {
return res.status(400).json({ error: 'Invalid source id' });
}
const source = await sources.getById(sid);
if (!source) {
return res.status(404).json({ error: 'Source not found' });
}
if (!canUserAccessSource(req.user, source)) {
return res.status(403).json({ error: 'No access to this source' });
}
req.grantedSource = source;
next();
} catch (err) {
next(err);
}
});
// Default cache max age in hours
const DEFAULT_MAX_AGE_HOURS = 24;
@@ -83,8 +112,8 @@ function getStreamsFromDb(sourceId, type, categoryId = null, includeHidden = fal
// Login / Authenticate
router.get('/xtream/:sourceId', async (req, res) => {
try {
const source = await sources.getById(req.params.sourceId);
if (!source || source.type !== 'xtream') return res.status(404).send('Source not found');
const source = req.grantedSource;
if (source.type !== 'xtream') return res.status(404).send('Source not found');
// Proxy auth check to upstream to ensure credentials are still valid
@@ -185,8 +214,7 @@ router.get('/xtream/:sourceId/series', async (req, res) => {
// Proxy series info request
router.get('/xtream/:sourceId/series_info', async (req, res) => {
try {
const source = await sources.getById(req.params.sourceId);
if (!source) return res.status(404).send('Source not found');
const source = req.grantedSource;
const seriesId = req.query.series_id;
if (!seriesId) return res.status(400).send('series_id required');
@@ -207,8 +235,7 @@ router.get('/xtream/:sourceId/series_info', async (req, res) => {
// VOD Info
router.get('/xtream/:sourceId/vod_info', async (req, res) => {
try {
const source = await sources.getById(req.params.sourceId);
if (!source) return res.status(404).send('Source not found');
const source = req.grantedSource;
const vodId = req.query.vod_id;
if (!vodId) return res.status(400).send('vod_id required');
@@ -230,14 +257,14 @@ router.get('/xtream/:sourceId/vod_info', async (req, res) => {
// Returns the direct stream URL for a given stream ID
router.get('/xtream/:sourceId/stream/:streamId/:type', async (req, res) => {
try {
const source = await sources.getById(req.params.sourceId);
if (!source || source.type !== 'xtream') {
const source = req.grantedSource;
if (source.type !== 'xtream') {
return res.status(404).json({ error: 'Xtream source not found' });
}
const streamId = req.params.streamId;
const type = req.params.type || 'live';
const container = req.query.container || 'm3u8';
const container = req.query.container || 'ts';
// Construct the Xtream stream URL
// Format: http://server:port/live/username/password/streamId.container (for live)
@@ -393,8 +420,8 @@ router.delete('/cache/:sourceId', (req, res) => {
router.get('/xtream/:sourceId/:action', async (req, res) => {
try {
const sourceId = req.params.sourceId;
const source = await sources.getById(sourceId);
if (!source || source.type !== 'xtream') {
const source = req.grantedSource;
if (source.type !== 'xtream') {
return res.status(404).json({ error: 'Xtream source not found' });
}
@@ -478,14 +505,14 @@ router.get('/xtream/:sourceId/:action', async (req, res) => {
*/
router.get('/xtream/:sourceId/stream/:streamId/:type?', async (req, res) => {
try {
const source = await sources.getById(req.params.sourceId);
if (!source || source.type !== 'xtream') {
const source = req.grantedSource;
if (source.type !== 'xtream') {
return res.status(404).json({ error: 'Xtream source not found' });
}
const api = xtreamApi.createFromSource(source);
const { streamId, type = 'live' } = req.params;
const { container = 'm3u8' } = req.query;
const { container = 'ts' } = req.query;
const url = api.buildStreamUrl(streamId, type, container);
res.json({ url });
@@ -505,8 +532,8 @@ router.get('/xtream/:sourceId/stream/:streamId/:type?', async (req, res) => {
router.get('/epg/:sourceId', async (req, res) => {
try {
const sourceId = req.params.sourceId;
const source = await sources.getById(sourceId);
if (!source || (source.type !== 'epg' && source.type !== 'xtream')) {
const source = req.grantedSource;
if (source.type !== 'epg' && source.type !== 'xtream') {
return res.status(404).json({ error: 'Valid EPG source not found' });
}
@@ -567,8 +594,8 @@ router.delete('/epg/:sourceId/cache', (req, res) => {
*/
router.post('/epg/:sourceId/channels', async (req, res) => {
try {
const source = await sources.getById(req.params.sourceId);
if (!source || source.type !== 'epg') {
const source = req.grantedSource;
if (source.type !== 'epg') {
return res.status(404).json({ error: 'EPG source not found' });
}