<commit_before>/* npm deps */
stream  = require('stream');
http    = require('http');
url     = require('url');
express = require('express');
git     = require('git-rev');
ffmpeg  = require('fluent-ffmpeg');
require('array.prototype.findindex');

/* internal deps */
config = require('./channels_config');
streams = require('./lib/stream-manager');
changlue = require('./lib/channels-glue');

/*
 * express webapp: expose a number of endpoints and APIs
 */
var app = express();

app.get('/', function (req, res) {
        res.send("node-iptv-proxy");
});

app.get('/list', function(req, res) {
        /* list available channels */
        res.send({ channels: Object.keys(config.channels) });
});

app.get('/version', function(req, res) {
        git.short(function(commit) {
                res.send({ version: commit });
        });
});

app.get('/status', function(req, res){
        function decode_authdata(headers) {
                var username = "";
                try {
                        username = new Buffer(headers['authorization'].replace("Basic ",""), 'base64')
                                .toString().split(':')[0];
                } catch(e) {
                        username = "none";
                }

                return username;
        }

        function get_http_stats(client) {
                try {
                        return {
                                sent: client.socket.bytesWritten,
                                remoteAddr: client.req.headers['x-forwarded-for']
                                            || client.req.connection.remoteAddress,
                                remotePort: client.socket.remotePort,
                                username: decode_authdata(client.req.headers)
                        };
                } catch(e) {
                        return {
                                sent: 0,
                                remoteAddr: "127.0.0.1",
                                remotePort: 0,
                                username: "internal"
                        };
                }
        }

        /* reply with some useful info / statistics */
        res.send({
                streams: streams.current.map(function(el) {
                        return { id: el.id,
                                clients: el.clients.map(function(el) {
                                        return get_http_stats(el);
                                })
                        };
                })
        });
});

app.get('/transcode/:chan/:profile?', function(req, res) {
        if (!config.transcode) {
                res.send(404, "Transcoding not enabled");
                return;
        }

        var chan = req.params.chan;
        var profile = req.params.profile || config.transcode["default"];

        if (!config.transcode[profile]) {
                res.send(404, "Invalid profile");
                return;
        }

        console.log("TRANSCODE: " + chan + " " + profile);

        /* check wether the channel is already being transcoded*/
        changlue.try_chan("trans-" + profile + "-" + chan, function(liveStream) {
                res.set(liveStream.headers);
                streams.addClient("trans-" + profile + "-" + chan, res);
        }, function() {
                /* if not, grab the corresponding (uncompressed) source stream
                 * and fire up ffmpeg */
                changlue.get_chan(chan, function(sourceStream) {
                        var fakeClient = new (stream.PassThrough)({allowHalfOpen: false});
                        var transcodedChan = new (stream.PassThrough)();

                        res.writeHead(200, sourceStream.headers);

                        /* register fake (internal) client on source stream */
                        streams.addClient(chan, fakeClient);

                        /* register new transcoding channel and attach actual client to it */
                        streams.addChan("trans-" + profile + "-" + chan,
                                        transcodedChan, sourceStream.headers,
                                        function() {
                                                fakeClient.end();

                                                /* FIXME: hackish, for some reason end
                                                 *  doesn't always trigger this */
                                                streams.killClient(chan, fakeClient);
                                        });
                        streams.addClient("trans-" + profile + "-" + chan, res);

                        /* start encoding */
                        config.transcode[profile](new ffmpeg({ source: fakeClient }))
                                .fromFormat('mpegts')
                                .toFormat('mpegts')
                                .writeToStream(transcodedChan);
                }, function() {
                        res.send(503, "Failed to start stream");
                });
        });
});

app.get('/stream/:chan', function(req, res) {
        var chan = req.params.chan;

        changlue.get_chan(chan, function(liveStream) {
                res.set(liveStream.headers);
                streams.addClient(chan, res);
        }, function(err) {
                if (err == "NotFound")
                        res.send(404, "not found");
                else
                        res.send(503, "Failed to start stream");
        });
});

app.listen(config.server.port);
<commit_msg>proxy: expose m3u playlist for channels<commit_after>/* npm deps */
stream  = require('stream');
http    = require('http');
url     = require('url');
express = require('express');
git     = require('git-rev');
ffmpeg  = require('fluent-ffmpeg');
require('array.prototype.findindex');

/* internal deps */
config = require('./channels_config');
streams = require('./lib/stream-manager');
changlue = require('./lib/channels-glue');

/*
 * express webapp: expose a number of endpoints and APIs
 */
var app = express();

app.get('/', function (req, res) {
        res.send("node-iptv-proxy");
});

app.get('/list', function(req, res) {
        /* list available channels */
        res.send({ channels: Object.keys(config.channels) });
});

app.get('/playlist', function(req, res) {
        var playlist = "";

        res.header("Content-Type", "audio/x-mpegurl");

        playlist += '#EXTM3U\r\n';
        Object.keys(config.channels).map(function(val) {
                playlist += '#EXTINF:-1 tvg-name="' + val + '",' + val + '\r\n';
                playlist += 'http://' + req.headers['host'] + '/stream/' + val+ '\r\n';
        });

        res.send(playlist);
});

app.get('/version', function(req, res) {
        git.short(function(commit) {
                res.send({ version: commit });
        });
});

app.get('/status', function(req, res){
        function decode_authdata(headers) {
                var username = "";
                try {
                        username = new Buffer(headers['authorization'].replace("Basic ",""), 'base64')
                                .toString().split(':')[0];
                } catch(e) {
                        username = "none";
                }

                return username;
        }

        function get_http_stats(client) {
                try {
                        return {
                                sent: client.socket.bytesWritten,
                                remoteAddr: client.req.headers['x-forwarded-for']
                                            || client.req.connection.remoteAddress,
                                remotePort: client.socket.remotePort,
                                username: decode_authdata(client.req.headers)
                        };
                } catch(e) {
                        return {
                                sent: 0,
                                remoteAddr: "127.0.0.1",
                                remotePort: 0,
                                username: "internal"
                        };
                }
        }

        /* reply with some useful info / statistics */
        res.send({
                streams: streams.current.map(function(el) {
                        return { id: el.id,
                                clients: el.clients.map(function(el) {
                                        return get_http_stats(el);
                                })
                        };
                })
        });
});

app.get('/transcode/:chan/:profile?', function(req, res) {
        if (!config.transcode) {
                res.send(404, "Transcoding not enabled");
                return;
        }

        var chan = req.params.chan;
        var profile = req.params.profile || config.transcode["default"];

        if (!config.transcode[profile]) {
                res.send(404, "Invalid profile");
                return;
        }

        console.log("TRANSCODE: " + chan + " " + profile);

        /* check wether the channel is already being transcoded*/
        changlue.try_chan("trans-" + profile + "-" + chan, function(liveStream) {
                res.set(liveStream.headers);
                streams.addClient("trans-" + profile + "-" + chan, res);
        }, function() {
                /* if not, grab the corresponding (uncompressed) source stream
                 * and fire up ffmpeg */
                changlue.get_chan(chan, function(sourceStream) {
                        var fakeClient = new (stream.PassThrough)({allowHalfOpen: false});
                        var transcodedChan = new (stream.PassThrough)();

                        res.writeHead(200, sourceStream.headers);

                        /* register fake (internal) client on source stream */
                        streams.addClient(chan, fakeClient);

                        /* register new transcoding channel and attach actual client to it */
                        streams.addChan("trans-" + profile + "-" + chan,
                                        transcodedChan, sourceStream.headers,
                                        function() {
                                                fakeClient.end();

                                                /* FIXME: hackish, for some reason end
                                                 *  doesn't always trigger this */
                                                streams.killClient(chan, fakeClient);
                                        });
                        streams.addClient("trans-" + profile + "-" + chan, res);

                        /* start encoding */
                        config.transcode[profile](new ffmpeg({ source: fakeClient }))
                                .fromFormat('mpegts')
                                .toFormat('mpegts')
                                .writeToStream(transcodedChan);
                }, function() {
                        res.send(503, "Failed to start stream");
                });
        });
});

app.get('/stream/:chan', function(req, res) {
        var chan = req.params.chan;

        changlue.get_chan(chan, function(liveStream) {
                res.set(liveStream.headers);
                streams.addClient(chan, res);
        }, function(err) {
                if (err == "NotFound")
                        res.send(404, "not found");
                else
                        res.send(503, "Failed to start stream");
        });
});

app.listen(config.server.port);

{'fraction_non_alphanumeric': 0.12474989125706828, 'fraction_numerical': 0.005306655067420617, 'mean_word_length': 1.1926378027846654, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 2, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 28, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2301650', 'n_tokens_mistral': 2866, 'n_tokens_neox': 2660, 'n_words': 558}