Init WebCG
This commit is contained in:
@@ -0,0 +1,265 @@
|
||||
|
||||
// ================== functions alphabetical order ==================================================
|
||||
|
||||
const logger = require('./logger.js');
|
||||
const fs = require('fs');
|
||||
const path = require('path')
|
||||
const moment = require('moment');
|
||||
|
||||
|
||||
module.exports = {
|
||||
|
||||
isDisabled: function (serverName) {
|
||||
let outcome = false;
|
||||
config.casparcg.servers.forEach((item,index) => {
|
||||
// console.log('Iterating server ' + item.name + ', disabled = ' + item.disabled);
|
||||
if (item.name == serverName && item.disabled == true) {
|
||||
outcome = true
|
||||
}
|
||||
});
|
||||
return outcome;
|
||||
},
|
||||
|
||||
|
||||
clearChannelsFromGCServer: function (serverName='') {
|
||||
//TODO: add 2nd parameter an Array of layer numbers...
|
||||
let ClearAMCPCommand = 'CLEAR 1\r\n CLEAR 2\r\n CLEAR 3\r\n CLEAR 4\r\n CLEAR 5\r\n CLEAR 6\r\n CLEAR 7\r\n CLEAR 8\r\n';
|
||||
if (serverName) {
|
||||
// clear the given server
|
||||
global.CCGSockets[this.getSockIndex(serverName)].write(ClearAMCPCommand);
|
||||
} else {
|
||||
// clear all configured servers
|
||||
if ( config.casparcg && config.casparcg.servers ) {
|
||||
config.casparcg.servers.forEach((item,index) => {
|
||||
global.CCGSockets[this.getSockIndex(item.name)].write(ClearAMCPCommand);
|
||||
});
|
||||
}
|
||||
}
|
||||
logger.verbose('Clearing CasparCG [server: ' + serverName + ']');
|
||||
},
|
||||
|
||||
|
||||
CGComponentFactory: function (fieldID, value) {
|
||||
// Generate template data entry XML for CasparCG.
|
||||
// decodeURIComponent and UnSwapCharacters() added here to workaround issue #5.
|
||||
// require ..... fieldID, such as 'f0' and value such as 'Tuomo'
|
||||
// returns ..... component element as xml string, example:
|
||||
/*
|
||||
<componentData id=\"f0\">
|
||||
<data id=\"text\" value=\"Donald Trump\"/>
|
||||
</componentData>
|
||||
*/
|
||||
|
||||
logger.debug('CGComponentFactory (for XML data only) - fieldID: ' + fieldID + ', value: ' + value);
|
||||
|
||||
let decodedValue = decodeURIComponent(value) || ""; // changed again. Was += " " and then "null" <:-/
|
||||
return `<componentData id=\\"${fieldID}\\"><data id=\\"text\\" value=\\"${decodedValue}\\"/></componentData>`;
|
||||
},
|
||||
|
||||
|
||||
|
||||
playoutController: function (data){
|
||||
// We get data object which has
|
||||
// - data.command (ADD | STOP | UPDATE)
|
||||
|
||||
// let GFX_Teml = data.relpathCCG; // before 1.0.7
|
||||
// let GFX_Teml = 'http://' + ip.address() + ':' + config.general.port + '/templates/' + data.relpathCCG + '.html'; // changed to http in 1.0.7
|
||||
// let GFX_Teml = 'http://localhost:' + config.general.port + '/templates/' + data.relpathCCG + '.html'; // accidentally left to "localhost" in 1.0.8. Oops <:-O
|
||||
|
||||
// 1.0.12 - Check if we do have CCG servers configured
|
||||
let CCGSERVER = global.CCGSockets[this.getSockIndex(data.playserver)];
|
||||
if (!CCGSERVER || typeof CCGSERVER === 'undefined') {
|
||||
logger.verbose('No CasparCG servers configured (or server [' + data.playserver + '] not found) in SPX-GC. Skipping CCG command ' + data.command);
|
||||
return
|
||||
}
|
||||
|
||||
if (!data.playserver || data.playserver=='' || typeof data.playserver === 'undefined') {
|
||||
logger.verbose('No CasparCG server configured in the template. Skipping CCG command ' + data.command);
|
||||
return
|
||||
}
|
||||
|
||||
if (this.isDisabled(data.playserver)===true) {
|
||||
logger.verbose('Server ' + data.playserver + ' temporarily disabled, canceling playout commands.' );
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
let GFX_Teml = getCCGTemplateFilepath(data.relpathCCG); // v.1.0.9 = Supports both FILE or HTTP template paths, see config>general.casparcg-template-folder
|
||||
let GFX_Serv = data.playserver;
|
||||
let GFX_Chan = data.playchannel;
|
||||
let GFX_Laye = data.playlayer;
|
||||
let DataType = data.dataformat || 'json'; // added in 1.2.0
|
||||
let InvFunct = data.invoke;
|
||||
data.command = data.command.toUpperCase();
|
||||
|
||||
// Notify user
|
||||
if ( !global.CCGSockets[this.getSockIndex(data.playserver)] ) {
|
||||
logger.warn('Server [' + GFX_Serv + '] not found in your config! Make sure configuration and project settings match.' )
|
||||
}
|
||||
|
||||
logger.verbose('CasparCG playoutController - command ' + data.command + ', Template: ' + GFX_Teml, ', CasparCG: ' + GFX_Serv + ', ' + GFX_Chan + ', ' + GFX_Laye);
|
||||
logger.debug('CasparCG playoutController ' + JSON.stringify(data,null,4));
|
||||
logger.debug('Generating DATA for CasparCG [command ' + data.command + '], source: ' + JSON.stringify(data.fields,null,4));
|
||||
let TEMPLATEDATA = "";
|
||||
var DataStr = "";
|
||||
if (data.command == "ADD" || data.command == "UPDATE") {
|
||||
if (data.fields) {
|
||||
data.fields.forEach((item,index) => {
|
||||
logger.debug(' DATA --> ' + item.field + ' : ' + item.value);
|
||||
|
||||
// Fixes "undefined" issue in AMCP playout
|
||||
let value = ""; // default value if null or undefined
|
||||
if (item.value!=null && typeof item.value !== 'undefined') {
|
||||
value = item.value.toString();
|
||||
}
|
||||
|
||||
if (DataType == 'xml'){
|
||||
// generate data in XML format
|
||||
TEMPLATEDATA += this.CGComponentFactory(item.field, value);
|
||||
} else {
|
||||
|
||||
logger.debug('Generating JSON data for CasparCG, field: ' + item.field + ', value: ' + value);
|
||||
|
||||
TEMPLATEDATA += '\\"' + item.field + '\\":\\"' + value + '\\",';
|
||||
}
|
||||
});
|
||||
}
|
||||
if (DataType == 'xml'){
|
||||
// finalize XML format
|
||||
DataStr = "<templateData>" + TEMPLATEDATA + "</templateData>";
|
||||
} else {
|
||||
// finalize JSON formatby removing trailing comma
|
||||
if (TEMPLATEDATA.slice(-1)==','){
|
||||
TEMPLATEDATA = TEMPLATEDATA.slice(0, -1);
|
||||
}
|
||||
DataStr = "{" + TEMPLATEDATA + "}";
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug('[' + DataType + '] ' + DataStr);
|
||||
|
||||
|
||||
try {
|
||||
switch (data.command) {
|
||||
case 'ADD':
|
||||
global.CCGSockets[this.getSockIndex(data.playserver)].write('CG ' + GFX_Chan + '-' + GFX_Laye + ' ADD 1 "' + GFX_Teml + '" 1 "' + DataStr + '"\r\n');
|
||||
break;
|
||||
|
||||
case 'UPDATE':
|
||||
global.CCGSockets[this.getSockIndex(data.playserver)].write('CG ' + GFX_Chan + '-' + GFX_Laye + ' UPDATE 1 "' + DataStr + '"\r\n');
|
||||
break;
|
||||
|
||||
case 'STOP':
|
||||
global.CCGSockets[this.getSockIndex(data.playserver)].write('CG ' + GFX_Chan + '-' + GFX_Laye + ' STOP 1\r\n');
|
||||
break;
|
||||
|
||||
case 'NEXT':
|
||||
global.CCGSockets[this.getSockIndex(data.playserver)].write('CG ' + GFX_Chan + '-' + GFX_Laye + ' NEXT 0\r\n');
|
||||
break;
|
||||
|
||||
case 'INVOKE':
|
||||
InvFunct = InvFunct.replace(/"/g, '\\"'); // replace " with \" globally. Added in 1.1.0.
|
||||
global.CCGSockets[this.getSockIndex(data.playserver)].write('CG ' + GFX_Chan + '-' + GFX_Laye + ' INVOKE 1 \"' + InvFunct + '\"\r\n');
|
||||
break;
|
||||
|
||||
default:
|
||||
logger.warn('CCG/Control (util) - Unknown command: ' + data.command);
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('ERROR in playoutController: ' + error)
|
||||
}
|
||||
|
||||
}, // end add,
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
VideoPlayerController: function (data) {
|
||||
// We get data object which has
|
||||
// - data.command (ADD | STOP | UPDATE)
|
||||
|
||||
// handle video commands
|
||||
console.log('Handling VIDEO PLAYOUT', data);
|
||||
let GFX_Serv = data.playserver;
|
||||
let GFX_Chan = data.playchannel;
|
||||
let GFX_Laye = data.playlayer;
|
||||
let GFX_File = data.relpath;
|
||||
let GFX_CCGf = data.relpathCCG;
|
||||
let GFX_OPTS = data.playoptions;
|
||||
|
||||
logger.info("VideoPlayerController / CasparCG: " + GFX_Serv + ", " + GFX_Chan + ", " + GFX_Laye + ", Videofile " + GFX_CCGf);
|
||||
logger.debug('CasparCG VideoPlayerController ' + JSON.stringify(data,null,4));
|
||||
|
||||
try {
|
||||
switch (data.command) {
|
||||
case 'play':
|
||||
logger.verbose('Playing VIDEO / ' + GFX_CCGf);
|
||||
global.CCGSockets[this.getSockIndex(GFX_Serv)].write('PLAY ' + GFX_Chan + '-' + GFX_Laye + ' ' + GFX_CCGf + ' ' + GFX_OPTS + ' \r\n');
|
||||
break;
|
||||
|
||||
case 'stop':
|
||||
logger.verbose('Stopping VIDEO / ' + GFX_CCGf);
|
||||
global.CCGSockets[this.getSockIndex(GFX_Serv)].write('STOP ' + GFX_Chan + '-' + GFX_Laye + '\r\n');
|
||||
break;
|
||||
|
||||
case 'fadeout':
|
||||
logger.verbose('Fadeout VIDEO / ' + GFX_CCGf);
|
||||
// PLAY 1-10 CLEAR MIX 50
|
||||
global.CCGSockets[this.getSockIndex(GFX_Serv)].write('PLAY ' + GFX_Chan + '-' + GFX_Laye + ' EMPTY MIX 13\r\n');
|
||||
break;
|
||||
|
||||
default:
|
||||
logger.warn('CCG/controlvideo - Unknown command: ' + data.command);
|
||||
}
|
||||
// res.sendStatus(200);
|
||||
} catch (error) {
|
||||
logger.error('ERROR in VideoPlayerController: ' + error)
|
||||
// res.sendStatus(500);
|
||||
}
|
||||
}, // end video player
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
getSockIndex: function (SERVERNAME) {
|
||||
// Get an index of a CasparCG socket connection reference, not the object directly.
|
||||
// require .... SERVERNAME (example "TG")
|
||||
// returns .... CCG Server object INDEX (such as 0)
|
||||
logger.debug('getSockIndex / Searching for Socket reference for connection "' + SERVERNAME + '"...');
|
||||
let serverIndex = "";
|
||||
CCGSockets.forEach(function (item, index) {
|
||||
if (global.CCGSockets[index].spxname.toLowerCase() == SERVERNAME.toLowerCase()) {
|
||||
serverIndex = index;
|
||||
logger.debug('getSockIndex found [' + global.CCGSockets[index].spxname + '] so index is [' + serverIndex + '].');
|
||||
}
|
||||
else {
|
||||
logger.debug('getSockIndex skipping ' + global.CCGSockets[index].spxname) + '...';
|
||||
}
|
||||
});
|
||||
return serverIndex;
|
||||
}
|
||||
|
||||
} // end of exports PlayoutCCG.<functionName>
|
||||
|
||||
|
||||
function getCCGTemplateFilepath(fileRef) {
|
||||
// Added in 1.0.9.
|
||||
// Return either the "simple filepath" or "full URL" for the CasparCG command.
|
||||
const spx = require('./spx_server_functions.js');
|
||||
let TemplateSource = spx.getTemplateSourcePath()
|
||||
let TemplatePathForCasparCGServer
|
||||
|
||||
if ( TemplateSource.substring(0, 4)=='http' )
|
||||
// HTTP
|
||||
TemplatePathForCasparCGServer = TemplateSource + ':' + config.general.port + '/templates/' + fileRef + '.html'; //
|
||||
else {
|
||||
// FILE
|
||||
TemplatePathForCasparCGServer = fileRef; // as-is, a filepath in CasparCG server's own template-path directory
|
||||
}
|
||||
return TemplatePathForCasparCGServer
|
||||
}
|
||||
Reference in New Issue
Block a user