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)
|
||||
//alternate logins
|
||||
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
|
||||
await require('./libs/startup.js')(s,config,lang)
|
||||
//p2p, commander
|
||||
|
|
|
@ -7904,6 +7904,11 @@ module.exports = function(s,config,lang){
|
|||
pageOpen: 'onvifDeviceManager',
|
||||
eval: `!$user.details.sub || $user.details.monitor_create !== 0`,
|
||||
},
|
||||
{
|
||||
icon: 'ravelry',
|
||||
label: `${lang['Rally']}`,
|
||||
pageOpen: 'rally',
|
||||
},
|
||||
{
|
||||
icon: 'eyedropper',
|
||||
label: `${lang['FFprobe']}`,
|
||||
|
@ -9220,6 +9225,6 @@ module.exports = function(s,config,lang){
|
|||
]
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -18,6 +18,12 @@
|
|||
"accountEditError": "Account Edit Error",
|
||||
"Monitor Map": "Monitor Map",
|
||||
"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",
|
||||
"Architecture": "Architecture",
|
||||
"Operating Systems": "Operating Systems",
|
||||
|
@ -354,6 +360,7 @@
|
|||
"File Type": "File Type",
|
||||
"Filesize": "Filesize",
|
||||
"Created": "Created",
|
||||
"Status": "Status",
|
||||
"Size": "Size",
|
||||
"Video Status": "Video Status",
|
||||
"Custom Auto Load": "Custom Auto Load",
|
||||
|
@ -770,6 +777,9 @@
|
|||
"Install": "Install",
|
||||
"Disable": "Disable",
|
||||
"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",
|
||||
"Skip Ping": "Skip Ping",
|
||||
"Retry Connection": "Retry Connection <small>Number of times allowed to fail</small>",
|
||||
|
@ -1322,6 +1332,7 @@
|
|||
"Male": "Male",
|
||||
"Female": "Female",
|
||||
"Channel": "Channel",
|
||||
"Channels Available": "Channels Available",
|
||||
"Stream Key": "Stream Key",
|
||||
"Server URL": "Server URL",
|
||||
"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