0.1 Rally Framework
parent
76b662a139
commit
fb0cfe966a
|
@ -89,6 +89,8 @@ require('./libs/ffmpeg.js')(s,config,lang, async () => {
|
||||||
require('./libs/onvifDeviceManager.js')(s,config,lang,app,io)
|
require('./libs/onvifDeviceManager.js')(s,config,lang,app,io)
|
||||||
//alternate logins
|
//alternate logins
|
||||||
require('./libs/auth/logins.js')(s,config,lang,app)
|
require('./libs/auth/logins.js')(s,config,lang,app)
|
||||||
|
//rally other Shinobi
|
||||||
|
require('./libs/rally.js')(s,config,lang,app,io)
|
||||||
//on-start actions, daemon(s) starter
|
//on-start actions, daemon(s) starter
|
||||||
await require('./libs/startup.js')(s,config,lang)
|
await require('./libs/startup.js')(s,config,lang)
|
||||||
//p2p, commander
|
//p2p, commander
|
||||||
|
|
|
@ -7904,6 +7904,11 @@ module.exports = function(s,config,lang){
|
||||||
pageOpen: 'onvifDeviceManager',
|
pageOpen: 'onvifDeviceManager',
|
||||||
eval: `!$user.details.sub || $user.details.monitor_create !== 0`,
|
eval: `!$user.details.sub || $user.details.monitor_create !== 0`,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
icon: 'ravelry',
|
||||||
|
label: `${lang['Rally']}`,
|
||||||
|
pageOpen: 'rally',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
icon: 'eyedropper',
|
icon: 'eyedropper',
|
||||||
label: `${lang['FFprobe']}`,
|
label: `${lang['FFprobe']}`,
|
||||||
|
@ -9220,6 +9225,6 @@ module.exports = function(s,config,lang){
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,12 @@
|
||||||
"accountEditError": "Account Edit Error",
|
"accountEditError": "Account Edit Error",
|
||||||
"Monitor Map": "Monitor Map",
|
"Monitor Map": "Monitor Map",
|
||||||
"Geolocation": "Geolocation",
|
"Geolocation": "Geolocation",
|
||||||
|
"Configure": "Configure",
|
||||||
|
"Scan": "Scan",
|
||||||
|
"Rally": "Rally",
|
||||||
|
"rallyApiKeyFieldText": "Permissions required : Get Monitors, Edit Monitors, View Streams, View Videos.",
|
||||||
|
"rallyDescription": "Here you can connect to a separate Shinobi server and add their cameras to this Shinobi server. The connection would be directly from the separate server instead of directly from the cameras.",
|
||||||
|
"rallyChannelDescription": "Channel to rally from. Default is 0 (Main).",
|
||||||
"Tested on": "Tested on",
|
"Tested on": "Tested on",
|
||||||
"Architecture": "Architecture",
|
"Architecture": "Architecture",
|
||||||
"Operating Systems": "Operating Systems",
|
"Operating Systems": "Operating Systems",
|
||||||
|
@ -354,6 +360,7 @@
|
||||||
"File Type": "File Type",
|
"File Type": "File Type",
|
||||||
"Filesize": "Filesize",
|
"Filesize": "Filesize",
|
||||||
"Created": "Created",
|
"Created": "Created",
|
||||||
|
"Status": "Status",
|
||||||
"Size": "Size",
|
"Size": "Size",
|
||||||
"Video Status": "Video Status",
|
"Video Status": "Video Status",
|
||||||
"Custom Auto Load": "Custom Auto Load",
|
"Custom Auto Load": "Custom Auto Load",
|
||||||
|
@ -770,6 +777,9 @@
|
||||||
"Install": "Install",
|
"Install": "Install",
|
||||||
"Disable": "Disable",
|
"Disable": "Disable",
|
||||||
"Add All": "Add All",
|
"Add All": "Add All",
|
||||||
|
"Add All (Rallied)": "Add All (Rallied)",
|
||||||
|
"Add All (Direct)": "Add All (Direct)",
|
||||||
|
"AddAllRalliedText": "Do you want to rally and add these Monitors from the specified Shinobi server?",
|
||||||
"Name": "Name",
|
"Name": "Name",
|
||||||
"Skip Ping": "Skip Ping",
|
"Skip Ping": "Skip Ping",
|
||||||
"Retry Connection": "Retry Connection <small>Number of times allowed to fail</small>",
|
"Retry Connection": "Retry Connection <small>Number of times allowed to fail</small>",
|
||||||
|
@ -1322,6 +1332,7 @@
|
||||||
"Male": "Male",
|
"Male": "Male",
|
||||||
"Female": "Female",
|
"Female": "Female",
|
||||||
"Channel": "Channel",
|
"Channel": "Channel",
|
||||||
|
"Channels Available": "Channels Available",
|
||||||
"Stream Key": "Stream Key",
|
"Stream Key": "Stream Key",
|
||||||
"Server URL": "Server URL",
|
"Server URL": "Server URL",
|
||||||
"Video Bit Rate": "Video Bit Rate",
|
"Video Bit Rate": "Video Bit Rate",
|
||||||
|
|
|
@ -0,0 +1,128 @@
|
||||||
|
const {
|
||||||
|
ShinobiAPI,
|
||||||
|
formatDateTime,
|
||||||
|
getCameraTemplate,
|
||||||
|
cleanStringForMonitorId,
|
||||||
|
} = require('node-shinobi');
|
||||||
|
|
||||||
|
module.exports = (s,config,lang,app,io) => {
|
||||||
|
function getServerInfo(){}
|
||||||
|
async function getMonitors({ host, groupKey, apiKey }){
|
||||||
|
const shinobi = new ShinobiAPI(host, apiKey, groupKey);
|
||||||
|
const monitors = await shinobi.getMonitor();
|
||||||
|
return monitors
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* API : Get Monitors
|
||||||
|
*/
|
||||||
|
app.post(config.webPaths.apiPrefix+':auth/rally/:ke/getMonitors', function (req,res){
|
||||||
|
s.auth(req.params, async function(user){
|
||||||
|
const groupKey = req.params.ke
|
||||||
|
const asis = s.getPostData(req,'asis') === '1'
|
||||||
|
const connectionInfo = s.getPostData(req,'connectionInfo',true) || {}
|
||||||
|
const {
|
||||||
|
isRestricted,
|
||||||
|
isRestrictedApiKey,
|
||||||
|
apiKeyPermissions,
|
||||||
|
userPermissions,
|
||||||
|
} = s.checkPermission(user)
|
||||||
|
if(
|
||||||
|
isRestrictedApiKey && apiKeyPermissions.get_monitors_disallowed
|
||||||
|
){
|
||||||
|
s.closeJsonResponse(res,[]);
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if(!connectionInfo.host || !connectionInfo.groupKey || !connectionInfo.apiKey){
|
||||||
|
s.closeJsonResponse(res,{ok: false, msg: lang['No Data']});
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const monitors = await getMonitors(connectionInfo) || [];
|
||||||
|
s.closeJsonResponse(res, monitors);
|
||||||
|
},res,req);
|
||||||
|
});
|
||||||
|
|
||||||
|
// page structure
|
||||||
|
config.webBlocksPreloaded.push(`home/rally`)
|
||||||
|
s.definitions['Rally'] = {
|
||||||
|
"name": "Rally",
|
||||||
|
"blocks": {
|
||||||
|
"Search Settings": {
|
||||||
|
"id": "rallyConfigure",
|
||||||
|
"name": lang.Scan,
|
||||||
|
"blockquote": lang.RallyDescription,
|
||||||
|
"color": "green",
|
||||||
|
"section-pre-class": "col-md-4",
|
||||||
|
isFormGroupGroup: true,
|
||||||
|
"info": [
|
||||||
|
{
|
||||||
|
"name": "host",
|
||||||
|
"field": lang["Host"],
|
||||||
|
"placeholder": "http://shinobi_host:8080",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "groupKey",
|
||||||
|
"field": lang["Group Key"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "apiKey",
|
||||||
|
"field": lang["API Key"],
|
||||||
|
"description": lang.rallyApiKeyFieldText,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "channel",
|
||||||
|
"field": lang["Channel"],
|
||||||
|
"description": lang.rallyChannelDescription,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldType": "btn-group",
|
||||||
|
"btns": [
|
||||||
|
{
|
||||||
|
forForm: true,
|
||||||
|
"fieldType": "btn",
|
||||||
|
"class": `btn-success fill mb-3`,
|
||||||
|
"icon": `search`,
|
||||||
|
"attribute": `type="submit"`,
|
||||||
|
"btnContent": `${lang['Scan']}`,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Management": {
|
||||||
|
"id": "rallyManagement",
|
||||||
|
"noHeader": true,
|
||||||
|
"color": "blue",
|
||||||
|
"section-pre-class": "col-md-8",
|
||||||
|
"info": [
|
||||||
|
{
|
||||||
|
"id":"rallyServerInfo",
|
||||||
|
"fieldType": "div",
|
||||||
|
"class": "mb-3",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldType": "btn-group",
|
||||||
|
"class": "mb-3",
|
||||||
|
"btns": [
|
||||||
|
{
|
||||||
|
"fieldType": "btn",
|
||||||
|
"class": `btn-success add-all`,
|
||||||
|
"btnContent": `<i class="fa fa-plus"></i> ${lang['Add All (Rallied)']}`,
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// "fieldType": "btn",
|
||||||
|
// "class": `btn-success add-all-direct`,
|
||||||
|
// "btnContent": `<i class="fa fa-plus"></i> ${lang['Add All (Direct)']}`,
|
||||||
|
// },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id":"rallyCameras",
|
||||||
|
"fieldType": "table",
|
||||||
|
"attribute": `data-classes="table table-striped"`,
|
||||||
|
"divContent": ""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,127 @@
|
||||||
|
$(document).ready(function(e){
|
||||||
|
//api window
|
||||||
|
var foundMonitors = {}
|
||||||
|
var theWindow = $('#tab-rally')
|
||||||
|
var theWindowForm = $('#rallyForm')
|
||||||
|
var tableEl = $('#rallyCameras')
|
||||||
|
function getMonitors(connectionInfo, asis){
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
$.post(`${getApiPrefix('rally')}/getMonitors`,{
|
||||||
|
connectionInfo,
|
||||||
|
},function(data){
|
||||||
|
resolve(data)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function saveMonitor(monitor){
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
$.post(`${getApiPrefix('configureMonitor')}/${monitor.mid}`,{
|
||||||
|
data: JSON.stringify(monitor)
|
||||||
|
},function(data){
|
||||||
|
resolve(data)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
async function saveMonitors(monitors){
|
||||||
|
for(monitor of monitors){
|
||||||
|
await saveMonitor(monitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function channelsAvailable(monitor){
|
||||||
|
const streams = monitor.streams;
|
||||||
|
return Object.keys(streams).join(', ')
|
||||||
|
}
|
||||||
|
function findRallyTypeFromStreamPath(streamPath) {
|
||||||
|
let chosenType = null;
|
||||||
|
if(streamPath.endsWith('m3u8')){
|
||||||
|
chosenType = 'hls'
|
||||||
|
}else if(streamPath.endsWith('mp4')){
|
||||||
|
chosenType = 'mp4'
|
||||||
|
}
|
||||||
|
return chosenType;
|
||||||
|
}
|
||||||
|
function drawTable(monitors){
|
||||||
|
var html = ''
|
||||||
|
foundMonitors = {}
|
||||||
|
for(monitor of monitors){
|
||||||
|
var monitorKey = `${monitor.ke}${monitor.mid}`
|
||||||
|
foundMonitors[monitorKey] = monitor;
|
||||||
|
html += `
|
||||||
|
<tr monitor="${monitorKey}">
|
||||||
|
<td title="${lang['Monitor Name']}">${monitor.name}</td>
|
||||||
|
<td title="${lang['Monitor ID']}">${monitor.mid}</td>
|
||||||
|
<td title="${lang.Host}">${monitor.host}</td>
|
||||||
|
<td title="${lang.Status}">${monitorStatusCodes[monitor.code]}</td>
|
||||||
|
<td title="${lang['Channels Available']}">${channelsAvailable(monitor)}</td>
|
||||||
|
<td><a class="add-monitor btn btn-sm btn-success" href="#">${lang.Add}</a></td>
|
||||||
|
</tr>`
|
||||||
|
}
|
||||||
|
tableEl.html(html);
|
||||||
|
}
|
||||||
|
function convertToRalliedMonitor(monitor, connectionInfo){
|
||||||
|
const { host, groupKey, apiKey, channel } = connectionInfo;
|
||||||
|
let [ hostname, port ] = host.split('://')[1].split(':');
|
||||||
|
const protocol = host.startsWith('https') ? 'https' : 'http';
|
||||||
|
hostname = hostname.endsWith('/') ? hostname.substring(0, str.length - 1) : hostname;
|
||||||
|
const streamPath = monitor.streams[parseInt(channel) || 0];
|
||||||
|
const rallyType = findRallyTypeFromStreamPath(streamPath);
|
||||||
|
if(hostname && rallyType){
|
||||||
|
const autoHostUrl = `${protocol}://${hostname}:${port}${streamPath}`;
|
||||||
|
monitor.protocol = protocol;
|
||||||
|
monitor.host = hostname;
|
||||||
|
monitor.port = port;
|
||||||
|
monitor.path = streamPath;
|
||||||
|
monitor.type = rallyType;
|
||||||
|
monitor.details.auto_host = autoHostUrl;
|
||||||
|
return monitor
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
function convertToRalliedMonitors(monitors, connectionInfo){
|
||||||
|
const ralliedMonitors = []
|
||||||
|
monitors.forEach(monitor => {
|
||||||
|
var converted = convertToRalliedMonitor(monitor, connectionInfo)
|
||||||
|
if(converted)ralliedMonitors.push(converted)
|
||||||
|
});
|
||||||
|
return ralliedMonitors;
|
||||||
|
}
|
||||||
|
function getConnectionInfo(){
|
||||||
|
const connectionInfo = theWindowForm.serializeObject();
|
||||||
|
return connectionInfo
|
||||||
|
}
|
||||||
|
theWindowForm.submit(async function(e){
|
||||||
|
e.preventDefault();
|
||||||
|
const connectionInfo = getConnectionInfo()
|
||||||
|
const monitors = await getMonitors(connectionInfo)
|
||||||
|
drawTable(monitors)
|
||||||
|
return false;
|
||||||
|
})
|
||||||
|
theWindow.on('click','.add-monitor',function(e){
|
||||||
|
e.preventDefault();
|
||||||
|
const connectionInfo = getConnectionInfo()
|
||||||
|
const el = $(this).parents('[monitor]')
|
||||||
|
const monitorKey = el.attr('monitor')
|
||||||
|
const monitor = foundMonitors[monitorKey]
|
||||||
|
const convertedMonitor = convertToRalliedMonitor(monitor, connectionInfo)
|
||||||
|
saveMonitor(convertedMonitor)
|
||||||
|
})
|
||||||
|
theWindow.on('click','.add-all',function(e){
|
||||||
|
const connectionInfo = getConnectionInfo()
|
||||||
|
const monitors = convertToRalliedMonitors(foundMonitors, connectionInfo)
|
||||||
|
$.confirm.create({
|
||||||
|
title: lang['Add All (Rallied)'],
|
||||||
|
body: lang.AddAllRalliedText,
|
||||||
|
clickOptions: {
|
||||||
|
class: 'btn-success',
|
||||||
|
title: `<i class="fa fa-check"></i> ${lang.Save}`,
|
||||||
|
},
|
||||||
|
clickCallback: function(){
|
||||||
|
saveMonitors(monitors)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
// addOnTabOpen('rally', function () {
|
||||||
|
// })
|
||||||
|
// addOnTabReopen('rally', function () {
|
||||||
|
// })
|
||||||
|
})
|
|
@ -0,0 +1,11 @@
|
||||||
|
<main class="container page-tab dark pt-3" id="tab-rally">
|
||||||
|
<form id="rallyForm" class="row">
|
||||||
|
<% Object.keys(define['Rally'].blocks).forEach(function(blockKey) { -%>
|
||||||
|
<%- include('drawBlock', {
|
||||||
|
theBlock: define['Rally'].blocks[blockKey],
|
||||||
|
details: $user.details
|
||||||
|
}) %>
|
||||||
|
<% }) -%>
|
||||||
|
</form>
|
||||||
|
<script src="<%-window.libURL%>assets/js/bs5.rally.js"></script>
|
||||||
|
</main>
|
Loading…
Reference in New Issue