const express = require('express');
const { spawn } = require('child_process');
const fs = require('fs');
const path = require('path');
const fetch = require('node-fetch');

const app = express();
const port = 8080;
const tmp_folder = '/tmp/ramdisk/';

let channelsPath = path.resolve(__dirname, 'channels.json');
let channels = JSON.parse(fs.readFileSync(channelsPath, 'utf8'));

const activeStreams = {}; // Store active stream processes with unique IDs

fs.watch(channelsPath, (eventType, filename) => {
    if (eventType === 'change') {
        console.log(`====================================`);
        console.log(`channels.json was updated, reloading...`);
		try {
        	channels = JSON.parse(fs.readFileSync(channelsPath, 'utf8'));
            // Log every channel loaded
            for (const channel in channels) {
                console.log(`Channel loaded: ${channel}`);
            }
            console.log(`Channels reloaded`);	
            console.log(`====================================`);
		} catch (error) {
		    console.log(error.message);
		}
    }
});

async function startStreamlink(channel, clientID) {
const useragent = channel.useragent;
const authorization = channel.authorization;
const referer = channel.referer;
const proxy = channel.proxy;
const key1 = channel.key1;
const key2 = channel.key2;
const key3 = channel.key3;
const key4 = channel.key4;
const key5 = channel.key5;
const audios = channel.audios;
const resolution = channel.resolution;
const delay = channel.delay;

let url = channel.url;
let baseurl;
    
if (url.includes("telefonica.com")) {
    const now = new Date();
    
    const startTime = new Date(now.getTime() - 45000).toISOString().slice(0, -5) + "Z";
    const endTime = new Date(now.getTime() + (9 * 60 * 60 * 1000)).toISOString().slice(0, -5) + "Z";

    baseurl = url + `&start_time=${startTime}&end_time=${endTime}`;

    url = `http://proxy_movistar:8080/` + url + `&start_time=${startTime}&end_time=${endTime}`;
}

let args = [
    url,
    '--use-shaka-packager',
    '--log-level', 'OFF',
    '--no-log',
//    '--live-real-time-merge', 'true',
//    '--mp4-real-time-decryption', 'true',
//    '--live-wait-time', '1',
    '--thread-count', '3',
    '--concurrent-download',
    '--live-pipe-mux',
    '--live-keep-segments', 'false',
    '--check-segments-count', 'false',
//    '--live-take-count', '10',
    '--tmp-dir', tmp_folder + `${channel.name}_${clientID}`, // Unique folder for each client
    '--del-after-done', 'true'
];

if (useragent) {
    args.splice(1, 0, '--header', `User-Agent: ${useragent}`);
} else {
    args.splice(1, 0, '--header', `User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 Edge/16.16299`);
}
if (authorization) {
    args.splice(1, 0, '--header', `Authorization: ${authorization}`);
}
if (referer) {
    args.splice(1, 0, '--header', `Referer: ${referer}`);
}
if (proxy) {
    args.splice(1, 0, '--custom-proxy', proxy);
}
if (key5) {
    args.splice(1, 0, '--key', key5);
}
if (key4) {
    args.splice(1, 0, '--key', key4);
}
if (key3) {
    args.splice(1, 0, '--key', key3);
}
if (key2) {
    args.splice(1, 0, '--key', key2);
}
if (key1) {
    args.splice(1, 0, '--key', key1);
}
if (audios) {
    args.splice(1, 0, '-sa', audios);
} else {
    args.splice(1, 0, '-sa', 'best2');
}
if (resolution) {
    args.splice(1, 0, '-sv', 'res=' + resolution);
} else {
    args.splice(1, 0, '-sv', 'best');
}

if (delay) {
    args.splice(1, 0, '--live-pipe-options', '-itsoffset ' + delay + ' -re -loglevel error {INPUTS} -c copy -f mpegts -fflags +genpts -shortest pipe:1');
}

if (baseurl) {
    args.splice(1, 0, '--base-url', baseurl);
}

console.log(`Args: ${args}`);

const streamlinkProcess = spawn('N_m3u8DL-RE', args);

const processID = `${channel.name}_${clientID}`;
activeStreams[processID] = streamlinkProcess;

return streamlinkProcess;
}

app.get('/stream/:channelName', async (req, res) => {
const channelName = req.params.channelName;
const clientID = req.ip;

const channel = channels[channelName];
console.log(`Received request for /stream/${channelName} from Client IP: ${clientID}`);
channels[channelName]['name'] = channelName;

if (!channel) {
    res.status(404).send('Channel not found');
    return;
}

console.log(`Starting Streamlink command for channel: ${channelName} (Client IP: ${clientID})`);

try {
    let streamlinkProcess = await startStreamlink(channel, clientID);

    res.setHeader('Content-Type', 'video/MP2T');
    streamlinkProcess.stdout.pipe(res);

    streamlinkProcess.stderr.on('data', data => {
        console.error(`Streamlink: ${data}`);
    });

    streamlinkProcess.on('close', code => {
        console.log(`Streamlink process exited with code ${code} for Client IP: ${clientID}`);
        res.end();
        const processID = `${channelName}_${clientID}`;
        delete activeStreams[processID];
        const tempFolderPath = tmp_folder + `${channel.name}_${clientID}`;
        if (fs.existsSync(tempFolderPath)) {
            fs.rmSync(tempFolderPath, { recursive: true });
        }
        logActiveConnections();
    });

    res.on('close', () => {
        console.log(`Response closed, killing channel ${channel.name} for Client IP: ${clientID}`);
        const processID = `${channelName}_${clientID}`;
        if (activeStreams[processID]) {
            activeStreams[processID].kill();
            delete activeStreams[processID];
        }
        const tempFolderPath = tmp_folder + `${channel.name}_${clientID}`;
        if (fs.existsSync(tempFolderPath)) {
            fs.rmSync(tempFolderPath, { recursive: true });
        }
        logActiveConnections();
    });

    req.on('abort', () => {
        console.log(`Request aborted, killing Streamlink process for Client IP: ${clientID}`);
        const processID = `${channelName}_${clientID}`;
        if (activeStreams[processID]) {
            activeStreams[processID].kill();
            delete activeStreams[processID];
        }
        const tempFolderPath = tmp_folder + `${channel.name}_${clientID}`;
        if (fs.existsSync(tempFolderPath)) {
            fs.rmSync(tempFolderPath, { recursive: true });
        }
        logActiveConnections();
    });

    logActiveConnections();
} catch (error) {
    console.error(`Streamlink failed: ${error}`);
    res.status(500).send('Streamlink failed');
}
});

function logActiveConnections() {
const activeConnections = Object.keys(activeStreams).length;
console.log(`Active connections: ${activeConnections}`);
}

app.listen(port, "0.0.0.0", () => {
console.log(`Server listening on port ${port}`);
});

process.on('SIGINT', () => {
	for (const processID in activeStreams) {
    	if (activeStreams.hasOwnProperty(processID)) {
        	activeStreams[processID].kill();
	        delete activeStreams[processID];
	    }
	}
	process.exit();
});
