diff --git a/camera.js b/camera.js index a5df52fd..e02633c5 100644 --- a/camera.js +++ b/camera.js @@ -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 diff --git a/definitions/base.js b/definitions/base.js index ac661d6e..0fbd1067 100644 --- a/definitions/base.js +++ b/definitions/base.js @@ -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){ ] }, } - }, + } }) } diff --git a/languages/en_CA.json b/languages/en_CA.json index 7eb947c6..b48af012 100644 --- a/languages/en_CA.json +++ b/languages/en_CA.json @@ -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 Number of times allowed to fail", @@ -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", diff --git a/libs/rally.js b/libs/rally.js new file mode 100644 index 00000000..14b480de --- /dev/null +++ b/libs/rally.js @@ -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": ` ${lang['Add All (Rallied)']}`, + }, + // { + // "fieldType": "btn", + // "class": `btn-success add-all-direct`, + // "btnContent": ` ${lang['Add All (Direct)']}`, + // }, + ], + }, + { + "id":"rallyCameras", + "fieldType": "table", + "attribute": `data-classes="table table-striped"`, + "divContent": "" + } + ] + }, + } + } +} diff --git a/web/assets/js/bs5.rally.js b/web/assets/js/bs5.rally.js new file mode 100644 index 00000000..8e8e0c13 --- /dev/null +++ b/web/assets/js/bs5.rally.js @@ -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 += ` +