Init WebCG
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
const spx = require('./spx_server_functions.js');
|
||||
const PlayoutCCG = require('./playout_casparCG.js');
|
||||
|
||||
// Experimental. Not in use yet.
|
||||
|
||||
|
||||
module.exports = {
|
||||
|
||||
panic: function () {
|
||||
try {
|
||||
io.emit('SPXMessage2Client', {spxcmd: 'clearAllLayers'}); // clear webrenderers
|
||||
io.emit('SPXMessage2Controller', {APIcmd:'RundownAllStatesToStopped'}); // stop UI and save stopped values to rundown
|
||||
if (spx.CCGServersConfigured){
|
||||
PlayoutCCG.clearChannelsFromGCServer() // server is optional, so doing ALL!!!!!
|
||||
}
|
||||
console.log('PANIC HANDLER');
|
||||
return true
|
||||
} catch (error) {
|
||||
console.log('Panic error' + error);
|
||||
return false
|
||||
}
|
||||
},
|
||||
|
||||
} // end of exports
|
||||
@@ -0,0 +1,49 @@
|
||||
// Import modules
|
||||
const path = require('path')
|
||||
const { createLogger, format, transports } = require('winston');
|
||||
const { combine, timestamp, label, printf } = format;
|
||||
|
||||
const myFormat = printf(({ level, message, label, timestamp }) => {
|
||||
return `${timestamp} [${label}] ${level}: ${message}`;
|
||||
});
|
||||
|
||||
|
||||
// logger.error("hello world!, this is error 0");
|
||||
// logger.warn("hello world!, this is warn 1");
|
||||
// logger.info("hello world!, this is info 2");
|
||||
// logger.verbose("hello world!, this is verbose 3");
|
||||
// logger.debug("hello world!, this is debug 4");
|
||||
// logger.silly("hello world!, this is silly 5");
|
||||
|
||||
let rootPath
|
||||
if ( process.pkg ) {
|
||||
// PKG process
|
||||
rootPath = path.resolve(process.execPath + '/..');
|
||||
} else {
|
||||
// NODE process
|
||||
rootPath = process.cwd();
|
||||
}
|
||||
|
||||
let LOGLEVEL = config.general.loglevel || 'debug';
|
||||
let LOGFOLDER = config.general.logfolder || rootPath + '/LOG';
|
||||
var logFile = path.resolve(LOGFOLDER, 'access.log');
|
||||
|
||||
const logger = createLogger({
|
||||
format: combine(
|
||||
label({ label: 'WebCG' }),
|
||||
timestamp(),
|
||||
myFormat
|
||||
),
|
||||
transports: [
|
||||
new transports.Console({
|
||||
level: LOGLEVEL
|
||||
}),
|
||||
new transports.File({
|
||||
level: LOGLEVEL,
|
||||
filename: logFile
|
||||
})
|
||||
]
|
||||
});
|
||||
|
||||
// Export the logger
|
||||
module.exports = logger;
|
||||
@@ -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
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
|
||||
// ================== functions alphabetical order ==================================================
|
||||
|
||||
const logger = require('./logger.js');
|
||||
const fs = require('fs');
|
||||
const path = require('path')
|
||||
const moment = require('moment');
|
||||
|
||||
|
||||
module.exports = {
|
||||
|
||||
|
||||
webPlayoutController: function (data){
|
||||
// We pass the data object to be processed by the web renderer
|
||||
// First dataformat sanity check.
|
||||
// THIS FORMATS JSON AND SENDS IT FORWARDS in all cases: play, stop, update...
|
||||
|
||||
|
||||
|
||||
let TEMPLATEDATA = [];
|
||||
//console.log('Format [' + DataType + '] data.fields coming in before emit', data.fields);
|
||||
if (data.fields && data.fields.length > 0) {
|
||||
data.fields.forEach((item,index) => {
|
||||
let tempObj={};
|
||||
tempObj[item.field] = item.value;
|
||||
TEMPLATEDATA.push(tempObj);
|
||||
});
|
||||
data.fields=TEMPLATEDATA;
|
||||
}
|
||||
// console.log('webPlayoutController data.fields going out for emit', data);
|
||||
io.emit('SPXMessage2Client', data);
|
||||
}
|
||||
|
||||
|
||||
} // end of exports PlayoutWEB.<functionName>
|
||||
@@ -0,0 +1,27 @@
|
||||
module.exports = function (io) {
|
||||
|
||||
io.sockets.on('connection', function (socket) {
|
||||
|
||||
console.log('*** Socket connection (' + socket.id + ") Connections: " + io.engine.clientsCount);
|
||||
clients[socket.id] = socket;
|
||||
// send stuff out
|
||||
data = [{ color: '#FFFF00' }, { color: '#FF00FF' }];
|
||||
socket.broadcast.emit('ServerIndicatorUpdate', data);
|
||||
|
||||
|
||||
socket.on('IncomingNamedCall', spxMessage);
|
||||
function spxMessage(data) {
|
||||
// this data was received via socket from client!
|
||||
console.log(data);
|
||||
|
||||
// send stuff out
|
||||
data = [{ color: '#FFFF00' }, { color: '#FF00FF' }];
|
||||
socket.broadcast.emit('ServerIndicatorUpdate', data);
|
||||
};
|
||||
|
||||
socket.on('disconnect', function () {
|
||||
console.log('*** Socket disconnected (' + socket.id + ") Connections: " + io.engine.clientsCount);
|
||||
delete clients[socket.id];
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,134 @@
|
||||
|
||||
const logger = require('./logger.js');
|
||||
const spx = require('../utils/spx_server_functions.js');
|
||||
|
||||
|
||||
async function validateIfApiKey(req) {
|
||||
// Added in 1.3.2
|
||||
// TODO: Test carefully and write documentation
|
||||
// about user/pass/apikey, for devs also!
|
||||
// console.log("validateIfApiKey", req.method, req.body, req.query);
|
||||
let message = '';
|
||||
if (config.general.username && !config.general.apikey) {
|
||||
message = 'Warning! When username is configured in WebCG, apikey is required then also.'
|
||||
logger.warn(message);
|
||||
return [false, message];
|
||||
}
|
||||
|
||||
|
||||
if (!config.general.apikey || config.general.apikey=='') {
|
||||
message = 'No api key WebCG in config, allow all.'
|
||||
logger.verbose(message);
|
||||
return [true, message];
|
||||
}
|
||||
|
||||
let KEY = req.method=='POST' ? (req.body?.apikey || null) : (req.query?.apikey || null);
|
||||
if ( !KEY ) {
|
||||
message = 'API key configured in WebCG, but apikey missing from the ' + req.method + ' API request. Access denied.'
|
||||
logger.warn(message);
|
||||
return [false, message];
|
||||
}
|
||||
|
||||
if ( KEY==config.general.apikey) {
|
||||
message = 'API key matches. Access allowed.'
|
||||
logger.verbose(message);
|
||||
return [true, message];
|
||||
} else {
|
||||
message = 'API key does not match. Access denied.'
|
||||
logger.warn(message);
|
||||
return [false, message];
|
||||
}
|
||||
|
||||
} // validateIfApiKey utility
|
||||
|
||||
|
||||
async function CheckAPIKey(req,res,next) {
|
||||
// Improved in 1.3.2
|
||||
// Middleware to check API key in each API endpoint
|
||||
var apiChecked = await validateIfApiKey(req);
|
||||
|
||||
// console.log("Api Checked with in CheckAPIKey", apiChecked);
|
||||
|
||||
if ( apiChecked[0]===true ) {
|
||||
// console.log("CheckAPIKey YEAH");
|
||||
next();
|
||||
return true;
|
||||
} else {
|
||||
// console.log("CheckAPIKey NOPE");
|
||||
let dataOut = {};
|
||||
dataOut.error = apiChecked[1]
|
||||
res.status(200).json(dataOut);
|
||||
return false;
|
||||
}
|
||||
} // CheckAPIKey
|
||||
|
||||
|
||||
async function CheckLogin(req,res,next) {
|
||||
// Middleware to check auth in each router.
|
||||
// require ..... username
|
||||
// returns ..... true / false
|
||||
|
||||
let USER = req.body.username || '';
|
||||
let PASS = req.body.password || '';
|
||||
let AllowedUser = config.general.username || '';
|
||||
|
||||
// let apiChecked = await validateIfApiKey(req);
|
||||
// if ( apiChecked[0]===true ) {
|
||||
// next();
|
||||
// return;
|
||||
// } else {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// See if auth is used (if user in config is present)
|
||||
if (!AllowedUser){
|
||||
logger.verbose('CheckLogin: No username in config, authorize "default" user...');
|
||||
req.session.user = 'default';
|
||||
next();
|
||||
return;
|
||||
}
|
||||
|
||||
// See if only user is present in config: ask for auth policy
|
||||
if (AllowedUser && !config.general.password || AllowedUser && config.general.password==''){
|
||||
logger.verbose('CheckLogin: No password in config, prompt for auth policy...');
|
||||
res.render('view-authpolicy', { layout: false, user: AllowedUser});
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (req.session.user && req.session.user==AllowedUser) {
|
||||
// correct user in session
|
||||
logger.verbose('CheckLogin: User "' + req.session.user + '" authorized ok.');
|
||||
next();
|
||||
return;
|
||||
}
|
||||
|
||||
if (USER==AllowedUser) {
|
||||
if (spx.hashcompare(PASS,config.general.password)) {
|
||||
logger.info('CheckLogin: User "' + USER + '" logged in.');
|
||||
req.session.user = USER;
|
||||
next();
|
||||
return;
|
||||
}
|
||||
}
|
||||
logger.verbose('CheckLogin: Not authenticated (or wrong user/pass), redirect to login');
|
||||
res.redirect('/login');
|
||||
// res.status(403);
|
||||
// res.render('view-login', { layout: false });
|
||||
} // CheckLogin
|
||||
|
||||
|
||||
function Logout(req,res,next) {
|
||||
// logout user
|
||||
logger.info('CheckLogin: User "' + req.session.user + '" logged out.');
|
||||
req.session.user = '';
|
||||
res.redirect('/');
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
CheckAPIKey,
|
||||
CheckLogin,
|
||||
Logout
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
// Note, these functions execute BEFORE logger or utils are loaded
|
||||
// so none of those functions can be used here!
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
|
||||
makeFolderIfNotExist: function (fullfolderpath) {
|
||||
try {
|
||||
if (!fs.existsSync(fullfolderpath)) {
|
||||
fs.mkdirSync(fullfolderpath);
|
||||
}
|
||||
return true
|
||||
} catch (error) {
|
||||
console.log('makeFolderIfNotExist: error while checking or creating folder [' + fullfolderpath + '].' + error);
|
||||
return false
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
readConfig: function () {
|
||||
// read config.json. Usage: "cfg.readConfig()"
|
||||
return new Promise(resolve => {
|
||||
try {
|
||||
let CURRENT_FOLDER // For reading config only. See spx.getStartUpFolder() after load.
|
||||
if ( process.pkg ) {
|
||||
// pkg process
|
||||
CURRENT_FOLDER = path.resolve(process.execPath + '/..');
|
||||
} else {
|
||||
// node process
|
||||
CURRENT_FOLDER = process.cwd();
|
||||
}
|
||||
|
||||
let CONFIG_FILE = path.join(CURRENT_FOLDER, 'config.json'); // Config file MUST BE in app folder.
|
||||
var myArgs = process.argv.slice(2);
|
||||
var ConfigArg = myArgs[0] || '';
|
||||
if (ConfigArg){
|
||||
console.log('Command line arguments given: [' + process.argv + '], reading config from ' + ConfigArg + '.');
|
||||
CONFIG_FILE = path.join(CURRENT_FOLDER, ConfigArg);
|
||||
}
|
||||
|
||||
// check if config file exists
|
||||
if (!fs.existsSync(CONFIG_FILE)) {
|
||||
// config file not found, let's create the default one
|
||||
console.log(' Config file not found, generating defaults.\n');
|
||||
global.generatingDefaultConfig = true;
|
||||
// console.log('Please note, starting from v.1.0.12 CasparCG servers are NOT in the config by default and must be added for CasparCG playout to work. Please see the README file for more information.');
|
||||
let cfg = {}
|
||||
cfg.general = {}
|
||||
cfg.general.username = "admin"
|
||||
cfg.general.password = ""
|
||||
// cfg.general.showusercommapass = "username,password"
|
||||
cfg.general.hostname = generateDefaultHostname()
|
||||
cfg.general.greeting = ""
|
||||
cfg.general.langfile = "english.json"
|
||||
cfg.general.loglevel = "info"
|
||||
// cfg.general.launchchrome = false // deprecated
|
||||
cfg.general.launchBrowser = false
|
||||
cfg.general.apikey = ""
|
||||
cfg.general.logfolder = path.join(CURRENT_FOLDER, 'LOG').replace(/\\/g, "/") + "/"
|
||||
cfg.general.dataroot = path.join(CURRENT_FOLDER, 'DATAROOT').replace(/\\/g, "/") + "/"
|
||||
cfg.general.templatesource = "spx-ip-address"
|
||||
cfg.general.port = 5656
|
||||
cfg.general.disableConfigUI = false
|
||||
cfg.general.disableLocalRenderer = false
|
||||
cfg.general.disableOpenFolderCommand = false
|
||||
cfg.general.disableSeveralControllersWarning = false
|
||||
cfg.general.hideRendererCursor = false
|
||||
cfg.general.resolution = "HD"
|
||||
cfg.general.preview = "selected"
|
||||
cfg.general.renderer = "normal"
|
||||
cfg.general.autoplayLocalRenderer = true
|
||||
// cfg.general.allowstats = true
|
||||
|
||||
cfg.general.recents = []
|
||||
|
||||
cfg.casparcg = {}
|
||||
cfg.casparcg.servers = []
|
||||
newcasparcg = {}
|
||||
|
||||
// Experimental, WIP
|
||||
cfg.osc = {}
|
||||
cfg.osc.enable = false
|
||||
cfg.osc.port = 57121
|
||||
|
||||
cfg.globalExtras = {}
|
||||
cfg.globalExtras.customscript = "/ExtraFunctions/demoFunctions.js"
|
||||
cfg.globalExtras.CustomControls = []
|
||||
|
||||
// Write config file. Note, this does not use utility function.
|
||||
cfg.warning = "GENERATED DEFAULT CONFIG. Modifications done in the WebCG will overwrite this file.";
|
||||
cfg.copyright = "(c) 2020- WebCG Graphics";
|
||||
cfg.updated = new Date().toISOString();
|
||||
global.config = cfg; // <---- config to global scope
|
||||
let filedata = JSON.stringify(cfg, null, 2);
|
||||
fs.writeFileSync(CONFIG_FILE, filedata, 'utf8', function (err) {
|
||||
if (err){
|
||||
console.error("Error writing default config to [" + CONFIG_FILE + "]")
|
||||
process.exit(2)
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
let configFileStr = fs.readFileSync(CONFIG_FILE);
|
||||
global.config = JSON.parse(configFileStr);
|
||||
}
|
||||
|
||||
// folderchecks (improved in 1.0.15)
|
||||
let CurrentLogFolder = global.config.general.logfolder || CURRENT_FOLDER + '/LOG'
|
||||
let CurrentDataRootF = global.config.general.logfolder || CURRENT_FOLDER + '/DATAROOT'
|
||||
this.makeFolderIfNotExist(CurrentLogFolder);
|
||||
this.makeFolderIfNotExist(CurrentDataRootF);
|
||||
global.configfileref = CONFIG_FILE;
|
||||
resolve()
|
||||
}
|
||||
catch (error) {
|
||||
let msg = 'CATASTROPHIC FAILURE WHILE INITIALIZING WebCG CONFIG, CANNOT CONTINUE. You can remove config.json and SPX will recreate one with defaults at startup.' + error;
|
||||
console.log(msg); // note, LOGGER is not necessarily initialized yet.
|
||||
global.configfileref = "";
|
||||
return
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} // end of exports
|
||||
|
||||
|
||||
function generateDefaultHostname() {
|
||||
let ip = ""
|
||||
let os = require('os');
|
||||
let interfaces = os.networkInterfaces();
|
||||
let addresses = [];
|
||||
for (let iface in interfaces) {
|
||||
for (let i = 0; i < interfaces[iface].length; i++) {
|
||||
let address = interfaces[iface][i];
|
||||
if (address.family === 'IPv4' && !address.internal) {
|
||||
addresses.push(address.address);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (addresses.length > 0) {
|
||||
ip = addresses[0]; // Return the first non-internal IPv4 address found
|
||||
} else {
|
||||
ip = 'localhost'; // Fallback to localhost if no external IP is found
|
||||
}
|
||||
|
||||
let lastIpNro = ip.split('.').slice(-1)[0];
|
||||
let hostname = os.hostname();
|
||||
let value = ("WebCG-" + (hostname ? hostname : '') + "-" + lastIpNro).toUpperCase();
|
||||
|
||||
return value;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,19 @@
|
||||
|
||||
' Tiny Text to Speech (only works on Windows)!
|
||||
' Commandline usage:
|
||||
' wscript talk.vbs Hello world!
|
||||
|
||||
sub Talk(message)
|
||||
Dim sapi
|
||||
Set sapi=CreateObject("sapi.spvoice")
|
||||
sapi.Speak message
|
||||
end sub
|
||||
|
||||
If WScript.Arguments.Count > 0 Then
|
||||
dim message
|
||||
for x=0 to WScript.Arguments.Count-1
|
||||
message = message & " " & WScript.Arguments.Item(x)
|
||||
next
|
||||
Talk(message)
|
||||
End If
|
||||
|
||||
Reference in New Issue
Block a user