Visigothic Kings
parent
887acf80d3
commit
8dc03de9ea
|
@ -0,0 +1,44 @@
|
||||||
|
#!/bin/sh
|
||||||
|
echo "------------------------------------------"
|
||||||
|
echo "-- Installing CUDA Toolkit and CUDA DNN --"
|
||||||
|
echo "------------------------------------------"
|
||||||
|
# Install CUDA Drivers and Toolkit
|
||||||
|
echo "Install CUDA Drivers and Toolkit?"
|
||||||
|
echo "(y)es or (N)o"
|
||||||
|
read installTheStuffHomie
|
||||||
|
if [ "$installTheStuffHomie" = "y" ] || [ "$installTheStuffHomie" = "Y" ]; then
|
||||||
|
if [ -x "$(command -v apt)" ]; then
|
||||||
|
# CUDA Toolkit
|
||||||
|
wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86_64/cuda-ubuntu2204.pin
|
||||||
|
sudo mv cuda-ubuntu2204.pin /etc/apt/preferences.d/cuda-repository-pin-600
|
||||||
|
echo "Downloading CUDA Toolkit..."
|
||||||
|
wget https://developer.download.nvidia.com/compute/cuda/12.0.0/local_installers/cuda-repo-ubuntu2204-12-0-local_12.0.0-525.60.13-1_amd64.deb
|
||||||
|
sudo dpkg -i cuda-repo-ubuntu2204-12-0-local_12.0.0-525.60.13-1_amd64.deb
|
||||||
|
sudo cp /var/cuda-repo-ubuntu2204-12-0-local/cuda-*-keyring.gpg /usr/share/keyrings/
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get -y install cuda-toolkit-12-0
|
||||||
|
|
||||||
|
# Driver
|
||||||
|
echo "Installing nvidia-driver-515-server"
|
||||||
|
sudo apt install nvidia-driver-515-server -y
|
||||||
|
|
||||||
|
# Install CUDA DNN
|
||||||
|
wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/libcudnn8_8.8.1.3-1+cuda12.0_amd64.deb -O cuda-dnn.deb --no-verbose
|
||||||
|
sudo dpkg -i cuda-dnn.deb
|
||||||
|
wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/libcudnn8-dev_8.8.1.3-1+cuda12.0_amd64.deb -O cuda-dnn-dev.deb --no-verbose
|
||||||
|
sudo dpkg -i cuda-dnn-dev.deb
|
||||||
|
echo "-- Cleaning Up --"
|
||||||
|
# Cleanup
|
||||||
|
sudo rm cuda.deb
|
||||||
|
sudo rm cuda-dnn.deb
|
||||||
|
sudo rm cuda-dnn-dev.deb
|
||||||
|
fi
|
||||||
|
echo "------------------------------"
|
||||||
|
echo "Reboot is required. Do it now?"
|
||||||
|
echo "------------------------------"
|
||||||
|
echo "(y)es or (N)o. Default is No."
|
||||||
|
read rebootTheMachineHomie
|
||||||
|
if [ "$rebootTheMachineHomie" = "y" ] || [ "$rebootTheMachineHomie" = "Y" ]; then
|
||||||
|
sudo reboot
|
||||||
|
fi
|
||||||
|
fi
|
|
@ -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,89 @@ module.exports = function(s,config,lang){
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Rally": {
|
||||||
|
"section": "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": ""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -164,13 +164,17 @@ module.exports = function(s,config,lang,app,io){
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
var createDropInEventsDirectory = function(){
|
var createDropInEventsDirectory = function(){
|
||||||
if(!config.dropInEventsDir){
|
try{
|
||||||
config.dropInEventsDir = s.dir.streams + 'dropInEvents/'
|
if(!config.dropInEventsDir){
|
||||||
}
|
config.dropInEventsDir = s.dir.streams + 'dropInEvents/'
|
||||||
s.dir.dropInEvents = s.checkCorrectPathEnding(config.dropInEventsDir)
|
}
|
||||||
//dropInEvents dir
|
s.dir.dropInEvents = s.checkCorrectPathEnding(config.dropInEventsDir)
|
||||||
if(!fs.existsSync(s.dir.dropInEvents)){
|
//dropInEvents dir
|
||||||
fs.mkdirSync(s.dir.dropInEvents)
|
if(!fs.existsSync(s.dir.dropInEvents)){
|
||||||
|
fs.mkdirSync(s.dir.dropInEvents)
|
||||||
|
}
|
||||||
|
}catch(err){
|
||||||
|
console.error(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var getDropInEventDir = function(monitorConfig){
|
var getDropInEventDir = function(monitorConfig){
|
||||||
|
@ -209,58 +213,65 @@ module.exports = function(s,config,lang,app,io){
|
||||||
createDropInEventDirectory(monitorConfig,function(err,monitorEventDropDir){})
|
createDropInEventDirectory(monitorConfig,function(err,monitorEventDropDir){})
|
||||||
}
|
}
|
||||||
// FTP Server
|
// FTP Server
|
||||||
|
createDropInEventsDirectory()
|
||||||
if(config.ftpServer === true){
|
if(config.ftpServer === true){
|
||||||
createDropInEventsDirectory()
|
try{
|
||||||
if(!config.ftpServerPort)config.ftpServerPort = 21
|
const FtpSrv = require('ftp-srv')
|
||||||
if(!config.ftpServerUrl)config.ftpServerUrl = `ftp://0.0.0.0:${config.ftpServerPort}`
|
console.error('WARNING : FTP Server is enabled.')
|
||||||
if(!config.ftpServerPasvUrl)config.ftpServerPasvUrl = config.ftpServerUrl.replace(/.*:\/\//, '').replace(/:.*/, '');
|
if(!config.ftpServerPort)config.ftpServerPort = 21
|
||||||
if(!config.ftpServerPasvMinPort)config.ftpServerPasvMinPort = 10050;
|
if(!config.ftpServerUrl)config.ftpServerUrl = `ftp://0.0.0.0:${config.ftpServerPort}`
|
||||||
if(!config.ftpServerPasvMaxPort)config.ftpServerPasvMaxPort = 10100;
|
if(!config.ftpServerPasvUrl)config.ftpServerPasvUrl = config.ftpServerUrl.replace(/.*:\/\//, '').replace(/:.*/, '');
|
||||||
config.ftpServerUrl = config.ftpServerUrl.replace('{{PORT}}',config.ftpServerPort)
|
if(!config.ftpServerPasvMinPort)config.ftpServerPasvMinPort = 10050;
|
||||||
const FtpSrv = require('ftp-srv')
|
if(!config.ftpServerPasvMaxPort)config.ftpServerPasvMaxPort = 10100;
|
||||||
|
config.ftpServerUrl = config.ftpServerUrl.replace('{{PORT}}',config.ftpServerPort)
|
||||||
|
|
||||||
const ftpServer = new FtpSrv({
|
const ftpServer = new FtpSrv({
|
||||||
url: config.ftpServerUrl,
|
url: config.ftpServerUrl,
|
||||||
// pasv_url must be set to enable PASV; ftp-srv uses its known IP if given 127.0.0.1,
|
// pasv_url must be set to enable PASV; ftp-srv uses its known IP if given 127.0.0.1,
|
||||||
// and smart clients will ignore the IP anyway. Some Dahua IP cams require PASV mode.
|
// and smart clients will ignore the IP anyway. Some Dahua IP cams require PASV mode.
|
||||||
// ftp-srv just wants an IP only (no protocol or port)
|
// ftp-srv just wants an IP only (no protocol or port)
|
||||||
pasv_url: config.ftpServerPasvUrl,
|
pasv_url: config.ftpServerPasvUrl,
|
||||||
pasv_min: config.ftpServerPasvMinPort,
|
pasv_min: config.ftpServerPasvMinPort,
|
||||||
pasv_max: config.ftpServerPasvMaxPort,
|
pasv_max: config.ftpServerPasvMaxPort,
|
||||||
greeting: "Shinobi FTP dropInEvent Server says hello!",
|
greeting: "Shinobi FTP dropInEvent Server says hello!",
|
||||||
log: require('bunyan').createLogger({
|
log: require('bunyan').createLogger({
|
||||||
name: 'ftp-srv',
|
name: 'ftp-srv',
|
||||||
level: 100
|
level: 100
|
||||||
}),
|
}),
|
||||||
})
|
|
||||||
|
|
||||||
ftpServer.on('login', ({connection, username, password}, resolve, reject) => {
|
|
||||||
s.basicOrApiAuthentication(username,password,function(err,user){
|
|
||||||
if(user){
|
|
||||||
connection.on('STOR', (error, fileName) => {
|
|
||||||
if(!fileName)return;
|
|
||||||
var pathPieces = fileName.replace(s.dir.dropInEvents,'').split('/')
|
|
||||||
var ke = pathPieces[0]
|
|
||||||
var mid = pathPieces[1]
|
|
||||||
var firstDroppedPart = pathPieces[2]
|
|
||||||
var monitorEventDropDir = s.dir.dropInEvents + ke + '/' + mid + '/'
|
|
||||||
var deleteKey = monitorEventDropDir + firstDroppedPart
|
|
||||||
onFileOrFolderFound(monitorEventDropDir + firstDroppedPart,deleteKey,Object.assign({},s.group[ke].rawMonitorConfigurations[mid]))
|
|
||||||
})
|
|
||||||
resolve({root: s.dir.dropInEvents + user.ke})
|
|
||||||
}else{
|
|
||||||
// reject(new Error('Failed Authorization'))
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
})
|
|
||||||
ftpServer.on('client-error', ({connection, context, error}) => {
|
ftpServer.on('login', ({connection, username, password}, resolve, reject) => {
|
||||||
console.log('client-error',error)
|
s.basicOrApiAuthentication(username,password,function(err,user){
|
||||||
})
|
if(user){
|
||||||
ftpServer.listen().then(() => {
|
connection.on('STOR', (error, fileName) => {
|
||||||
s.systemLog(`FTP Server running on port ${config.ftpServerPort}...`)
|
if(!fileName)return;
|
||||||
}).catch(function(err){
|
var pathPieces = fileName.replace(s.dir.dropInEvents,'').split('/')
|
||||||
s.systemLog(err)
|
var ke = pathPieces[0]
|
||||||
})
|
var mid = pathPieces[1]
|
||||||
|
var firstDroppedPart = pathPieces[2]
|
||||||
|
var monitorEventDropDir = s.dir.dropInEvents + ke + '/' + mid + '/'
|
||||||
|
var deleteKey = monitorEventDropDir + firstDroppedPart
|
||||||
|
onFileOrFolderFound(monitorEventDropDir + firstDroppedPart,deleteKey,Object.assign({},s.group[ke].rawMonitorConfigurations[mid]))
|
||||||
|
})
|
||||||
|
resolve({root: s.dir.dropInEvents + user.ke})
|
||||||
|
}else{
|
||||||
|
// reject(new Error('Failed Authorization'))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
ftpServer.on('client-error', ({connection, context, error}) => {
|
||||||
|
console.log('client-error',error)
|
||||||
|
})
|
||||||
|
ftpServer.listen().then(() => {
|
||||||
|
s.systemLog(`FTP Server running on port ${config.ftpServerPort}...`)
|
||||||
|
}).catch(function(err){
|
||||||
|
s.systemLog(err)
|
||||||
|
})
|
||||||
|
}catch(err){
|
||||||
|
console.error(err.message)
|
||||||
|
console.error('Could not start FTP Server, please run "npm install ftp-srv" inside the Shinobi folder.')
|
||||||
|
console.error('The ftp-srv Module is known to have possible vulnerabilities. Due to the nature of the vulnerability you should be unaffected unless the FTP Port is public facing. Use at your own risk.')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
//add extensions
|
//add extensions
|
||||||
s.onMonitorInit(onMonitorInit)
|
s.onMonitorInit(onMonitorInit)
|
||||||
|
|
|
@ -265,7 +265,7 @@ Run "npm install ffbinaries" to get a different static FFmpeg downloader.`
|
||||||
console.log('ffbinaries : Downloading FFmpeg. Please Wait...');
|
console.log('ffbinaries : Downloading FFmpeg. Please Wait...');
|
||||||
ffbinaries.downloadBinaries(['ffmpeg', 'ffprobe'], {
|
ffbinaries.downloadBinaries(['ffmpeg', 'ffprobe'], {
|
||||||
destination: ffbinaryDir,
|
destination: ffbinaryDir,
|
||||||
version : '4.2'
|
version : config.ffbinariesVersion || '4.2'
|
||||||
},function () {
|
},function () {
|
||||||
config.ffmpegDir = ffbinaryDir + 'ffmpeg'
|
config.ffmpegDir = ffbinaryDir + 'ffmpeg'
|
||||||
response.msg = 'ffbinaries : FFmpeg Downloaded.'
|
response.msg = 'ffbinaries : FFmpeg Downloaded.'
|
||||||
|
|
|
@ -116,7 +116,6 @@ module.exports = function(s,config,lang,getSnapshot){
|
||||||
detector_mail_timeout = parseFloat(monitorConfig.details.detector_mail_timeout) * 1000 * 60;
|
detector_mail_timeout = parseFloat(monitorConfig.details.detector_mail_timeout) * 1000 * 60;
|
||||||
}
|
}
|
||||||
s.group[d.ke].activeMonitors[d.id].detector_mail = setTimeout(function(){
|
s.group[d.ke].activeMonitors[d.id].detector_mail = setTimeout(function(){
|
||||||
clearTimeout(s.group[d.ke].activeMonitors[d.id].detector_mail);
|
|
||||||
s.group[d.ke].activeMonitors[d.id].detector_mail = null
|
s.group[d.ke].activeMonitors[d.id].detector_mail = null
|
||||||
},detector_mail_timeout);
|
},detector_mail_timeout);
|
||||||
const sendMail = function(files){
|
const sendMail = function(files){
|
||||||
|
|
|
@ -20,8 +20,10 @@ module.exports = function(s,config,lang,getSnapshot){
|
||||||
|
|
||||||
//telegram bot
|
//telegram bot
|
||||||
if(config.telegramBot === true){
|
if(config.telegramBot === true){
|
||||||
const TelegramBot = require('node-telegram-bot-api');
|
|
||||||
try{
|
try{
|
||||||
|
const TelegramBot = require('node-telegram-bot-api');
|
||||||
|
console.error('WARNING : Telegram bot is enabled.')
|
||||||
|
|
||||||
const sendMessage = async function(sendBody,attachments,groupKey){
|
const sendMessage = async function(sendBody,attachments,groupKey){
|
||||||
var bot = s.group[groupKey].telegramBot
|
var bot = s.group[groupKey].telegramBot
|
||||||
if(!bot){
|
if(!bot){
|
||||||
|
@ -363,8 +365,9 @@ module.exports = function(s,config,lang,getSnapshot){
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
}catch(err){
|
}catch(err){
|
||||||
console.error(err)
|
console.error(err.message)
|
||||||
console.log('Could not start Telegram bot, please run "npm install node-telegram-bot-api" inside the Shinobi folder.')
|
console.error('Could not start Telegram bot, please run "npm install node-telegram-bot-api" inside the Shinobi folder.')
|
||||||
|
console.error('The Telegram Module is known to have possible vulnerabilities. Use at your own risk.')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -370,9 +370,13 @@ module.exports = function(s,config,lang,app,io){
|
||||||
function sendCopyOfAllMonitorConfigs(){
|
function sendCopyOfAllMonitorConfigs(){
|
||||||
const groupKeys = Object.keys(s.group);
|
const groupKeys = Object.keys(s.group);
|
||||||
for(groupKey of groupKeys){
|
for(groupKey of groupKeys){
|
||||||
const monitorConfigs = Object.values(s.group[groupKey].rawMonitorConfigurations);
|
try{
|
||||||
for(monitorConfig of monitorConfigs){
|
const monitorConfigs = Object.values(s.group[groupKey].rawMonitorConfigurations);
|
||||||
onMonitorUpdate(monitorConfig)
|
for(monitorConfig of monitorConfigs){
|
||||||
|
onMonitorUpdate(monitorConfig)
|
||||||
|
}
|
||||||
|
}catch(err){
|
||||||
|
s.debugLog(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
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`);
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const { Readable } = require('stream');
|
const { Readable } = require('stream');
|
||||||
const B2 = require('backblaze-b2')
|
const B2 = require('shinobi-backblaze-b2')
|
||||||
module.exports = function(s,config,lang){
|
module.exports = function(s,config,lang){
|
||||||
//Backblaze B2
|
//Backblaze B2
|
||||||
var serviceProvider = 'b2'
|
var serviceProvider = 'b2'
|
||||||
|
|
|
@ -10,6 +10,7 @@ module.exports = function(s,config,lang){
|
||||||
await s.group[groupKey].whcs.send(requestOptions);
|
await s.group[groupKey].whcs.send(requestOptions);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('AMZ genericRequest',groupKey,requestOptions)
|
console.error('AMZ genericRequest',groupKey,requestOptions)
|
||||||
|
console.error('AMZ genericRequest ERR',err)
|
||||||
response.ok = false
|
response.ok = false
|
||||||
response.err = err
|
response.err = err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,356 +1,372 @@
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var webdav = require("webdav-fs");
|
module.exports = async function(s,config,lang){
|
||||||
module.exports = function(s,config,lang){
|
if(config.webDavUpload){
|
||||||
// WebDAV
|
|
||||||
var beforeAccountSaveForWebDav = function(d){
|
|
||||||
//d = save event
|
|
||||||
d.formDetails.webdav_use_global=d.d.webdav_use_global
|
|
||||||
d.formDetails.use_webdav=d.d.use_webdav
|
|
||||||
}
|
|
||||||
var cloudDiskUseStartupForWebDav = function(group,userDetails){
|
|
||||||
group.cloudDiskUse['webdav'].name = 'WebDAV'
|
|
||||||
group.cloudDiskUse['webdav'].sizeLimitCheck = (userDetails.use_webdav_size_limit === '1')
|
|
||||||
if(!userDetails.webdav_size_limit || userDetails.webdav_size_limit === ''){
|
|
||||||
group.cloudDiskUse['webdav'].sizeLimit = 10000
|
|
||||||
}else{
|
|
||||||
group.cloudDiskUse['webdav'].sizeLimit = parseFloat(userDetails.webdav_size_limit)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var loadWebDavForUser = function(e){
|
|
||||||
// e = user
|
|
||||||
var userDetails = JSON.parse(e.details);
|
|
||||||
if(userDetails.webdav_use_global === '1' && config.cloudUploaders && config.cloudUploaders.WebDAV){
|
|
||||||
// {
|
|
||||||
// webdav_user: "",
|
|
||||||
// webdav_pass: "",
|
|
||||||
// webdav_url: "",
|
|
||||||
// webdav_dir: "",
|
|
||||||
// }
|
|
||||||
userDetails = Object.assign(userDetails,config.cloudUploaders.WebDAV)
|
|
||||||
}
|
|
||||||
//owncloud/webdav
|
|
||||||
if(!s.group[e.ke].webdav &&
|
|
||||||
userDetails.webdav_user&&
|
|
||||||
userDetails.webdav_user!==''&&
|
|
||||||
userDetails.webdav_pass&&
|
|
||||||
userDetails.webdav_pass!==''&&
|
|
||||||
userDetails.webdav_url&&
|
|
||||||
userDetails.webdav_url!==''
|
|
||||||
){
|
|
||||||
if(!userDetails.webdav_dir||userDetails.webdav_dir===''){
|
|
||||||
userDetails.webdav_dir='/'
|
|
||||||
}
|
|
||||||
userDetails.webdav_dir = s.checkCorrectPathEnding(userDetails.webdav_dir)
|
|
||||||
s.group[e.ke].webdav = webdav.createAdapter(userDetails.webdav_url, {
|
|
||||||
username: userDetails.webdav_user,
|
|
||||||
password: userDetails.webdav_pass
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var unloadWebDavForUser = function(user){
|
|
||||||
s.group[user.ke].webdav = null
|
|
||||||
}
|
|
||||||
var deleteVideoFromWebDav = function(groupKey,video,callback){
|
|
||||||
// e = user
|
|
||||||
try{
|
try{
|
||||||
var videoDetails = JSON.parse(video.details)
|
const webdav = await import("webdav-fs");
|
||||||
}catch(err){
|
console.error('WARNING : WebDAV is enabled.')
|
||||||
var videoDetails = video.details
|
// WebDAV
|
||||||
}
|
var beforeAccountSaveForWebDav = function(d){
|
||||||
if(video.type !== 'webdav'){
|
//d = save event
|
||||||
callback()
|
d.formDetails.webdav_use_global=d.d.webdav_use_global
|
||||||
return
|
d.formDetails.use_webdav=d.d.use_webdav
|
||||||
}
|
}
|
||||||
if(!videoDetails.location){
|
var cloudDiskUseStartupForWebDav = function(group,userDetails){
|
||||||
var prefix = s.addUserPassToUrl(s.checkCorrectPathEnding(s.group[groupKey].init.webdav_url),s.group[groupKey].init.webdav_user,s.group[groupKey].init.webdav_pass)
|
group.cloudDiskUse['webdav'].name = 'WebDAV'
|
||||||
videoDetails.location = video.href.replace(prefix,'')
|
group.cloudDiskUse['webdav'].sizeLimitCheck = (userDetails.use_webdav_size_limit === '1')
|
||||||
}
|
if(!userDetails.webdav_size_limit || userDetails.webdav_size_limit === ''){
|
||||||
s.group[groupKey].webdav.unlink(videoDetails.location, function(err) {
|
group.cloudDiskUse['webdav'].sizeLimit = 10000
|
||||||
if (err) console.log(videoDetails.location,err)
|
}else{
|
||||||
callback()
|
group.cloudDiskUse['webdav'].sizeLimit = parseFloat(userDetails.webdav_size_limit)
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
var uploadVideoToWebDav = function(e,k){
|
var loadWebDavForUser = function(e){
|
||||||
//e = video object
|
// e = user
|
||||||
//k = temporary values
|
var userDetails = JSON.parse(e.details);
|
||||||
if(!k)k={};
|
if(userDetails.webdav_use_global === '1' && config.cloudUploaders && config.cloudUploaders.WebDAV){
|
||||||
//cloud saver - webdav
|
// {
|
||||||
var wfs = s.group[e.ke].webdav
|
// webdav_user: "",
|
||||||
if(wfs && s.group[e.ke].init.use_webdav !== '0' && s.group[e.ke].init.webdav_save === "1"){
|
// webdav_pass: "",
|
||||||
var webdavUploadDir = s.group[e.ke].init.webdav_dir+e.ke+'/'+e.mid+'/'
|
// webdav_url: "",
|
||||||
var startWebDavUpload = function(){
|
// webdav_dir: "",
|
||||||
s.group[e.ke].activeMonitors[e.id].webdavDirExist = true
|
// }
|
||||||
var wfsWriteStream =
|
userDetails = Object.assign(userDetails,config.cloudUploaders.WebDAV)
|
||||||
fs.createReadStream(k.dir + k.filename).pipe(wfs.createWriteStream(webdavUploadDir + k.filename))
|
}
|
||||||
if(s.group[e.ke].init.webdav_log === '1'){
|
//owncloud/webdav
|
||||||
var webdavRemoteUrl = s.addUserPassToUrl(s.checkCorrectPathEnding(s.group[e.ke].init.webdav_url),s.group[e.ke].init.webdav_user,s.group[e.ke].init.webdav_pass) + s.group[e.ke].init.webdav_dir + e.ke + '/'+e.mid+'/'+k.filename
|
if(!s.group[e.ke].webdav &&
|
||||||
s.knexQuery({
|
userDetails.webdav_user&&
|
||||||
action: "insert",
|
userDetails.webdav_user!==''&&
|
||||||
table: "Cloud Videos",
|
userDetails.webdav_pass&&
|
||||||
insert: {
|
userDetails.webdav_pass!==''&&
|
||||||
mid: e.mid,
|
userDetails.webdav_url&&
|
||||||
ke: e.ke,
|
userDetails.webdav_url!==''
|
||||||
time: k.startTime,
|
){
|
||||||
status: 1,
|
if(!userDetails.webdav_dir||userDetails.webdav_dir===''){
|
||||||
type : 'webdav',
|
userDetails.webdav_dir='/'
|
||||||
details: s.s({
|
}
|
||||||
location : webdavUploadDir + k.filename
|
userDetails.webdav_dir = s.checkCorrectPathEnding(userDetails.webdav_dir)
|
||||||
}),
|
s.group[e.ke].webdav = webdav.createAdapter(userDetails.webdav_url, {
|
||||||
size: k.filesize,
|
username: userDetails.webdav_user,
|
||||||
end: k.endTime,
|
password: userDetails.webdav_pass
|
||||||
href: ''
|
})
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
s.setCloudDiskUsedForGroup(e.ke,{
|
var unloadWebDavForUser = function(user){
|
||||||
amount: k.filesizeMB,
|
s.group[user.ke].webdav = null
|
||||||
storageType: 'webdav'
|
}
|
||||||
})
|
var deleteVideoFromWebDav = function(groupKey,video,callback){
|
||||||
s.purgeCloudDiskForGroup(e,'webdav')
|
// e = user
|
||||||
}
|
try{
|
||||||
}
|
var videoDetails = JSON.parse(video.details)
|
||||||
if(s.group[e.ke].activeMonitors[e.id].webdavDirExist !== true){
|
}catch(err){
|
||||||
//check if webdav dir exist
|
var videoDetails = video.details
|
||||||
var parentPoint = 0
|
}
|
||||||
var webDavParentz = webdavUploadDir.split('/')
|
if(video.type !== 'webdav'){
|
||||||
var webDavParents = []
|
callback()
|
||||||
webDavParentz.forEach(function(v){
|
return
|
||||||
if(v && v !== '')webDavParents.push(v)
|
}
|
||||||
})
|
if(!videoDetails.location){
|
||||||
var stitchPieces = './'
|
var prefix = s.addUserPassToUrl(s.checkCorrectPathEnding(s.group[groupKey].init.webdav_url),s.group[groupKey].init.webdav_user,s.group[groupKey].init.webdav_pass)
|
||||||
var lastParentCheck = function(){
|
videoDetails.location = video.href.replace(prefix,'')
|
||||||
++parentPoint
|
}
|
||||||
if(parentPoint === webDavParents.length){
|
s.group[groupKey].webdav.unlink(videoDetails.location, function(err) {
|
||||||
startWebDavUpload()
|
if (err) console.log(videoDetails.location,err)
|
||||||
}
|
callback()
|
||||||
checkPathPiece(webDavParents[parentPoint])
|
})
|
||||||
}
|
}
|
||||||
var checkPathPiece = function(pathPiece){
|
var uploadVideoToWebDav = function(e,k){
|
||||||
if(pathPiece && pathPiece !== ''){
|
//e = video object
|
||||||
stitchPieces += pathPiece + '/'
|
//k = temporary values
|
||||||
wfs.stat(stitchPieces, function(error, stats) {
|
if(!k)k={};
|
||||||
if(error){
|
//cloud saver - webdav
|
||||||
reply = {
|
var wfs = s.group[e.ke].webdav
|
||||||
status : error.status,
|
if(wfs && s.group[e.ke].init.use_webdav !== '0' && s.group[e.ke].init.webdav_save === "1"){
|
||||||
msg : lang.WebdavErrorTextTryCreatingDir,
|
var webdavUploadDir = s.group[e.ke].init.webdav_dir+e.ke+'/'+e.mid+'/'
|
||||||
dir : stitchPieces,
|
var startWebDavUpload = function(){
|
||||||
|
s.group[e.ke].activeMonitors[e.id].webdavDirExist = true
|
||||||
|
var wfsWriteStream =
|
||||||
|
fs.createReadStream(k.dir + k.filename).pipe(wfs.createWriteStream(webdavUploadDir + k.filename))
|
||||||
|
if(s.group[e.ke].init.webdav_log === '1'){
|
||||||
|
var webdavRemoteUrl = s.addUserPassToUrl(s.checkCorrectPathEnding(s.group[e.ke].init.webdav_url),s.group[e.ke].init.webdav_user,s.group[e.ke].init.webdav_pass) + s.group[e.ke].init.webdav_dir + e.ke + '/'+e.mid+'/'+k.filename
|
||||||
|
s.knexQuery({
|
||||||
|
action: "insert",
|
||||||
|
table: "Cloud Videos",
|
||||||
|
insert: {
|
||||||
|
mid: e.mid,
|
||||||
|
ke: e.ke,
|
||||||
|
time: k.startTime,
|
||||||
|
status: 1,
|
||||||
|
type : 'webdav',
|
||||||
|
details: s.s({
|
||||||
|
location : webdavUploadDir + k.filename
|
||||||
|
}),
|
||||||
|
size: k.filesize,
|
||||||
|
end: k.endTime,
|
||||||
|
href: ''
|
||||||
}
|
}
|
||||||
s.userLog(e,{type:lang['Webdav Error'],msg:reply})
|
})
|
||||||
wfs.mkdir(stitchPieces, function(error) {
|
s.setCloudDiskUsedForGroup(e.ke,{
|
||||||
|
amount: k.filesizeMB,
|
||||||
|
storageType: 'webdav'
|
||||||
|
})
|
||||||
|
s.purgeCloudDiskForGroup(e,'webdav')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(s.group[e.ke].activeMonitors[e.id].webdavDirExist !== true){
|
||||||
|
//check if webdav dir exist
|
||||||
|
var parentPoint = 0
|
||||||
|
var webDavParentz = webdavUploadDir.split('/')
|
||||||
|
var webDavParents = []
|
||||||
|
webDavParentz.forEach(function(v){
|
||||||
|
if(v && v !== '')webDavParents.push(v)
|
||||||
|
})
|
||||||
|
var stitchPieces = './'
|
||||||
|
var lastParentCheck = function(){
|
||||||
|
++parentPoint
|
||||||
|
if(parentPoint === webDavParents.length){
|
||||||
|
startWebDavUpload()
|
||||||
|
}
|
||||||
|
checkPathPiece(webDavParents[parentPoint])
|
||||||
|
}
|
||||||
|
var checkPathPiece = function(pathPiece){
|
||||||
|
if(pathPiece && pathPiece !== ''){
|
||||||
|
stitchPieces += pathPiece + '/'
|
||||||
|
wfs.stat(stitchPieces, function(error, stats) {
|
||||||
if(error){
|
if(error){
|
||||||
reply = {
|
reply = {
|
||||||
status : error.status,
|
status : error.status,
|
||||||
msg : lang.WebdavErrorTextCreatingDir,
|
msg : lang.WebdavErrorTextTryCreatingDir,
|
||||||
dir : stitchPieces,
|
dir : stitchPieces,
|
||||||
}
|
}
|
||||||
s.userLog(e,{type:lang['Webdav Error'],msg:reply})
|
s.userLog(e,{type:lang['Webdav Error'],msg:reply})
|
||||||
|
wfs.mkdir(stitchPieces, function(error) {
|
||||||
|
if(error){
|
||||||
|
reply = {
|
||||||
|
status : error.status,
|
||||||
|
msg : lang.WebdavErrorTextCreatingDir,
|
||||||
|
dir : stitchPieces,
|
||||||
|
}
|
||||||
|
s.userLog(e,{type:lang['Webdav Error'],msg:reply})
|
||||||
|
}else{
|
||||||
|
lastParentCheck()
|
||||||
|
}
|
||||||
|
})
|
||||||
}else{
|
}else{
|
||||||
lastParentCheck()
|
lastParentCheck()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}else{
|
}else{
|
||||||
lastParentCheck()
|
++parentPoint
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
checkPathPiece(webDavParents[0])
|
||||||
}else{
|
}else{
|
||||||
++parentPoint
|
startWebDavUpload()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
checkPathPiece(webDavParents[0])
|
|
||||||
}else{
|
|
||||||
startWebDavUpload()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function onInsertTimelapseFrame(monitorObject,queryInfo,filePath){
|
|
||||||
var e = monitorObject
|
|
||||||
if(s.group[e.ke].webdav && s.group[e.ke].init.use_webdav !== '0' && s.group[e.ke].init.webdav_save === '1'){
|
|
||||||
const wfs = s.group[e.ke].webdav
|
|
||||||
const saveLocation = s.group[e.ke].init.webdav_dir+e.ke+'/'+e.mid+'_timelapse/' + queryInfo.filename
|
|
||||||
fs.createReadStream(filePath).pipe(wfs.createWriteStream(saveLocation))
|
|
||||||
if(s.group[e.ke].init.webdav_log === '1'){
|
|
||||||
s.knexQuery({
|
|
||||||
action: "insert",
|
|
||||||
table: "Cloud Timelapse Frames",
|
|
||||||
insert: {
|
|
||||||
mid: queryInfo.mid,
|
|
||||||
ke: queryInfo.ke,
|
|
||||||
time: queryInfo.time,
|
|
||||||
filename: queryInfo.filename,
|
|
||||||
type : 'webdav',
|
|
||||||
details: s.s({
|
|
||||||
location : saveLocation
|
|
||||||
}),
|
|
||||||
size: queryInfo.size,
|
|
||||||
href: ''
|
|
||||||
}
|
|
||||||
})
|
|
||||||
s.setCloudDiskUsedForGroup(e.ke,{
|
|
||||||
amount : s.kilobyteToMegabyte(queryInfo.size),
|
|
||||||
storageType : 'webdav'
|
|
||||||
},'timelapseFrames')
|
|
||||||
s.purgeCloudDiskForGroup(e,'webdav','timelapseFrames')
|
|
||||||
}
|
}
|
||||||
}
|
function onInsertTimelapseFrame(monitorObject,queryInfo,filePath){
|
||||||
}
|
var e = monitorObject
|
||||||
function onDeleteTimelapseFrameFromCloud(e,frame,callback){
|
if(s.group[e.ke].webdav && s.group[e.ke].init.use_webdav !== '0' && s.group[e.ke].init.webdav_save === '1'){
|
||||||
// e = user
|
const wfs = s.group[e.ke].webdav
|
||||||
try{
|
const saveLocation = s.group[e.ke].init.webdav_dir+e.ke+'/'+e.mid+'_timelapse/' + queryInfo.filename
|
||||||
var frameDetails = JSON.parse(frame.details)
|
fs.createReadStream(filePath).pipe(wfs.createWriteStream(saveLocation))
|
||||||
}catch(err){
|
if(s.group[e.ke].init.webdav_log === '1'){
|
||||||
var frameDetails = frame.details
|
s.knexQuery({
|
||||||
}
|
action: "insert",
|
||||||
if(frame.type !== 'webdav'){
|
table: "Cloud Timelapse Frames",
|
||||||
callback()
|
insert: {
|
||||||
return
|
mid: queryInfo.mid,
|
||||||
}
|
ke: queryInfo.ke,
|
||||||
if(!frameDetails.location){
|
time: queryInfo.time,
|
||||||
frameDetails.location = frame.href.split(locationUrl)[1]
|
filename: queryInfo.filename,
|
||||||
}
|
type : 'webdav',
|
||||||
s.group[e.ke].webdav.unlink(frameDetails.location, function(err) {
|
details: s.s({
|
||||||
if (err) console.log(frameDetails.location,err)
|
location : saveLocation
|
||||||
callback()
|
}),
|
||||||
})
|
size: queryInfo.size,
|
||||||
}
|
href: ''
|
||||||
async function onGetVideoData(video){
|
}
|
||||||
const wfs = s.group[video.ke].webdav
|
})
|
||||||
const videoDetails = s.parseJSON(video.details)
|
s.setCloudDiskUsedForGroup(e.ke,{
|
||||||
const saveLocation = videoDetails.location
|
amount : s.kilobyteToMegabyte(queryInfo.size),
|
||||||
const fileStream = wfs.createReadStream(saveLocation);
|
storageType : 'webdav'
|
||||||
return fileStream
|
},'timelapseFrames')
|
||||||
}
|
s.purgeCloudDiskForGroup(e,'webdav','timelapseFrames')
|
||||||
//webdav
|
}
|
||||||
s.addCloudUploader({
|
}
|
||||||
name: 'webdav',
|
}
|
||||||
loadGroupAppExtender: loadWebDavForUser,
|
function onDeleteTimelapseFrameFromCloud(e,frame,callback){
|
||||||
unloadGroupAppExtender: unloadWebDavForUser,
|
// e = user
|
||||||
insertCompletedVideoExtender: uploadVideoToWebDav,
|
try{
|
||||||
deleteVideoFromCloudExtensions: deleteVideoFromWebDav,
|
var frameDetails = JSON.parse(frame.details)
|
||||||
cloudDiskUseStartupExtensions: cloudDiskUseStartupForWebDav,
|
}catch(err){
|
||||||
beforeAccountSave: beforeAccountSaveForWebDav,
|
var frameDetails = frame.details
|
||||||
onAccountSave: cloudDiskUseStartupForWebDav,
|
}
|
||||||
onInsertTimelapseFrame: () => {},
|
if(frame.type !== 'webdav'){
|
||||||
onDeleteTimelapseFrameFromCloud: () => {},
|
callback()
|
||||||
onGetVideoData
|
return
|
||||||
})
|
}
|
||||||
return {
|
if(!frameDetails.location){
|
||||||
"evaluation": "details.use_webdav !== '0'",
|
frameDetails.location = frame.href.split(locationUrl)[1]
|
||||||
"name": lang.WebDAV,
|
}
|
||||||
"color": "forestgreen",
|
s.group[e.ke].webdav.unlink(frameDetails.location, function(err) {
|
||||||
"info": [
|
if (err) console.log(frameDetails.location,err)
|
||||||
{
|
callback()
|
||||||
"name": "detail=webdav_save",
|
})
|
||||||
"selector":"autosave_webdav",
|
}
|
||||||
"field": lang.Autosave,
|
async function onGetVideoData(video){
|
||||||
"description": "",
|
const wfs = s.group[video.ke].webdav
|
||||||
"default": lang.No,
|
const videoDetails = s.parseJSON(video.details)
|
||||||
"example": "",
|
const saveLocation = videoDetails.location
|
||||||
"fieldType": "select",
|
const fileStream = wfs.createReadStream(saveLocation);
|
||||||
"possible": [
|
return fileStream
|
||||||
{
|
}
|
||||||
"name": lang.No,
|
//webdav
|
||||||
"value": "0"
|
s.addCloudUploader({
|
||||||
|
name: 'webdav',
|
||||||
|
loadGroupAppExtender: loadWebDavForUser,
|
||||||
|
unloadGroupAppExtender: unloadWebDavForUser,
|
||||||
|
insertCompletedVideoExtender: uploadVideoToWebDav,
|
||||||
|
deleteVideoFromCloudExtensions: deleteVideoFromWebDav,
|
||||||
|
cloudDiskUseStartupExtensions: cloudDiskUseStartupForWebDav,
|
||||||
|
beforeAccountSave: beforeAccountSaveForWebDav,
|
||||||
|
onAccountSave: cloudDiskUseStartupForWebDav,
|
||||||
|
onInsertTimelapseFrame: () => {},
|
||||||
|
onDeleteTimelapseFrameFromCloud: () => {},
|
||||||
|
onGetVideoData
|
||||||
|
})
|
||||||
|
return {
|
||||||
|
"evaluation": "details.use_webdav !== '0'",
|
||||||
|
"name": lang.WebDAV,
|
||||||
|
"color": "forestgreen",
|
||||||
|
"info": [
|
||||||
|
{
|
||||||
|
"name": "detail=webdav_save",
|
||||||
|
"selector":"autosave_webdav",
|
||||||
|
"field": lang.Autosave,
|
||||||
|
"description": "",
|
||||||
|
"default": lang.No,
|
||||||
|
"example": "",
|
||||||
|
"fieldType": "select",
|
||||||
|
"possible": [
|
||||||
|
{
|
||||||
|
"name": lang.No,
|
||||||
|
"value": "0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": lang.Yes,
|
||||||
|
"value": "1"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": true,
|
||||||
|
"field": lang.URL,
|
||||||
|
"name": "detail=webdav_url",
|
||||||
|
"form-group-class": "autosave_webdav_input autosave_webdav_1",
|
||||||
|
"description": "",
|
||||||
|
"default": "",
|
||||||
|
"example": "",
|
||||||
|
"possible": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": true,
|
||||||
|
"field": lang.Username,
|
||||||
|
"name": "detail=webdav_user",
|
||||||
|
"form-group-class": "autosave_webdav_input autosave_webdav_1",
|
||||||
|
"description": "",
|
||||||
|
"default": "",
|
||||||
|
"example": "",
|
||||||
|
"possible": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": true,
|
||||||
|
"field": lang.Password,
|
||||||
|
"fieldType": "password",
|
||||||
|
"name": "detail=webdav_pass",
|
||||||
|
"form-group-class": "autosave_webdav_input autosave_webdav_1",
|
||||||
|
"description": "",
|
||||||
|
"default": "",
|
||||||
|
"example": "",
|
||||||
|
"possible": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": lang.Yes,
|
"hidden": true,
|
||||||
"value": "1"
|
"name": "detail=webdav_log",
|
||||||
}
|
"field": lang['Save Links to Database'],
|
||||||
]
|
"fieldType": "select",
|
||||||
},
|
"selector": "h_webdavsld",
|
||||||
{
|
"form-group-class":"autosave_webdav_input autosave_webdav_1",
|
||||||
"hidden": true,
|
"description": "",
|
||||||
"field": lang.URL,
|
"default": "",
|
||||||
"name": "detail=webdav_url",
|
"example": "",
|
||||||
"form-group-class": "autosave_webdav_input autosave_webdav_1",
|
"possible": [
|
||||||
"description": "",
|
{
|
||||||
"default": "",
|
"name": lang.No,
|
||||||
"example": "",
|
"value": "0"
|
||||||
"possible": ""
|
},
|
||||||
},
|
{
|
||||||
{
|
"name": lang.Yes,
|
||||||
"hidden": true,
|
"value": "1"
|
||||||
"field": lang.Username,
|
}
|
||||||
"name": "detail=webdav_user",
|
]
|
||||||
"form-group-class": "autosave_webdav_input autosave_webdav_1",
|
|
||||||
"description": "",
|
|
||||||
"default": "",
|
|
||||||
"example": "",
|
|
||||||
"possible": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"hidden": true,
|
|
||||||
"field": lang.Password,
|
|
||||||
"fieldType": "password",
|
|
||||||
"name": "detail=webdav_pass",
|
|
||||||
"form-group-class": "autosave_webdav_input autosave_webdav_1",
|
|
||||||
"description": "",
|
|
||||||
"default": "",
|
|
||||||
"example": "",
|
|
||||||
"possible": ""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"hidden": true,
|
|
||||||
"name": "detail=webdav_log",
|
|
||||||
"field": lang['Save Links to Database'],
|
|
||||||
"fieldType": "select",
|
|
||||||
"selector": "h_webdavsld",
|
|
||||||
"form-group-class":"autosave_webdav_input autosave_webdav_1",
|
|
||||||
"description": "",
|
|
||||||
"default": "",
|
|
||||||
"example": "",
|
|
||||||
"possible": [
|
|
||||||
{
|
|
||||||
"name": lang.No,
|
|
||||||
"value": "0"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": lang.Yes,
|
"hidden": true,
|
||||||
"value": "1"
|
"name": "detail=use_webdav_size_limit",
|
||||||
}
|
"field": lang['Use Max Storage Amount'],
|
||||||
]
|
"fieldType": "select",
|
||||||
},
|
"selector": "h_webdavzl",
|
||||||
{
|
"form-group-class":"autosave_webdav_input autosave_webdav_1",
|
||||||
"hidden": true,
|
"form-group-class-pre-layer":"h_webdavsld_input h_webdavsld_1",
|
||||||
"name": "detail=use_webdav_size_limit",
|
"description": "",
|
||||||
"field": lang['Use Max Storage Amount'],
|
"default": "",
|
||||||
"fieldType": "select",
|
"example": "",
|
||||||
"selector": "h_webdavzl",
|
"possible": [
|
||||||
"form-group-class":"autosave_webdav_input autosave_webdav_1",
|
{
|
||||||
"form-group-class-pre-layer":"h_webdavsld_input h_webdavsld_1",
|
"name": lang.No,
|
||||||
"description": "",
|
"value": "0"
|
||||||
"default": "",
|
},
|
||||||
"example": "",
|
{
|
||||||
"possible": [
|
"name": lang.Yes,
|
||||||
{
|
"value": "1"
|
||||||
"name": lang.No,
|
}
|
||||||
"value": "0"
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": lang.Yes,
|
"hidden": true,
|
||||||
"value": "1"
|
"name": "detail=webdav_size_limit",
|
||||||
}
|
"field": lang['Max Storage Amount'],
|
||||||
]
|
"form-group-class":"autosave_webdav_input autosave_webdav_1",
|
||||||
},
|
"form-group-class-pre-layer":"h_webdavsld_input h_webdavsld_1",
|
||||||
{
|
"description": "",
|
||||||
"hidden": true,
|
"default": "10000",
|
||||||
"name": "detail=webdav_size_limit",
|
"example": "",
|
||||||
"field": lang['Max Storage Amount'],
|
"possible": ""
|
||||||
"form-group-class":"autosave_webdav_input autosave_webdav_1",
|
},
|
||||||
"form-group-class-pre-layer":"h_webdavsld_input h_webdavsld_1",
|
{
|
||||||
"description": "",
|
"hidden": true,
|
||||||
"default": "10000",
|
"name": "detail=webdav_dir",
|
||||||
"example": "",
|
"field": lang['Save Directory'],
|
||||||
"possible": ""
|
"form-group-class":"autosave_webdav_input autosave_webdav_1",
|
||||||
},
|
"description": "",
|
||||||
{
|
"default": "/",
|
||||||
"hidden": true,
|
"example": "",
|
||||||
"name": "detail=webdav_dir",
|
"possible": ""
|
||||||
"field": lang['Save Directory'],
|
},
|
||||||
"form-group-class":"autosave_webdav_input autosave_webdav_1",
|
]
|
||||||
"description": "",
|
}
|
||||||
"default": "/",
|
}catch(err){
|
||||||
"example": "",
|
console.error(err.message)
|
||||||
"possible": ""
|
console.error('Could not start WebDAV Uploader, please run "npm install webdav-fs" inside the Shinobi folder.')
|
||||||
},
|
console.error('The webdav-fs Module is known to have possible vulnerabilities. Due to the nature of the vulnerability you should be unaffected unless Shinobi is public facing. Use at your own risk.')
|
||||||
]
|
}
|
||||||
|
}else{
|
||||||
|
try{
|
||||||
|
const webdav = await import("webdav-fs");
|
||||||
|
console.error('!! Looks like you have webdav-fs installed but its not enabled. Due to a vulnerability with it you now need to add "webDavUpload": true to your conf.json to enable it.')
|
||||||
|
}catch(err){
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
10
package.json
10
package.json
|
@ -16,7 +16,6 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-s3": "^3.226.0",
|
"@aws-sdk/client-s3": "^3.226.0",
|
||||||
"async": "^3.2.2",
|
"async": "^3.2.2",
|
||||||
"backblaze-b2": "^1.7.0",
|
|
||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.19.0",
|
||||||
"bson": "^4.6.1",
|
"bson": "^4.6.1",
|
||||||
"connection-tester": "^0.2.0",
|
"connection-tester": "^0.2.0",
|
||||||
|
@ -29,7 +28,6 @@
|
||||||
"express-fileupload": "^1.4.0",
|
"express-fileupload": "^1.4.0",
|
||||||
"form-data": "^4.0.0",
|
"form-data": "^4.0.0",
|
||||||
"fs-extra": "9.0.1",
|
"fs-extra": "9.0.1",
|
||||||
"ftp-srv": "^4.6.2",
|
|
||||||
"googleapis": "^100.0.0",
|
"googleapis": "^100.0.0",
|
||||||
"http-proxy": "^1.18.1",
|
"http-proxy": "^1.18.1",
|
||||||
"jsonfile": "^3.0.1",
|
"jsonfile": "^3.0.1",
|
||||||
|
@ -43,8 +41,8 @@
|
||||||
"mysql2": "^3.9.7",
|
"mysql2": "^3.9.7",
|
||||||
"node-abort-controller": "^3.0.1",
|
"node-abort-controller": "^3.0.1",
|
||||||
"node-fetch": "^2.6.7",
|
"node-fetch": "^2.6.7",
|
||||||
|
"node-shinobi": "^1.0.4",
|
||||||
"node-ssh": "^12.0.4",
|
"node-ssh": "^12.0.4",
|
||||||
"node-telegram-bot-api": "^0.65.1",
|
|
||||||
"nodemailer": "^6.7.1",
|
"nodemailer": "^6.7.1",
|
||||||
"onvif": "^0.7.1",
|
"onvif": "^0.7.1",
|
||||||
"pam-diff": "^1.1.0",
|
"pam-diff": "^1.1.0",
|
||||||
|
@ -53,7 +51,8 @@
|
||||||
"pixel-change": "^1.1.0",
|
"pixel-change": "^1.1.0",
|
||||||
"pushover-notifications": "^1.2.2",
|
"pushover-notifications": "^1.2.2",
|
||||||
"sat": "^0.7.1",
|
"sat": "^0.7.1",
|
||||||
"shinobi-node-moving-things-tracker": "^0.9.3",
|
"shinobi-backblaze-b2": "^1.7.1",
|
||||||
|
"shinobi-node-moving-things-tracker": "^0.9.5",
|
||||||
"shinobi-onvif": "0.2.2",
|
"shinobi-onvif": "0.2.2",
|
||||||
"shinobi-sound-detection": "^0.1.13",
|
"shinobi-sound-detection": "^0.1.13",
|
||||||
"shinobi-zwave": "^1.0.11",
|
"shinobi-zwave": "^1.0.11",
|
||||||
|
@ -61,8 +60,7 @@
|
||||||
"socket.io": "^4.4.1",
|
"socket.io": "^4.4.1",
|
||||||
"socket.io-client": "^4.5.3",
|
"socket.io-client": "^4.5.3",
|
||||||
"tree-kill": "1.2.2",
|
"tree-kill": "1.2.2",
|
||||||
"unzipper": "0.10.11",
|
"unzipper": "0.10.11"
|
||||||
"webdav-fs": "^4.0.1"
|
|
||||||
},
|
},
|
||||||
"bin": "camera.js",
|
"bin": "camera.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
@ -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