UNPKG

node-red-contrib-mog

Version:

mog

602 lines (515 loc) 26.2 kB
const express = require('express'); var bodyParser = require('body-parser'); var cors = require('cors'); var fs = require('fs'); const fetch = require("node-fetch"); const homedir = require('os').homedir(); var spawn = require("child_process").spawn,child; const app = express(); const port = 3000; if(process.argv.length != 3) { console.log("Missing connection port or too many arguments. Use 'node server.js <connection_port>"); console.log("Aborting..."); console.log("Server shut down"); return(1); } const connection_port = process.argv[2]; var children = []; var last_event = ""; var filename; fs.writeFile("video-switcher.bat", "", 'utf8', function (err) { if (err) throw err; console.log('Saved!'); }); fs.writeFile("audio-switcher.bat", "", 'utf8', function (err) { if (err) throw err; console.log('Saved!'); }); var change = 0; app.use(cors()); app.use(bodyParser.urlencoded({ extended: false })); app.use(bodyParser.json()); app.use(function(req, res, next) { res.setHeader("Content-Type", "application/json"); next(); }); app.get('/database', function (req, res) { var obj; fs.readFile('db.json', 'utf8', function(err, data){ if (err){ console.log(err); } else { obj = JSON.parse(data); console.log(obj); res.end(JSON.stringify(obj)); }}); }) app.post('/database', function (req, res) { var dir = homedir + '\\.node-red\\flows_'+ process.env.COMPUTERNAME +'.json'; fs.watch(dir, function (event) { if(event == 'change' && last_event != 'change') { console.log("Sending database"); var data = fs.readFileSync(dir); var node_red_nodes = JSON.parse(data); var nodes = req.body.nodes; var toDelete = []; for(var i = 0; i<children.length; i++) { spawn("taskkill", ["/pid", children[i].pid, '/f', '/t']); } children = []; var cmd = "Get-Process | Where-Object { $_.ProcessName -eq 'ffplay' } | ForEach-Object {taskkill /F /PID $_.Id }"; child = spawn("powershell",[cmd]); children.push(child); console.log("PowerShell: " + cmd); child.stdout.on("data",function(data){ console.log("PowerShell: " + data); }); child.stderr.on("data",function(data){ console.log("PowerShell: " + data); }); child.on("exit",function(){ console.log("Powershell Script finished"); //call batchfile command = "\"OutputDistributor\" " + filename; child = spawn("start",[command], {shell: true, detached: true}); children.push(child); console.log("PowerShell: " + command); child.stdout.on("data",function(data){ console.log("Powershell: " + data); }); child.stderr.on("data",function(data){ console.log("Powershell: " + data); }); child.on("exit",function(){ console.log("Powershell Script finished"); }); child.stdin.end(); }); child.stdin.end(); cmd = "Get-Process | Where-Object { $_.mainWindowTitle -eq 'OutputDistributor - "+filename+"' } | ForEach-Object {taskkill /F /PID $_.Id }"; child = spawn("powershell",[cmd]); children.push(child); console.log("PowerShell: " + cmd); child.stdout.on("data",function(data){ console.log("PowerShell: " + data); }); child.stderr.on("data",function(data){ console.log("PowerShell: " + data); }); child.on("exit",function(){ console.log("Powershell Script finished"); }); child.stdin.end(); cmd = "Get-Process | Where-Object { $_.mainWindowTitle -eq 'VideoSwitcher - video-switcher.bat' } | ForEach-Object {taskkill /F /PID $_.Id }"; child = spawn("powershell",[cmd]); children.push(child); console.log("PowerShell: " + cmd); child.stdout.on("data",function(data){ console.log("PowerShell: " + data); }); child.stderr.on("data",function(data){ console.log("PowerShell: " + data); }); child.on("exit",function(){ console.log("Powershell Script finished"); //call batchfile command = "\"VideoSwitcher\" " + 'video-switcher.bat'; child = spawn("start",[command], {shell: true, detached: true}); children.push(child); console.log("PowerShell: " + command); child.stdout.on("data",function(data){ console.log("Powershell: " + data); }); child.stderr.on("data",function(data){ console.log("Powershell: " + data); }); child.on("exit",function(){ console.log("Powershell Script finished"); }); child.stdin.end(); }); child.stdin.end(); cmd = "Get-Process | Where-Object { $_.mainWindowTitle -eq 'AudioSwitcher - audio-switcher.bat' } | ForEach-Object {taskkill /F /PID $_.Id }"; child = spawn("powershell",[cmd]); children.push(child); console.log("PowerShell: " + cmd); child.stdout.on("data",function(data){ console.log("PowerShell: " + data); }); child.stderr.on("data",function(data){ console.log("PowerShell: " + data); }); child.on("exit",function(){ console.log("Powershell Script finished"); //call batchfile command = "\"AudioSwitcher\" " + 'audio-switcher.bat'; child = spawn("start",[command], {shell: true, detached: true}); children.push(child); console.log("PowerShell: " + command); child.stdout.on("data",function(data){ console.log("Powershell: " + data); }); child.stderr.on("data",function(data){ console.log("Powershell: " + data); }); child.on("exit",function(){ console.log("Powershell Script finished"); }); child.stdin.end(); }); child.stdin.end(); for(var i=0; i<nodes.length; i++) { var match = 0; for(var m=0; m<node_red_nodes.length; m++) { if(nodes[i].id == node_red_nodes[m].id) { match = 1; break; } } if(match == 0) { toDelete.push(i); } else { if(nodes[i].type == "input" || nodes[i].type == "input-file"){ var command = 'ffmpeg -loglevel quiet -re -f lavfi -i testsrc=size=qcif:rate=25 -f lavfi -i "sine=frequency=1000" '; if(change == 1) { command = 'ffmpeg -loglevel quiet -re -f lavfi -i smptebars=size=qcif:rate=25 -f lavfi -i "sine=frequency=2000" '; change = 0; } else if(change == 0) { change = 1; } for(var n = 0; n<nodes[i].video_ips.length; n++){ command += '-pix_fmt yuv420p -an -f rawvideo udp://' + nodes[i].video_ips[n] + ':' + nodes[i].video_ports[n] + ' '; } for(var n = 0; n<nodes[i].audio_ips.length; n++){ command += '-vn -f s16le udp://' + nodes[i].audio_ips[n] + ':' + nodes[i].audio_ports[n] + ' '; } command += ' -loglevel quiet'; child = spawn("powershell.exe",[command], {shell: true, detached: true}); children.push(child); console.log("PowerShell: " + command); child.stdout.on("data",function(data){ console.log("Powershell: " + data); }); child.stderr.on("data",function(data){ console.log("Powershell: " + data); }); child.on("exit",function(){ console.log("Powershell Script finished"); }); child.stdin.end(); } else if(nodes[i].type == "output") { nodes[i].audio_ips = []; nodes[i].video_ips = []; nodes[i].audio_ports = []; nodes[i].video_ports = []; for(var j=0; j<node_red_nodes.length; j++) { if(node_red_nodes[j].wires != undefined){ for(var k=0; k<node_red_nodes[j].wires.length; k++) { for(var z=0; z<node_red_nodes[j].wires[k].length; z++) { if(node_red_nodes[j].wires[k][z] == nodes[i].id){ var aux; if((k==1 || node_red_nodes[j].type == 'audio-switcher') && node_red_nodes[j].audio_ips != undefined) { aux = nodes[i].audio_ips.concat(node_red_nodes[j].audio_ips); for(var z = 0; z<aux.length; z++){ if (nodes[i].audio_ips.indexOf(aux[z]) < 0 ) nodes[i].audio_ips.push(aux[z]); } aux = nodes[i].audio_ports.concat(node_red_nodes[j].audio_ports); for(var z = 0; z<aux.length; z++){ if (nodes[i].audio_ports.indexOf(aux[z]) < 0 ) nodes[i].audio_ports.push(aux[z]); } //nodes[i].n_audio_inputs = nodes[i].audio_ips.length; } if(k==0 && node_red_nodes[j].video_ips != undefined) { aux = nodes[i].video_ips.concat(node_red_nodes[j].video_ips); for(var z = 0; z<aux.length; z++){ if (nodes[i].video_ips.indexOf(aux[z]) < 0 ) nodes[i].video_ips.push(aux[z]); } aux = nodes[i].video_ports.concat(node_red_nodes[j].video_ports); for(var z = 0; z<aux.length; z++){ if (nodes[i].video_ports.indexOf(aux[z]) < 0 ) nodes[i].video_ports.push(aux[z]); } //nodes[i].n_video_inputs = nodes[i].video_ips.length; } } } } } } // Write to a batch file var command = '@echo off\r\nffmpeg -loglevel quiet '; for(var n = 0; n<nodes[i].video_ips.length; n++){ command += '-f rawvideo -pixel_format yuv420p -framerate 25 -video_size 176x144 -i "udp://' + nodes[i].video_ips[n] + ':' + nodes[i].video_ports[n] + '?fifo_size=100000&buffer_size=10000000" '; } for(var n = 0; n<nodes[i].audio_ips.length; n++){ command += '-f s16le -i "udp://' + nodes[i].audio_ips[n] + ':' + nodes[i].audio_ports[n] + '?fifo_size=100000&buffer_size=10000000" '; } command += '-c copy -f avi - | ffplay -loglevel quiet -'; filename = 'output'+i+'.bat'; fs.writeFile(filename, command, 'utf8', function (err) { if (err) throw err; console.log('Saved!'); }); } else if(nodes[i].type == "video-switcher") { nodes[i].input_video_ips = []; nodes[i].input_video_ports = []; for(var j=0; j<node_red_nodes.length; j++) { if(node_red_nodes[j].wires != undefined){ for(var k=0; k<node_red_nodes[j].wires.length; k++) { for(var z=0; z<node_red_nodes[j].wires[k].length; z++) { if(node_red_nodes[j].wires[k][z] == nodes[i].id){ var aux = nodes[i].input_video_ips.concat(node_red_nodes[j].video_ips); nodes[i].input_video_ips = aux; aux = nodes[i].input_video_ports.concat(node_red_nodes[j].video_ports); nodes[i].input_video_ports = aux; //nodes[i].n_input_video_inputs = nodes[i].input_video_ips.length; } } } } } var command = '@echo off\r\nffmpeg -loglevel quiet '; for(var n = 0; n<nodes[i].input_video_ips.length; n++){ command += '-f rawvideo -pixel_format yuv420p -framerate 25 -video_size 176x144 -i "udp://' + nodes[i].input_video_ips[n] + ':' + nodes[i].input_video_ports[n] + '?fifo_size=100000&buffer_size=10000000" '; } command += '-lavfi "null, zmq [bg];[bg]'; for(var n = 0; n<nodes[i].input_video_ips.length; n++){ command += '['+ n + ']'; } var number = nodes[i].input_video_ips.length + 1; command += 'streamselect=inputs=' + number + ':map=' + nodes[i].video_ips.length + '[out]" -map [out] '; for(var n = 0; n<nodes[i].video_ips.length; n++){ command += '-f rawvideo -pixel_format yuv420p -framerate 25 -video_size 176x144 "udp://' + nodes[i].video_ips[n] + ':' + nodes[i].video_ports[n] + '" '; } command += ' -loglevel quiet'; var video_file = 'video-switcher.bat'; fs.writeFile(video_file, command, 'utf8', function (err) { if (err) throw err; console.log('Saved!video'); }); } else if(nodes[i].type == "audio-switcher") { nodes[i].input_audio_ips = []; nodes[i].input_audio_ports = []; for(var j=0; j<node_red_nodes.length; j++) { if(node_red_nodes[j].wires != undefined){ for(var k=0; k<node_red_nodes[j].wires.length; k++) { for(var z=0; z<node_red_nodes[j].wires[k].length; z++) { if(node_red_nodes[j].wires[k][z] == nodes[i].id){ var aux = nodes[i].input_audio_ips.concat(node_red_nodes[j].audio_ips); nodes[i].input_audio_ips = aux; aux = nodes[i].input_audio_ports.concat(node_red_nodes[j].audio_ports); nodes[i].input_audio_ports = aux; //nodes[i].n_input_audio_inputs = nodes[i].input_audio_ips.length; } } } } } var command = '@echo off\r\nffmpeg -loglevel quiet '; for(var n = 0; n<nodes[i].input_audio_ips.length; n++){ command += '-f s16le -i "udp://' + nodes[i].input_audio_ips[n] + ':' + nodes[i].input_audio_ports[n] + '?fifo_size=100000&buffer_size=10000000" '; } command += '-lavfi "anull, azmq=bind_address=\'tcp\\://127.0.0.1\\:1235\' [bg];[bg]'; for(var n = 0; n<nodes[i].input_audio_ips.length; n++){ command += '['+ n + ']'; } var number = nodes[i].input_audio_ips.length + 1; command += 'astreamselect=inputs=' + number + ':map=' + nodes[i].audio_ips.length + '[out]" -map [out] '; for(var n = 0; n<nodes[i].audio_ips.length; n++){ command += '-f s16le "udp://' + nodes[i].audio_ips[n] + ':' + nodes[i].audio_ports[n] + '" '; } command += '-loglevel quiet'; var audio_file = 'audio-switcher.bat'; fs.writeFile(audio_file, command, 'utf8', function (err) { if (err) throw err; console.log('Saved!audio'); }); } } } for(var q = 0; q<toDelete.length; q++) { nodes.splice(toDelete[q],1); } console.log(nodes); console.log(req.body); var json = JSON.stringify(req.body); fs.writeFile('db.json', json, 'utf8', function (err) { if (err) throw err; console.log('Saved!'); }); res.end(json); } last_event = event; }); }) app.get('/devices', function (req, res) { var obj; fs.readFile('available-devices.json', 'utf8', function(err, data){ if (err){ console.log(err); } else { obj = JSON.parse(data); var availableDevices = { devices: [] }; for(var i = 0; i < obj.services.length; i++) { if(obj.services[i].protocol == req.query.type) { availableDevices.devices.push(obj.services[i].fullname); } } res.end(JSON.stringify(availableDevices)); }}); }) app.get('/services', function (req, res) { //get request ao node var deviceChosen; var data = fs.readFileSync('available-devices.json'); obj = JSON.parse(data); for(var i = 0; i < obj.services.length; i++) { if(obj.services[i].fullname == req.query.deviceID) { deviceChosen = obj.services[i]; break; } } var serviceType; if(req.query.type == 'input'){ serviceType = "senders"; } else if(req.query.type == 'output') { serviceType = "receivers"; } else { serviceType = "senders"; } const url = 'http://' + deviceChosen.addresses[0] + ':' + deviceChosen.port + '/x-nmos/node/v1.3/' + serviceType; console.log(url); fetch(url) .then(function(response) { console.log('Status code:', response.status); return response.json(); }) .then(function(myJson) { var data = JSON.parse(JSON.stringify(myJson)); console.log(data); var availableServices = { services: [], available: [] }; var exec = async() => { for(var i = 0; i < data.length; i++) { const url = "http://" + deviceChosen.addresses[0] + ":" + connection_port + "/x-nmos/connection/v1.1/single/" + serviceType + "/" + data[i].id + "/active"; console.log(url); var d = data[i].id; const request = async(d) => { const response = await fetch(url) .then(function(response) { console.log('Status code:', response.status); return response.json(); }) .catch(error => console.error(error)); return response.activation.activation_time; } var response = await request(d); if(response == null){ availableServices.services.push(d); availableServices.available.push(1); } else { availableServices.services.push(d); availableServices.available.push(0); } } console.log(availableServices); return availableServices; } async function finish() { await exec(); res.end(JSON.stringify(availableServices)); }; finish(); }) .catch(error => console.error(error)); }) app.post('/', function (req, res) { var deviceChosen; var data = fs.readFileSync('available-devices.json'); obj = JSON.parse(data); for(var i = 0; i < obj.services.length; i++) { if(obj.services[i].fullname == req.body.device) { deviceChosen = obj.services[i]; break; } } var serviceType1; var serviceType2; if(req.body.type == 'input'){ serviceType1 = "senders"; serviceType2 = "senders"; } else if(req.body.type == 'output') { serviceType1 = "receivers"; serviceType2 = "receivers"; } else if(req.body.type == "audio-switcher" || req.body.type == "video-switcher") { serviceType1 = "senders"; serviceType2 = "receivers"; } else { serviceType = "senders"; } if(deviceChosen != "" && deviceChosen != undefined) { var services1 = []; var services2 = []; if(req.body.type == "audio-switcher" || req.body.type == "video-switcher"){ services1 = req.body.output_services; services2 = req.body.input_services; } else { services1 = req.body.audio_services; services2 = req.body.video_services; } for(var i = 0; i < services1.length; i++){ var url = "http://" + deviceChosen.addresses[0] + ":" + connection_port + "/x-nmos/connection/v1.1/single/" + serviceType1 + "/" + services1[i] + "/staged"; console.log("POST" + url); fetch(url, { method : "PATCH", headers:{ 'Accept': 'application/json', 'Content-Type': 'application/json' }, body : JSON.stringify({ activation: { mode: "activate_immediate", requested_time: null } }) }) .then(function(response) { console.log('Status code:', response.status); if(response.status == 200){ res.send({ status: 'SUCCESS' }); } }) .catch(error => console.error('Error:', error)); } for(var i = 0; i < services2.length; i++){ var url = "http://" + deviceChosen.addresses[0] + ":" + connection_port + "/x-nmos/connection/v1.1/single/" + serviceType2 + "/" + services2[i] + "/staged"; console.log("POST" + url); fetch(url, { method : "PATCH", headers:{ 'Accept': 'application/json', 'Content-Type': 'application/json' }, body : JSON.stringify({ activation: { mode: "activate_immediate", requested_time: null } }) }) .then(function(response) { console.log('Status code:', response.status); if(response.status == 200){ res.send({ status: 'SUCCESS' }); } }) .catch(error => console.error('Error:', error)); } } }) app.listen(port, () => console.log(`Server listening on port ${port}!`))