diff --git a/INSTALL/centos.sh b/INSTALL/centos.sh index 129822a9..c762adb8 100644 --- a/INSTALL/centos.sh +++ b/INSTALL/centos.sh @@ -27,7 +27,7 @@ if [ ! -e "./super.json" ]; then fi echo "Shinobi - Run yum update" sudo yum update -y -sudo yum install make -y +sudo yum install make zip -y echo "=============" echo "Shinobi - Do you want to Install FFMPEG?" echo "(y)es or (N)o" @@ -143,21 +143,14 @@ if [ "$mysqlDefaultData" = "y" ] || [ "$mysqlDefaultData" = "Y" ]; then echo "=====================================" echo "=====================================" fi -if [ ! "$sqliteormariadb" = "M" ] && [ ! "$sqliteormariadb" = "m" ]; then - echo "=====================================" - echo "||===== Install Completed =====||" - echo "=====================================" - echo "|| Login with the Superuser and create a new user!!" - echo "||===================================" - echo "|| Open http://$(ifconfig | sed -En 's/127.0.0.1//;s/.*inet (addr:)?(([0-9]*\.){3}[0-9]*).*/\2/p'):8080/super in your web browser." - echo "||===================================" - echo "|| Default Superuser : admin@shinobi.video" - echo "|| Default Password : admin" - echo "=====================================" - echo "=====================================" -else - echo "+=================================+" - echo "||===== Install Completed =====||" - echo "|| Access the main Shinobi panel at http://$(ifconfig | sed -En 's/127.0.0.1//;s/.*inet (addr:)?(([0-9]*\.){3}[0-9]*).*/\2/p'):8080 in your web browser." - echo "+=================================+" -fi +echo "=====================================" +echo "||===== Install Completed =====||" +echo "=====================================" +echo "|| Login with the Superuser and create a new user!!" +echo "||===================================" +echo "|| Open http://$(ifconfig | sed -En 's/127.0.0.1//;s/.*inet (addr:)?(([0-9]*\.){3}[0-9]*).*/\2/p'):8080/super in your web browser." +echo "||===================================" +echo "|| Default Superuser : admin@shinobi.video" +echo "|| Default Password : admin" +echo "=====================================" +echo "=====================================" diff --git a/INSTALL/openalpr-cpu-easy.sh b/INSTALL/openalpr-cpu-easy.sh new file mode 100644 index 00000000..56e54b72 --- /dev/null +++ b/INSTALL/openalpr-cpu-easy.sh @@ -0,0 +1,41 @@ +# Install prerequisites +DIR=`dirname $0` +INSTALLERS_DIR="$DIR" +echo "-----------------------------------" +if ! [ -x "$(command -v opencv_version)" ]; then + echo "Installing OpenCV" + dos2unix $INSTALLERS_DIR/opencv-cuda.sh + sh $INSTALLERS_DIR/opencv-cuda.sh +else + echo "OpenCV found... : $(opencv_version)" +fi +# this includes all the ones missing from OpenALPR's guide. +sudo apt install libtesseract-dev git cmake build-essential libleptonica-dev -y +sudo apt install liblog4cplus-dev libcurl3-dev -y +sudo apt install libleptonica-dev -y +sudo apt install libcurl4-openssl-dev -y +sudo apt install liblog4cplus-dev -y +sudo apt install beanstalkd -y +sudo apt install openjdk-8-jdk -y + +# Clone the latest code from GitHub +git clone https://github.com/openalpr/openalpr.git + +# Setup the build directory +cd openalpr/src +mkdir build +cd build + +# setup the compile environment +cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_INSTALL_SYSCONFDIR:PATH=/etc .. + +# compile the library +make + +# Install the binaries/libraries to your local system (prefix is /usr) +sudo make install + +# Test the library +wget http://plates.openalpr.com/h786poj.jpg -O lp.jpg +alpr lp.jpg +rm lp.jpg diff --git a/INSTALL/openalpr-gpu-easy.sh b/INSTALL/openalpr-gpu-easy.sh index 09496295..f78e3615 100644 --- a/INSTALL/openalpr-gpu-easy.sh +++ b/INSTALL/openalpr-gpu-easy.sh @@ -1,4 +1,14 @@ # Install prerequisites +DIR=`dirname $0` +INSTALLERS_DIR="$DIR" +echo "-----------------------------------" +if ! [ -x "$(command -v opencv_version)" ]; then + echo "Installing OpenCV" + dos2unix $INSTALLERS_DIR/opencv-cuda.sh + sh $INSTALLERS_DIR/opencv-cuda.sh +else + echo "OpenCV found... : $(opencv_version)" +fi # this includes all the ones missing from OpenALPR's guide. sudo apt install libtesseract-dev git cmake build-essential libleptonica-dev -y sudo apt install liblog4cplus-dev libcurl3-dev -y @@ -28,4 +38,4 @@ sudo make install # Test the library wget http://plates.openalpr.com/h786poj.jpg -O lp.jpg alpr lp.jpg -rm lp.jpg \ No newline at end of file +rm lp.jpg diff --git a/INSTALL/opensuse.sh b/INSTALL/opensuse.sh index db021bee..3a10b3e8 100644 --- a/INSTALL/opensuse.sh +++ b/INSTALL/opensuse.sh @@ -115,15 +115,6 @@ sudo chmod -R 755 . touch INSTALL/installed.txt dos2unix /home/Shinobi/INSTALL/shinobi ln -s /home/Shinobi/INSTALL/shinobi /usr/bin/shinobi -if [ "$mysqlDefaultData" = "y" ] || [ "$mysqlDefaultData" = "Y" ]; then - echo "=====================================" > INSTALL/installed.txt - echo "======= Login Credentials =======" >> INSTALL/installed.txt - echo "|| Username : $userEmail" >> INSTALL/installed.txt - echo "|| Password : $userPasswordPlain" >> INSTALL/installed.txt - echo "|| API Key : $apiKey" >> INSTALL/installed.txt - echo "=====================================" >> INSTALL/installed.txt - echo "=====================================" >> INSTALL/installed.txt -fi echo "Shinobi - Start Shinobi and set to start on boot?" echo "(y)es or (N)o" read startShinobi @@ -134,31 +125,14 @@ if [ "$startShinobi" = "y" ] || [ "$startShinobi" = "Y" ]; then sudo pm2 save sudo pm2 list fi -if [ "$mysqlDefaultData" = "y" ] || [ "$mysqlDefaultData" = "Y" ]; then - echo "details written to INSTALL/installed.txt" - echo "=====================================" - echo "======= Login Credentials =======" - echo "|| Username : $userEmail" - echo "|| Password : $userPasswordPlain" - echo "|| API Key : $apiKey" - echo "=====================================" - echo "=====================================" -fi -if [ ! "$sqliteormariadb" = "M" ] && [ ! "$sqliteormariadb" = "m" ]; then - echo "=====================================" - echo "||===== Install Completed =====||" - echo "=====================================" - echo "|| Login with the Superuser and create a new user!!" - echo "||===================================" - echo "|| Open http://$(/sbin/ip -o -4 addr list eth0 | awk '{print $4}' | cut -d/ -f1):8080/super in your web browser." - echo "||===================================" - echo "|| Default Superuser : admin@shinobi.video" - echo "|| Default Password : admin" - echo "=====================================" - echo "=====================================" -else - echo "+=================================+" - echo "||===== Install Completed =====||" - echo "|| Access the main Shinobi panel at http://$(/sbin/ip -o -4 addr list eth0 | awk '{print $4}' | cut -d/ -f1):8080 in your web browser." - echo "+=================================+" -fi +echo "=====================================" +echo "||===== Install Completed =====||" +echo "=====================================" +echo "|| Login with the Superuser and create a new user!!" +echo "||===================================" +echo "|| Open http://$(/sbin/ip -o -4 addr list eth0 | awk '{print $4}' | cut -d/ -f1):8080/super in your web browser." +echo "||===================================" +echo "|| Default Superuser : admin@shinobi.video" +echo "|| Default Password : admin" +echo "=====================================" +echo "=====================================" diff --git a/INSTALL/shinobi b/INSTALL/shinobi index 30b94016..ed172942 100644 --- a/INSTALL/shinobi +++ b/INSTALL/shinobi @@ -1,30 +1,68 @@ #!/bin/bash -installationDirectory="/home/Shinobi" -if [ ! "$1" ]; then +if [ ! -e "/etc/shinobisystems/path.txt" ]; then + installationDirectory="/home/Shinobi" +else + installationDirectory=$(cat /etc/shinobisystems/cctv.txt) +fi +cd $installationDirectory +currentBuild=$(git show --oneline -s) +gitOrigin=$(git remote show origin) +splitBuildString=($currentBuild) +currentCommitNumber=${splitBuildString[0]} +if [[ $gitOrigin == *'ShinobiCE'* ]]; then + repo="CE" +else + repo="Pro" +fi +if [[ $@ == *'help'* ]] || [ ! "$1" ]; then echo "=========================================================" echo "==!! Shinobi : The Open Source CCTV and NVR Solution !!==" echo "=========================================================" - echo "You are missing function parameters." - echo "Example : shinobi [command] .." - echo "Example : shinobi flush restart logs" + if [ ! "$1" ]; then + echo "You are missing function parameters." + echo "Example : shinobi [command] .." + echo "Example : shinobi flush restart logs" + else + echo "Hello there! if you need support come on over" + echo "to the Shinobi Community Chat! :)" + echo "https://discordapp.com/invite/mdhmvuH/" + fi echo "=========================================================" echo "Your available options for COMMAND are as follows" echo "=========================================================" - echo "| start or s :" + echo "| start :" echo "|--> Start camera.js and cron.js under PM2 (Process Manager)" echo "-" - echo "| restart or r :" + echo "| restart :" echo "|--> Restart all processes running under the PM2 daemon." echo "-" - echo "| stop, exit, or e :" + echo "| stop, exit :" echo "|--> Stop all processes running under the PM2 daemon." echo "-" + echo "| version :" + echo "|--> get version of your current build by git." + echo "-" echo "| logs :" echo "|--> Get PM2 log stream with last 100 lines." echo "-" - echo "| clear, flush, or f :" + echo "| update :" + echo "|--> Update via Git." + echo "-" + echo "| getMaster :" + echo "|--> Switch to the Master Branch (For Pro Repo only)." + echo "-" + echo "| getDev :" + echo "|--> Switch to the Development Branch (For Pro Repo only)." + echo "-" + echo "| clear, flush :" echo "|--> Clear all PM2 logs." echo "-" + echo "| bootupEnable :" + echo "|--> Start Shinobi on OS reboot." + echo "-" + echo "| bootupDisable :" + echo "|--> Disable starting Shinobi on OS reboot." + echo "-" echo "| kill :" echo "|--> Stop the entire PM2 daemon." fi @@ -34,7 +72,8 @@ fi if [[ $@ == *'restart'* ]]; then proccessAlive=$(pm2 list | grep camera) if [ "$proccessAlive" ]; then - pm2 restart all + pm2 restart $installationDirectory/camera.js + pm2 restart $installationDirectory/cron.js else echo "Shinobi process is not running." fi @@ -58,11 +97,64 @@ fi if [[ $@ == *'stop'* ]] || [[ $@ == *'exit'* ]]; then proccessAlive=$(pm2 list | grep camera) if [ "$proccessAlive" ]; then - pm2 kill + pm2 stop $installationDirectory/camera.js + pm2 stop $installationDirectory/cron.js else echo "Shinobi process is not running." fi fi +if [[ $@ == *'version'* ]]; then + echo "Build ID : $currentCommitNumber" + if [[ $repo == "Pro" ]]; then + echo "Repository : Shinobi Pro" + else + echo "Repository : Shinobi CE" + fi + echo $currentBuild +fi +if [[ $@ == *'bootupEnable'* ]] || [[ $@ == *'bootupenable'* ]]; then + pm2 startup + pm2 save +fi +if [[ $@ == *'bootupDisable'* ]] || [[ $@ == *'bootupdisable'* ]]; then + pm2 unstartup + pm2 save +fi +if [[ $@ == *'getDev'* ]] || [[ $@ == *'getdev'* ]]; then + if [[ $repo == "Pro" ]]; then + git checkout dev + echo "Shinobi - Restart Shinobi to make the changes take affect." + else + echo "Shinobi - Cannot use \"getDev\" with Shinobi CE" + fi +fi +if [[ $@ == *'getMaster'* ]] || [[ $@ == *'getmaster'* ]]; then + if [[ $repo == "Pro" ]]; then + git checkout master + echo "Shinobi - Restart Shinobi to make the changes take affect." + else + echo "Shinobi - Cannot use \"getMaster\" with Shinobi CE" + fi +fi +if [[ $@ == *'update'* ]]; then + echo "=============" + echo "Shinobi - Are you sure you want to update? This will restart Shinobi." + echo "(y)es or (N)o" + read updateshinobi + if [ "$updateshinobi" = "y" ] || [ "$updateshinobi" = "Y" ]; then + echo "Beginning Update Process..." + pm2 stop $installationDirectory/camera.js + pm2 stop $installationDirectory/cron.js + npm install --unsafe-perm + npm audit fix --force + git reset --hard + git pull + pm2 start $installationDirectory/camera.js + pm2 start $installationDirectory/cron.js + else + echo "Cancelled Update Process." + fi +fi if [[ $@ == *'kill'* ]]; then pm2 kill fi diff --git a/INSTALL/terminalCommands.js b/INSTALL/terminalCommands.js new file mode 100644 index 00000000..bc6e4e7a --- /dev/null +++ b/INSTALL/terminalCommands.js @@ -0,0 +1,41 @@ +var fs = require('fs'); +var moment = require('moment'); +var exec = require('child_process').exec; +var execSync = require('child_process').execSync; +s = { + isWin: (process.platform === 'win32' || process.platform === 'win64'), + mainDirectory: __dirname.split('/INSTALL')[0] +} +var createTerminalCommands = function(callback){ + var next = function(){ + if(callback)callback() + } + if(!s.isWin){ + var etcPath = '/etc/shinobisystems/' + console.log('Creating "' + etcPath + '"...') + var createPathFile = function(){ + var pathTxt = etcPath + 'cctv.txt' + console.log('Creating "' + pathTxt + '"...') + fs.writeFile(pathTxt,s.mainDirectory,function(err){ + if(err)console.log(err) + fs.chmod(pathTxt,0o777,function(err){ + if(err)console.log(err) + console.log('Linking "' + s.mainDirectory + '/INSTALL/shinobi" to "/usr/bin/shinobi"...') + fs.symlink(s.mainDirectory + '/INSTALL/shinobi', '/usr/bin/shinobi', next) + console.log('You can now use `shinobi` in terminal.') + }) + }) + } + fs.stat(etcPath,function(err,stat){ + if(!err && stat){ + createPathFile() + }else{ + fs.mkdir(etcPath,createPathFile) + } + }) + }else{ + //no commands for windows yet + next() + } +} +createTerminalCommands() diff --git a/INSTALL/ubuntu.sh b/INSTALL/ubuntu.sh index b033daf1..87bd23f7 100644 --- a/INSTALL/ubuntu.sh +++ b/INSTALL/ubuntu.sh @@ -30,12 +30,17 @@ if [ ! -e "./super.json" ]; then echo "* You can edit these settings in \"super.json\" located in the Shinobi directory." sudo cp super.sample.json super.json fi +if ! [ -x "$(command -v ifconfig)" ]; then + echo "=============" + echo "Shinobi - Installing Net-Tools" + sudo apt install net-tools -y +fi if ! [ -x "$(command -v node)" ]; then echo "=============" echo "Shinobi - Installing Node.js" - wget https://deb.nodesource.com/setup_9.x - chmod +x setup_9.x - ./setup_9.x + wget https://deb.nodesource.com/setup_8.x + chmod +x setup_8.x + ./setup_8.x sudo apt install nodejs -y else echo "Node.js Found..." @@ -44,7 +49,7 @@ fi if ! [ -x "$(command -v npm)" ]; then sudo apt install npm -y fi -sudo apt install make -y +sudo apt install make zip -y if ! [ -x "$(command -v ffmpeg)" ]; then if [ "$getubuntuversion" = "16" ] || [ "$getubuntuversion" < "16" ]; then echo "=============" @@ -132,21 +137,14 @@ if [ "$startShinobi" = "y" ] || [ "$startShinobi" = "y" ]; then sudo pm2 save sudo pm2 list fi -if [ ! "$sqliteormariadb" = "M" ] && [ ! "$sqliteormariadb" = "m" ]; then - echo "=====================================" - echo "||===== Install Completed =====||" - echo "=====================================" - echo "|| Login with the Superuser and create a new user!!" - echo "||===================================" - echo "|| Open http://$(ifconfig | sed -En 's/127.0.0.1//;s/.*inet (addr:)?(([0-9]*\.){3}[0-9]*).*/\2/p'):8080/super in your web browser." - echo "||===================================" - echo "|| Default Superuser : admin@shinobi.video" - echo "|| Default Password : admin" - echo "=====================================" - echo "=====================================" -else - echo "+=================================+" - echo "||===== Install Completed =====||" - echo "|| Access the main Shinobi panel at http://$(ifconfig | sed -En 's/127.0.0.1//;s/.*inet (addr:)?(([0-9]*\.){3}[0-9]*).*/\2/p'):8080 in your web browser." - echo "+=================================+" -fi +echo "=====================================" +echo "||===== Install Completed =====||" +echo "=====================================" +echo "|| Login with the Superuser and create a new user!!" +echo "||===================================" +echo "|| Open http://$(ifconfig | sed -En 's/127.0.0.1//;s/.*inet (addr:)?(([0-9]*\.){3}[0-9]*).*/\2/p'):8080/super in your web browser." +echo "||===================================" +echo "|| Default Superuser : admin@shinobi.video" +echo "|| Default Password : admin" +echo "=====================================" +echo "=====================================" diff --git a/camera.js b/camera.js index 7c74e14a..9fe8f4ff 100644 --- a/camera.js +++ b/camera.js @@ -26,7 +26,9 @@ loadLib('codeTester')(s,config,lang,io) //basic functions loadLib('basic')(s,config) //video processing engine -loadLib('ffmpeg')(s,config,function(){ +loadLib('ffmpeg')(s,config,function(ffmpeg){ + //ffmpeg coProcessor + loadLib('ffmpegCoProcessor')(s,config,lang,ffmpeg) //database connection : mysql, sqlite3.. loadLib('sql')(s,config) //working directories : videos, streams, fileBin.. @@ -67,6 +69,8 @@ loadLib('ffmpeg')(s,config,function(){ loadLib('notification')(s,config,lang) //custom module loader loadLib('customAutoLoad')(s,config,lang,app,io) + //scheduling engine + loadLib('scheduler')(s,config,lang,app,io) //on-start actions, daemon(s) starter loadLib('startup')(s,config,lang) }) diff --git a/languages/en_CA.json b/languages/en_CA.json index 05b28ab8..898003b7 100644 --- a/languages/en_CA.json +++ b/languages/en_CA.json @@ -34,6 +34,10 @@ "API": "API", "ONVIF": "ONVIF", "FFprobe": "Probe", + "Monitor States": "Monitor States", + "Schedule": "Schedule", + "Schedules": "Schedules", + "Monitor States and Schedules": "Monitor States and Schedules", "Filters": "Filters", "Full URL Path": "Full URL Path", "Logs": "Logs", @@ -116,6 +120,8 @@ "Can View Videos and Events": "Can View Videos and Events", "Can Delete Videos and Events": "Can Delete Videos and Events", "Saved Filters": "Saved Filters", + "Saved Presets": "Saved Presets", + "Saved Schedules": "Saved Schedules", "Filter Name": "Filter Name", "Find Where": "Find Where", "Reason": "Reason", @@ -288,9 +294,15 @@ "DeleteMonitorText": "Do you want to delete this monitor? You cannot recover it. You can choose for the files to remain in the filesystem. If you choose to recreate a monitor with the same ID the videos and events will become visible in the dashboard.", "DeleteMonitorsText": "Do you want to delete these monitors? You cannot recover them. You can choose to keep the files for these IDs in the filesystem. If you choose to recreate a monitor with one of the IDs the videos and events will become visible in the dashboard.", "Invalid JSON": "Invalid JSON", + "Invalid Data": "Invalid Data", + "Name cannot be empty.": "Name cannot be empty.", + "Start Time cannot be empty.": "Start Time cannot be empty.", + "Must be atleast one row": "Must be atleast one row", "InvalidJSONText": "Please ensure this is a valid JSON string for Shinobi monitor configuration.", "Passwords don't match": "Passwords don't match", "Email address is in use.": "Email address is in use.", + "Group Key is in use.": "Group Key is in use.", + "Create Sub-Accounts at /admin": "Create Sub-Accounts at /admin", "No Events found for this video": "No Events found for this video", "Video and Time Span (Minutes)": "Video and Time Span (Minutes)", "Video Length (minutes) and Motion Count per video": "Video Length (minutes) and Motion Count per video", @@ -329,6 +341,10 @@ "Filter for Objects only": "Filter for Objects only", "Custom": "Custom", "Detector": "Detector", + "Audio Detector": "Audio Detector", + "Audio Detection": "Audio Detection", + "Minimum dB": "Minimum dB", + "Maximum dB": "Maximum dB", "Connected": "Connected", "Not Saved": "Not Saved", "Not Connected": "Not Connected", @@ -400,7 +416,17 @@ "Image Height": "Image Height", "Record File Type": "Record File Type", "Video Codec": "Video Codec", + "Delete Monitor States Preset": "Delete Monitor States Preset", + "Delete Monitor State?": "Delete Monitor State", + "deleteMonitorStateText1": "Do you want to delete this Monitor States Preset?", + "deleteMonitorStateText2": "Do you want to delete this Monitor's Preset?", + "Search Images": "Search Images", + "Launch in New Window": "Launch in New Window", "Preset": "Preset", + "Presets": "Presets", + "possibleInternalError": "Possible Internal Error", + "sizePurgeLockedText": "The Size Purge Lock (deleteOverMax) appears to have failed to unlock. Unlocking now...", + "Use coProcessor": "Use coProcessor", "Audio Codec": "Audio Codec", "Video Record Rate": "Video Record Rate (FPS)", "Record Width": "Record Width", @@ -415,6 +441,7 @@ "Stream to YouTube": "Stream to YouTube", "Stream to YouTube Flags": "Stream to YouTube Flags", "Recording Flags": "Recording Flags", + "Traditional Recording Flags": "Traditional Recording Flags", "Output Method": "Output Method", "Webhook": "Webhook", "Event Webhook Error": "Event Webhook Error", @@ -426,6 +453,8 @@ "Save Events to SQL": "Save Events to SQL", "Email on Trigger": "Email on Trigger Emails go to the main account holder's login address.", "Attach Video Clip": "Attach Video Clip", + "Error While Decoding": "Error While Decoding", + "ErrorWhileDecodingText": "Your hardware may have an unstable connection to the network. Check your network connections.", "Discord": "Discord", "Discord Alert on Trigger": "Discord Alert on Trigger", "Allow Next Email": "Allow Next Email in Minutes", @@ -642,6 +671,7 @@ "Monitor mode is already": "Monitor mode is already", "Monitor or Key does not exist.": "Monitor or Key does not exist.", "No Group with this key exists": "No Group with this key exists", + "Success": "Success", "Trigger Successful": "Trigger Successful", "No such file": "No such file", "h265BrowserText1": "If you are trying to play an H.265 file, you may need to download it and open it in another application like VLC.", @@ -666,6 +696,10 @@ "Inserted State Configuration": "Inserted State Configuration", "Edited State Configuration": "Edited State Configuration", "Deleted State Configuration": "Deleted State Configuration", + "Schedule Configuration Not Found": "Schedule Configuration Not Found", + "Inserted Schedule Configuration": "Inserted Schedule Configuration", + "Edited Schedule Configuration": "Edited Schedule Configuration", + "Deleted Schedule Configuration": "Deleted Schedule Configuration", "Dashboard Language": "Dashboard Language", "Form Data Not Found": "Form Data Not Found", "File Not Found": "File Not Found", @@ -777,6 +811,11 @@ "RTMP Stream":"RTMP Stream", "Stream Channel":"Stream Channel", "Confidence":"Confidence", + "Trainer Engine":"Trainer Engine", + "Train":"Train", + "TrainConfirm":"Are you sure you want to begin training? This can take more than 12 hours with over 500 images. This will consume a large amount of resources, like RAM or CPU.", + "Batch":"Batch", + "Subdivision":"Subdivision", "Map":"Map", "Add Map":"Add Map", "Add Input Feed":"Add Input Feed", @@ -792,6 +831,7 @@ "TV Channel ID":"TV Channel ID", "TV Channel Group":"TV Channel Group", "Emotion Average":"Emotion Average", + "Require Object to be in Region":"Require Object to be in Region", "Show Regions of Interest":"Show Regions of Interest", "Confidence of Detection":"Confidence of Detection", "Edit Selected":"Edit Selected", diff --git a/libs/basic.js b/libs/basic.js index 6f6d187a..252854bf 100644 --- a/libs/basic.js +++ b/libs/basic.js @@ -12,7 +12,7 @@ module.exports = function(s,config){ if(s.isWin===true){ cmd = "Taskkill /IM ffmpeg.exe /F" }else{ - cmd = "ps aux | grep -ie ffmpeg | awk '{print $2}' | xargs kill -9" + cmd = "pkill -9 ffmpeg" } exec(cmd,{detached: true}) }; @@ -33,7 +33,7 @@ module.exports = function(s,config){ s.parseJSON = function(string){ var parsed try{ - string = JSON.parse(string) + parsed = JSON.parse(string) }catch(err){ } @@ -226,4 +226,19 @@ module.exports = function(s,config){ break; } } + s.createTimeout = function(timeoutVar,timeoutLength,defaultLength,multiplier,callback){ + var theTimeout + if(!multiplier)multiplier = 1000 * 60 + if(!timeoutLength || timeoutLength === ''){ + theTimeout = defaultLength + }else{ + theTimeout = parseFloat(timeoutLength) * multiplier + } + clearTimeout(timeoutVar) + timeoutVar = setTimeout(function(){ + clearTimeout(timeoutVar) + delete(timeoutVar) + if(callback)callback() + },theTimeout) + } } diff --git a/libs/customAutoLoad.js b/libs/customAutoLoad.js index 39bd32f4..70b6b86a 100644 --- a/libs/customAutoLoad.js +++ b/libs/customAutoLoad.js @@ -1,24 +1,134 @@ var fs = require('fs') +var express = require('express') module.exports = function(s,config,lang,app,io){ - var checkFolder = function(folderName){ - var folderPath = __dirname + '/' + folderName - fs.readdir(folderPath,function(err,folderContents){ - if(!err && folderContents){ - folderContents.forEach(function(filename){ - if(filename.indexOf('.js') > -1){ - var customModulePath = folderPath + '/' + filename + s.customAutoLoadModules = {} + s.customAutoLoadTree = { + pages: [], + PageBlocks: [], + LibsJs: [], + LibsCss: [], + adminPageBlocks: [], + adminLibsJs: [], + adminLibsCss: [], + superPageBlocks: [], + superLibsJs: [], + superLibsCss: [] + } + var folderPath = __dirname + '/customAutoLoad' + var search = function(searchFor,searchIn){return searchIn.indexOf(searchFor) > -1} + fs.readdir(folderPath,function(err,folderContents){ + if(!err && folderContents){ + folderContents.forEach(function(filename){ + s.customAutoLoadModules[filename] = {} + var customModulePath = folderPath + '/' + filename + if(filename.indexOf('.js') > -1){ + s.customAutoLoadModules[filename].type = 'file' + try{ + require(customModulePath)(s,config,lang,app,io) + }catch(err){ + console.log('Failed to Load Module : ' + filename) + console.log(err) + } + }else{ + if(fs.lstatSync(customModulePath).isDirectory()){ + s.customAutoLoadModules[filename].type = 'folder' try{ require(customModulePath)(s,config,lang,app,io) + fs.readdir(customModulePath,function(err,folderContents){ + folderContents.forEach(function(name){ + switch(name){ + case'web': + var webFolder = s.checkCorrectPathEnding(customModulePath) + 'web/' + fs.readdir(webFolder,function(err,webFolderContents){ + webFolderContents.forEach(function(name){ + switch(name){ + case'libs': + case'pages': + if(name === 'libs'){ + if(config.webPaths.home !== '/'){ + app.use('/libs',express.static(webFolder + '/libs')) + } + app.use(s.checkCorrectPathEnding(config.webPaths.home)+'libs',express.static(webFolder + '/libs')) + app.use(s.checkCorrectPathEnding(config.webPaths.admin)+'libs',express.static(webFolder + '/libs')) + app.use(s.checkCorrectPathEnding(config.webPaths.super)+'libs',express.static(webFolder + '/libs')) + } + var libFolder = webFolder + name + '/' + fs.readdir(libFolder,function(err,webFolderContents){ + webFolderContents.forEach(function(libName){ + var thirdLevelName = libFolder + libName + switch(libName){ + case'js': + case'css': + case'blocks': + fs.readdir(thirdLevelName,function(err,webFolderContents){ + webFolderContents.forEach(function(filename){ + var fullPath = thirdLevelName + '/' + filename + var blockPrefix = '' + switch(true){ + case search('super.',filename): + blockPrefix = 'super' + break; + case search('admin.',filename): + blockPrefix = 'admin' + break; + } + switch(libName){ + case'js': + s.customAutoLoadTree[blockPrefix + 'LibsJs'].push(filename) + break; + case'css': + s.customAutoLoadTree[blockPrefix + 'LibsCss'].push(filename) + break; + case'blocks': + s.customAutoLoadTree[blockPrefix + 'PageBlocks'].push(fullPath) + break; + } + }) + }) + break; + default: + if(libName.indexOf('.ejs') > -1){ + s.customAutoLoadTree.pages.push(thirdLevelName) + } + break; + } + }) + }) + break; + } + }) + }) + break; + case'languages': + var languagesFolder = s.checkCorrectPathEnding(customModulePath) + 'languages/' + fs.readdir(languagesFolder,function(err,files){ + if(err)return console.log(err); + files.forEach(function(filename){ + var fileData = require(languagesFolder + filename) + var rule = filename.replace('.json','') + if(config.language === rule){ + lang = Object.assign(lang,fileData) + } + if(s.loadedLanguages[rule]){ + s.loadedLanguages[rule] = Object.assign(s.loadedLanguages[rule],fileData) + }else{ + s.loadedLanguages[rule] = Object.assign(s.copySystemDefaultLanguage(),fileData) + } + }) + }) + break; + } + }) + }) }catch(err){ console.log('Failed to Load Module : ' + filename) console.log(err) } } - }) - }else{ - fs.mkdirSync(folderPath) - } - }) - } - checkFolder('customAutoLoad') + } + }) + }else{ + fs.mkdirSync(folderPath) + } + }) } diff --git a/libs/detector.js b/libs/detector.js index 834c0dbf..49ff7a59 100644 --- a/libs/detector.js +++ b/libs/detector.js @@ -1,6 +1,11 @@ -var P2P = require('pipe2pam'); +// Matrix In Region Libs > +var SAT = require('sat') +var V = SAT.Vector; +var P = SAT.Polygon; +// Matrix In Region Libs /> +var P2P = require('pipe2pam') // pamDiff is based on https://www.npmjs.com/package/pam-diff -var PamDiff = require('./detectorPamDiff.js'); +var PamDiff = require('pam-diff') module.exports = function(s,config){ s.createPamDiffEngine = function(e){ var width, @@ -46,7 +51,7 @@ module.exports = function(s,config){ [width,height], [width,0] ] - }; + } } e.triggerTimer = {} @@ -58,7 +63,7 @@ module.exports = function(s,config){ regions : regions.forPam, drawMatrix : e.details.detector_show_matrix }); - s.group[e.ke].mon[e.id].p2p = new P2P(); + s.group[e.ke].mon[e.id].p2p = new P2P() var regionArray = Object.values(regionJson) if(config.detectorMergePamRegionTriggers === true){ // merge pam triggers for performance boost @@ -317,4 +322,39 @@ module.exports = function(s,config){ } return trigger } + s.isAtleastOneMatrixInRegion = function(regions,matrices,callback){ + var regionPolys = [] + var matrixPoints = [] + regions.forEach(function(region,n){ + var polyPoints = [] + region.points.forEach(function(point){ + polyPoints.push(new V(parseInt(point[0]),parseInt(point[1]))) + }) + regionPolys[n] = new P(new V(0,0), polyPoints) + }) + var collisions = [] + var foundInRegion = false + matrices.forEach(function(matrix){ + var matrixPoints = [ + new V(matrix.x,matrix.y), + new V(matrix.width,matrix.y), + new V(matrix.width,matrix.height), + new V(matrix.x,matrix.height) + ] + var matrixPoly = new P(new V(0,0), matrixPoints) + regionPolys.forEach(function(region,n){ + var response = new SAT.Response() + var collided = SAT.testPolygonPolygon(matrixPoly, region, response) + if(collided === true){ + collisions.push({ + matrix: matrix, + region: regions[n] + }) + foundInRegion = true + } + }) + }) + if(callback)callback(foundInRegion,collisions) + return foundInRegion + } } diff --git a/libs/events.js b/libs/events.js index a8a2db97..9c2ad807 100644 --- a/libs/events.js +++ b/libs/events.js @@ -4,6 +4,25 @@ var exec = require('child_process').exec; var spawn = require('child_process').spawn; var request = require('request'); module.exports = function(s,config,lang){ + var addEventDetailsToString = function(eventData,string,addOps){ + //d = event data + if(!addOps)addOps = {} + var newString = string + '' + var d = Object.assign(eventData,addOps) + var detailString = s.stringJSON(d.details) + newString + .replace(/{{TIME}}/g,d.currentTimestamp) + .replace(/{{REGION_NAME}}/g,d.details.name) + .replace(/{{SNAP_PATH}}/g,s.dir.streams+'/'+d.ke+'/'+d.id+'/s.jpg') + .replace(/{{MONITOR_ID}}/g,d.id) + .replace(/{{GROUP_KEY}}/g,d.ke) + .replace(/{{DETAILS}}/g,detailString) + if(d.details.confidence){ + newString = newString + .replace(/{{CONFIDENCE}}/g,d.details.confidence) + } + return newString + } s.filterEvents = function(x,d){ switch(x){ case'archive': @@ -45,6 +64,7 @@ module.exports = function(s,config,lang){ } d.mon=s.group[d.ke].mon_conf[d.id]; var currentConfig = s.group[d.ke].mon[d.id].details + var hasMatrices = (d.details.matrices && d.details.matrices.length > 0) //read filters if( currentConfig.use_detector_filters === '1' && @@ -160,7 +180,7 @@ module.exports = function(s,config,lang){ }) if(d.details.matrices && d.details.matrices.length === 0 || filter.halt === true){ return - }else if(d.details.matrices && d.details.matrices.length > 0){ + }else if(hasMatrices){ var reviewedMatrix = [] d.details.matrices.forEach(function(matrix){ if(matrix)reviewedMatrix.push(matrix) @@ -193,6 +213,16 @@ module.exports = function(s,config,lang){ return } } + // check if object should be in region + if(hasMatrices && currentConfig.detector_obj_region === '1'){ + var regions = s.group[d.ke].mon[d.id].parsedObjects.cords + var isMatrixInRegions = s.isAtleastOneMatrixInRegion(regions,d.details.matrices) + if(isMatrixInRegions){ + s.debugLog('Matrix in region!') + }else{ + return + } + } // check modified indifference if(filter.indifference !== false && d.details.confidence < parseFloat(filter.indifference)){ // fails indifference check for modified indifference @@ -210,12 +240,12 @@ module.exports = function(s,config,lang){ }) }else{ //save this detection result in SQL, only coords. not image. - if(filter.save && currentConfig.detector_save==='1'){ + if(filter.save && currentConfig.detector_save === '1'){ s.sqlQuery('INSERT INTO Events (ke,mid,details) VALUES (?,?,?)',[d.ke,d.id,detailString]) } if(currentConfig.detector_notrigger === '1'){ var detector_notrigger_timeout - if(!currentConfig.detector_notrigger_timeout||currentConfig.detector_notrigger_timeout===''){ + if(!currentConfig.detector_notrigger_timeout||currentConfig.detector_notrigger_timeout === ''){ detector_notrigger_timeout = 10 } detector_notrigger_timeout = parseFloat(currentConfig.detector_notrigger_timeout)*1000*60; @@ -270,17 +300,7 @@ module.exports = function(s,config,lang){ }) if(filter.webhook && currentConfig.detector_webhook === '1'){ - var detector_webhook_url = currentConfig.detector_webhook_url - .replace(/{{TIME}}/g,d.currentTimestamp) - .replace(/{{REGION_NAME}}/g,d.details.name) - .replace(/{{SNAP_PATH}}/g,s.dir.streams+'/'+d.ke+'/'+d.id+'/s.jpg') - .replace(/{{MONITOR_ID}}/g,d.id) - .replace(/{{GROUP_KEY}}/g,d.ke) - .replace(/{{DETAILS}}/g,detailString) - if(d.details.confidence){ - detector_webhook_url = detector_webhook_url - .replace(/{{CONFIDENCE}}/g,d.details.confidence) - } + var detector_webhook_url = addEventDetailsToString(d,currentConfig.detector_webhook_url) request({url:detector_webhook_url,method:'GET',encoding:null},function(err,data){ if(err){ s.userLog(d,{type:lang["Event Webhook Error"],msg:{error:err,data:data}}) @@ -289,28 +309,8 @@ module.exports = function(s,config,lang){ } if(filter.command && currentConfig.detector_command_enable === '1' && !s.group[d.ke].mon[d.id].detector_command){ - var detector_command_timeout - if(!currentConfig.detector_command_timeout||currentConfig.detector_command_timeout===''){ - detector_command_timeout = 1000*60*10; - }else{ - detector_command_timeout = parseFloat(currentConfig.detector_command_timeout)*1000*60; - } - s.group[d.ke].mon[d.id].detector_command=setTimeout(function(){ - clearTimeout(s.group[d.ke].mon[d.id].detector_command); - delete(s.group[d.ke].mon[d.id].detector_command); - - },detector_command_timeout); - var detector_command = currentConfig.detector_command - .replace(/{{TIME}}/g,d.currentTimestamp) - .replace(/{{REGION_NAME}}/g,d.details.name) - .replace(/{{SNAP_PATH}}/g,s.dir.streams+'/'+d.ke+'/'+d.id+'/s.jpg') - .replace(/{{MONITOR_ID}}/g,d.id) - .replace(/{{GROUP_KEY}}/g,d.ke) - .replace(/{{DETAILS}}/g,detailString) - if(d.details.confidence){ - detector_command = detector_command - .replace(/{{CONFIDENCE}}/g,d.details.confidence) - } + s.createTimeout(s.group[d.ke].mon[d.id].detector_command,currentConfig.detector_command_timeout,10) + var detector_command = addEventDetailsToString(d,currentConfig.detector_command) exec(detector_command,{detached: true}) } } diff --git a/libs/ffmpeg.js b/libs/ffmpeg.js index 28f7a385..6b01927c 100644 --- a/libs/ffmpeg.js +++ b/libs/ffmpeg.js @@ -382,7 +382,16 @@ module.exports = function(s,config,onFinish){ //x = temporary values //check if CUDA is enabled e.isStreamer = (e.type === 'dashcam'|| e.type === 'socket') - if(e.details.accelerator === '1' && e.details.hwaccel === 'cuvid' && e.details.hwaccel_vcodec === ('h264_cuvid' || 'hevc_cuvid' || 'mjpeg_cuvid' || 'mpeg4_cuvid')){ + e.coProcessor = false + if( + e.details.use_coprocessor === '1' && + e.details.accelerator === '1' && + e.isStreamer === false && + (!e.details.input_maps || e.details.input_maps.length === 0) && + (e.details.snap === '1' || e.details.stream_type === 'mjpeg' || e.details.stream_type === 'b64' || e.details.detector === '1') + ){ + e.coProcessor = true + }else if(e.details.accelerator === '1' && e.details.hwaccel === 'cuvid' && e.details.hwaccel_vcodec === ('h264_cuvid' || 'hevc_cuvid' || 'mjpeg_cuvid' || 'mpeg4_cuvid')){ e.cudaEnabled = true } // @@ -563,9 +572,11 @@ module.exports = function(s,config,onFinish){ x.pipe+=x.preset_stream+x.stream_acodec+x.stream_vcodec+' -f hls'+x.cust_stream+' -hls_time '+x.hls_time+' -hls_list_size '+x.hls_list_size+' -start_number 0 -hls_allow_cache 0 -hls_flags +delete_segments+omit_endlist "'+e.sdir+'s.m3u8"'; break; case'mjpeg': + if(e.coProcessor === false){ if(e.details.stream_quality && e.details.stream_quality !== '')x.cust_stream+=' -q:v '+e.details.stream_quality; if(x.dimensions && x.cust_stream.indexOf('-s ')===-1){x.cust_stream+=' -s '+x.dimensions} x.pipe+=' -an -c:v mjpeg -f mpjpeg -boundary_tag shinobi'+x.cust_stream+x.stream_video_filters+' pipe:1'; + } break; case'h265': x.cust_stream+=' -movflags +frag_keyframe+empty_moov+default_base_moof -metadata title="Shinobi H.265 Stream" -reset_timestamps 1' @@ -578,9 +589,11 @@ module.exports = function(s,config,onFinish){ x.pipe+=' -f hevc'+x.stream_acodec+x.stream_vcodec+x.cust_stream+' pipe:1'; break; case'b64':case'':case undefined:case null://base64 - if(e.details.stream_quality && e.details.stream_quality !== '')x.cust_stream+=' -q:v '+e.details.stream_quality; - if(x.dimensions && x.cust_stream.indexOf('-s ')===-1){x.cust_stream+=' -s '+x.dimensions} - x.pipe+=' -an -c:v mjpeg -f image2pipe'+x.cust_stream+x.stream_video_filters+' pipe:1'; + if(e.coProcessor === false){ + if(e.details.stream_quality && e.details.stream_quality !== '')x.cust_stream+=' -q:v '+e.details.stream_quality; + if(x.dimensions && x.cust_stream.indexOf('-s ')===-1){x.cust_stream+=' -s '+x.dimensions} + x.pipe+=' -an -c:v mjpeg -f image2pipe'+x.cust_stream+x.stream_video_filters+' pipe:1'; + } break; default: x.pipe='' @@ -588,11 +601,12 @@ module.exports = function(s,config,onFinish){ } if(e.details.stream_channels){ e.details.stream_channels.forEach(function(v,n){ + // if(v.stream_type === 'mjpeg')e.coProcessor = true; x.pipe += s.createStreamChannel(e,n+config.pipeAddition,v) }) } //api - snapshot bin/ cgi.bin (JPEG Mode) - if(e.details.snap === '1'){ + if(e.details.snap === '1' && e.coProcessor === false){ if(e.details.input_map_choices&&e.details.input_map_choices.snap){ //add input feed map x.pipe += s.createFFmpegMap(e,e.details.input_map_choices.snap) @@ -735,12 +749,23 @@ module.exports = function(s,config,onFinish){ x.record_string+=x.vcodec+x.record_fps+x.record_video_filters+x.record_dimensions+x.segment; } } + ffmpeg.buildAudioDetector = function(e,x){ + if(e.details.detector_audio === '1'){ + if(e.details.input_map_choices&&e.details.input_map_choices.detector_audio){ + //add input feed map + x.pipe += s.createFFmpegMap(e,e.details.input_map_choices.detector_audio) + }else{ + x.pipe += ' -map 0:a' + } + x.pipe += ' -acodec pcm_s16le -f s16le -ac 1 -ar 16000 pipe:6' + } + } ffmpeg.buildMainDetector = function(e,x){ //e = monitor object //x = temporary values x.cust_detect = ' ' //detector - plugins, motion - if(e.details.detector === '1' && e.details.detector_send_frames === '1'){ + if(e.details.detector === '1' && e.details.detector_send_frames === '1' && e.coProcessor === false){ if(e.details.input_map_choices&&e.details.input_map_choices.detector){ //add input feed map x.pipe += s.createFFmpegMap(e,e.details.input_map_choices.detector) @@ -761,14 +786,15 @@ module.exports = function(s,config,onFinish){ if(e.details.detector_use_detect_object === '1'){ //for object detection x.pipe += s.createFFmpegMap(e,e.details.input_map_choices.detector) - x.pipe += ' -f singlejpeg '+x.detector_vf+x.cust_detect+x.dratio+' pipe:4'; + x.pipe += ' -an -f singlejpeg '+x.detector_vf+x.cust_detect+x.dratio+' pipe:4'; } }else{ - x.pipe+=' -f image2pipe '+x.detector_vf+x.cust_detect+x.dratio+' pipe:3'; + x.pipe+=' -an -f image2pipe '+x.detector_vf+x.cust_detect+x.dratio+' pipe:3'; } } //Traditional Recording Buffer if(e.details.detector=='1'&&e.details.detector_trigger=='1'&&e.details.detector_record_method==='sip'){ + if(e.details.cust_sip_record && e.details.cust_sip_record !== ''){x.pipe += ' ' + e.details.cust_sip_record} if(e.details.input_map_choices&&e.details.input_map_choices.detector_sip_buffer){ //add input feed map x.pipe += s.createFFmpegMap(e,e.details.input_map_choices.detector_sip_buffer) @@ -836,6 +862,12 @@ module.exports = function(s,config,onFinish){ x.pipe+=x.detector_buffer_fps+x.detector_buffer_acodec+' -c:v '+e.details.detector_buffer_vcodec+' -f hls -tune '+e.details.detector_buffer_tune+' -g '+e.details.detector_buffer_g+' -hls_time '+e.details.detector_buffer_hls_time+' -hls_list_size '+e.details.detector_buffer_hls_list_size+' -start_number '+e.details.detector_buffer_start_number+' -live_start_index '+e.details.detector_buffer_live_start_index+' -hls_allow_cache 0 -hls_flags +delete_segments+omit_endlist "'+e.sdir+'detectorStream.m3u8"' } } + ffmpeg.buildCoProcessorFeed = function(e,x){ + if(e.coProcessor === true){ + // the coProcessor ffmpeg consumes this HLS stream (no audio, frames only) + x.pipe += ' -q:v 1 -an -c:v copy -f hls -tune zerolatency -g 1 -hls_time 2 -hls_list_size 3 -start_number 0 -live_start_index 3 -hls_allow_cache 0 -hls_flags +delete_segments+omit_endlist "'+e.sdir+'coProcessor.m3u8"' + } + } ffmpeg.assembleMainPieces = function(e,x){ //create executeable FFMPEG command x.ffmpegCommandString = x.loglevel+x.input_fps; @@ -852,6 +884,9 @@ module.exports = function(s,config,onFinish){ case'mjpeg': x.ffmpegCommandString += ' -reconnect 1 -f mjpeg'+x.cust_input+x.hwaccel+' -i "'+e.url+'"'; break; + // case'rtmp': + // x.ffmpegCommandString += x.cust_input+x.hwaccel+' -i -'; + // break; case'h264':case'hls':case'mp4': x.ffmpegCommandString += x.cust_input+x.hwaccel+' -i "'+e.url+'"'; break; @@ -886,7 +921,9 @@ module.exports = function(s,config,onFinish){ ffmpeg.buildMainInput(e,x) ffmpeg.buildMainStream(e,x) ffmpeg.buildMainRecording(e,x) + ffmpeg.buildAudioDetector(e,x) ffmpeg.buildMainDetector(e,x) + ffmpeg.buildCoProcessorFeed(e,x) s.onFfmpegCameraStringCreationExtensions.forEach(function(extender){ extender(e,x) }) diff --git a/libs/ffmpegCoProcessor.js b/libs/ffmpegCoProcessor.js new file mode 100644 index 00000000..055e0539 --- /dev/null +++ b/libs/ffmpegCoProcessor.js @@ -0,0 +1,234 @@ +var spawn = require('child_process').spawn; +module.exports = function(s,config,lang,ffmpeg){ + ffmpeg.buildCoProcessorInput = function(e,x){ + if(e.details.userLoglevel&&e.details.userLoglevel!==''){x.loglevel='-loglevel '+e.details.userLoglevel;}else{x.loglevel='-loglevel error'} + x.input = x.loglevel+' -re -i '+e.sdir+'coProcessor.m3u8' + } + ffmpeg.buildCoProcessorStream = function(e,x){ + x.stream_video_filters = [] + //stream - timestamp + if(e.details.stream_timestamp&&e.details.stream_timestamp=="1"&&e.details.vcodec!=='copy'){ + //font + if(e.details.stream_timestamp_font&&e.details.stream_timestamp_font!==''){x.stream_timestamp_font=e.details.stream_timestamp_font}else{x.stream_timestamp_font='/usr/share/fonts/truetype/freefont/FreeSans.ttf'} + //position x + if(e.details.stream_timestamp_x&&e.details.stream_timestamp_x!==''){x.stream_timestamp_x=e.details.stream_timestamp_x}else{x.stream_timestamp_x='(w-tw)/2'} + //position y + if(e.details.stream_timestamp_y&&e.details.stream_timestamp_y!==''){x.stream_timestamp_y=e.details.stream_timestamp_y}else{x.stream_timestamp_y='0'} + //text color + if(e.details.stream_timestamp_color&&e.details.stream_timestamp_color!==''){x.stream_timestamp_color=e.details.stream_timestamp_color}else{x.stream_timestamp_color='white'} + //box color + if(e.details.stream_timestamp_box_color&&e.details.stream_timestamp_box_color!==''){x.stream_timestamp_box_color=e.details.stream_timestamp_box_color}else{x.stream_timestamp_box_color='0x00000000@1'} + //text size + if(e.details.stream_timestamp_font_size&&e.details.stream_timestamp_font_size!==''){x.stream_timestamp_font_size=e.details.stream_timestamp_font_size}else{x.stream_timestamp_font_size='10'} + + x.stream_video_filters.push('drawtext=fontfile='+x.stream_timestamp_font+':text=\'%{localtime}\':x='+x.stream_timestamp_x+':y='+x.stream_timestamp_y+':fontcolor='+x.stream_timestamp_color+':box=1:boxcolor='+x.stream_timestamp_box_color+':fontsize='+x.stream_timestamp_font_size); + } + //stream - watermark for -vf + if(e.details.stream_watermark&&e.details.stream_watermark=="1"&&e.details.stream_watermark_location&&e.details.stream_watermark_location!==''){ + switch(e.details.stream_watermark_position){ + case'tl'://top left + x.stream_watermark_position='10:10' + break; + case'tr'://top right + x.stream_watermark_position='main_w-overlay_w-10:10' + break; + case'bl'://bottom left + x.stream_watermark_position='10:main_h-overlay_h-10' + break; + default://bottom right + x.stream_watermark_position='(main_w-overlay_w-10)/2:(main_h-overlay_h-10)/2' + break; + } + x.stream_video_filters.push('movie='+e.details.stream_watermark_location+'[watermark],[in][watermark]overlay='+x.stream_watermark_position+'[out]'); + } + //stream - rotation + if(e.details.rotate_stream&&e.details.rotate_stream!==""&&e.details.rotate_stream!=="no"&&e.details.stream_vcodec!=='copy'){ + x.stream_video_filters.push('transpose='+e.details.rotate_stream); + } + if(e.details.svf&&e.details.svf!==''){ + x.stream_video_filters.push(e.details.svf) + } + if(x.stream_video_filters.length>0){ + x.stream_video_filters=' -vf '+x.stream_video_filters.join(',') + }else{ + x.stream_video_filters='' + } + if(e.details.cust_stream&&e.details.cust_stream!==''){x.cust_stream=' '+e.details.cust_stream}else{x.cust_stream=''} + if(e.details.stream_fps&&e.details.stream_fps!==''){x.stream_fps=' -r '+e.details.stream_fps}else{x.stream_fps=''} + if(e.details.stream_vcodec !== 'copy' || e.details.stream_type === 'mjpeg' || e.details.stream_type === 'b64'){ + x.cust_stream += x.stream_fps + } + switch(e.details.stream_type){ + case'mjpeg': + if(e.details.stream_quality && e.details.stream_quality !== '')x.cust_stream+=' -q:v '+e.details.stream_quality; + if(x.dimensions && x.cust_stream.indexOf('-s ')===-1){x.cust_stream+=' -s '+x.dimensions} + x.pipe += ' -an -c:v mjpeg -f mpjpeg -boundary_tag shinobi'+x.cust_stream+x.stream_video_filters+' pipe:1'; + break; + case'b64':case'':case undefined:case null://base64 + if(e.details.stream_quality && e.details.stream_quality !== '')x.cust_stream+=' -q:v '+e.details.stream_quality; + if(x.dimensions && x.cust_stream.indexOf('-s ')===-1){x.cust_stream+=' -s '+x.dimensions} + x.pipe += ' -an -c:v mjpeg -f image2pipe'+x.cust_stream+x.stream_video_filters+' pipe:1'; + break; + } + } + ffmpeg.buildCoProcessorDetector = function(e,x){ + //detector frames + x.cust_detect=' ' + if(e.details.detector === '1'){ + if(e.details.detector_fps && e.details.detector_fps !== ''){ + x.detector_fps = e.details.detector_fps + }else{ + x.detector_fps = '2' + } + if(e.details.detector_scale_x && e.details.detector_scale_x !== '' && e.details.detector_scale_y && e.details.detector_scale_y !== ''){ + x.dratio=' -s '+e.details.detector_scale_x+'x'+e.details.detector_scale_y + }else{ + x.dratio=' -s 320x240' + } + + if(e.details.cust_detect&&e.details.cust_detect!==''){x.cust_detect+=e.details.cust_detect;} + if(e.details.detector_pam==='1'){ + x.pipe += ' -an -c:v pam -pix_fmt gray -f image2pipe -r '+x.detector_fps+x.cust_detect+x.dratio+' pipe:3' + if(e.details.detector_use_detect_object === '1'){ + if(e.details.detector_use_motion === '1'){ + if(e.details.detector_scale_x_object && e.details.detector_scale_x_object !== '' && e.details.detector_scale_y_object && e.details.detector_scale_y_object !== ''){ + x.dratio=' -s '+e.details.detector_scale_x_object+'x'+e.details.detector_scale_y_object + } + if(e.details.detector_fps_object && e.details.detector_fps_object !== ''){ + x.detector_fps = e.details.detector_fps_object + } + } + //for object detection + x.pipe += s.createFFmpegMap(e,e.details.input_map_choices.detector) + x.pipe += ' -f singlejpeg -vf fps='+x.detector_fps+x.cust_detect+x.dratio+' pipe:4'; + } + }else{ + x.pipe+=' -f singlejpeg -vf fps='+x.detector_fps+x.cust_detect+x.dratio+' pipe:3'; + } + } + } + ffmpeg.buildCoProcessorJpegApi = function(e,x){ + //snapshot frames + if(e.details.snap === '1'){ + if(!e.details.snap_fps || e.details.snap_fps === ''){e.details.snap_fps = 1} + if(e.details.snap_vf && e.details.snap_vf !== ''){x.snap_vf=' -vf '+e.details.snap_vf}else{x.snap_vf=''} + if(e.details.snap_scale_x && e.details.snap_scale_x !== '' && e.details.snap_scale_y && e.details.snap_scale_y !== ''){x.snap_ratio = ' -s '+e.details.snap_scale_x+'x'+e.details.snap_scale_y}else{x.snap_ratio=''} + if(e.details.cust_snap && e.details.cust_snap !== ''){x.cust_snap = ' '+e.details.cust_snap}else{x.cust_snap=''} + x.pipe += ' -update 1 -r '+e.details.snap_fps+x.cust_snap+x.snap_ratio+x.snap_vf+' "'+e.sdir+'s.jpg" -y'; + } + } + ffmpeg.buildCoProcessorPipeArray = function(e,x){ + x.stdioPipes = []; + var times = config.pipeAddition; + if(e.details.stream_channels){ + times+=e.details.stream_channels.length + } + for(var i=0; i < times; i++){ + x.stdioPipes.push('pipe') + } + } + s.ffmpegCoProcessor = function(e){ + if(e.coProcessor === false)return; + var x = {} + x.pipe = '' + ffmpeg.buildCoProcessorInput(e,x) + ffmpeg.buildCoProcessorStream(e,x) + ffmpeg.buildCoProcessorDetector(e,x) + ffmpeg.buildCoProcessorJpegApi(e,x) + ffmpeg.buildCoProcessorPipeArray(e,x) + var commandString = x.input + x.pipe + if(commandString === x.input){ + return false + } + s.group[e.ke].mon[e.mid].coProcessorCmd = commandString + return spawn(config.ffmpegDir,s.splitForFFPMEG((commandString).replace(/\s+/g,' ').trim()),{detached: true,stdio:x.stdioPipes}) + } + s.coSpawnLauncher = function(e){ + if(s.group[e.ke].mon[e.id].isStarted === true && e.coProcessor === true){ + s.coSpawnClose(e) + s.group[e.ke].mon[e.id].coSpawnProcessor = s.ffmpegCoProcessor(e) + if(s.group[e.ke].mon[e.id].coSpawnProcessor === false){ + return + } + s.userLog(e,{type:lang['coProcessor Started'],msg:{msg:lang.coProcessorTextStarted,cmd:s.group[e.ke].mon[e.id].coProcessorCmd}}); + s.group[e.ke].mon[e.id].coSpawnProcessorExit = function(){ + s.userLog(e,{type:lang['coProcess Unexpected Exit'],msg:{msg:lang['coProcess Crashed for Monitor']+' : '+e.id,cmd:s.group[e.ke].mon[e.id].coProcessorCmd}}); + setTimeout(function(){ + s.coSpawnLauncher(e) + },2000) + } + s.group[e.ke].mon[e.id].coSpawnProcessor.on('end',s.group[e.ke].mon[e.id].coSpawnProcessorExit) + s.group[e.ke].mon[e.id].coSpawnProcessor.on('exit',s.group[e.ke].mon[e.id].coSpawnProcessorExit) + var checkLog = function(d,x){return d.indexOf(x)>-1;} + s.group[e.ke].mon[e.id].coSpawnProcessor.stderr.on('data',function(d){ + d=d.toString(); + switch(true){ + case checkLog(d,'deprecated pixel format used'): + case checkLog(d,'[hls @'): + case checkLog(d,'Past duration'): + case checkLog(d,'Last message repeated'): + case checkLog(d,'pkt->duration = 0'): + case checkLog(d,'Non-monotonous DTS'): + case checkLog(d,'NULL @'): + return + break; + } + s.userLog(e,{type:lang.coProcessor,msg:d}); + }) + if(e.frame_to_stream){ + s.group[e.ke].mon[e.id].coSpawnProcessor.stdout.on('data',e.frame_to_stream) + } + if(e.details.detector === '1'){ + s.ocvTx({f:'init_monitor',id:e.id,ke:e.ke}) + //frames from motion detect + if(e.details.detector_pam === '1'){ + s.createPamDiffEngine(e) + s.group[e.ke].mon[e.id].coSpawnProcessor.stdio[3].pipe(s.group[e.ke].mon[e.id].p2p).pipe(s.group[e.ke].mon[e.id].pamDiff) + if(e.details.detector_use_detect_object === '1'){ + s.group[e.ke].mon[e.id].coSpawnProcessor.stdio[4].on('data',function(d){ + s.group[e.ke].mon[e.id].lastJpegDetectorFrame = d + }) + } + }else if(s.ocv){ + if(s.ocv.connectionType !== 'ram'){ + s.group[e.ke].mon[e.id].coSpawnProcessor.stdio[3].on('data',function(d){ + s.ocvTx({f:'frame',mon:s.group[e.ke].mon_conf[e.id].details,ke:e.ke,id:e.id,time:s.formattedTime(),frame:d}); + }) + }else{ + s.group[e.ke].mon[e.id].coSpawnProcessor.stdio[3].on('data',function(d){ + if(!s.group[e.ke].mon[e.id].detectorFrameSaveBuffer){ + s.group[e.ke].mon[e.id].detectorFrameSaveBuffer=[d] + }else{ + s.group[e.ke].mon[e.id].detectorFrameSaveBuffer.push(d) + } + if(d[d.length-2] === 0xFF && d[d.length-1] === 0xD9){ + var buffer = Buffer.concat(s.group[e.ke].mon[e.id].detectorFrameSaveBuffer); + var frameLocation = s.dir.streams + e.ke + '/' + e.id + '/' + s.gid(5) + '.jpg' + if(s.ocv){ + fs.writeFile(frameLocation,buffer,function(err){ + if(err){ + s.debugLog(err) + }else{ + s.ocvTx({f:'frameFromRam',mon:s.group[e.ke].mon_conf[e.id].details,ke:e.ke,id:e.id,time:s.formattedTime(),frameLocation:frameLocation}) + } + }) + } + s.group[e.ke].mon[e.id].detectorFrameSaveBuffer = null; + } + }) + } + } + } + } + } + s.coSpawnClose = function(e){ + if(s.group[e.ke].mon[e.id].coSpawnProcessor){ + s.group[e.ke].mon[e.id].coSpawnProcessor.removeListener('end',s.group[e.ke].mon[e.id].coSpawnProcessorExit); + s.group[e.ke].mon[e.id].coSpawnProcessor.removeListener('exit',s.group[e.ke].mon[e.id].coSpawnProcessorExit); + s.group[e.ke].mon[e.id].coSpawnProcessor.stdin.pause() + s.group[e.ke].mon[e.id].coSpawnProcessor.kill() + delete(s.group[e.ke].mon[e.id].coSpawnProcessor) + s.userLog(e,{type:lang['coProcessor Stopped'],msg:{msg:lang.coProcessorTextStopped+' : '+e.id}}); + } + } +} diff --git a/libs/monitor.js b/libs/monitor.js index cb9ed240..1743e162 100644 --- a/libs/monitor.js +++ b/libs/monitor.js @@ -6,6 +6,7 @@ var Mp4Frag = require('mp4frag'); var onvif = require('node-onvif'); var request = require('request'); var connectionTester = require('connection-tester') +var SoundDetection = require('shinobi-sound-detection') var URL = require('url') module.exports = function(s,config,lang){ s.initiateMonitorObject = function(e){ @@ -21,6 +22,7 @@ module.exports = function(s,config,lang){ if(!s.group[e.ke].mon[e.mid].eventBasedRecording){s.group[e.ke].mon[e.mid].eventBasedRecording={}}; if(!s.group[e.ke].mon[e.mid].watch){s.group[e.ke].mon[e.mid].watch={}}; if(!s.group[e.ke].mon[e.mid].fixingVideos){s.group[e.ke].mon[e.mid].fixingVideos={}}; + if(!s.group[e.ke].mon[e.mid].parsedObjects){s.group[e.ke].mon[e.mid].parsedObjects={}}; if(!s.group[e.ke].mon[e.mid].isStarted){s.group[e.ke].mon[e.mid].isStarted = false}; if(s.group[e.ke].mon[e.mid].delete){clearTimeout(s.group[e.ke].mon[e.mid].delete)} if(!s.group[e.ke].mon_conf){s.group[e.ke].mon_conf={}} @@ -103,7 +105,7 @@ module.exports = function(s,config,lang){ var snapBuffer = [] var snapProcess = spawn(config.ffmpegDir,('-loglevel quiet -re -i '+url+options+' -frames:v 1 -f image2pipe pipe:1').split(' '),{detached: true}) snapProcess.stdout.on('data',function(data){ - snapBuffer.push(data) + if(snapBuffer)snapBuffer.push(data) }) snapProcess.stderr.on('data',function(data){ console.log(data.toString()) @@ -266,6 +268,7 @@ module.exports = function(s,config,lang){ if(s.group[e.ke].mon[e.id].childNode){ s.cx({f:'kill',d:s.cleanMonitorObject(e)},s.group[e.ke].mon[e.id].childNodeId) }else{ + s.coSpawnClose(e) if(!x||x===1){return}; p=x.pid; if(s.group[e.ke].mon_conf[e.id].type===('dashcam'||'socket'||'jpeg'||'pipe')){ @@ -282,12 +285,20 @@ module.exports = function(s,config,lang){ s.cameraCheckObjectsInDetails = function(e){ //parse Objects (['detector_cascades','cords','detector_filters','input_map_choices']).forEach(function(v){ - if(e.details&&e.details[v]&&(e.details[v] instanceof Object)===false){ + if(e.details && e.details[v]){ try{ - if(e.details[v] === '') e.details[v] = '{}' - e.details[v]=JSON.parse(e.details[v]); - if(!e.details[v])e.details[v]={}; - s.group[e.ke].mon[e.id].details = e.details; + if(!e.details[v] || e.details[v] === '')e.details[v] = '{}' + e.details[v] = s.parseJSON(e.details[v]) + if(!e.details[v])e.details[v] = {} + s.group[e.ke].mon[e.id].details = e.details + switch(v){ + case'cords': + s.group[e.ke].mon[e.id].parsedObjects[v] = Object.values(s.parseJSON(e.details[v])) + break; + default: + s.group[e.ke].mon[e.id].parsedObjects[v] = s.parseJSON(e.details[v]) + break; + } }catch(err){ } @@ -776,7 +787,47 @@ module.exports = function(s,config,lang){ if(e.type==='jpeg'){ s.cameraPullJpegStream(e) } - if(e.details.detector === '1'){ + if(e.details.detector_audio === '1'){ + var triggerLevel + var triggerLevelMax + if(e.details.detector_audio_min_db && e.details.detector_audio_min_db !== ''){ + triggerLevel = parseInt(e.details.detector_audio_min_db) + }else{ + triggerLevel = 5 + } + if(e.details.detector_audio_max_db && e.details.detector_audio_max_db !== ''){ + triggerLevelMax = parseInt(e.details.detector_audio_max_db) + } + var audioDetector = new SoundDetection({ + format: { + bitDepth: 16, + numberOfChannels: 1, + signed: true + }, + triggerLevel: triggerLevel, + triggerLevelMax: triggerLevelMax + },function(dB) { + s.triggerEvent({ + f:'trigger', + id:e.id, + ke:e.ke, + name: 'db', + details:{ + plug:'audio', + name:'db', + reason:'soundChange', + confidence:dB + }, + plates:[], + imgHeight:e.details.detector_scale_y, + imgWidth:e.details.detector_scale_x + }) + }) + s.group[e.ke].mon[e.id].audioDetector = audioDetector + audioDetector.start() + s.group[e.ke].mon[e.id].spawn.stdio[6].pipe(audioDetector.streamDecoder) + } + if(e.details.detector === '1' && e.coProcessor === false){ s.ocvTx({f:'init_monitor',id:e.id,ke:e.ke}) //frames from motion detect if(e.details.detector_pam === '1'){ @@ -865,7 +916,11 @@ module.exports = function(s,config,lang){ break; } if(e.frameToStream){ - s.group[e.ke].mon[e.id].spawn.stdout.on('data',e.frameToStream) + if(e.coProcessor === true && e.details.stream_type === ('b64'||'mjpeg')){ + + }else{ + s.group[e.ke].mon[e.id].spawn.stdout.on('data',e.frameToStream) + } } if(e.details.stream_channels && e.details.stream_channels !== ''){ var createStreamEmitter = function(channel,number){ @@ -911,6 +966,12 @@ module.exports = function(s,config,lang){ s.group[e.ke].mon[e.id].spawn.stderr.on('data',function(d){ d=d.toString(); switch(true){ + // case checkLog(d,'No space left on device'): + // + // break; + case checkLog(d,'error while decoding'): + s.userLog(e,{type:lang['Error While Decoding'],msg:lang.ErrorWhileDecodingText}); + break; case checkLog(d,'[hls @'): case checkLog(d,'Past duration'): case checkLog(d,'Last message repeated'): @@ -938,6 +999,7 @@ module.exports = function(s,config,lang){ case checkLog(d,'mjpeg_decode_dc'): case checkLog(d,'bad vlc'): case checkLog(d,'error dc'): + case checkLog(d,'No route to host'): s.launchMonitorProcesses(e) break; case /T[0-9][0-9]-[0-9][0-9]-[0-9][0-9]./.test(d): @@ -999,7 +1061,7 @@ module.exports = function(s,config,lang){ e.detector_notrigger_timeout = parseFloat(e.details.detector_notrigger_timeout)*1000*60; s.group[e.ke].mon[e.id].detector_notrigger_timeout_function = function(){ s.onDetectorNoTriggerTimeoutExtensions.forEach(function(extender){ - extender(r,e) + extender(e) }) } clearInterval(s.group[e.ke].mon[e.id].detector_notrigger_timeout) @@ -1012,7 +1074,11 @@ module.exports = function(s,config,lang){ if(s.group[e.ke].mon[e.id].isStarted === true){ fs.stat(e.sdir+'s.jpg',function(err,snap){ var notStreaming = function(){ - s.launchMonitorProcesses(e) + if(e.coProcessor === true){ + s.coSpawnLauncher(e) + }else{ + s.launchMonitorProcesses(e) + } s.userLog(e,{type:lang['Camera is not streaming'],msg:{msg:lang['Restarting Process']}}) s.orphanedVideoCheck(e,2,null,true) } @@ -1097,6 +1163,11 @@ module.exports = function(s,config,lang){ ){ s.cameraFilterFfmpegLog(e) } + if(e.coProcessor === true){ + setTimeout(function(){ + s.coSpawnLauncher(e) + },6000) + } s.onMonitorStartExtensions.forEach(function(extender){ extender(Object.assign(s.group[e.ke].mon_conf[e.id],{}),e) }) @@ -1435,8 +1506,8 @@ module.exports = function(s,config,lang){ s.group[e.ke].mon[e.mid].isRecording = false } //set up fatal error handler - if(e.details.fatal_max===''){ - e.details.fatal_max = 10 + if(e.details.fatal_max === ''){ + e.details.fatal_max = 0 }else{ e.details.fatal_max = parseFloat(e.details.fatal_max) } @@ -1454,4 +1525,48 @@ module.exports = function(s,config,lang){ } if(typeof cn === 'function'){setTimeout(function(){cn()},1000)} } + // + s.activateMonitorStates = function(groupKey,stateName,user,callback){ + var endData = { + ok: false + } + s.findPreset([groupKey,'monitorStates',stateName],function(notFound,preset){ + if(notFound === false){ + var sqlQuery = 'SELECT * FROM Monitors WHERE ke=? AND ' + var monitorQuery = [] + var sqlQueryValues = [groupKey] + var monitorPresets = {} + preset.details.monitors.forEach(function(monitor){ + monitorQuery.push('mid=?') + sqlQueryValues.push(monitor.mid) + monitorPresets[monitor.mid] = monitor + }) + sqlQuery += '('+monitorQuery.join(' OR ')+')' + s.sqlQuery(sqlQuery,sqlQueryValues,function(err,monitors){ + if(monitors && monitors[0]){ + monitors.forEach(function(monitor){ + s.checkDetails(monitor) + s.checkDetails(monitorPresets[monitor.mid]) + var monitorPreset = monitorPresets[monitor.mid] + monitorPreset.details = Object.assign(monitor.details,monitorPreset.details) + monitor = s.cleanMonitorObjectForDatabase(Object.assign(monitor,monitorPreset)) + monitor.details = JSON.stringify(monitor.details) + s.addOrEditMonitor(Object.assign(monitor,{}),function(err,endData){ + + },user) + }) + endData.ok = true + s.tx({f:'change_group_state',ke:groupKey,name:stateName},'GRP_'+groupKey) + callback(endData) + }else{ + endData.msg = user.lang['State Configuration has no monitors associated'] + callback(endData) + } + }) + }else{ + endData.msg = user.lang['State Configuration Not Found'] + callback(endData) + } + }) + } } diff --git a/libs/notification.js b/libs/notification.js index b90daafb..9d24930f 100644 --- a/libs/notification.js +++ b/libs/notification.js @@ -1,3 +1,4 @@ +var fs = require("fs") var Discord = require("discord.js") module.exports = function(s,config,lang){ //discord bot diff --git a/libs/scheduler.js b/libs/scheduler.js new file mode 100644 index 00000000..4cf08121 --- /dev/null +++ b/libs/scheduler.js @@ -0,0 +1,263 @@ +module.exports = function(s,config,lang,app,io){ + s.schedules = {} + //Get all Schedules + s.getAllSchedules = function(callback){ + s.schedules = {} + s.sqlQuery('SELECT * FROM Schedules',function(err,rows){ + rows.forEach(function(schedule){ + s.updateSchedule(schedule) + }) + if(callback)callback() + }) + } + //update schedule + s.updateSchedule = function(row){ + var schedule = Object.assign(row,{}) + if(!s.schedules[schedule.ke])s.schedules[schedule.ke] = {} + s.checkDetails(schedule) + if(!s.schedules[schedule.ke][schedule.name]){ + s.schedules[schedule.ke][schedule.name] = schedule + }else{ + s.schedules[schedule.ke][schedule.name] = Object.assign(s.schedules[schedule.ke][schedule.name],schedule) + } + } + //check time in schedule + s.checkTimeAgainstSchedule = function(start,end,callback){ + try{ + if( + start + ){ + var checkStartTime = new Date() + var startSplit = start.split(':') + var startHour = parseInt(startSplit[0]) + var startMin = parseInt(startSplit[1]) + checkStartTime.setHours(startHour) + checkStartTime.setMinutes(startMin) + if(end){ + var checkEndTime = new Date() + var endSplit = end.split(':') + var endHour = parseInt(endSplit[0]) + var endMin = parseInt(endSplit[1]) + checkEndTime.setHours(endHour) + checkEndTime.setMinutes(endMin) + } + var currentDate = new Date() + if( + ( + currentDate >= checkStartTime && + currentDate <= checkEndTime + ) || + currentDate >= checkStartTime && !end + ){ + callback() + }else{ + callback({ + currentDate : currentDate, + startTime : checkStartTime, + endTime : checkEndTime + }) + } + }else{ + callback() + } + }catch(err){ + console.log(err) + callback() + } + } + //check all Schedules + s.checkSchedules = function(v,callback){ + var groupKeys = Object.keys(s.schedules) + groupKeys.forEach(function(key){ + var scheduleNames = Object.keys(s.schedules[key]) + scheduleNames.forEach(function(name){ + var schedule = s.schedules[key][name] + if(!schedule.active && schedule.enabled === 1 && schedule.start && schedule.details.monitorStates){ + s.checkTimeAgainstSchedule(schedule.start,schedule.end,function(err){ + if(!err){ + schedule.active = true + var monitorStates = schedule.details.monitorStates + monitorStates.forEach(function(stateName){ + s.activateMonitorStates(key,stateName,{ + ke: key, + uid: 'System', + details: {}, + permissions: {}, + lang: lang + },function(endData){ + // console.log(endData) + }) + }) + }else{ + schedule.active = false + } + }) + } + }) + }) + } + // + s.findSchedule = function(groupKey,name,callback){ + //presetQueryVals = [ke, type, name] + s.sqlQuery("SELECT * FROM Schedules WHERE ke=? AND name=? LIMIT 1",[groupKey,name],function(err,schedules){ + var schedule + var notFound = false + if(schedules && schedules[0]){ + schedule = schedules[0] + s.checkDetails(schedule) + }else{ + notFound = true + } + callback(notFound,schedule) + }) + } + // + var onProcessReady = function(){ + s.getAllSchedules(function(){ + s.checkSchedules() + }) + setInterval(function(){ + s.checkSchedules() + },1000 * 60 * 5) + } + /** + * WebServerPath : API : Get Schedule + */ + app.all([ + config.webPaths.apiPrefix+':auth/schedule/:ke', + config.webPaths.adminApiPrefix+':auth/schedule/:ke', + config.webPaths.apiPrefix+':auth/schedule/:ke/:name', + config.webPaths.adminApiPrefix+':auth/schedule/:ke/:name', + config.webPaths.apiPrefix+':auth/schedules/:ke', + config.webPaths.adminApiPrefix+':auth/schedules/:ke', + config.webPaths.apiPrefix+':auth/schedules/:ke/:name', + config.webPaths.adminApiPrefix+':auth/schedules/:ke/:name', + ],function (req,res){ + s.auth(req.params,function(user){ + var endData = { + ok : false + } + if(user.details.sub){ + endData.msg = user.lang['Not Permitted'] + s.closeJsonResponse(res,endData) + return + } + var theQuery = "SELECT * FROM Schedules WHERE ke=?" + var theQueryValues = [req.params.ke] + if(req.params.name){ + theQuery += ' AND name=?' + theQueryValues.push(req.params.name) + } + s.sqlQuery(theQuery,theQueryValues,function(err,schedules){ + if(schedules && schedules[0]){ + endData.ok = true + schedules.forEach(function(schedule){ + s.checkDetails(schedule) + }) + endData.schedules = schedules + }else{ + endData.msg = user.lang['Not Found'] + } + s.closeJsonResponse(res,endData) + }) + }) + }) + /** + * WebServerPath : API : Update Schedule + */ + app.all([ + config.webPaths.apiPrefix+':auth/schedule/:ke/:name/:action', + config.webPaths.adminApiPrefix+':auth/schedule/:ke/:name/:action', + config.webPaths.apiPrefix+':auth/schedules/:ke/:name/:action', + config.webPaths.adminApiPrefix+':auth/schedules/:ke/:name/:action' + ],function (req,res){ + s.auth(req.params,function(user){ + var endData = { + ok : false + } + if(user.details.sub){ + endData.msg = user.lang['Not Permitted'] + s.closeJsonResponse(res,endData) + return + } + switch(req.params.action){ + case'insert':case'edit': + var form = s.getPostData(req) + s.checkDetails(form) + if(!form || !form.details){ + endData.msg = user.lang['Form Data Not Found'] + s.closeJsonResponse(res,endData) + return + } + form.enabled = parseInt(form.enabled) || 1; + s.findSchedule(req.params.ke,req.params.name,function(notFound,preset){ + if(notFound === true){ + endData.msg = lang["Inserted Schedule Configuration"] + var insertData = { + ke: req.params.ke, + name: req.params.name, + details: s.stringJSON(form.details), + start: form.start, + end: form.end, + enabled: form.enabled + } + s.sqlQuery('INSERT INTO Schedules ('+Object.keys(insertData).join(',')+') VALUES (?,?,?,?,?,?)',Object.values(insertData)) + s.tx({ + f: 'add_schedule', + insertData: insertData, + ke: req.params.ke, + name: req.params.name + },'GRP_'+req.params.ke) + }else{ + endData.msg = lang["Edited Schedule Configuration"] + var insertData = { + details: s.stringJSON(form.details), + start: form.start, + end: form.end, + enabled: form.enabled, + ke: req.params.ke, + name: req.params.name + } + s.sqlQuery('UPDATE Schedules SET details=?,start=?,end=?,enabled=? WHERE ke=? AND name=?',Object.values(insertData)) + s.tx({ + f: 'edit_schedule', + insertData: insertData, + ke: req.params.ke, + name: req.params.name + },'GRP_'+req.params.ke) + } + s.updateSchedule({ + ke: req.params.ke, + name: req.params.name, + details: s.stringJSON(form.details), + start: form.start, + end: form.end, + enabled: form.enabled + }) + endData.ok = true + s.closeJsonResponse(res,endData) + }) + break; + case'delete': + s.findSchedule(req.params.ke,req.params.name,function(notFound,schedule){ + if(notFound === true){ + endData.msg = user.lang['Schedule Configuration Not Found'] + s.closeJsonResponse(res,endData) + }else{ + s.sqlQuery('DELETE FROM Schedules WHERE ke=? AND name=?',[req.params.ke,req.params.name],function(err){ + if(!err){ + endData.msg = lang["Deleted Schedule Configuration"] + endData.ok = true + if(s.schedules[schedule.ke])delete(s.schedules[schedule.ke][schedule.name]) + } + s.closeJsonResponse(res,endData) + }) + } + }) + break; + } + }) + }) + //bind events + s.onProcessReady(onProcessReady) +} diff --git a/libs/socketio.js b/libs/socketio.js index da8ee32d..6256fa17 100644 --- a/libs/socketio.js +++ b/libs/socketio.js @@ -1365,7 +1365,7 @@ module.exports = function(s,config,lang,io){ if(cn.cron){ delete(s.cron); } - if(cn.ocv){ + if(cn.ocv && s.ocv){ s.tx({f:'detector_unplugged',plug:s.ocv.plug},'CPU') delete(s.ocv); delete(s.api[cn.id]) diff --git a/libs/sql.js b/libs/sql.js index 67fec78b..92d3e187 100644 --- a/libs/sql.js +++ b/libs/sql.js @@ -10,6 +10,12 @@ module.exports = function(s,config){ if(s.databaseOptions.client.indexOf('sqlite')>-1){ s.databaseOptions.client = 'sqlite3'; s.databaseOptions.useNullAsDefault = true; + try{ + require('sqlite3') + }catch(err){ + console.log('Installing SQlite3 Module...') + require('child_process').execSync('npm install sqlite3 --unsafe-perm') + } } if(s.databaseOptions.client === 'sqlite3' && s.databaseOptions.connection.filename === undefined){ s.databaseOptions.connection.filename = s.mainDirectory+"/shinobi.sqlite" @@ -99,6 +105,10 @@ module.exports = function(s,config){ },true) } },true) + //add Schedules table, will remove in future + s.sqlQuery("CREATE TABLE IF NOT EXISTS `Schedules` (`ke` varchar(50) DEFAULT NULL,`name` text,`details` text,`start` varchar(10) DEFAULT NULL,`end` varchar(10) DEFAULT NULL,`enabled` int(1) NOT NULL DEFAULT '1')" + mySQLtail + ';',[],function(err){ + if(err)console.error(err) + },true) //add Cloud Videos table, will remove in future s.sqlQuery('CREATE TABLE IF NOT EXISTS `Cloud Videos` (`mid` varchar(50) NOT NULL,`ke` varchar(50) DEFAULT NULL,`href` text NOT NULL,`size` float DEFAULT NULL,`time` timestamp NULL DEFAULT NULL,`end` timestamp NULL DEFAULT NULL,`status` int(1) DEFAULT \'0\',`details` text)' + mySQLtail + ';',[],function(err){ if(err)console.error(err) diff --git a/libs/startup.js b/libs/startup.js index 068f644a..e78c63f4 100644 --- a/libs/startup.js +++ b/libs/startup.js @@ -4,16 +4,33 @@ var moment = require('moment'); var crypto = require('crypto'); var exec = require('child_process').exec; var execSync = require('child_process').execSync; -module.exports = function(s,config,lang,io,processReady){ +module.exports = function(s,config,lang,io,){ console.log('FFmpeg version : '+s.ffmpegVersion) console.log('Node.js version : '+execSync("node -v")) s.processReady = function(){ s.systemLog(lang.startUpText5) - process.send('ready') s.onProcessReadyExtensions.forEach(function(extender){ extender(true) }) - if(processReady)processReady() + process.send('ready') + } + var checkForTerminalCommands = function(callback){ + var next = function(){ + if(callback)callback() + } + if(!s.isWin){ + var etcPath = '/etc/shinobisystems/cctv.txt' + fs.stat(etcPath,function(err,stat){ + if(err || !stat){ + exec('node '+ s.mainDirectory + '/INSTALL/terminalCommands.js',function(err){ + if(err)console.log(err) + }) + } + next() + }) + }else{ + next() + } } var loadedAccounts = [] var loadMonitors = function(callback){ @@ -151,6 +168,9 @@ module.exports = function(s,config,lang,io,processReady){ }) }) },10000) + //hourly check to see if sizePurge has failed to unlock + //checks to see if request count is the number of monitors + 10 + s.checkForStalePurgeLocks() //run prerequsite queries, load users and monitors if(config.childNodes.mode !== 'child'){ //sql/database connection with knex @@ -158,11 +178,13 @@ module.exports = function(s,config,lang,io,processReady){ //run prerequsite queries s.preQueries() setTimeout(function(){ - //load administrators (groups) - loadAdminUsers(function(){ - //load monitors (for groups) - loadMonitors(function(){ - s.processReady() + checkForTerminalCommands(function(){ + //load administrators (groups) + loadAdminUsers(function(){ + //load monitors (for groups) + loadMonitors(function(){ + s.processReady() + }) }) }) },1500) diff --git a/libs/user.js b/libs/user.js index 7803f65a..362c55e4 100644 --- a/libs/user.js +++ b/libs/user.js @@ -15,7 +15,7 @@ module.exports = function(s,config){ if(s.group[e.ke].sizePurgeQueue.length > 0){ checkQueue() }else{ - s.group[e.ke].sizePurging=false + s.group[e.ke].sizePurging = false s.sendDiskUsedAmountToClients(e) } } @@ -260,11 +260,11 @@ module.exports = function(s,config){ d.form.details.use_admin=d.d.use_admin d.form.details.use_ldap=d.d.use_ldap //check - if(d.d.edit_days=="0"){ - d.form.details.days=d.d.days; + if(d.d.edit_days == "0"){ + d.form.details.days = d.d.days; } - if(d.d.edit_size=="0"){ - d.form.details.size=d.d.size; + if(d.d.edit_size == "0"){ + d.form.details.size = d.d.size; } if(d.d.sub){ d.form.details.sub=d.d.sub; @@ -308,4 +308,43 @@ module.exports = function(s,config){ } }) } + s.findPreset = function(presetQueryVals,callback){ + //presetQueryVals = [ke, type, name] + s.sqlQuery("SELECT * FROM Presets WHERE ke=? AND type=? AND name=? LIMIT 1",presetQueryVals,function(err,presets){ + var preset + var notFound = false + if(presets && presets[0]){ + preset = presets[0] + s.checkDetails(preset) + }else{ + notFound = true + } + callback(notFound,preset) + }) + } + if(config.cron.deleteOverMax === true){ + s.checkForStalePurgeLocks = function(){ + var doCheck = function(){ + Object.keys(s.group).forEach(function(groupKey){ + var userGroup = s.group[groupKey] + var monitorCount = 10 + if(userGroup.mon)monitorCount = Object.keys(userGroup.mon).length + var purgeRequestCount = userGroup.sizePurgeQueue.length + var isLocked = (userGroup.sizePurging === true) + if(isLocked && purgeRequestCount > monitorCount + 10){ + s.group[groupKey].sizePurgeQueue = [] + s.group[groupKey].sizePurging = false + s.systemLog(lang.sizePurgeLockedText + ' : ' + groupKey) + } + }) + } + clearTimeout(s.checkForStalePurgeLocksInterval) + s.checkForStalePurgeLocksInterval = setInterval(function(){ + doCheck() + },1000 * 60 * 60) + doCheck() + } + }else{ + s.checkForStalePurgeLocks = function(){} + } } diff --git a/libs/webServerAdminPaths.js b/libs/webServerAdminPaths.js index 2d16090f..d75df62b 100644 --- a/libs/webServerAdminPaths.js +++ b/libs/webServerAdminPaths.js @@ -419,19 +419,7 @@ module.exports = function(s,config,lang,app){ s.closeJsonResponse(res,endData) return } - var findPreset = function(callback){ - s.sqlQuery("SELECT * FROM Presets WHERE ke=? AND type=? AND name=? LIMIT 1",[req.params.ke,'monitorStates',req.params.stateName],function(err,presets){ - var preset - var notFound = false - if(presets && presets[0]){ - preset = presets[0] - s.checkDetails(preset) - }else{ - notFound = true - } - callback(notFound,preset) - }) - } + var presetQueryVals = [req.params.ke,'monitorStates',req.params.stateName] switch(req.params.action){ case'insert':case'edit': var form = s.getPostData(req) @@ -441,7 +429,7 @@ module.exports = function(s,config,lang,app){ s.closeJsonResponse(res,endData) return } - findPreset(function(notFound,preset){ + s.findPreset(presetQueryVals,function(notFound,preset){ if(notFound === true){ endData.msg = lang["Inserted State Configuration"] var details = { @@ -478,7 +466,7 @@ module.exports = function(s,config,lang,app){ }) break; case'delete': - findPreset(function(notFound,preset){ + s.findPreset(presetQueryVals,function(notFound,preset){ if(notFound === true){ endData.msg = user.lang['State Configuration Not Found'] s.closeJsonResponse(res,endData) @@ -494,43 +482,8 @@ module.exports = function(s,config,lang,app){ }) break; default://change monitors according to state - findPreset(function(notFound,preset){ - if(notFound === false){ - var sqlQuery = 'SELECT * FROM Monitors WHERE ke=? AND ' - var monitorQuery = [] - var sqlQueryValues = [req.params.ke] - var monitorPresets = {} - preset.details.monitors.forEach(function(monitor){ - monitorQuery.push('mid=?') - sqlQueryValues.push(monitor.mid) - monitorPresets[monitor.mid] = monitor - }) - sqlQuery += '('+monitorQuery.join(' OR ')+')' - s.sqlQuery(sqlQuery,sqlQueryValues,function(err,monitors){ - if(monitors && monitors[0]){ - monitors.forEach(function(monitor){ - s.checkDetails(monitor) - s.checkDetails(monitorPresets[monitor.mid]) - var monitorPreset = monitorPresets[monitor.mid] - monitorPreset.details = Object.assign(monitor.details,monitorPreset.details) - monitor = s.cleanMonitorObjectForDatabase(Object.assign(monitor,monitorPreset)) - monitor.details = JSON.stringify(monitor.details) - s.addOrEditMonitor(Object.assign(monitor,{}),function(err,endData){ - - },user) - }) - endData.ok = true - s.tx({f:'change_group_state',ke:req.params.ke,name:req.params.stateName},'GRP_'+req.params.ke) - s.closeJsonResponse(res,endData) - }else{ - endData.msg = user.lang['State Configuration has no monitors associated'] - s.closeJsonResponse(res,endData) - } - }) - }else{ - endData.msg = user.lang['State Configuration Not Found'] - s.closeJsonResponse(res,endData) - } + s.activateMonitorStates(req.params.ke,req.params.stateName,user,function(endData){ + s.closeJsonResponse(res,endData) }) break; } diff --git a/libs/webServerPaths.js b/libs/webServerPaths.js index 419927db0..47e26364 100644 --- a/libs/webServerPaths.js +++ b/libs/webServerPaths.js @@ -8,6 +8,7 @@ var execSync = require('child_process').execSync; var exec = require('child_process').exec; var spawn = require('child_process').spawn; var httpProxy = require('http-proxy'); +var onvif = require('node-onvif'); var proxy = httpProxy.createProxyServer({}) var ejs = require('ejs'); var CircularJSON = require('circular-json'); @@ -286,7 +287,8 @@ module.exports = function(s,config,lang,app,io){ // config: config, $user: req.resp, lang: r.lang, - define: s.getDefinitonFile(r.details.lang) + define: s.getDefinitonFile(r.details.lang), + customAutoLoad: s.customAutoLoadTree }) }) break; @@ -297,7 +299,8 @@ module.exports = function(s,config,lang,app,io){ // config: config, $user: req.resp, lang: r.lang, - define: s.getDefinitonFile(r.details.lang) + define: s.getDefinitonFile(r.details.lang), + customAutoLoad: s.customAutoLoadTree }) }) break; @@ -311,17 +314,36 @@ module.exports = function(s,config,lang,app,io){ $subs: rr, $mons: rrr, lang: r.lang, - define: s.getDefinitonFile(r.details.lang) + define: s.getDefinitonFile(r.details.lang), + customAutoLoad: s.customAutoLoadTree }) }) }) }else{ //not admin user - renderPage(config.renderPaths.home,{$user:req.resp,config:config,lang:r.lang,define:s.getDefinitonFile(r.details.lang),addStorage:s.dir.addStorage,fs:fs,__dirname:s.mainDirectory}); + renderPage(config.renderPaths.home,{ + $user:req.resp, + config:config, + lang:r.lang, + define:s.getDefinitonFile(r.details.lang), + addStorage:s.dir.addStorage, + fs:fs, + __dirname:s.mainDirectory, + customAutoLoad: s.customAutoLoadTree + }); } break; default: - renderPage(config.renderPaths.home,{$user:req.resp,config:config,lang:r.lang,define:s.getDefinitonFile(r.details.lang),addStorage:s.dir.addStorage,fs:fs,__dirname:s.mainDirectory}); + renderPage(config.renderPaths.home,{ + $user:req.resp, + config:config, + lang:r.lang, + define:s.getDefinitonFile(r.details.lang), + addStorage:s.dir.addStorage, + fs:fs, + __dirname:s.mainDirectory, + customAutoLoad: s.customAutoLoadTree + }); break; } s.userLog({ke:r.ke,mid:'$USER'},{type:r.lang['New Authentication Token'],msg:{for:req.body.function,mail:r.mail,id:r.uid,ip:req.ip}}) @@ -511,6 +533,7 @@ module.exports = function(s,config,lang,app,io){ r=[] } data.Logs = r + data.customAutoLoad = s.customAutoLoadTree fs.readFile(s.location.config,'utf8',function(err,file){ data.plainConfig = JSON.parse(file) renderPage(config.renderPaths.super,data) @@ -1375,7 +1398,7 @@ module.exports = function(s,config,lang,app,io){ values.push(time) }) s.sqlQuery('SELECT * FROM Videos WHERE '+where.join(' OR '),values,function(err,r){ - var resp = {ok:false} + var resp = {ok: false} if(r && r[0]){ resp.ok = true var zipDownload = null @@ -1396,7 +1419,7 @@ module.exports = function(s,config,lang,app,io){ fs.mkdirSync(fileBinDir); } r.forEach(function(video){ - timeFormatted = s.formattedTime(video.time) + var timeFormatted = s.formattedTime(video.time) video.filename = timeFormatted+'.'+video.ext var dir = s.getVideoDirectory(video)+video.filename var tempVideoFile = timeFormatted+' - '+video.mid+'.'+video.ext @@ -1418,16 +1441,27 @@ module.exports = function(s,config,lang,app,io){ var zipDownload = fs.createReadStream(zippedFile) zipDownload.pipe(res) zipDownload.on('error', function (error) { - s.userLog({ke:req.params.ke,mid:'$USER'},{title:'Zip Download Error',msg:error.toString()}) + var errorString = error.toString() + s.userLog({ + ke: req.params.ke, + mid: '$USER' + },{ + title: 'Zip Download Error', + msg: errorString + }) if(zipDownload && zipDownload.destroy){ zipDownload.destroy() } - }); + res.end(s.prettyPrint({ + ok: false, + msg: errorString + })) + }) zipDownload.on('close', function () { res.end() - zipDownload.destroy(); - fs.unlinkSync(zippedFile); - }); + zipDownload.destroy() + fs.unlinkSync(zippedFile) + }) }) }else{ failed({ok:false,msg:'No Videos Found'}) @@ -1437,7 +1471,121 @@ module.exports = function(s,config,lang,app,io){ }else{ failed({ok:false,msg:'"videos" query variable is missing from request.'}) } - }); + }) + /** + * API : Zip Cloud Videos and Get Link from fileBin + */ + app.get(config.webPaths.apiPrefix+':auth/zipCloudVideos/:ke', function (req,res){ + res.header("Access-Control-Allow-Origin",req.headers.origin); + var failed = function(resp){ + res.setHeader('Content-Type', 'application/json'); + res.end(s.prettyPrint(resp)) + } + if(req.query.videos && req.query.videos !== ''){ + s.auth(req.params,function(user){ + var videosSelected = JSON.parse(req.query.videos) + var where = [] + var values = [] + videosSelected.forEach(function(video){ + where.push("(ke=? AND mid=? AND `time`=?)") + if(!video.ke)video.ke = req.params.ke + values.push(video.ke) + values.push(video.mid) + var time = s.nameToTime(video.filename) + if(req.query.isUTC === 'true'){ + time = s.utcToLocal(time) + } + time = new Date(time) + values.push(time) + }) + s.sqlQuery('SELECT * FROM `Cloud Videos` WHERE '+where.join(' OR '),values,function(err,r){ + var resp = {ok: false} + if(r && r[0]){ + resp.ok = true + var zipDownload = null + var tempFiles = [] + var fileId = s.gid() + var fileBinDir = s.dir.fileBin+req.params.ke+'/' + var tempScript = s.dir.streams+req.params.ke+'/'+fileId+'.sh' + var zippedFilename = s.formattedTime()+'-'+fileId+'-Shinobi_Cloud_Backed_Recordings.zip' + var zippedFile = fileBinDir+zippedFilename + var script = 'cd '+fileBinDir+' && zip -9 -r '+zippedFile + res.on('close', () => { + if(zipDownload && zipDownload.destroy){ + zipDownload.destroy() + } + fs.unlink(zippedFile); + }) + if(!fs.existsSync(fileBinDir)){ + fs.mkdirSync(fileBinDir); + } + var cloudDownloadCount = 0 + var getFile = function(video,completed){ + if(!video)completed(); + s.checkDetails(video) + var filename = video.href.split('/') + filename = filename[filename.length - 1] + var timeFormatted = s.formattedTime(video.time) + var tempVideoFile = video.details.type + '-' + video.mid + '-' + filename + var tempFileWriteStream = fs.createWriteStream(fileBinDir+tempVideoFile) + tempFileWriteStream.on('finish', function() { + ++cloudDownloadCount + getFile(r[cloudDownloadCount],completed) + }) + var cloudVideoDownload = request(video.href) + cloudVideoDownload.on('response', function (res) { + res.pipe(tempFileWriteStream) + }) + tempFiles.push(fileBinDir+tempVideoFile) + script += ' "'+tempVideoFile+'"' + } + getFile(r[cloudDownloadCount],function(){ + fs.writeFileSync(tempScript,script,'utf8') + var zipCreate = spawn('sh',(tempScript).split(' '),{detached: true}) + zipCreate.stderr.on('data',function(data){ + s.userLog({ke:req.params.ke,mid:'$USER'},{title:'Zip Create Error',msg:data.toString()}) + }) + zipCreate.on('exit',function(data){ + fs.unlinkSync(tempScript) + tempFiles.forEach(function(file){ + fs.unlink(file,function(){}) + }) + res.setHeader('Content-Disposition', 'attachment; filename="' + zippedFilename + '"') + var zipDownload = fs.createReadStream(zippedFile) + zipDownload.pipe(res) + zipDownload.on('error', function (error) { + var errorString = error.toString() + s.userLog({ + ke: req.params.ke, + mid: '$USER' + },{ + title: 'Zip Download Error', + msg: errorString + }) + if(zipDownload && zipDownload.destroy){ + zipDownload.destroy() + } + res.end(s.prettyPrint({ + ok: false, + msg: errorString + })) + }) + zipDownload.on('close', function () { + res.end() + zipDownload.destroy() + fs.unlinkSync(zippedFile) + }) + }) + }) + }else{ + failed({ok:false,msg:'No Videos Found'}) + } + }) + },res,req); + }else{ + failed({ok:false,msg:'"videos" query variable is missing from request.'}) + } + }) /** * API : Get Cloud Video File (proxy) */ @@ -1524,27 +1672,34 @@ module.exports = function(s,config,lang,app,io){ /** * API : Motion Trigger via GET request */ - app.get(config.webPaths.apiPrefix+':auth/motion/:ke/:id', function (req,res){ - s.auth(req.params,function(user){ - if(req.query.data){ - try{ - var d={id:req.params.id,ke:req.params.ke,details:JSON.parse(req.query.data)}; - }catch(err){ - res.end('Data Broken',err); - return; - } - }else{ - res.end('No Data'); - return; - } - if(!d.ke||!d.id||!s.group[d.ke]){ - res.end(user.lang['No Group with this key exists']); - return; - } - s.triggerEvent(d) - res.end(user.lang['Trigger Successful']) - },res,req); - }) + app.get(config.webPaths.apiPrefix+':auth/motion/:ke/:id', function (req,res){ + s.auth(req.params,function(user){ + var endData = { + + } + if(req.query.data){ + try{ + var d = { + id: req.params.id, + ke: req.params.ke, + details: JSON.parse(req.query.data) + } + }catch(err){ + res.end('Data Broken',err) + return + } + }else{ + res.end('No Data') + return + } + if(!d.ke||!d.id||!s.group[d.ke]){ + res.end(user.lang['No Group with this key exists']) + return + } + s.triggerEvent(d) + res.end(user.lang['Trigger Successful']) + },res,req) + }) /** * API : WebHook Tester */ diff --git a/libs/webServerSuperPaths.js b/libs/webServerSuperPaths.js index 79e58056..7a25a6ba 100644 --- a/libs/webServerSuperPaths.js +++ b/libs/webServerSuperPaths.js @@ -622,4 +622,16 @@ module.exports = function(s,config,lang,app){ } },res,req) }) + /** + * API : Superuser : Force Check for Stale Purge Locks + */ + app.all(config.webPaths.superApiPrefix+':auth/system/checkForStalePurgeLocks', function (req,res){ + s.superAuth(req.params,function(resp){ + var endData = { + ok : true + } + s.checkForStalePurgeLocks() + res.end(s.prettyPrint(endData)) + },res,req) + }) } diff --git a/package.json b/package.json index 67f2e33b..c03d67d6 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,12 @@ { "name": "shinobi", "productName": "Shinobi", - "version": "1.0.38", + "version": "2.0.0", "description": "CCTV and NVR in Node.js", "main": "camera.js", "bin": "camera.js", "scripts": { - "test": "node test.js", + "test": "node camera.js test", "start": "chmod +x INSTALL/start.sh && INSTALL/start.sh" }, "repository": { @@ -19,30 +19,33 @@ }, "homepage": "https://gitlab.com/Shinobi-Systems/Shinobi#readme", "dependencies": { + "aws-sdk": "^2.279.1", + "backblaze-b2": "^1.0.4", "body-parser": "^1.15.2", "circular-json": "0.3.1", "connection-tester": "^0.1.1", - "mp4frag": "^0.0.22", + "discord.js": "^11.3.2", "ejs": "^2.5.5", "express": "^4.14.0", + "http-proxy": "^1.17.0", "jsonfile": "^3.0.1", - "moment": "^2.17.0", - "mysql": "^2.12.0", "knex": "^0.14.2", - "aws-sdk": "^2.279.1", - "pam-diff": "^0.10.2", - "pipe2pam": "^0.6.2", - "nodemailer": "^4.0.1", + "ldapauth-fork": "^4.0.2", + "moment": "^2.17.0", + "mp4frag": "^0.0.22", + "mysql": "^2.12.0", "node-onvif": "^0.1.4", + "nodemailer": "^4.0.1", + "pam-diff": "^0.10.2", "path": "^0.12.7", + "pipe2pam": "^0.6.2", "request": "^2.79.0", "socket.io": "^1.7.1", "socket.io-client": "^1.7.2", - "http-proxy": "^1.17.0", + "sqlite3": "^4.0.4", "webdav-fs": "^1.11.0", - "discord.js": "^11.3.2", - "backblaze-b2": "^1.0.4", - "ldapauth-fork": "^4.0.2" + "sat": "^0.7.1", + "shinobi-sound-detection": "^0.1.7" }, "devDependencies": {} } diff --git a/plugins/openalpr/INSTALL.sh b/plugins/openalpr/INSTALL.sh new file mode 100644 index 00000000..6fe455d5 --- /dev/null +++ b/plugins/openalpr/INSTALL.sh @@ -0,0 +1,53 @@ +DIR=`dirname $0` +INSTALLERS_DIR="$DIR/../../INSTALL" +if ! [ -x "$(command -v dos2unix)" ]; then + echo "-----------------------------------" + echo "Installing dos2unix" + apt install dos2unix -y +fi +echo "-----------------------------------" +if ! [ -x "$(command -v alpr)" ]; then + echo "Installing OpenALPR" + echo "Do you want to Install OpenALPR with CUDA enabled?" + echo "(Y)es or (n)o?" + echo "Press [ENTER] for default (Yes)" + read openalprcudaenabled + if [ "$openalprcudaenabled" = "n" ] || [ "$openalprcudaenabled" = "N" ]; then + sed -i -e 's/detector = lbpgpu/detector = lbpcpu/g' "$DIR/openalpr.conf" + dos2unix $INSTALLERS_DIR/openalpr-cpu-easy.sh + sh $INSTALLERS_DIR/openalpr-cpu-easy.sh + else + sed -i -e 's/detector = lbpcpu/detector = lbpgpu/g' "$DIR/openalpr.conf" + dos2unix $INSTALLERS_DIR/openalpr-gpu-easy.sh + sh $INSTALLERS_DIR/openalpr-gpu-easy.sh + fi +else + echo "OpenALPR found... : $(alpr --version)" +fi +echo "-----------------------------------" +if [ ! -e "$DIR/conf.json" ]; then + echo "Creating conf.json" + cp $DIR/conf.sample.json $DIR/conf.json +else + echo "conf.json already exists..." +fi +echo "-----------------------------------" +echo "Installing Modules.." +npm install --unsafe-perm +echo "Finding and Fixing Module Vulnerabilities.." +npm audit fix --force +echo "Shinobi - Do you want to start the plugin?" +echo "(Y)es or (n)o?" +echo "Press [ENTER] for default (Yes)" +read startplugin +if [ "$startplugin" = "n" ] || [ "$startplugin" = "N" ]; then + echo "-----------------------------------" + echo "Start the plugin with pm2 like so :" + echo "pm2 start $DIR/shinobi-openalpr.js" + echo "-----------------------------------" + echo "Start the plugin without pm2 :" + echo "node $DIR/shinobi-openalpr.js" +else + pm2 start shinobi-openalpr.js + pm2 save +fi diff --git a/plugins/openalpr/README.md b/plugins/openalpr/README.md index 44beac78..4e353f25 100644 --- a/plugins/openalpr/README.md +++ b/plugins/openalpr/README.md @@ -1,49 +1,32 @@ -# OpenALPR and Motion Detector +# OpenALPR Install required libraries. -**Ubuntu and Debian only** +**Ubuntu 17.10 and 18.04 only** -``` -sudo apt update && sudo apt install libcairo2-dev libjpeg-dev libpango1.0-dev libgif-dev build-essential g++ openalpr openalpr-daemon openalpr-utils libopenalpr-dev -y -``` +> By default plugins run as a client. `camera.js` is running as the host awaiting a plugin to connect to it. To learn about how to connect a plugin as a Host please review the "Run the plugin as a Host" section at the end of this README. -**Install the Node.js Canvas engine** +1. Go to the plugin's directory and run the installer for OpenALPR. **/home/Shinobi** is the default directory for where Shinobi is installed. + ``` + cd /home/Shinobi/plugins/openalpr + sh INSTALL.sh + ``` -``` -sudo npm install canvas@1.6 -``` -Go to the Shinobi directory. **Below is an example.** +2. Then add the plugin key to the **Main Configuration** file, the `conf.json` located in **/home/Shinobi**. You will find the `pluginKeys` object empty on a new install as seen below. + ``` + "pluginKeys":{} + ``` + > Add the key so it looks something like this. -``` -cd /home/Shinobi -``` + ``` + "pluginKeys":{ + "OpenALPR": "SomeOpenALPRkeySoPeopleDontMessWithYourShinobi" + } + ``` -Copy the config file. +3. Restart Shinobi to apply the Plugin Key. Shinobi does not need to be restarted when a plugin is initiated or stopped after applying changes to the Main Configuration file. -``` -cp plugins/openalpr/conf.sample.json plugins/openalpr/conf.json -``` - -Edit it the new file. Host should be `localhost` and port should match the `listening port for camera.js`. - -``` -nano plugins/openalpr/conf.json -``` - -Start the plugin. - -``` -node plugins/openalpr/shinobi-openalpr.js -``` - -Or to daemonize with PM2. - -``` -pm2 start plugins/openalpr/shinobi-openalpr.js -``` - -Doing this will reveal options in the monitor configuration. Shinobi does not need to be restarted when a plugin is initiated or stopped. +> You should change `SomeOpenALPRkeySoPeopleDontMessWithYourShinobi` to something else in both the main configuration and plugin configuration. Both files changed need to be matching keys of course. ## Run the plugin as a Host > The main app (Shinobi) will be the client and the plugin will be the host. The purpose of allowing this method is so that you can use one plugin for multiple Shinobi instances. Allowing you to easily manage connections without starting multiple processes. @@ -51,7 +34,7 @@ Doing this will reveal options in the monitor configuration. Shinobi does not ne Edit your plugins configuration file. Set the `hostPort` **to be different** than the `listening port for camera.js`. ``` -nano plugins/openalpr/conf.json +nano conf.json ``` Here is a sample of a Host configuration for the plugin. @@ -70,7 +53,7 @@ Here is a sample of a Host configuration for the plugin. } ``` -Now modify the **main configuration file** located in the main directory of Shinobi. *Where you currently should be.* +Now modify the **Main Configuration** file located in the main directory of Shinobi. *Where you currently should be.* ``` nano conf.json @@ -90,4 +73,4 @@ Add the `plugins` array if you don't already have it. Add the following *object "type" : "detector" } ], -``` \ No newline at end of file +``` diff --git a/plugins/openalpr/package.json b/plugins/openalpr/package.json index 7f6d5aa0..ed0b314e 100644 --- a/plugins/openalpr/package.json +++ b/plugins/openalpr/package.json @@ -4,7 +4,6 @@ "description": "OpenALPR plugin for Shinobi", "main": "shinobi-openalpr.js", "dependencies": { - "canvas": "^1.6.7", "express": "^4.16.2", "moment": "^2.19.2", "socket.io": "^2.0.4" diff --git a/plugins/openalpr/shinobi-openalpr.js b/plugins/openalpr/shinobi-openalpr.js index d74bd87d..76a9f40e 100644 --- a/plugins/openalpr/shinobi-openalpr.js +++ b/plugins/openalpr/shinobi-openalpr.js @@ -26,7 +26,9 @@ try{ } // Base Init />> // OpenALPR Init >> -if(config.alprConfig===undefined){config.alprConfig=__dirname+'/openalpr.conf'} +if(config.alprConfig === undefined){ + config.alprConfig = __dirname + '/openalpr.conf' +} // OpenALPR Init />> s.detectObject = function(buffer,d,tx,frameLocation){ var detectStuff = function(frame){ diff --git a/sql/framework.sql b/sql/framework.sql index 84d4cc20..7ef15e1b 100644 --- a/sql/framework.sql +++ b/sql/framework.sql @@ -125,6 +125,18 @@ CREATE TABLE IF NOT EXISTS `Files` ( `status` int(1) NOT NULL DEFAULT '0' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; ALTER TABLE `Files` ADD COLUMN `time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP AFTER `status`; + +-- Data exporting was unselected. +-- Dumping structure for table ccio.Schedules +CREATE TABLE IF NOT EXISTS `Schedules` ( + `ke` varchar(50) DEFAULT NULL, + `name` text, + `details` text, + `start` varchar(10) DEFAULT NULL, + `end` varchar(10) DEFAULT NULL, + `enabled` int(1) NOT NULL DEFAULT '1' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + -- Data exporting was unselected. /*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */; /*!40014 SET FOREIGN_KEY_CHECKS=IF(@OLD_FOREIGN_KEY_CHECKS IS NULL, 1, @OLD_FOREIGN_KEY_CHECKS) */; diff --git a/tools/modifyConfiguration.js b/tools/modifyConfiguration.js index 94046e86..aa32523f 100644 --- a/tools/modifyConfiguration.js +++ b/tools/modifyConfiguration.js @@ -12,7 +12,7 @@ processArgv.forEach(function(val) { var theSplit = val.split('='); var index = theSplit[0]; var value = theSplit[1]; - if(index === 'addToConfig'){ + if(index.indexOf('addToConfig') > -1){ try{ value = JSON.parse(value) config = Object.assign(config,value) @@ -36,4 +36,4 @@ processArgv.forEach(function(val) { jsonfile.writeFile(configLocation,config,{spaces: 2},function(){ console.log('Changes Complete. Here is what it is now.') console.log(JSON.stringify(config,null,2)) -}) \ No newline at end of file +}) diff --git a/web/libs/css/dash2.basic.css b/web/libs/css/dash2.basic.css new file mode 100644 index 00000000..0e96231f --- /dev/null +++ b/web/libs/css/dash2.basic.css @@ -0,0 +1,89 @@ +/*Cusotm Bootstrap*/ +.col-5ths, +.col-sm-5ths, +.col-md-5ths, +.col-lg-5ths { + position: relative; + min-height: 1px; + padding-right: 15px; + padding-left: 15px; +} + +.col-5ths { + -webkit-box-flex: 0; + -webkit-flex: 0 0 20%; + -ms-flex: 0 0 20%; + flex: 0 0 20%; + max-width: 20%; +} + +@media (min-width: 576px) { + .col-sm-5ths { + -webkit-box-flex: 0; + -webkit-flex: 0 0 20%; + -ms-flex: 0 0 20%; + flex: 0 0 20%; + max-width: 20%; + } +} + +@media (min-width: 768px) { + .col-md-5ths { + -webkit-box-flex: 0; + -webkit-flex: 0 0 20%; + -ms-flex: 0 0 20%; + flex: 0 0 20%; + max-width: 20%; + } +} +::-webkit-scrollbar-thumb:hover { + background-color:#bd9565; +} +::-webkit-scrollbar-thumb { + background-color:#bd9565; + border: 2px solid transparent; + border-radius: 10px; + background-clip: padding-box; +} +/**/ +.flex{display:flex} +.flex>div{flex:1} +.flex-block{display:inline-flex;width:100%;flex-flow: row wrap;} +.flex-unit-3{flex:3} +.flex-inline{display: inline-flex;position:relative} +@import (less) "../less/pie.less"; +ul{list-style:none} +*{transition:0.2s;box-sizing:border-box} +.affix-top{position:fixed} +.no-padding{padding:0!important} +.no-margin{margin:0!important} +.pre-inline{white-space: normal;word-break: normal} +.pre-inline>ul{margin:0;padding:0} +a{cursor:pointer} +nav h4{cursor:default;font-size:95%;padding:16px 40px;font-weight:100;text-transform:uppercase;letter-spacing:2px} +.m-r{margin-right:10px} +.m-b{margin-bottom:10px} +.m-t{margin-top:10px} +.m-l{margin-left:10px} +.overflow-hidden{overflow: hidden!important} +.list-inline{list-style:none} +.list-inline li{display:inline-block;vertical-align: top;margin:0;} +.truncate{width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;} +img{max-width:100%} +.display-table{display:table;width:100%} +.display-inline{display:inline-block} +.display-table-cell{display:table-cell} +.small{font-size:80%} +.super-center{position:absolute;left:0;top:0;right:0;bottom:0;margin:auto;width: 4em;height: 1em;} +.permission_monitor_edit{display:none} +.permission_video_delete{display:none} +.nodata .divider{margin:5px 0} +.loading .divider{margin:5px 0} +/* Video Grid */ +.video_grid{overflow: auto;height: 100%;display: block;} +.video_grid .col-md-2{padding-left:5px;padding-right:5px;padding-bottom:10px} +.video_grid .thumb{width:100%;height:150px;display:inline-block;background-size:cover;position:relative;overflow:hidden;border-radius:4px;border:1px solid #000;box-shadow:0 0 10px #151515} +.video_grid .thumb .title-strip, .video_grid .thumb .button-strip{width:100%;position:absolute;left:0;background:rgba(0,0,0,0.7);color:#fff;padding:4px} +.video_grid .thumb .title-strip{top:0;opacity:0.5} +.video_grid .thumb .button-strip{bottom:0;opacity:0} +.video_grid .thumb:hover .title-strip, .video_grid .thumb:hover .button-strip{opacity:1} diff --git a/web/libs/css/dash2.forms.css b/web/libs/css/dash2.forms.css new file mode 100644 index 00000000..a1d0ffea --- /dev/null +++ b/web/libs/css/dash2.forms.css @@ -0,0 +1,44 @@ +form.modal-body{margin:0} +.form-group label span{padding:5px;font-weight: 400;color: #2d2d2d;display:block;border-bottom: 1px dotted #ddd;font-size: 10pt;} +.form-group label{display:table} +.form-group label>div{display:table-cell} +.form-group label>div:nth-child(2n-1){width:30%} +.form-group label>div:nth-child(2){width:70%;padding:5px;border:1px solid #dedede;border-radius:5px} +.dark .form-group label>div,.dark .form-group label>div>span{border-color:#454545;color:#fff} +.important.form-group label>div:nth-child(2),.important.form-group label>div>span{border-color:red} +.form-group label span small{margin-left: 2px;display:block;font-weight: 600;} +.form-group-group .round-left{border-radius: 50px 0 0 50px;margin-left:10px} +.form-group-group blockquote:before,.form-group-group blockquote:after{display:none!important} +.form-group-group blockquote{letter-spacing:normal;font-style:normal} +.form-group-group blockquote p:empty{display:none} +.form-group-group blockquote p{font-size:inherit} +.form-group-group blockquote p:last-child{margin-bottom:0} +.form-group-group-group>div,.form-group-group-group .h_us_advanced>div{margin-bottom:15px;} +.form-group-group{padding:0 10px 10px 10px;overflow:hidden;margin-bottom:15px;border-radius:5px;border:1px solid #ddd;background:#fff} +.form-group-group table{width:100%} +.form-group-group table tr td{padding:10px 5px} +.form-group-group table tr:not(:last-child) td{border-bottom:1px dotted #eee} +.form-group-group .mdl-list__item{border-bottom:1px solid #eee;} +.form-group-group .mdl-list__item:hover{background:#e6e6e6;border-radius:4px;} +.dark .form-group-group .mdl-list__item{color:#fff;border-bottom:1px solid #444;} +.dark .form-group-group .mdl-list__item:hover{background:#555;} +.form-group-group:last-child,.form-group-group > .form-group:last-child{margin-bottom:0} +.form-group-group h4{margin:0 -10px 15px -10px;padding:15px;background:#ddd;} +.form-group-group h4 small{color:#fff;} +.form-group-group.red{border-color:#d9534f} +.form-group-group.red h4{background:#d9534f;color:#fff} +.form-group-group.purple{border-color:#3f51b5} +.form-group-group.purple h4{background:#3f51b5;color:#fff} +.form-group-group.blue{border-color:#337ab7} +.form-group-group.blue h4{background:#337ab7;color:#fff} +.form-group-group.navy{border-color:#31708f} +.form-group-group.navy h4{background:#31708f;color:#fff} +.form-group-group.green{border-color:#449d44} +.form-group-group.green h4{background:#449d44;color:#fff} +.form-group-group.forestgreen{border-color:#1e4046} +.form-group-group.forestgreen h4{background:#1e4046;color:#fff} +.form-group-group.orange{border-color:#c49a68} +.form-group-group.orange h4{background:#c49a68;color:#fff} +.form-group-group.grey{border-color:#777} +.form-group-group.grey h4{background:#777;color:#fff} +.dark .form-group-group{background:#222} diff --git a/web/libs/css/dash2.modal.css b/web/libs/css/dash2.modal.css new file mode 100644 index 00000000..e69de29b diff --git a/web/libs/css/dash2.monitors.css b/web/libs/css/dash2.monitors.css new file mode 100644 index 00000000..f22fe484 --- /dev/null +++ b/web/libs/css/dash2.monitors.css @@ -0,0 +1,86 @@ + +.jpegMode .cpu_load .progress-bar,.jpegMode .ram_load .progress-bar{background-color:#5cb85c} +.jpegMode [system="jpegToggle"],[system].text-success{color:#5cb85c!important} + +.monitor_item .stream-hud{opacity:0;position:absolute;top:0;left:0;width:100%;height:100%;z-index:2} +.monitor_item .stream-hud .camera_cpu_usage{position:absolute;top:0;left:0;width: 100%;} +.monitor_item .stream-hud .camera_cpu_usage .progress{width: 100%;} +.monitor_item .stream-hud .camera_cpu_usage:hover .progress{height:20px;transition:0.2s} +.monitor_item .stream-hud .controls{position:absolute;top:10px;left:10px;} +.monitor_item .stream-hud:hover{opacity:1} +.monitor_item .stream-hud .bottom-text{position:absolute;bottom:0;left:0;width:70%;padding:5px;text-shadow: 0 0 10px #333;} +.monitor_item .stream-hud:hover .bottom-text{top:0;} +.monitor_item .stream-hud .bottom-text .detector-fade{background: rgba(0,0,0,0.4);padding:10px 20px;border-radius:10px} +.monitor_item .stream-hud .lamp{position:absolute;top:5px;right:5px;z-index:1;text-shadow: 0 0 15px #333;} +.monitor_item[mode="Disabled"] .stream-hud .lamp{color:#5d5d5d} +.monitor_item[mode="Watch Only"] .stream-hud .lamp{color:#5da8e8} +.monitor_item[mode="Idle"] .stream-hud .lamp{color:#fff} +.monitor_item[mode="Record"] .stream-hud .lamp{color:#d9534f} +/*.data-menu{max-height:700px}*/ +.data-menu:not(:last-child){border-right:1px solid #fff;} +.data-menu.logs{list-style:none;} +.monitor_item .motionVision{display:none} + +.monitor_item .grid-stack-item-content{width:100%!important;left:0!important;right:0!important} +.monitor_item .ui-resizable-se {bottom: 10px!important;} +.monitor_item .stream-block{position: relative;text-align: center} +.monitor_item .mdl-data_window{overflow-x: auto;background:rgba(0,0,0,0.7);color:#fff;height:100%} +.monitor_item .mdl-data_window:not(.col-md-6){width:0;min-width:0;height:0px;min-height:0} + + +.monitor_item.fullscreen img.stream-element{height:100%;width:auto} +.monitor_item.fullscreen canvas.stream-element{height:auto;width:auto;background-color:black;} +.monitor_item .stream-element{border: 0;object-fit: fill;height: 100%;width:100%} +.monitor_item{position:relative;padding:0;transition:none;background:#000} +.monitor_item .mdl-card{min-height:auto;border:1px solid #272727;border-radius:0px;overflow:hidden} +.monitor_item .mdl-card__media{position:relative;padding:0!important;display:block!important;background:#000;} +.monitor_item.selected .stream-element{height:600px} +.monitor_item.selected .fa-expand:before{content:"\f066"} +.monitor_item .mdl-card__supporting-text{background:#222;color:#fff!important;display:block;min-height:auto!important} +.monitor_item.detector_triggered .detector-fade{opacity:1} +.monitor_item .detector-fade{opacity:0} +.monitor_item .indifference{position:absolute;width:100%;left:0;top:0;transition:0.2s;} +.monitor_item .progress{width:100%;background:#333;box-shadow:0;} +.monitor_item .indifference:hover .progress{height:20px;transition:0.2s} +.hide_indifference .indifference{display:none!important} +.hide_indifference [class_toggle="hide_indifference"]{color:#d9534f!important} +.monitor_item .mdl-card:not(.mdl-cell--4-col-desktop) .mdl-card__supporting-text .monitor_details{display:none;font-size:90%;margin-bottom:10px} +.monitor_item[mode="Record"] [mode="record"]{display:none} +.monitor_item[mode="Watch Only"] [mode="start"]{display:none} +.monitor_item .stream-hud .controls .btn{opacity:0.7} +.monitor_item.doObjectDetection .progress-bar{background-color: #57d94f} + +.data-menu{text-align:left} +.data-menu ul,.side-menu ul{list-style:none;margin:0;padding:0;} +.data-menu li,.side-menu li:not(.mdl-menu__item){ + border-bottom:1px solid #54502d;padding:10px; +} +.data-menu .progress-circle{margin:0 10px 0 0;position:relative;height:40px;width:40px;float:left} +.data-menu .progress-circle span:after{content:''} +img.circle-img,div.circle-img{border-radius:50%;height:50px;width:50px} +.circle-img.sm{height:25px;width:25px} + +@media screen and (max-width:1500px){ + .monitor_item .mdl-card__supporting-text .btn{ + padding: 5px 10px; + font-size: 11px; + line-height: 1.5; + } +} + +#monitors_live .monitor_item [class_toggle="show_logs"]{display:none} +#monitors_live .monitor_item .indifference{top:-5px} +#monitors_live .monitor_item .mdl-cell--8-col{width:100%;border:0;border-radius:0;margin:0;position:relative} +#monitors_live .monitor_item .mdl-cell--4-col-desktop,.monitor_item .mdl-card__supporting-text{display:none} +#monitors_live .monitor_item .mdl-card__supporting-text .monitor_details,#monitors_live .monitor_item .mdl-card__supporting-text .btn-group{display:none;text-align:center} +#monitors_live .monitor_item .mdl-card__supporting-text:not(.meta){display:block;position:absolute;bottom:0;left:0;height:0;padding:0;overflow:visible} +#monitors_live .monitor_item.show_data .mdl-card__supporting-text:not(.meta){width:50%} +#monitors_live .monitor_item.detector_triggered .mdl-card__supporting-text:not(.meta) .indifference{opacity:0.5;} +#monitors_live .monitor_item:hover .mdl-card__supporting-text:not(.meta){padding:15px;z-index:15;height:auto;} +#monitors_live .monitor_item:hover .mdl-card__supporting-text .monitor_details{display:block} +#monitors_live .monitor_item:hover .mdl-card__supporting-text .btn-group{display:inline-block} + +.signal.green{background:#5cb85c} +[status="1"] .btn[video="launch"],[data-status="1"] .btn[video="launch"]{background:#337ab7;border-color:#337ab7} +[status="2"] .btn[launch="video"],[status="2"] .btn[video="launch"],[data-status="2"] .btn[video="launch"]{background:#a59100;border-color:#a59100} +.signal.red{background:#c9302c} diff --git a/web/libs/css/dash2.powervideo.css b/web/libs/css/dash2.powervideo.css new file mode 100644 index 00000000..624a3be8 --- /dev/null +++ b/web/libs/css/dash2.powervideo.css @@ -0,0 +1,8 @@ +#pvideo_viewer iframe{border:0;width:100%;height:350px;margin-bottom:10px;overflow:hidden} +#pvideo_viewer video{max-height:300px;max-width:100%;} +#pvideo_viewer .holder{height:300px;} +#pvideo_viewer h3{margin-top:0} +#pvideo_viewer .progressBar{position:relative;} +#pvideo_viewer .bufferBar{position:absolute;left:0;top:0;opacity:0.4} +#pvideo_viewer .timeBar{position:relative;z-index: 222;background:transparent} +#pvideo_viewer h3{font-family:monospace} diff --git a/web/libs/css/dash2.ptzcontrols.css b/web/libs/css/dash2.ptzcontrols.css new file mode 100644 index 00000000..0ecee292 --- /dev/null +++ b/web/libs/css/dash2.ptzcontrols.css @@ -0,0 +1,89 @@ +/*Control Pad*/ +.PTZ_controls { + z-index: 111; + position: absolute; + left: 20px; + top: 20px; + margin:0; + display: inline-block; + width: 120px; +} +.PTZ_controls .btn-group{margin-top:10px} +.PTZ_controls .pad { + position: relative; + height: 120px; + width: 120px; + background: #b7b7b7; + border-radius: 50%; + box-shadow: inset 0 0 1px rgba(120, 120, 120, 0.6), inset 0 2px 2px rgba(0, 0, 0, 0.1), 0 2px 2px rgba(240, 240, 240, 0.4); +} +.PTZ_controls .control { + position: absolute; +} +.PTZ_controls .pad .control { + height: 30px; + width: 30px; + background: #636363; + box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.6), 0 0 0 3px rgba(60, 60, 60, 0.2), 0 0 0 4px rgba(60, 60, 60, 0.2); + border-radius: 2px; +} +.PTZ_controls .zoom_in{ + top: 0; + right: 0; +} +.PTZ_controls .zoom_out{ + bottom: 0; + right: 0; +} +.PTZ_controls .nv_enabled{ + top: 0; + right: 0; +} +.PTZ_controls .nv_disable{ + bottom: 0; + right: 0; +} +.PTZ_controls .pad .top { + top: 15px; + left: 50%; + margin: 0 0 0 -15px; +} +.PTZ_controls .pad .left { + top: 45px; + left: 15px; +} +.PTZ_controls .pad .right { + top: 45px; + right: 15px; +} +.PTZ_controls .pad .control.right:before { + transform: rotate(90deg) translate(-3px, -5px); +} +.PTZ_controls .pad .bottom { + bottom: 15px; + left: 50%; + margin: 0 0 0 -15px; +} +/* Overlap the other controls to hide box-shadow */ +.PTZ_controls .pad .middle { + height: 34px; + width: 34px; + z-index: 5; + top: 43px; + left: 50%; + margin: 0 0 0 -17px; + box-shadow: none; + border-radius: 3px; +} +.PTZ_controls .pad .middle:after { + position: absolute; + top: 50%; + left: 50%; + margin: -35% 0 0 -35%; + content: ''; + background: #636363; + height: 70%; + width: 70%; + border-radius: 100%; + box-shadow: inset 0 0 2px rgba(120, 120, 120, 0.6), inset 0 2px 8px rgba(0, 0, 0, 0.1), 0 2px 2px rgba(240, 240, 240, 0.2); +} diff --git a/web/libs/css/dash2.regioneditor.css b/web/libs/css/dash2.regioneditor.css new file mode 100644 index 00000000..09d3ee60 --- /dev/null +++ b/web/libs/css/dash2.regioneditor.css @@ -0,0 +1,5 @@ +#region_editor .modal-body{text-align:center;overflow:auto;max-height:800px} +#region_editor .canvas_holder{position:relative;display:inline-block;overflow:auto;min-height:450px} +#region_editor .cord_element{position:absolute;background:rgba(221, 221, 221, 0.8);z-index:11;padding:5px;} +#region_editor .cord_element.selected{z-index:12;} +#region_editor .cord_element .controls{margin-bottom:5px;} diff --git a/web/libs/css/dash2.righttoleft.css b/web/libs/css/dash2.righttoleft.css new file mode 100644 index 00000000..39d59a40 --- /dev/null +++ b/web/libs/css/dash2.righttoleft.css @@ -0,0 +1,11 @@ +.right-to-left {text-align:right} +.right-to-left select{direction: rtl;} +.right-to-left input,.right-to-left textarea{direction: rtl;text-align:right} +.right-to-left .form-group label span{padding-right:10px} +.right-to-left .modal-footer{text-align:left} +.right-to-left .mdl-menu__item>div>*{flex:1} +.right-to-left .mdl-menu__item>div>i{margin-right:0;margin-left:5px} +.right-to-left .mdl-menu__item{text-align:right} +.right-to-left .mdl-menu__item i{float:right} +.right-to-left .pull-right,.right-to-left .close{float:left!important} +.right-to-left .pull-left,.right-to-left .mdl-menu__item span{float:right!important} diff --git a/web/libs/css/dash2.timelapse.css b/web/libs/css/dash2.timelapse.css new file mode 100644 index 00000000..924bc2fc --- /dev/null +++ b/web/libs/css/dash2.timelapse.css @@ -0,0 +1,13 @@ +#timelapse_video_line{overflow-y:scroll;overflow-x:hidden;max-height:400px;margin:0;text-align:left} +#timelapse_video_display .videoBefore,#timelapse_video_display .videoAfter{display:none} +.timelapse_video:not(:last-child){border-bottom:1px solid #444;} +.timelapse_video .frame{width:50px;height:50px;background-size:cover;background-position:center;border-radius:5px} +.timelapse_video>div>div:not(:last-child){padding-right:10px} +.timelapse_video .flex-block:not(:last-child){padding-bottom:10px} +.timelapse_video.list-group-item{padding:10px} +.timelapse_hud{position: relative;background:#000} +.timelapse_hud .timelapse_playRate{position: absolute;font-family: monospace;top:10px;right:0;left:0;margin:auto;font-size:23px} +#timelapse .progress-bar{transition:0.5s!important} +.timelapse_hud .controlBar{position: absolute;background:rgba(22,22,22,0.8);width:100%;left:0;bottom:0;} +.timelapse_hud .hover-hide{opacity:0} +.timelapse_hud:hover .hover-hide{opacity:1;z-index:5} diff --git a/web/libs/css/main.dash2.css b/web/libs/css/main.dash2.css index a62515ca..fd7f402e 100644 --- a/web/libs/css/main.dash2.css +++ b/web/libs/css/main.dash2.css @@ -1,87 +1,3 @@ -/*Cusotm Bootstrap*/ -.col-5ths, -.col-sm-5ths, -.col-md-5ths, -.col-lg-5ths { - position: relative; - min-height: 1px; - padding-right: 15px; - padding-left: 15px; -} - -.col-5ths { - -webkit-box-flex: 0; - -webkit-flex: 0 0 20%; - -ms-flex: 0 0 20%; - flex: 0 0 20%; - max-width: 20%; -} - -@media (min-width: 576px) { - .col-sm-5ths { - -webkit-box-flex: 0; - -webkit-flex: 0 0 20%; - -ms-flex: 0 0 20%; - flex: 0 0 20%; - max-width: 20%; - } -} - -@media (min-width: 768px) { - .col-md-5ths { - -webkit-box-flex: 0; - -webkit-flex: 0 0 20%; - -ms-flex: 0 0 20%; - flex: 0 0 20%; - max-width: 20%; - } -} -::-webkit-scrollbar-thumb:hover { - background-color:#bd9565; -} -::-webkit-scrollbar-thumb { - background-color:#bd9565; - border: 2px solid transparent; - border-radius: 10px; - background-clip: padding-box; -} -/**/ -.flex{display:flex} -.flex>div{flex:1} -.flex-block{display:inline-flex;width:100%;flex-flow: row wrap;} -.flex-unit-3{flex:3} -.flex-inline{display: inline-flex;position:relative} -@import (less) "../less/pie.less"; -ul{list-style:none} -*{transition:0.2s;box-sizing:border-box} -.affix-top{position:fixed} -.no-padding{padding:0!important} -.no-margin{margin:0!important} -.pre-inline{white-space: normal;word-break: normal} -.pre-inline>ul{margin:0;padding:0} -a{cursor:pointer} -nav h4{cursor:default;font-size:95%;padding:16px 40px;font-weight:100;text-transform:uppercase;letter-spacing:2px} -.m-r{margin-right:10px} -.m-b{margin-bottom:10px} -.m-t{margin-top:10px} -.m-l{margin-left:10px} -.overflow-hidden{overflow: hidden!important} -.list-inline{list-style:none} -.list-inline li{display:inline-block;vertical-align: top;margin:0;} -.truncate{width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;} -img{max-width:100%} -.display-table{display:table;width:100%} -.display-inline{display:inline-block} -.display-table-cell{display:table-cell} -.small{font-size:80%} -.super-center{position:absolute;left:0;top:0;right:0;bottom:0;margin:auto;width: 4em;height: 1em;} -.jpegMode .cpu_load .progress-bar,.jpegMode .ram_load .progress-bar{background-color:#5cb85c} -.jpegMode [system="jpegToggle"],[system].text-success{color:#5cb85c!important} -.permission_monitor_edit{display:none} -.permission_video_delete{display:none} -.nodata .divider{margin:5px 0} -.loading .divider{margin:5px 0} - #accbtn{ text-overflow: ellipsis; white-space: nowrap; @@ -89,75 +5,6 @@ img{max-width:100%} padding: 0; } -.monitor_item .stream-hud{opacity:0;position:absolute;top:0;left:0;width:100%;height:100%;z-index:2} -.monitor_item .stream-hud .camera_cpu_usage{position:absolute;top:0;left:0;width: 100%;} -.monitor_item .stream-hud .camera_cpu_usage .progress{width: 100%;} -.monitor_item .stream-hud .camera_cpu_usage:hover .progress{height:20px;transition:0.2s} -.monitor_item .stream-hud .controls{position:absolute;top:10px;left:10px;} -.monitor_item .stream-hud:hover{opacity:1} -.monitor_item .stream-hud .bottom-text{position:absolute;bottom:0;left:0;width:100%;padding:5px;text-shadow: 0 0 10px #333;} -.monitor_item .stream-hud .bottom-text .detector-fade{background: rgba(0,0,0,0.4);padding:10px 20px;border-radius:10px} -.monitor_item .stream-hud .lamp{position:absolute;top:5px;right:5px;z-index:1;text-shadow: 0 0 15px #333;} -.monitor_item[mode="Disabled"] .stream-hud .lamp{color:#5d5d5d} -.monitor_item[mode="Watch Only"] .stream-hud .lamp{color:#5da8e8} -.monitor_item[mode="Idle"] .stream-hud .lamp{color:#fff} -.monitor_item[mode="Record"] .stream-hud .lamp{color:#d9534f} -/*.data-menu{max-height:700px}*/ -.data-menu:not(:last-child){border-right:1px solid #fff;} -.data-menu.logs{list-style:none;} -.monitor_item .motionVision{display:none} - -.monitor_item .grid-stack-item-content{width:100%!important;left:0!important;right:0!important} -.monitor_item .ui-resizable-se {bottom: 10px!important;} -.monitor_item .stream-block{position: relative;text-align: center} -.monitor_item .mdl-data_window{overflow-x: auto;background:rgba(0,0,0,0.7);color:#fff;height:100%} -.monitor_item .mdl-data_window:not(.col-md-6){width:0;min-width:0;height:0px;min-height:0} - - -.monitor_item.fullscreen img.stream-element{height:100%;width:auto} -.monitor_item.fullscreen canvas.stream-element{height:auto;width:auto;background-color:black;} -.monitor_item .stream-element{border: 0;object-fit: fill;height: 100%;width:100%} -.monitor_item{position:relative;padding:0;transition:none;background:#000} -.monitor_item .mdl-card{min-height:auto;border:1px solid #272727;border-radius:0px;overflow:hidden} -.monitor_item .mdl-card__media{position:relative;padding:0!important;display:block!important;background:#000;} -.monitor_item.selected .stream-element{height:600px} -.monitor_item.selected .fa-expand:before{content:"\f066"} -.monitor_item .mdl-card__supporting-text{background:#222;color:#fff!important;display:block;min-height:auto!important} -.monitor_item.detector_triggered .detector-fade{opacity:1} -.monitor_item .detector-fade{opacity:0} -.monitor_item .indifference{position:absolute;width:100%;left:0;top:0;transition:0.2s;} -.monitor_item .progress{width:100%;background:#333;box-shadow:0;} -.monitor_item .indifference:hover .progress{height:20px;transition:0.2s} -.hide_indifference .indifference{display:none!important} -.hide_indifference [class_toggle="hide_indifference"]{color:#d9534f!important} -.monitor_item .mdl-card:not(.mdl-cell--4-col-desktop) .mdl-card__supporting-text .monitor_details{display:none;font-size:90%;margin-bottom:10px} -.monitor_item[mode="Record"] [mode="record"]{display:none} -.monitor_item[mode="Watch Only"] [mode="start"]{display:none} -.monitor_item .stream-hud .controls .btn{opacity:0.7} -.monitor_item.doObjectDetection .progress-bar{background-color: #57d94f} - -@media screen and (max-width:1500px){ - .monitor_item .mdl-card__supporting-text .btn{ - padding: 5px 10px; - font-size: 11px; - line-height: 1.5; - } -} - -#monitors_live .monitor_item [class_toggle="show_logs"]{display:none} -#monitors_live .monitor_item .indifference{top:-5px} -#monitors_live .monitor_item .mdl-cell--8-col{width:100%;border:0;border-radius:0;margin:0;position:relative} -#monitors_live .monitor_item .mdl-cell--4-col-desktop,.monitor_item .mdl-card__supporting-text{display:none} -#monitors_live .monitor_item .mdl-card__supporting-text .monitor_details,#monitors_live .monitor_item .mdl-card__supporting-text .btn-group{display:none;text-align:center} -#monitors_live .monitor_item .mdl-card__supporting-text:not(.meta){display:block;position:absolute;bottom:0;left:0;height:0;padding:0;} -#monitors_live .monitor_item.show_data .mdl-card__supporting-text:not(.meta){width:50%} -#monitors_live .monitor_item.detector_triggered .mdl-card__supporting-text:not(.meta) .indifference{opacity:0.5;} -#monitors_live .monitor_item:hover .mdl-card__supporting-text:not(.meta){padding:15px;z-index:15;height:auto;} -#monitors_live .monitor_item:hover .mdl-card__supporting-text .monitor_details{display:block} -#monitors_live .monitor_item:hover .mdl-card__supporting-text .btn-group{display:inline-block} - - - #vis_pwrvideo{height:250px} #monSectionStreamChannels,#monSectionInputMaps{margin-bottom: 15px;} #monSectionStreamChannels:empty,#monSectionInputMaps:empty{display:none} @@ -169,19 +16,12 @@ img{max-width:100%} .demo-blog .demo-blog__posts.montage{max-width:100%} - +.mdl-layout__drawer{overflow-y: visible!important} +.hide-side .mdl-layout__drawer{overflow-y: hidden} .mdl-layout__header-row{padding-left:10!important} .mdl-layout__header-row .nav>li>a{border-radius:50%;} .mdl-layout__drawer-button i{position:absolute;top:0;right:0;bottom:0;left:0;margin:auto;height:1em;color:#fff} -.data-menu{text-align:left} -.data-menu ul,.side-menu ul{list-style:none;margin:0;padding:0;} -.data-menu li,.side-menu li{ - border-bottom:1px solid #54502d;padding:10px; -} -.data-menu .progress-circle{margin:0 10px 0 0;position:relative;height:40px;width:40px;float:left} -.data-menu .progress-circle span:after{content:''} -img.circle-img,div.circle-img{border-radius:50%;height:50px;width:50px} -.circle-img.sm{height:25px;width:25px} + .video_video{margin:auto;max-width:100%;max-height:600px;} #confirm_window .video_video{margin-top:15px} @@ -200,60 +40,9 @@ img.circle-img,div.circle-img{border-radius:50%;height:50px;width:50px} .flex-container-modal-body .flex-block>div{flex:1;float:none} .modal{overflow:auto!important} -form.modal-body{margin:0} -#region_editor .modal-body{text-align:center;overflow:auto;max-height:800px} -#region_editor .canvas_holder{position:relative;display:inline-block;overflow:auto;min-height:450px} -#region_editor .cord_element{position:absolute;background:rgba(221, 221, 221, 0.8);z-index:11;padding:5px;} -#region_editor .cord_element.selected{z-index:12;} -#region_editor .cord_element .controls{margin-bottom:5px;} -.form-group label span{padding:5px;font-weight: 400;color: #2d2d2d;display:block;border-bottom: 1px dotted #ddd;font-size: 10pt;} -.form-group label{display:table} -.form-group label>div{display:table-cell} -.form-group label>div:nth-child(2n-1){width:30%} -.form-group label>div:nth-child(2){width:70%;padding:5px;border:1px solid #dedede;border-radius:5px} -.dark .form-group label>div,.dark .form-group label>div>span{border-color:#454545;color:#fff} -.important.form-group label>div:nth-child(2),.important.form-group label>div>span{border-color:red} -.form-group label span small{margin-left: 2px;display:block;font-weight: 600;} -.form-group-group .round-left{border-radius: 50px 0 0 50px;margin-left:10px} -.form-group-group blockquote:before,.form-group-group blockquote:after{display:none!important} -.form-group-group blockquote{letter-spacing:normal;font-style:normal} -.form-group-group blockquote p:empty{display:none} -.form-group-group blockquote p{font-size:inherit} -.form-group-group blockquote p:last-child{margin-bottom:0} -.form-group-group-group>div,.form-group-group-group .h_us_advanced>div{margin-bottom:15px;} -.form-group-group{padding:0 10px 10px 10px;overflow:hidden;margin-bottom:15px;border-radius:5px;border:1px solid #ddd;background:#fff} -.form-group-group table{width:100%} -.form-group-group table tr td{padding:10px 5px} -.form-group-group table tr:not(:last-child) td{border-bottom:1px dotted #eee} -.form-group-group .mdl-list__item{border-bottom:1px solid #eee;} -.form-group-group .mdl-list__item:hover{background:#e6e6e6;border-radius:4px;} -.dark .form-group-group .mdl-list__item{color:#fff;border-bottom:1px solid #444;} -.dark .form-group-group .mdl-list__item:hover{background:#555;} -.form-group-group:last-child,.form-group-group > .form-group:last-child{margin-bottom:0} -.form-group-group h4{margin:0 -10px 15px -10px;padding:15px;background:#ddd;} -.form-group-group h4 small{color:#fff;} -.form-group-group.red{border-color:#d9534f} -.form-group-group.red h4{background:#d9534f;color:#fff} -.form-group-group.purple{border-color:#3f51b5} -.form-group-group.purple h4{background:#3f51b5;color:#fff} -.form-group-group.blue{border-color:#337ab7} -.form-group-group.blue h4{background:#337ab7;color:#fff} -.form-group-group.navy{border-color:#31708f} -.form-group-group.navy h4{background:#31708f;color:#fff} -.form-group-group.green{border-color:#449d44} -.form-group-group.green h4{background:#449d44;color:#fff} -.form-group-group.forestgreen{border-color:#1e4046} -.form-group-group.forestgreen h4{background:#1e4046;color:#fff} -.form-group-group.orange{border-color:#c49a68} -.form-group-group.orange h4{background:#c49a68;color:#fff} -.form-group-group.grey{border-color:#777} -.form-group-group.grey h4{background:#777;color:#fff} -.dark .form-group-group{background:#222} + .videos_list .title{font-size:12pt;padding:0 10px} -[status="1"] .btn[video="launch"],[data-status="1"] .btn[video="launch"]{background:#337ab7;border-color:#337ab7} -[status="2"] .btn[launch="video"],[status="2"] .btn[video="launch"],[data-status="2"] .btn[video="launch"]{background:#a59100;border-color:#a59100} -.signal.red{background:#c9302c} -.signal.green{background:#5cb85c} + .demo-drawer{background:#2b2a2a;color:#fff;} .demo-drawer.mdl-layout__drawer .mdl-navigation{padding-top:0;} .demo-drawer::-webkit-scrollbar{display:none;} @@ -285,6 +74,8 @@ form.modal-body{margin:0} .nav-xs.side-menu.list-blocks .monitor_block img{width:40px;height:40px;} .side-menu.list-blocks .monitor_block .box{width:calc(100% - 70px);display:inline-block} .nav-xs.side-menu.list-blocks .monitor_block .list-data{display:none} +.side-menu .mdl-menu{z-index: 12} + #monitors_list .monitor_block{transition:none} .dropdown-menu.scrollable{max-height:300px} .upload_file input{display:none} @@ -306,39 +97,11 @@ form.modal-body{margin:0} .form-group label{width:100%} -#pvideo_viewer iframe{border:0;width:100%;height:350px;margin-bottom:10px;overflow:hidden} -#pvideo_viewer video{max-height:300px;max-width:100%;} -#pvideo_viewer .holder{height:300px;} -#pvideo_viewer h3{margin-top:0} -#pvideo_viewer .progressBar{position:relative;} -#pvideo_viewer .bufferBar{position:absolute;left:0;top:0;opacity:0.4} -#pvideo_viewer .timeBar{position:relative;z-index: 222;background:transparent} -#pvideo_viewer h3{font-family:monospace} + #vis_monitors{overflow:auto;max-height:400px} #vis_monitors .btn-group-vertical{width:100%} -/*timeline*/ -#timelapse_video_line{overflow-y:scroll;overflow-x:hidden;max-height:400px;margin:0;text-align:left} -#timelapse_video_display .videoBefore,#timelapse_video_display .videoAfter{display:none} -.timelapse_video:not(:last-child){border-bottom:1px solid #444;} -.timelapse_video .frame{width:50px;height:50px;background-size:cover;background-position:center;border-radius:5px} -.timelapse_video>div>div:not(:last-child){padding-right:10px} -.timelapse_video .flex-block:not(:last-child){padding-bottom:10px} -.timelapse_video.list-group-item{padding:10px} -.timelapse_hud{position: relative;background:#000} -.timelapse_hud .timelapse_playRate{position: absolute;font-family: monospace;top:10px;right:0;left:0;margin:auto;font-size:23px} -#timelapse .progress-bar{transition:0.5s!important} -.timelapse_hud .controlBar{position: absolute;background:rgba(22,22,22,0.8);width:100%;left:0;bottom:0;} -.timelapse_hud .hover-hide{opacity:0} -.timelapse_hud:hover .hover-hide{opacity:1;z-index:5} -.video_grid{overflow: auto;height: 100%;display: block;} -.video_grid .col-md-2{padding-left:5px;padding-right:5px;padding-bottom:10px} -.video_grid .thumb{width:100%;height:150px;display:inline-block;background-size:cover;position:relative;overflow:hidden;border-radius:4px;border:1px solid #000;box-shadow:0 0 10px #151515} -.video_grid .thumb .title-strip, .video_grid .thumb .button-strip{width:100%;position:absolute;left:0;background:rgba(0,0,0,0.7);color:#fff;padding:4px} -.video_grid .thumb .title-strip{top:0;opacity:0.5} -.video_grid .thumb .button-strip{bottom:0;opacity:0} -.video_grid .thumb:hover .title-strip, .video_grid .thumb:hover .button-strip{opacity:1} .table-striped>tbody>tr>td{vertical-align:middle} .table-striped .thumbnail{width:100px;height:80px;border-radius:5px;margin:0;display:inline-block;} @@ -352,13 +115,6 @@ form.modal-body{margin:0} background-color: #c49a68; border-color: #c49a68; } - -.dark.modal .modal-header,.dark.modal .modal-footer{background:#333;border-color:#444;} -.dark.modal .modal-header{color:#fff;} -.dark.modal .modal-footer>*:not(.btn){color:#fff;} -.dark.modal .modal-body{background:#333;} -.dark.modal .close{color:#fff;} -.dark.modal{color:#fff;} .dark .table-striped>tbody>tr:nth-of-type(even){background:#616161} .dark .table-striped>tbody>tr>td{border-color:#222;color:#fff} .dark .table-striped>thead>tr>th{border-color:#222;color:#fff;background:#616161;vertical-align:middle;} @@ -481,95 +237,10 @@ ul.msg_list li .message { .mdl-js-layout.hide-side:not(.is-small-screen)>.mdl-layout__header{ margin-left: 0px;width:100%;transition:0.2s } -/*Control Pad*/ -.PTZ_controls { - z-index: 111; - position: absolute; - left: 20px; - top: 20px; - margin:0; - display: inline-block; - width: 120px; -} -.PTZ_controls .btn-group{margin-top:10px} -.PTZ_controls .pad { - position: relative; - height: 120px; - width: 120px; - background: #b7b7b7; - border-radius: 50%; - box-shadow: inset 0 0 1px rgba(120, 120, 120, 0.6), inset 0 2px 2px rgba(0, 0, 0, 0.1), 0 2px 2px rgba(240, 240, 240, 0.4); -} -.PTZ_controls .control { - position: absolute; -} -.PTZ_controls .pad .control { - height: 30px; - width: 30px; - background: #636363; - box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.6), 0 0 0 3px rgba(60, 60, 60, 0.2), 0 0 0 4px rgba(60, 60, 60, 0.2); - border-radius: 2px; -} -.PTZ_controls .zoom_in{ - top: 0; - right: 0; -} -.PTZ_controls .zoom_out{ - bottom: 0; - right: 0; -} -.PTZ_controls .nv_enabled{ - top: 0; - right: 0; -} -.PTZ_controls .nv_disable{ - bottom: 0; - right: 0; -} -.PTZ_controls .pad .top { - top: 15px; - left: 50%; - margin: 0 0 0 -15px; -} -.PTZ_controls .pad .left { - top: 45px; - left: 15px; -} -.PTZ_controls .pad .right { - top: 45px; - right: 15px; -} -.PTZ_controls .pad .control.right:before { - transform: rotate(90deg) translate(-3px, -5px); -} -.PTZ_controls .pad .bottom { - bottom: 15px; - left: 50%; - margin: 0 0 0 -15px; -} -/* Overlap the other controls to hide box-shadow */ -.PTZ_controls .pad .middle { - height: 34px; - width: 34px; - z-index: 5; - top: 43px; - left: 50%; - margin: 0 0 0 -17px; - box-shadow: none; - border-radius: 3px; -} -.PTZ_controls .pad .middle:after { - position: absolute; - top: 50%; - left: 50%; - margin: -35% 0 0 -35%; - content: ''; - background: #636363; - height: 70%; - width: 70%; - border-radius: 100%; - box-shadow: inset 0 0 2px rgba(120, 120, 120, 0.6), inset 0 2px 8px rgba(0, 0, 0, 0.1), 0 2px 2px rgba(240, 240, 240, 0.2); -} +.mdl-menu__item>div{display:flex;align-items: center;width:100%} +.mdl-menu__item>div>i{margin-right:5px} + + /*Digital Zoom*/ .stream-block{ position: relative; @@ -663,20 +334,7 @@ ul.msg_list li .message { -moz-animation: blink 1s linear infinite; animation: blink 1s linear infinite; } -.mdl-menu__item>div{display:flex;align-items: center;width:100%} -.mdl-menu__item>div>i{margin-right:5px} -/*For languages that are right to left*/ -.right-to-left {text-align:right} -.right-to-left select{direction: rtl;} -.right-to-left input,.right-to-left textarea{direction: rtl;text-align:right} -.right-to-left .form-group label span{padding-right:10px} -.right-to-left .modal-footer{text-align:left} -.right-to-left .mdl-menu__item>div>*{flex:1} -.right-to-left .mdl-menu__item>div>i{margin-right:0;margin-left:5px} -.right-to-left .mdl-menu__item{text-align:right} -.right-to-left .mdl-menu__item i{float:right} -.right-to-left .pull-right,.right-to-left .close{float:left!important} -.right-to-left .pull-left,.right-to-left .mdl-menu__item span{float:right!important} + /* All-CSS Toggle Switch (Checkbox Hack) by Marcus Burnette - https://codepen.io/mburnette/pen/LxNxNg */ .marc-toggle { width: 50px; diff --git a/web/libs/css/main.dash2.old.css b/web/libs/css/main.dash2.old.css new file mode 100644 index 00000000..afd35588 --- /dev/null +++ b/web/libs/css/main.dash2.old.css @@ -0,0 +1,742 @@ +/*Cusotm Bootstrap*/ +.col-5ths, +.col-sm-5ths, +.col-md-5ths, +.col-lg-5ths { + position: relative; + min-height: 1px; + padding-right: 15px; + padding-left: 15px; +} + +.col-5ths { + -webkit-box-flex: 0; + -webkit-flex: 0 0 20%; + -ms-flex: 0 0 20%; + flex: 0 0 20%; + max-width: 20%; +} + +@media (min-width: 576px) { + .col-sm-5ths { + -webkit-box-flex: 0; + -webkit-flex: 0 0 20%; + -ms-flex: 0 0 20%; + flex: 0 0 20%; + max-width: 20%; + } +} + +@media (min-width: 768px) { + .col-md-5ths { + -webkit-box-flex: 0; + -webkit-flex: 0 0 20%; + -ms-flex: 0 0 20%; + flex: 0 0 20%; + max-width: 20%; + } +} +::-webkit-scrollbar-thumb:hover { + background-color:#bd9565; +} +::-webkit-scrollbar-thumb { + background-color:#bd9565; + border: 2px solid transparent; + border-radius: 10px; + background-clip: padding-box; +} +/**/ +.flex{display:flex} +.flex>div{flex:1} +.flex-block{display:inline-flex;width:100%;flex-flow: row wrap;} +.flex-unit-3{flex:3} +.flex-inline{display: inline-flex;position:relative} +@import (less) "../less/pie.less"; +ul{list-style:none} +*{transition:0.2s;box-sizing:border-box} +.affix-top{position:fixed} +.no-padding{padding:0!important} +.no-margin{margin:0!important} +.pre-inline{white-space: normal;word-break: normal} +.pre-inline>ul{margin:0;padding:0} +a{cursor:pointer} +nav h4{cursor:default;font-size:95%;padding:16px 40px;font-weight:100;text-transform:uppercase;letter-spacing:2px} +.m-r{margin-right:10px} +.m-b{margin-bottom:10px} +.m-t{margin-top:10px} +.m-l{margin-left:10px} +.overflow-hidden{overflow: hidden!important} +.list-inline{list-style:none} +.list-inline li{display:inline-block;vertical-align: top;margin:0;} +.truncate{width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;} +img{max-width:100%} +.display-table{display:table;width:100%} +.display-inline{display:inline-block} +.display-table-cell{display:table-cell} +.small{font-size:80%} +.super-center{position:absolute;left:0;top:0;right:0;bottom:0;margin:auto;width: 4em;height: 1em;} +.jpegMode .cpu_load .progress-bar,.jpegMode .ram_load .progress-bar{background-color:#5cb85c} +.jpegMode [system="jpegToggle"],[system].text-success{color:#5cb85c!important} +.permission_monitor_edit{display:none} +.permission_video_delete{display:none} +.nodata .divider{margin:5px 0} +.loading .divider{margin:5px 0} + +#accbtn{ + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + padding: 0; +} + +.monitor_item .stream-hud{opacity:0;position:absolute;top:0;left:0;width:100%;height:100%;z-index:2} +.monitor_item .stream-hud .camera_cpu_usage{position:absolute;top:0;left:0;width: 100%;} +.monitor_item .stream-hud .camera_cpu_usage .progress{width: 100%;} +.monitor_item .stream-hud .camera_cpu_usage:hover .progress{height:20px;transition:0.2s} +.monitor_item .stream-hud .controls{position:absolute;top:10px;left:10px;} +.monitor_item .stream-hud:hover{opacity:1} +.monitor_item .stream-hud .bottom-text{position:absolute;bottom:0;left:0;width:100%;padding:5px;text-shadow: 0 0 10px #333;} +.monitor_item .stream-hud .bottom-text .detector-fade{background: rgba(0,0,0,0.4);padding:10px 20px;border-radius:10px} +.monitor_item .stream-hud .lamp{position:absolute;top:5px;right:5px;z-index:1;text-shadow: 0 0 15px #333;} +.monitor_item[mode="Disabled"] .stream-hud .lamp{color:#5d5d5d} +.monitor_item[mode="Watch Only"] .stream-hud .lamp{color:#5da8e8} +.monitor_item[mode="Idle"] .stream-hud .lamp{color:#fff} +.monitor_item[mode="Record"] .stream-hud .lamp{color:#d9534f} +/*.data-menu{max-height:700px}*/ +.data-menu:not(:last-child){border-right:1px solid #fff;} +.data-menu.logs{list-style:none;} +.monitor_item .motionVision{display:none} + +.monitor_item .grid-stack-item-content{width:100%!important;left:0!important;right:0!important} +.monitor_item .ui-resizable-se {bottom: 10px!important;} +.monitor_item .stream-block{position: relative;text-align: center} +.monitor_item .mdl-data_window{overflow-x: auto;background:rgba(0,0,0,0.7);color:#fff;height:100%} +.monitor_item .mdl-data_window:not(.col-md-6){width:0;min-width:0;height:0px;min-height:0} + + +.monitor_item.fullscreen img.stream-element{height:100%;width:auto} +.monitor_item.fullscreen canvas.stream-element{height:auto;width:auto;background-color:black;} +.monitor_item .stream-element{border: 0;object-fit: fill;height: 100%;width:100%} +.monitor_item{position:relative;padding:0;transition:none;background:#000} +.monitor_item .mdl-card{min-height:auto;border:1px solid #272727;border-radius:0px;overflow:hidden} +.monitor_item .mdl-card__media{position:relative;padding:0!important;display:block!important;background:#000;} +.monitor_item.selected .stream-element{height:600px} +.monitor_item.selected .fa-expand:before{content:"\f066"} +.monitor_item .mdl-card__supporting-text{background:#222;color:#fff!important;display:block;min-height:auto!important} +.monitor_item.detector_triggered .detector-fade{opacity:1} +.monitor_item .detector-fade{opacity:0} +.monitor_item .indifference{position:absolute;width:100%;left:0;top:0;transition:0.2s;} +.monitor_item .progress{width:100%;background:#333;box-shadow:0;} +.monitor_item .indifference:hover .progress{height:20px;transition:0.2s} +.hide_indifference .indifference{display:none!important} +.hide_indifference [class_toggle="hide_indifference"]{color:#d9534f!important} +.monitor_item .mdl-card:not(.mdl-cell--4-col-desktop) .mdl-card__supporting-text .monitor_details{display:none;font-size:90%;margin-bottom:10px} +.monitor_item[mode="Record"] [mode="record"]{display:none} +.monitor_item[mode="Watch Only"] [mode="start"]{display:none} +.monitor_item .stream-hud .controls .btn{opacity:0.7} +.monitor_item.doObjectDetection .progress-bar{background-color: #57d94f} + +@media screen and (max-width:1500px){ + .monitor_item .mdl-card__supporting-text .btn{ + padding: 5px 10px; + font-size: 11px; + line-height: 1.5; + } +} + +#monitors_live .monitor_item [class_toggle="show_logs"]{display:none} +#monitors_live .monitor_item .indifference{top:-5px} +#monitors_live .monitor_item .mdl-cell--8-col{width:100%;border:0;border-radius:0;margin:0;position:relative} +#monitors_live .monitor_item .mdl-cell--4-col-desktop,.monitor_item .mdl-card__supporting-text{display:none} +#monitors_live .monitor_item .mdl-card__supporting-text .monitor_details,#monitors_live .monitor_item .mdl-card__supporting-text .btn-group{display:none;text-align:center} +#monitors_live .monitor_item .mdl-card__supporting-text:not(.meta){display:block;position:absolute;bottom:0;left:0;height:0;padding:0;overflow:visible;} +#monitors_live .monitor_item.show_data .mdl-card__supporting-text:not(.meta){width:50%} +#monitors_live .monitor_item.detector_triggered .mdl-card__supporting-text:not(.meta) .indifference{opacity:0.5;} +#monitors_live .monitor_item:hover .mdl-card__supporting-text:not(.meta){padding:15px;z-index:15;height:auto;} +#monitors_live .monitor_item:hover .mdl-card__supporting-text .monitor_details{display:block} +#monitors_live .monitor_item:hover .mdl-card__supporting-text .btn-group{display:inline-block} + + + +#vis_pwrvideo{height:250px} +#monSectionStreamChannels,#monSectionInputMaps{margin-bottom: 15px;} +#monSectionStreamChannels:empty,#monSectionInputMaps:empty{display:none} +#region_editor_live iframe,.canvas_holder canvas{border:0;position:absolute;left:0;top:0} +.canvas_holder canvas{z-index:11} + +.demo-blog .mdl-card__media ~ .mdl-card__supporting-text{position:relative;overflow:initial;cursor:move} +.demo-blog .mdl-card__media ~ .mdl-card__supporting-text .btn-group{cursor: default} + +.demo-blog .demo-blog__posts.montage{max-width:100%} + + +.mdl-layout__header-row{padding-left:10!important} +.mdl-layout__header-row .nav>li>a{border-radius:50%;} +.mdl-layout__drawer-button i{position:absolute;top:0;right:0;bottom:0;left:0;margin:auto;height:1em;color:#fff} +.data-menu{text-align:left} +.data-menu ul,.side-menu ul{list-style:none;margin:0;padding:0;} +.data-menu li,.side-menu li{ + border-bottom:1px solid #54502d;padding:10px; +} +.data-menu .progress-circle{margin:0 10px 0 0;position:relative;height:40px;width:40px;float:left} +.data-menu .progress-circle span:after{content:''} +img.circle-img,div.circle-img{border-radius:50%;height:50px;width:50px} +.circle-img.sm{height:25px;width:25px} + +.video_video{margin:auto;max-width:100%;max-height:600px;} +#confirm_window .video_video{margin-top:15px} +#confirm_window .info-table{margin-top:15px} +@media (max-width: 768px){ + .full.modal .modal-body,.medium.modal .modal-body{max-height:400px;overflow:auto} +} +@media (min-width: 768px){ + .modal.full,.modal.medium{padding-left:0!important;} + .modal.full .modal-dialog{width:calc(100% - 10px)!important;margin: 30px auto;} + .modal.medium .modal-dialog{width:calc(70% - 10px)!important;margin: 30px auto;} + .full.modal .modal-body,.medium.modal .modal-body{height:calc(100% - 200px);overflow:auto} +} + +.flex-container-modal-body{overflow: auto} +.flex-container-modal-body .flex-block>div{flex:1;float:none} + +.modal{overflow:auto!important} +form.modal-body{margin:0} +#region_editor .modal-body{text-align:center;overflow:auto;max-height:800px} +#region_editor .canvas_holder{position:relative;display:inline-block;overflow:auto;min-height:450px} +#region_editor .cord_element{position:absolute;background:rgba(221, 221, 221, 0.8);z-index:11;padding:5px;} +#region_editor .cord_element.selected{z-index:12;} +#region_editor .cord_element .controls{margin-bottom:5px;} +.form-group label span{padding:5px;font-weight: 400;color: #2d2d2d;display:block;border-bottom: 1px dotted #ddd;font-size: 10pt;} +.form-group label{display:table} +.form-group label>div{display:table-cell} +.form-group label>div:nth-child(2n-1){width:30%} +.form-group label>div:nth-child(2){width:70%;padding:5px;border:1px solid #dedede;border-radius:5px} +.dark .form-group label>div,.dark .form-group label>div>span{border-color:#454545;color:#fff} +.important.form-group label>div:nth-child(2),.important.form-group label>div>span{border-color:red} +.form-group label span small{margin-left: 2px;display:block;font-weight: 600;} +.form-group-group .round-left{border-radius: 50px 0 0 50px;margin-left:10px} +.form-group-group blockquote:before,.form-group-group blockquote:after{display:none!important} +.form-group-group blockquote{letter-spacing:normal;font-style:normal} +.form-group-group blockquote p:empty{display:none} +.form-group-group blockquote p{font-size:inherit} +.form-group-group blockquote p:last-child{margin-bottom:0} +.form-group-group-group>div,.form-group-group-group .h_us_advanced>div{margin-bottom:15px;} +.form-group-group{padding:0 10px 10px 10px;overflow:hidden;margin-bottom:15px;border-radius:5px;border:1px solid #ddd;background:#fff} +.form-group-group table{width:100%} +.form-group-group table tr td{padding:10px 5px} +.form-group-group table tr:not(:last-child) td{border-bottom:1px dotted #eee} +.form-group-group .mdl-list__item{border-bottom:1px solid #eee;} +.form-group-group .mdl-list__item:hover{background:#e6e6e6;border-radius:4px;} +.dark .form-group-group .mdl-list__item{color:#fff;border-bottom:1px solid #444;} +.dark .form-group-group .mdl-list__item:hover{background:#555;} +.form-group-group:last-child,.form-group-group > .form-group:last-child{margin-bottom:0} +.form-group-group h4{margin:0 -10px 15px -10px;padding:15px;background:#ddd;} +.form-group-group h4 small{color:#fff;} +.form-group-group.red{border-color:#d9534f} +.form-group-group.red h4{background:#d9534f;color:#fff} +.form-group-group.purple{border-color:#3f51b5} +.form-group-group.purple h4{background:#3f51b5;color:#fff} +.form-group-group.blue{border-color:#337ab7} +.form-group-group.blue h4{background:#337ab7;color:#fff} +.form-group-group.navy{border-color:#31708f} +.form-group-group.navy h4{background:#31708f;color:#fff} +.form-group-group.green{border-color:#449d44} +.form-group-group.green h4{background:#449d44;color:#fff} +.form-group-group.forestgreen{border-color:#1e4046} +.form-group-group.forestgreen h4{background:#1e4046;color:#fff} +.form-group-group.orange{border-color:#c49a68} +.form-group-group.orange h4{background:#c49a68;color:#fff} +.form-group-group.grey{border-color:#777} +.form-group-group.grey h4{background:#777;color:#fff} +.dark .form-group-group{background:#222} +.videos_list .title{font-size:12pt;padding:0 10px} +[status="1"] .btn[video="launch"],[data-status="1"] .btn[video="launch"]{background:#337ab7;border-color:#337ab7} +[status="2"] .btn[launch="video"],[status="2"] .btn[video="launch"],[data-status="2"] .btn[video="launch"]{background:#a59100;border-color:#a59100} +.signal.red{background:#c9302c} +.signal.green{background:#5cb85c} +.demo-drawer{background:#2b2a2a;color:#fff;} +.demo-drawer.mdl-layout__drawer .mdl-navigation{padding-top:0;} +.demo-drawer::-webkit-scrollbar{display:none;} +.small-square-img{height:40px;width:40px;border-radius:5px} + +.side-menu .monitor_block{padding:0;position:relative} +.side-menu .monitor_block img{width:100%;height:75px;cursor:pointer;border: 0.5px inset #263238;} +@media screen and (max-width:1025px){ +.side-menu .monitor_block img{height:175px;} +} +.side-menu .monitor_block:hover .icons{opacity:1} +.side-menu .monitor_block:hover .title{opacity:1} +.side-menu .monitor_block .icons,.side-menu .monitor_block .title{opacity:0;width:100%;bottom:0;left:0;background:rgba(0,0,0,0.6);position:absolute;padding:2.5px;z-index:11;cursor:move} +.side-menu .monitor_block .title{bottom:auto;top:0;color:#fff} +.nav-xs.side-menu .monitor_block{width:100%} +.side-menu .monitor_block .list-data{display:none} +.output_data:empty{display:none} +.output_data{max-height:500px;font-family:monospace;padding:10px;border-radius:5px;background:#f3f3f3;overflow:auto} +.dark .output_data{background:#222;} +#probe .output_data div>div{margin-left:10px} +.side-menu.list-blocks .monitor_block .icons,.side-menu.list-blocks .monitor_block .title{position:inherit;opacity:1;background:none} +.side-menu.list-blocks .monitor_block .title{padding:5px;border-radius:5px;background:#222;} +.side-menu.list-blocks .monitor_block:not(:last-child){border-bottom: 1px solid #54502d;} +.side-menu.list-blocks .monitor_block:first-child{border-top: 1px solid #54502d;} +.side-menu.list-blocks .monitor_block{float:none;width:100%;padding: 10px} +.side-menu.list-blocks .monitor_block.ui-sortable-helper{background:rgba(0,0,0,0.6);border-radius:5px;padding:5px;border:0} +.side-menu.list-blocks .monitor_block .list-data{display:block} +.side-menu.list-blocks .monitor_block img{width:60px;height:60px;cursor:pointer;display:inline-block;margin-right:10px;border-radius:50%;vertical-align:top;border:0} +.nav-xs.side-menu.list-blocks .monitor_block img{width:40px;height:40px;} +.side-menu.list-blocks .monitor_block .box{width:calc(100% - 70px);display:inline-block} +.nav-xs.side-menu.list-blocks .monitor_block .list-data{display:none} +#monitors_list .monitor_block{transition:none} +.dropdown-menu.scrollable{max-height:300px} +.upload_file input{display:none} +#video_preview .stream-objects{right:0;margin:auto;display:inline-block;position:relative;width:auto} +.stream-block,.stream-objects{overflow: hidden!important} +.stream-objects{position:absolute;top:0;left:0;width:100%;height:100%;z-index:1} +.stream-objects .tag{position:absolute;bottom:100%;left:0;background:red;color:#fff;font-family:monospace;font-size:80%;border-radius:5px 5px 0 0 ;padding:3px 5px;} +.stream-objects .stream-detected-object{position:absolute;top:0;left:0;border:3px solid red;background:transparent;border-radius:5px} +.stream-objects .stream-detected-point{position:absolute;top:0;left:0;border:3px solid yellow;background:transparent;border-radius:5px} +.stream-objects .point{position:absolute;top:0;left:0;border:3px solid red;border-radius:50%} + + +#side_menu_right.nav-xs{width:0!important;overflow:hidden} +.side-menu table{color:#fff;} +#main_canvas{background:#333;color:#fff;padding-top:0} +#main_header{background:#222;color:#fff;} +#logs_modal table tr td:first-child{width:10%} +[class_toggle]{cursor:pointer} + +.form-group label{width:100%} + +#pvideo_viewer iframe{border:0;width:100%;height:350px;margin-bottom:10px;overflow:hidden} +#pvideo_viewer video{max-height:300px;max-width:100%;} +#pvideo_viewer .holder{height:300px;} +#pvideo_viewer h3{margin-top:0} +#pvideo_viewer .progressBar{position:relative;} +#pvideo_viewer .bufferBar{position:absolute;left:0;top:0;opacity:0.4} +#pvideo_viewer .timeBar{position:relative;z-index: 222;background:transparent} +#pvideo_viewer h3{font-family:monospace} + +#vis_monitors{overflow:auto;max-height:400px} +#vis_monitors .btn-group-vertical{width:100%} + +/*timeline*/ +#timelapse_video_line{overflow-y:scroll;overflow-x:hidden;max-height:400px;margin:0;text-align:left} +#timelapse_video_display .videoBefore,#timelapse_video_display .videoAfter{display:none} +.timelapse_video:not(:last-child){border-bottom:1px solid #444;} +.timelapse_video .frame{width:50px;height:50px;background-size:cover;background-position:center;border-radius:5px} +.timelapse_video>div>div:not(:last-child){padding-right:10px} +.timelapse_video .flex-block:not(:last-child){padding-bottom:10px} +.timelapse_video.list-group-item{padding:10px} +.timelapse_hud{position: relative;background:#000} +.timelapse_hud .timelapse_playRate{position: absolute;font-family: monospace;top:10px;right:0;left:0;margin:auto;font-size:23px} +#timelapse .progress-bar{transition:0.5s!important} +.timelapse_hud .controlBar{position: absolute;background:rgba(22,22,22,0.8);width:100%;left:0;bottom:0;} +.timelapse_hud .hover-hide{opacity:0} +.timelapse_hud:hover .hover-hide{opacity:1;z-index:5} +.video_grid{overflow: auto;height: 100%;display: block;} +.video_grid .col-md-2{padding-left:5px;padding-right:5px;padding-bottom:10px} +.video_grid .thumb{width:100%;height:150px;display:inline-block;background-size:cover;position:relative;overflow:hidden;border-radius:4px;border:1px solid #000;box-shadow:0 0 10px #151515} +.video_grid .thumb .title-strip, .video_grid .thumb .button-strip{width:100%;position:absolute;left:0;background:rgba(0,0,0,0.7);color:#fff;padding:4px} +.video_grid .thumb .title-strip{top:0;opacity:0.5} +.video_grid .thumb .button-strip{bottom:0;opacity:0} +.video_grid .thumb:hover .title-strip, .video_grid .thumb:hover .button-strip{opacity:1} + +.table-striped>tbody>tr>td{vertical-align:middle} +.table-striped .thumbnail{width:100px;height:80px;border-radius:5px;margin:0;display:inline-block;} +#motion_list{height:155px;overflow:auto;border-radius:5px;border:1px solid #444;position:relative;background: #222;margin:0} +.dark .list-group-item{border-color: #444;background:#222} +.dark .list-group-item.active{background:#c49a68;border-color:#a7865f} +.novideos{text-transform: uppercase;text-align: center;border-bottom:0!important;padding-top: 55%!important;letter-spacing:2px} + +.btn-warning { + color: #fff; + background-color: #c49a68; + border-color: #c49a68; +} + +.dark.modal .modal-header,.dark.modal .modal-footer{background:#333;border-color:#444;} +.dark.modal .modal-header{color:#fff;} +.dark.modal .modal-footer>*:not(.btn){color:#fff;} +.dark.modal .modal-body{background:#333;} +.dark.modal .close{color:#fff;} +.dark.modal{color:#fff;} +.dark .table-striped>tbody>tr:nth-of-type(even){background:#616161} +.dark .table-striped>tbody>tr>td{border-color:#222;color:#fff} +.dark .table-striped>thead>tr>th{border-color:#222;color:#fff;background:#616161;vertical-align:middle;} +.dark .table-striped>tbody>tr:nth-of-type(odd){background-color: #4c4747;} +.dark .table>tbody>tr.active>td{background:inherit;border:0} +.dark code{color: #c49a68;background-color: #36333d;} +.dark a:not(.btn){color: #c49a68;} +.follow-list ul{padding:0;margin:0;font-family:"Roboto","Helvetica","Arial",sans-serif;} +.follow-list ul a:not(.btn){color:#fff} +.os_bars{width:600px;display:inline-block;padding:5px 0 0 10px} +@media screen and (max-width: 600px){ + .os_bars{width:200px;} + .os_bars label{padding:2.5px 0;margin:0;font-size:8pt} +} +.os_bars .display-table .display-table-cell{padding:5px;vertical-align:center;width:33%} +.progress{height:5px;margin:0;} +.os_bars label,.os_bars .percent{padding:2.5px 0;margin:0;font-size:7.5pt} +.ui-pnotify-hide .ui-pnotify{display:none!important} +/*cool dropdown thing*/ +ul.msg_list li { + background: #f7f7f7;color:#333; + padding: 5px; + display: list-item; + margin: 6px 6px 0; + width: 96% !important +} + +ul.msg_list li div{display:block} + +ul.msg_list li:last-child { + margin-bottom: 6px; + padding: 10px +} + +ul.msg_list li a { + padding: 3px 5px !important +} +ul.msg_list li .progress { + height:5px;margin:10px 0 0 0; +} + +ul.msg_list li .image img { + border-radius: 2px 2px 2px 2px; + -webkit-border-radius: 2px 2px 2px 2px; + float: left; + margin-right: 10px; + width: 11% +} + +ul.msg_list li .time { + font-size: 11px; + font-style: italic; + font-weight: bold; + position: absolute; +} + +ul.msg_list li .message { + display: block !important; + font-size: 11px +} + +.dropdown-menu.msg_list span { + white-space: normal +} + +.dropdown-menu { + border: medium none; + box-shadow: none; + display: none; + float: left; + font-size: 12px; + left: 0; + list-style: none outside none; + padding: 0; + position: absolute; + text-shadow: none; + top: 100%; + z-index: 9998; + border: 1px solid #D9DEE4; + border-top-left-radius: 0; + border-top-right-radius: 0 +} + +.dropdown-menu>li>a { + color: #5A738E +} + +.navbar-nav .open .dropdown-menu { + position: absolute; + background: #fff; + margin-top: 0; + border: 1px solid #D9DEE4; + -webkit-box-shadow: none; + right: 0; + left: auto; + width: 220px +} +.is-small-screen .nav>li{display:inline-block} +.navbar-nav .open .dropdown-menu li a{padding:7px 15px} +.navbar-nav .open .dropdown-menu.msg_list { + width: 300px +} +.nav>li>a{color:#fff} +.nav>li>a:focus, .nav>li>a:hover,.nav .open>a, .nav .open>a:focus, .nav .open>a:hover{background:#867560} + +.mdl-js-layout.hide-side:not(.is-small-screen){ + +} +@media screen and (min-width: 1025px){ + .mdl-js-layout.hide-side:not(.is-small-screen)>.mdl-layout__drawer { + width: 0px;transition:0.2s + } +} +.mdl-js-layout.hide-side:not(.is-small-screen) .mdl-layout__header .mdl-layout__drawer-button{ + display:none; +} +.mdl-js-layout.hide-side:not(.is-small-screen)>.mdl-layout__content{ + margin-left: 0px;transition:0.2s +} +.mdl-js-layout.hide-side:not(.is-small-screen)>.mdl-layout__header{ + margin-left: 0px;width:100%;transition:0.2s +} +/*Control Pad*/ +.PTZ_controls { + z-index: 111; + position: absolute; + left: 20px; + top: 20px; + margin:0; + display: inline-block; + width: 120px; +} +.PTZ_controls .btn-group{margin-top:10px} +.PTZ_controls .pad { + position: relative; + height: 120px; + width: 120px; + background: #b7b7b7; + border-radius: 50%; + box-shadow: inset 0 0 1px rgba(120, 120, 120, 0.6), inset 0 2px 2px rgba(0, 0, 0, 0.1), 0 2px 2px rgba(240, 240, 240, 0.4); +} +.PTZ_controls .control { + position: absolute; +} +.PTZ_controls .pad .control { + height: 30px; + width: 30px; + background: #636363; + box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.6), 0 0 0 3px rgba(60, 60, 60, 0.2), 0 0 0 4px rgba(60, 60, 60, 0.2); + border-radius: 2px; +} +.PTZ_controls .zoom_in{ + top: 0; + right: 0; +} +.PTZ_controls .zoom_out{ + bottom: 0; + right: 0; +} +.PTZ_controls .nv_enabled{ + top: 0; + right: 0; +} +.PTZ_controls .nv_disable{ + bottom: 0; + right: 0; +} +.PTZ_controls .pad .top { + top: 15px; + left: 50%; + margin: 0 0 0 -15px; +} +.PTZ_controls .pad .left { + top: 45px; + left: 15px; +} +.PTZ_controls .pad .right { + top: 45px; + right: 15px; +} +.PTZ_controls .pad .control.right:before { + transform: rotate(90deg) translate(-3px, -5px); +} +.PTZ_controls .pad .bottom { + bottom: 15px; + left: 50%; + margin: 0 0 0 -15px; +} +/* Overlap the other controls to hide box-shadow */ +.PTZ_controls .pad .middle { + height: 34px; + width: 34px; + z-index: 5; + top: 43px; + left: 50%; + margin: 0 0 0 -17px; + box-shadow: none; + border-radius: 3px; +} +.PTZ_controls .pad .middle:after { + position: absolute; + top: 50%; + left: 50%; + margin: -35% 0 0 -35%; + content: ''; + background: #636363; + height: 70%; + width: 70%; + border-radius: 100%; + box-shadow: inset 0 0 2px rgba(120, 120, 120, 0.6), inset 0 2px 8px rgba(0, 0, 0, 0.1), 0 2px 2px rgba(240, 240, 240, 0.2); +} +/*Digital Zoom*/ +.stream-block{ + position: relative; + overflow: auto; +} +.zoomGlass { + overflow: hidden; + transition: none; + width: 175px; height: 175px; + position: absolute; + border-radius: 15px; + border: 3px solid #ddd; + z-index:9999; +} +.zoomGlass iframe,.zoomGlass canvas{position:absolute;transition: none;} +.zoomGlass .hoverShade{position:absolute;width:100%;height:100%} + +.dark.form-control,.dark .form-control { + display: block; + width: 100%; + height: 35px; + padding: 6px 12px; + font-size: 14px; + line-height: 1.5; + color: #eee; + background-color: #36333d; + background-image: none; + border: 1px solid #444; + border-radius: 4px; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; +} + +.dark.form-control:focus,.dark .form-control:focus { + color: #ddd; + background-color: #333; + box-shadow: none; +} + + +/*** custom checkboxes ***/ + +.checkbox input[type=checkbox] { display:none; } /* to hide the checkbox itself */ +.checkbox input[type=checkbox] + label:before { + font-family: FontAwesome; + display: inline-block; +} + +.checkbox input[type=checkbox] + label:before { content: "\f096"; } /* unchecked icon */ +.checkbox input[type=checkbox] + label:before { letter-spacing: 10px; } /* space between checkbox and label */ + +.checkbox input[type=checkbox]:checked + label:before { content: "\f046"; } /* checked icon */ +.checkbox input[type=checkbox]:checked + label:before { letter-spacing: 5px; } /* allow space for check mark */ + +/*Clock*/ +#time-date {font-size:12px; text-align:center;} +@media screen and (min-width:1025px){ + #clock {padding-right:35px} +} +#clock ul { width:150px; margin:0 auto; padding:0px; list-style:none; text-align:center; } +#clock ul li { display:inline; font-size:1.6em; text-align:center;font-family:monospace;} + +#clock .point { position:relative; -moz-animation:mymove 1s ease infinite; -webkit-animation:mymove 1s ease infinite; } + +/*custom vis.js css*/ +.vis-timeline{background:#212121;color:#fff;border-color:#444} +.vis-time-axis .vis-text{color: #dedede} +.vis-item.vis-range .vis-item-content{background:#333;color:#fff} +.vis-time-axis .vis-grid.vis-minor{border-color:#444} + + +@-moz-document url-prefix() { + .full.modal .modal-body, .medium.modal .modal-body { + height:70% + } +} + +/*animations*/ +@keyframes blink { + 0% { opacity:1 } + 50% { opacity:0 } + 100% { opacity:1 } +} +@-webkit-keyframes blink { + 0% { opacity:1 } + 50% { opacity:0 } + 100% { opacity:1 } +} +.blink,[mode="Record"] .lamp { + -webkit-animation: blink 1s linear infinite; + -moz-animation: blink 1s linear infinite; + animation: blink 1s linear infinite; +} +.mdl-menu__item>div{display:flex;align-items: center;width:100%} +.mdl-menu__item>div>i{margin-right:5px} +/*For languages that are right to left*/ +.right-to-left {text-align:right} +.right-to-left select{direction: rtl;} +.right-to-left input,.right-to-left textarea{direction: rtl;text-align:right} +.right-to-left .form-group label span{padding-right:10px} +.right-to-left .modal-footer{text-align:left} +.right-to-left .mdl-menu__item>div>*{flex:1} +.right-to-left .mdl-menu__item>div>i{margin-right:0;margin-left:5px} +.right-to-left .mdl-menu__item{text-align:right} +.right-to-left .mdl-menu__item i{float:right} +.right-to-left .pull-right,.right-to-left .close{float:left!important} +.right-to-left .pull-left,.right-to-left .mdl-menu__item span{float:right!important} +/* All-CSS Toggle Switch (Checkbox Hack) by Marcus Burnette - https://codepen.io/mburnette/pen/LxNxNg */ +.marc-toggle { + width: 50px; + height: 25px; +} +.marc-toggle.abs-bot-left { + position: absolute; + bottom: 10px; + left: 10px; +} +.marc-toggle.abs-bot-right { + position: absolute; + bottom: 10px; + right: 10px; +} +.marc-toggle input[type=checkbox]{ + height: 0; + width: 0; + visibility: hidden; +} + +.marc-toggle label { + cursor: pointer; + text-indent: -9999px; + width: 100px; + height: 20px; + background: grey; + display: block; + border-radius: 100px; + margin-bottom: 0; + position: relative; +} + +.marc-toggle label:after { + content: ''; + position: absolute; + top: 5px; + left: 5px; + width: 10px; + height: 10px; + background: #fff; + border-radius: 90px; + transition: 0.3s; +} + +.marc-toggle input:checked + label { + background: #00118c; +} + +.marc-toggle input:checked + label:after { + left: calc(100% - 5px); + transform: translateX(-100%); +} + +.marc-toggle label:active:after { + width: 10px; +} + +/*hexagon pattern*/ +.bg-hexagon { + background-color: #054e9f; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='28' height='49' viewBox='0 0 28 49'%3E%3Cg fill-rule='evenodd'%3E%3Cg id='hexagons' fill='%23fdfdfd' fill-opacity='0.4' fill-rule='nonzero'%3E%3Cpath d='M13.99 9.25l13 7.5v15l-13 7.5L1 31.75v-15l12.99-7.5zM3 17.9v12.7l10.99 6.34 11-6.35V17.9l-11-6.34L3 17.9zM0 15l12.98-7.5V0h-2v6.35L0 12.69v2.3zm0 18.5L12.98 41v8h-2v-6.85L0 35.81v-2.3zM15 0v7.5L27.99 15H28v-2.31h-.01L17 6.35V0h-2zm0 49v-8l12.99-7.5H28v2.31h-.01L17 42.15V49h-2z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E"); +} diff --git a/web/libs/css/material.style.css b/web/libs/css/material.style.css index 2cb82427..e971dee7 100644 --- a/web/libs/css/material.style.css +++ b/web/libs/css/material.style.css @@ -608,15 +608,6 @@ _:-ms-input-placeholder, :root .demo-graph { opacity: 0.46; border-radius: 2px; } -.social-btn__twitter { - background-image: url('https://www.gstatic.com/images/icons/material/system/2x/post_twitter_black_24dp.png'); -} -.social-btn__blogger { - background-image: url('https://www.gstatic.com/images/icons/material/system/2x/post_facebook_black_24dp.png'); -} -.social-btn__gplus { - background-image: url('https://www.gstatic.com/images/icons/material/system/2x/post_gplus_black_24dp.png'); -} .social-btn__share { color: rgba(0, 0, 0, 0.54); background: transparent; diff --git a/web/libs/js/dash2.apiwindow.js b/web/libs/js/dash2.apiwindow.js new file mode 100644 index 00000000..5ca46584 --- /dev/null +++ b/web/libs/js/dash2.apiwindow.js @@ -0,0 +1,32 @@ +$(document).ready(function(e){ +//api window +$.apM={e:$('#apis')};$.apM.f=$.apM.e.find('form'); +$.apM.md=$.apM.f.find('[detail]'); +$.apM.md.change($.ccio.form.details).first().change(); +$.apM.f.submit(function(e){ + e.preventDefault();e.e=$(this),e.s=e.e.serializeObject(); + e.er=[]; + if(!e.s.ip||e.s.ip.length<7){e.er.push('Enter atleast one IP')} + if(e.er.length>0){$.apM.e.find('.msg').html(e.er.join('
'));return;} + $.each(e.s,function(n,v){e.s[n]=v.trim()}) + // e.s = { + // "ip": "", + // "details": "{\"get_monitors\":\"1\",\"control_monitors\":\"1\",\"get_logs\":\"1\",\"watch_stream\":\"1\",\"watch_snapshot\":\"1\",\"watch_videos\":\"1\",\"delete_videos\":\"1\"}" + // } + $.post($.ccio.init('location',$user)+$user.auth_token+'/api/'+$user.ke+'/add',{data:JSON.stringify(e.s)},function(d){ + $.ccio.log(d) + }) +}); +$.apM.e.on('click','.delete',function(e){ + e.e=$(this);e.p=e.e.parents('[api_key]'),e.code=e.p.attr('api_key'); + $.confirm.e.modal('show'); + $.confirm.title.text('Delete API Key'); + e.html='Do you want to delete this API key? You cannot recover it.'; + $.confirm.body.html(e.html); + $.confirm.click({title:'Delete',class:'btn-danger'},function(){ + $.post($.ccio.init('location',$user)+$user.auth_token+'/api/'+$user.ke+'/delete',{data:JSON.stringify({code:e.code})},function(d){ + $.ccio.log(d) + }) + }) +}) +}) diff --git a/web/libs/js/dash2.basic.js b/web/libs/js/dash2.basic.js new file mode 100644 index 00000000..71e7987f --- /dev/null +++ b/web/libs/js/dash2.basic.js @@ -0,0 +1,299 @@ +$.ccio.permissionCheck = function(toCheck,monitorId){ + var details = $user.details + if(details.sub && details.allmonitors === '0'){ + var chosenValue = details[toCheck] + if(details[toCheck] instanceof Array && chosenValue.indexOf(monitorId) > -1){ + return true + }else if(chosenValue === '1'){ + return true + } + }else{ + return true + } + return false +} +$.ccio.op = function(r,rr,rrr){ + if(!rrr){rrr={};};if(typeof rrr === 'string'){rrr={n:rrr}};if(!rrr.n){rrr.n='ShinobiOptions_'+location.host} + ii={o:localStorage.getItem(rrr.n)};try{ii.o=JSON.parse(ii.o)}catch(e){ii.o={}} + if(!ii.o){ii.o={}} + if(r&&rr&&!rrr.x){ + ii.o[r]=rr; + } + switch(rrr.x){ + case 0: + delete(ii.o[r]) + break; + case 1: + delete(ii.o[r][rr]) + break; + } + localStorage.setItem(rrr.n,JSON.stringify(ii.o)) + return ii.o +} +$.ccio.log = function(x,y,z){ + if($.ccio.op().browserLog==="1"){ + if(!y){y=''};if(!z){z=''}; + console.log(x,y,z) + } +} +$.ccio.gid = function(x){ + if(!x){x=10};var t = "";var p = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + for( var i=0; i < x; i++ ) + t += p.charAt(Math.floor(Math.random() * p.length)); + return t; +}; +$.ccio.downloadJSON = function(jsonToDownload,filename,errorResponse){ + var arr = jsonToDownload; + if(arr.length===0 && errorResponse){ + errorResponse.type = 'error' + $.ccio.init('note',errorResponse); + return + } + var dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(arr,null,3)); + $('#temp').html('') + .find('a') + .attr('href',dataStr) + .attr('download',filename) + [0].click() +} +$.ccio.timeObject = function(time,isUTC){ + if(isUTC === true){ + return moment(time).utc() + } + return moment(time) +} +$.ccio.base64ArrayBuffer = function(arrayBuffer) { + var base64 = '' + var encodings = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' + + var bytes = new Uint8Array(arrayBuffer) + var byteLength = bytes.byteLength + var byteRemainder = byteLength % 3 + var mainLength = byteLength - byteRemainder + + var a, b, c, d + var chunk + + // Main loop deals with bytes in chunks of 3 + for (var i = 0; i < mainLength; i = i + 3) { + // Combine the three bytes into a single integer + chunk = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2] + + // Use bitmasks to extract 6-bit segments from the triplet + a = (chunk & 16515072) >> 18 // 16515072 = (2^6 - 1) << 18 + b = (chunk & 258048) >> 12 // 258048 = (2^6 - 1) << 12 + c = (chunk & 4032) >> 6 // 4032 = (2^6 - 1) << 6 + d = chunk & 63 // 63 = 2^6 - 1 + + // Convert the raw binary segments to the appropriate ASCII encoding + base64 += encodings[a] + encodings[b] + encodings[c] + encodings[d] + } + + // Deal with the remaining bytes and padding + if (byteRemainder == 1) { + chunk = bytes[mainLength] + + a = (chunk & 252) >> 2 // 252 = (2^6 - 1) << 2 + + // Set the 4 least significant bits to zero + b = (chunk & 3) << 4 // 3 = 2^2 - 1 + + base64 += encodings[a] + encodings[b] + '==' + } else if (byteRemainder == 2) { + chunk = (bytes[mainLength] << 8) | bytes[mainLength + 1] + + a = (chunk & 64512) >> 10 // 64512 = (2^6 - 1) << 10 + b = (chunk & 1008) >> 4 // 1008 = (2^6 - 1) << 4 + + // Set the 2 least significant bits to zero + c = (chunk & 15) << 2 // 15 = 2^4 - 1 + + base64 += encodings[a] + encodings[b] + encodings[c] + '=' + } + + return base64 +} +$.ccio.snapshot=function(e,cb){ + var image_data,url; + e.details=JSON.parse(e.mon.details); + if($.ccio.op().jpeg_on!==true){ + var extend=function(image_data,width,height){ + var len = image_data.length + var arraybuffer = new Uint8Array( len ); + for (var i = 0; i < len; i++) { + arraybuffer[i] = image_data.charCodeAt(i); + } + try { + var blob = new Blob([arraybuffer], {type: 'application/octet-stream'}); + } catch (e) { + var bb = new (window.WebKitBlobBuilder || window.MozBlobBuilder); + bb.append(arraybuffer); + var blob = bb.getBlob('application/octet-stream'); + } + url = (window.URL || window.webkitURL).createObjectURL(blob); + finish(url,image_data,width,height); + try{ + setTimeout(function(){ + URL.revokeObjectURL(url) + },10000) + }catch(er){} + } + var finish = function(url,image_data,width,height){ + cb(url,image_data,width,height); + } + switch(JSON.parse(e.mon.details).stream_type){ + case'hls':case'flv':case'mp4': + $.ccio.snapshotVideo($('[mid='+e.mon.mid+'].monitor_item video')[0],function(base64,video_data,width,height){ + extend(video_data,width,height) + }) + break; + case'mjpeg': + $('#temp').html('') + var c = $('#temp canvas')[0]; + var img = $('img',$('[mid='+e.mon.mid+'].monitor_item .stream-element').contents())[0]; + c.width = img.width; + c.height = img.height; + var ctx = c.getContext('2d'); + ctx.drawImage(img, 0, 0,c.width,c.height); + extend(atob(c.toDataURL('image/jpeg').split(',')[1]),c.width,c.height) + break; + case'h265': + var c = $('[mid='+e.mon.mid+'].monitor_item canvas')[0]; + var ctx = c.getContext('2d'); + extend(atob(c.toDataURL('image/jpeg').split(',')[1]),c.width,c.height) + break; + case'b64': + base64 = e.mon.last_frame.split(',')[1]; + var image_data = new Image(); + image_data.src = base64; + extend(atob(base64),image_data.width,image_data.height) + break; + case'jpeg':case'h265': + url=e.p.find('.stream-element').attr('src'); + image_data = new Image(); + image_data.src = url; + finish(url,image_data,image_data.width,image_data.height); + break; + } + }else{ + url=e.p.find('.stream-element').attr('src'); + image_data = new Image(); + image_data.src = url; + cb(url,image_data,image_data.width,image_data.height); + } +} +$.ccio.snapshotVideo=function(videoElement,cb){ + var image_data; + var base64 + $('#temp').html('') + var c = $('#temp canvas')[0]; + var img = videoElement; + c.width = img.videoWidth; + c.height = img.videoHeight; + var ctx = c.getContext('2d'); + ctx.drawImage(img, 0, 0,c.width,c.height); + base64=c.toDataURL('image/jpeg') + image_data=atob(base64.split(',')[1]); + var arraybuffer = new ArrayBuffer(image_data.length); + var view = new Uint8Array(arraybuffer); + for (var i=0; i'); + }else{ + e.p.append('
'; + break; + case'jpeg': + tmp+=''; + break; + default://base64//h265 + tmp+=''; + break; + } + } + k.e.append(tmp).find('.stream-element').resize(); + if($.ccio.op().switches.monitorMuteAudio === 1){ + k.e.find('video').each(function(n,el){ + el.muted = "muted" + }) + } + break; + case'user-row': + d.e=$('.user-row[uid="'+d.uid+'"][ke="'+d.ke+'"]') + if(d.e.length===0){ + tmp+='
  • '; + tmp+='
    '+d.mail+' : '+d.uid+'
    Logged in
    '; + tmp+='
  • '; + }else{ + d.e.find('.mail').text(d.mail) + d.e.find('.time').livestamp('destroy').toggleClass('livestamped livestamp').text(d.logged_in_at) + } + $.ccio.init('ls') + break; + case'detector-filters-where': + if(!d)d={}; + d.id=$('#filters_where .row').length; + if(!d.p1){d.p1='indifference'} + if(!d.p2){d.p2='='} + if(!d.p3){d.p3=''} + if(!d.p4){d.p4='&&'} + tmp+='
    ' + tmp+='
    ' + tmp+=' ' + tmp+='
    ' + tmp+='
    ' + tmp+=' ' + tmp+='
    ' + tmp+='
    ' + tmp+=' ' + tmp+='
    ' + tmp+='
    ' + tmp+=' ' + tmp+='
    ' + tmp+='
    ' + break; + case'filters-where': + if(!d)d={}; + d.id=$('#filters_where .row').length; + if(!d.p1){d.p1='mid'} + if(!d.p2){d.p2='='} + if(!d.p3){d.p3=''} + tmp+='
    '; + tmp+='
    '; + tmp+=' '; + tmp+='
    '; + tmp+='
    '; + tmp+=' '; + tmp+='
    '; + tmp+='
    '; + tmp+=' '; + tmp+='
    '; + tmp+='
    '; + break; + case 'link-set'://Link Shinobi - 1 set + if(!d.host){d.host=''} + if(!d.ke){d.ke=''} + if(!d.api){d.api=''} + if(!d.secure){d.secure="0"} + tmp+='
    ' + tmp+='

    '+d.host+' '+d.ke+' 

    ' + tmp+='
    ' + tmp+='
    ' + tmp+='
    ' + tmp+='
    ' + tmp+='
    '; + break; + case 'form-group'://Input Map Selector + var fields = [] + if(d.fields){ + if(d.fields instanceof Object){ + fields = [d] + }else{ + fields = d + } + } + $.each(fields,function(n,v){ + var value,hidden + if(!v.attribute)v.attribute=''; + if(!v.placeholder)v.placeholder=''; + if(!v.class)v.class=''; + if(!v.inputType)v.inputType='value'; + if(v.hidden){hidden='style="display:none"'}else{hidden=''}; + if(v.value){value='value=""'}else{value=''}; + tmp+='
    ' + tmp+=' ' + tmp+='
    ' + }) + break; + case 'input-map-selector'://Input Map Selector + if(!d.map){d.map=''} + tmp+='
    ' + tmp+=' ' + tmp+='
    ' + break; + case 'input-map'://Input Map Options + var tempID = $.ccio.gid(); + if(!d.channel){ + var numberOfChannelsDrawn = $('#monSectionInputMaps .input-map').length + d.channel=numberOfChannelsDrawn+1 + } + var fields = [ +// { +// name:'', +// class:'', +// placeholder:'', +// default:'', +// attribute:'', +// type:'text', +// }, + { + name:'type', + label:lang['Input Type'], + default:'h264', + attribute:'selector="h_i_'+tempID+'"', + type:'selector', + choices:[ + {label:lang['H.264 / H.265 / H.265+'],value:'h264'}, + {label:lang['JPEG'],value:'jpeg'}, + {label:lang['MJPEG'],value:'mjpeg'}, + {label:lang['HLS (.m3u8)'],value:'hls'}, + {label:lang['MPEG-4 (.mp4 / .ts)'],value:'mp4'}, + {label:lang['Local'],value:'local'}, + {label:lang['Raw'],value:'raw'}, + ] + }, + { + name:'fulladdress', + label:lang['Full URL Path'], + placeholder:'Example : rtsp://admin:password@123.123.123.123/stream/1', + type:'text', + }, + { + name:'sfps', + label:lang['Monitor Capture Rate'], + placeholder:'', + type:'text', + }, + { + name:'aduration', + label:lang['Analyzation Duration'], + placeholder:'Example : 1000000', + type:'text', + }, + { + name:'probesize', + label:lang['Probe Size'], + placeholder:'Example : 1000000', + type:'text', + }, + { + name:'stream_loop', + label:lang['Loop Stream'], + class:'h_i_'+tempID+'_input h_i_'+tempID+'_mp4 h_i_'+tempID+'_raw', + hidden:true, + default:'0', + type:'selector', + choices:[ + {label:'No',value:'0'}, + {label:'Yes',value:'1'} + ] + }, + { + name:'rtsp_transport', + label:lang['RTSP Transport'], + class:'h_i_'+tempID+'_input h_i_'+tempID+'_h264', + default:'0', + type:'selector', + choices:[ + {label:'Auto',value:''}, + {label:'TCP',value:'tcp'}, + {label:'UDP',value:'udp'} + ] + }, + { + name:'accelerator', + label:lang['Accelerator'], + attribute:'selector="h_accel_'+tempID+'"', + default:'0', + type:'selector', + choices:[ + {label:'No',value:'0'}, + {label:'Yes',value:'1'}, + ] + }, + { + name:'hwaccel', + label:lang['hwaccel'], + class:'h_accel_'+tempID+'_input h_accel_'+tempID+'_1', + hidden:true, + default:'', + type:'selector', + choices: $.ccio.HWAccelChoices + }, + { + name:'hwaccel_vcodec', + label:lang['hwaccel_vcodec'], + class:'h_accel_'+tempID+'_input h_accel_'+tempID+'_1', + hidden:true, + default:'auto', + type:'selector', + choices:[ + {label:lang['Auto'],value:'auto'}, + {label:lang['h264_cuvid'],value:'h264_cuvid',group:'NVIDIA'}, + {label:lang['hevc_cuvid'],value:'hevc_cuvid',group:'NVIDIA'}, + {label:lang['mjpeg_cuvid'],value:'mjpeg_cuvid',group:'NVIDIA'}, + {label:lang['mpeg4_cuvid'],value:'mpeg4_cuvid',group:'NVIDIA'}, + {label:lang['h264_qsv'],value:'h264_qsv',group:'Quick Sync Video'}, + {label:lang['hevc_qsv'],value:'hevc_qsv',group:'Quick Sync Video'}, + {label:lang['mpeg2_qsv'],value:'mpeg2_qsv',group:'Quick Sync Video'}, + {label:lang['h264_mmal'],value:'h264_mmal',group:'Raspberry Pi'}, + {label:lang['mpeg2_mmal'],value:'mpeg2_mmal',group:'Raspberry Pi'}, + {label:lang['mpeg4_mmal'],value:'mpeg4_mmal',group:'Raspberry Pi'}, + ] + }, + { + name:'hwaccel_device', + label:lang['hwaccel_device'], + class:'h_accel_'+tempID+'_input h_accel_'+tempID+'_1', + hidden:true, + placeholder:'Example : /dev/dri/video0', + type:'text', + }, + ]; + tmp+='
    ' + tmp+='

    '+lang["Input"]+' '+lang["Map"]+' : '+d.channel+'' + tmp+='
    ' + tmp+='

    ' + $.each(fields,function(n,v){ + if(!v.attribute)v.attribute=''; + if(!v.placeholder)v.placeholder=''; + if(!v.class)v.class=''; + if(v.hidden){v.hidden='style="display:none"'}else{v.hidden=''}; + tmp+='
    ' + tmp+=' ' + tmp+='
    ' + }) + tmp+='
    ' + break; + case 'stream-channel'://Stream Channel + var tempID = $.ccio.gid(); + if(!d.channel){ + var numberOfChannelsDrawn = $('#monSectionStreamChannels .stream-channel').length + d.channel=numberOfChannelsDrawn + } + tmp+='
    ' + tmp+='

    '+lang["Stream Channel"]+' '+d.channel+'' + tmp+='
    ' + tmp+='

    ' +// tmp+='
    ' +// tmp+=' ' +// tmp+='
    ' + tmp+='
    ' + tmp+='

    '+lang['Input Feed'] + tmp+='
    ' + tmp+=' ' + tmp+='
    ' + tmp+='

    ' + tmp+='
    ' + tmp+='
    ' + tmp+='
    ' + tmp+=' ' + tmp+='
    ' + tmp+='
    ' + tmp+='
    ' + tmp+=' ' + tmp+='
    ' + tmp+='
    ' + tmp+=' ' + tmp+='
    ' + tmp+='
    ' + tmp+=' ' + tmp+='
    ' + tmp+='
    ' + tmp+=' ' + tmp+='
    ' + tmp+='
    ' + tmp+=' ' + tmp+='
    ' + tmp+='
    ' + tmp+='
    ' + tmp+=' ' + tmp+='
    ' + tmp+=' ' + tmp+=' ' + tmp+='
    ' + break; + } + if(z && x !== 2){ + $(z).prepend(tmp) + } + switch(x){ + case 1: + z='#monitors_list .link-monitors-list[auth="'+user.auth_token+'"][ke="'+d.ke+'"]' + if($('.link-monitors-list[auth="'+user.auth_token+'"][ke="'+d.ke+'"]').length===0){ + $("#monitors_list").append('') + var options = { + cellHeight: 80, + verticalMargin: 10, + }; + //monitor="watch_off" + $(z).gridstack(options); + $(z).on('change', function(event, ui) { + var monitors = {} + $.grid.e.find(" .monitor_item").each(function(n,v){ + var el = $(v) + var item = {} + item.ke = el.attr('ke') + item.mid = el.attr('mid') + item.x = el.attr('data-gs-x') + item.y = el.attr('data-gs-y') + item.height = el.attr('data-gs-height') + item.width = el.attr('data-gs-width') + monitors[item.ke+item.mid] = item + }) + user.details.monitorOrder=monitors; + $.ccio.cx({f:'monitorOrder',monitorOrder:monitors},user) + }); + } + $(z).prepend(tmp) + componentHandler.upgradeAllRegistered() + break; + case 0:case 4: + $.ccio.init('ls'); + break; + case 2: + var x = 0; + var y = 0; + var width = $.grid.getMonitorsPerRow() + var height = width; + if(user.details && user.details.monitorOrder && user.details.monitorOrder[d.ke+d.mid]){ + var saved = user.details.monitorOrder[d.ke+d.mid]; + x = saved.x; + y = saved.y; + width = saved.width; + height = saved.height; + } + var autoPlacement = false + if($.ccio.op().switches.monitorOrder !== 1){ + autoPlacement = true + } + $(z).data('gridstack').addWidget($(tmp), x, y, width, height, autoPlacement); + k.e=$('#monitor_live_'+d.mid+user.auth_token); + try{ + if(JSON.parse(d.details).control=="1"){ + k.e.find('[monitor="control_toggle"]').show() + }else{ + k.e.find('.pad').remove(); + k.e.find('[monitor="control_toggle"]').hide() + } + $.ccio.tm('stream-element',d,null,user) + }catch(re){$.ccio.log(re)} + k.mid=d.mid + k.mon=$.ccio.mon[d.ke+d.mid+user.auth_token] + $.ccio.init('monitorInfo',k) + break; + case'detector-filters-where': + $('#detector_filters_where').append(tmp); + $('#detector_filters_where .row [where="p4"][disabled]').prop('disabled',false) + $('#detector_filters_where .row:last [where="p1"]').val(d.p1) + $('#detector_filters_where .row:last [where="p2"]').val(d.p2) + $('#detector_filters_where .row:last [where="p3"]').val(d.p3) + $('#detector_filters_where .row:last [where="p4"]').val(d.p4).prop('disabled',true) + break; + case'filters-where': + $('#filters_where').append(tmp); + $('#filters_where .row:last [where="p1"]').val(d.p1) + $('#filters_where .row:last [where="p2"]').val(d.p2) + $('#filters_where .row:last [where="p3"]').val(d.p3) + break; + case'input-map': + var mapsList = $.aM.maps + mapsList.append(tmp) + mapsList.find('.input-map').last().find('[map-detail="aduration"]').change() + return tempID; + break; + case'stream-channel': + var channeList = $.aM.channels + channeList.append(tmp) + channeList.find('.stream-channel').last().find('[channel-detail="stream_vcodec"]').change() + return tempID; + break; + case'link-set': + $('[links="'+d.host+'"] [link="secure"]').val(d.secure).change() + break; + } + return tmp; +} +$.ccio.pm=function(x,d,z,user){ + var tmp='';if(!d){d={}}; + if(!user){ + user=$user + } + switch(x){ + case 0: + d.mon=$.ccio.mon[d.ke+d.mid+user.auth_token]; + d.ev='.glM'+d.mid+user.auth_token+'.videos_list ul,.glM'+d.mid+user.auth_token+'.videos_monitor_list ul';d.fr=$.ccio.fr.find(d.ev),d.tmp=''; + if(d.fr.length===0){$.ccio.fr.append('

    '+d.mon.name+'

      ')} + if(d.videos&&d.videos.length>0){ + $.each(d.videos,function(n,v){ + if(v.status!==0){ + tmp+=$.ccio.tm(0,v,null,user) + } + }) + }else{ + $('.glM'+d.mid+user.auth_token+'.videos_list,.glM'+d.mid+user.auth_token+'.videos_monitor_list').appendTo($.ccio.fr) + tmp+='
    • No videos
    • '; + } + $(d.ev).html(tmp); + $.ccio.init('ls'); + break; + case 3: + z='#api_list'; + $(z).empty(); + $.each(d,function(n,v){ + tmp+=$.ccio.tm(3,v,null,user); + }) + break; + case'option': + $.each(d,function(n,v){ + tmp+=$.ccio.tm('option',v,null,user); + }) + break; + case'user-row': + $.each(d,function(n,v){ + tmp+=$.ccio.tm('user-row',v,null,user); + }) + z='#users_online' + break; + case'link-set': + $.sM.links.empty() + $.each(d,function(n,v){ + tmp+=$.ccio.tm('link-set',v,'#linkShinobi',user) + }) + break; + } + if(z){ + $(z).prepend(tmp) + } + return tmp; +} diff --git a/web/libs/js/dash2.elements.js b/web/libs/js/dash2.elements.js new file mode 100644 index 00000000..272b58c7 --- /dev/null +++ b/web/libs/js/dash2.elements.js @@ -0,0 +1,767 @@ +$(document).ready(function(e){ + console.log("%cWarning!", "font: 2em monospace; color: red;"); + console.log('%cLeaving the developer console open is fine if you turn off "Network Recording". This is because it will keep a log of all files, including frames and videos segments.', "font: 1.2em monospace; "); + if(!$.ccio.permissionCheck('monitor_create')){ + $('#add_monitor_button_main').remove() + } + $.each(['user_change','monitor_create','view_logs'],function(n,permission){ + if(!$.ccio.permissionCheck(permission)){ + $('.permission_'+permission).remove() + } + }) + + //Group Selector + $.gR={e:$('#group_list'),b:$('#group_list_button')}; + $.gR.drawList=function(){ + var e={}; + e.tmp=''; + $.each($.ccio.init('monGroup'),function(n,v){ + if($user.mon_groups[n]){ + e.tmp+='
    • '+$user.mon_groups[n].name+'
    • ' + } + }) + $.gR.e.html(e.tmp) + } + $.gR.e.on('click','[groups]',function(){ + var e={}; + e.e=$(this), + e.a=e.e.attr('groups'); + var user=$.users[e.e.attr('auth')]; + if(!user){user=$user} + if(user===$user){ + e.chosen_set='watch_on' + }else{ + e.chosen_set='watch_on_links' + } + $.each($.ccio.op()[e.chosen_set],function(n,v){ + $.each(v,function(m,b){ + $.ccio.cx({f:'monitor',ff:'watch_off',id:m,ke:n},user) + }) + }) + $.each($.ccio.mon_groups[e.a],function(n,v){ + $.ccio.cx({f:'monitor',ff:'watch_on',id:v.mid,ke:v.ke},user) + }) + }) + + + //open all monitors + $('[class_toggle="list-blocks"][data-target="#left_menu"]').dblclick(function(){ + $('#monitors_list .monitor_block').each(function(n,v){ + var el = $(v) + var ke = el.attr('ke') + var mid = el.attr('mid') + var auth = el.attr('auth') + var monItem = $('.monitor_item[ke='+ke+'][mid='+mid+'][auth='+auth+']') + if(monItem.length > 0){ + monItem.find('[monitor="watch_on"]').click() + }else{ + el.find('[monitor="watch"]').click() + } + }) + }) + //search monitors list + $('#monitors_list_search').keyup(function(){ + var monitorBlocks = $('.monitor_block'); + var searchTerms = $(this).val().toLowerCase().split(' ') + if(searchTerms.length === 0 || searchTerms[0] === ''){ + monitorBlocks.show() + return + } + monitorBlocks.hide() + $.each($.ccio.mon,function(n,monitor){ + var searchThis = JSON.stringify($.ccio.init('cleanMon',monitor)).toLowerCase().replace('"',''); + $.each(searchTerms,function(m,term){ + if(searchThis.indexOf(term) >-1 ){ + $('.monitor_block[ke="'+monitor.ke+'"][mid="'+monitor.mid+'"]').show() + } + }) + }) + }) + //dynamic bindings + $.ccio.windowFocus = true + $(window).focus(function() { + $.ccio.windowFocus = true + clearInterval($.ccio.soundAlarmInterval) + }).blur(function() { + $.ccio.windowFocus = false + }); + $('body') + .on('click','.logout',function(e){ + var logout = function(user,callback){ + $.get($.ccio.init('location',user)+user.auth_token+'/logout/'+user.ke+'/'+user.uid,callback) + } + $.each($.users,function(n,linkedShinobiUser){ + logout(linkedShinobiUser,function(){}); + }) + logout($user,function(data){ + console.log(data) + localStorage.removeItem('ShinobiLogin_'+location.host); + location.href=location.href; + }); + }) + .on('click','[video]',function(e){ + e.e=$(this), + e.a=e.e.attr('video'), + e.p=e.e.parents('[mid]'), + e.ke=e.p.attr('ke'), + e.mid=e.p.attr('mid'), + e.file=e.p.attr('file'); + e.auth=e.p.attr('auth'); + e.status=e.p.attr('status'); + if(!e.ke||!e.mid){ + //for calendar plugin + e.p=e.e.parents('[data-mid]'), + e.ke=e.p.data('ke'), + e.mid=e.p.data('mid'), + e.file=e.p.data('file'); + e.auth=e.p.data('auth'); + e.status=e.p.data('status'); + } + e.mon=$.ccio.mon[e.ke+e.mid+e.auth]; + switch(e.a){ + case'launch': + e.preventDefault(); + e.href=$(this).attr('href') + var el = $('#video_viewer') + var modalBody = el.find('.modal-body') + el.find('.modal-title span').html(e.mon.name+' - '+e.file) + var html = '
      ' + modalBody.html(html) + el.find('video')[0].onerror = function(){ + modalBody.find('.msg').text(lang.h265BrowserText1) + } + el.attr('mid',e.mid); + footer = el.find('.modal-footer'); + footer.find('.download_link').attr('href',e.href).attr('download',e.file); + footer.find('[monitor="download"][host="dropbox"]').attr('href',e.href); + el.modal('show') + .attr('ke',e.ke) + .attr('mid',e.mid) + .attr('auth',e.auth) + .attr('file',e.file); + if(e.status==1){ + $.get($.ccio.init('videoHrefToRead',e.href),function(d){ + if(d.ok !== true)console.log(d,new Error()) + }) + } + break; + case'delete': + e.preventDefault(); + var videoLink = e.p.find('[download]').attr('href') + var href = $(this).attr('href') + console.log('videoLink',videoLink) + console.log(href) + if(!href){ + href = $.ccio.init('location',$.users[e.auth])+e.auth+'/videos/'+e.ke+'/'+e.mid+'/'+e.file+'/delete<% if(config.useUTC === true){%>?isUTC=true<%}%>' + } + console.log(href) + $.confirm.e.modal('show'); + $.confirm.title.text(lang['Delete Video']+' : '+e.file) + e.html=lang.DeleteVideoMsg + e.html+=''; + $.confirm.body.html(e.html) + $.confirm.click({title:'Delete Video',class:'btn-danger'},function(){ + $.getJSON(href,function(d){ + $.ccio.log(d) + }) + }); + break; + case'download': + e.preventDefault(); + switch(e.e.attr('host')){ + case'dropbox': + if($.ccio.DropboxAppKey){ + Dropbox.save(e.e.attr('href'),e.e.attr('download'),{progress: function (progress) {$.ccio.log(progress)},success: function () { + $.ccio.log(lang.dropBoxSuccess); + }}); + } + break; + } + break; + } + }) + .on('change','[localStorage]',function(){ + e = {} + e.e=$(this) + e.localStorage = e.e.attr('localStorage') + e.value = e.e.val() + $.ccio.op(e.localStorage,e.value) + }) + .on('click','[system]',function(e){ + var e={}; + e.e=$(this), + e.a=e.e.attr('system');//the function + switch(e.a){ + case'switch': + e.switch=e.e.attr('switch'); + e.o=$.ccio.op().switches + if(!e.o){ + e.o={} + } + if(!e.o[e.switch]){ + e.o[e.switch]=0 + } + if(e.o[e.switch]===1){ + e.o[e.switch]=0 + }else{ + e.o[e.switch]=1 + } + $.ccio.op('switches',e.o) + switch(e.switch){ + case'monitorOrder': + if(e.o[e.switch] !== 1){ + $('.monitor_item').attr('data-gs-auto-position','yes') + }else{ + $('.monitor_item').attr('data-gs-auto-position','no') + } + break; + case'monitorMuteAudio': + $('.monitor_item video').each(function(n,el){ + if(e.o[e.switch] === 1){ + el.muted = true + }else{ + el.muted = false + } + }) + break; + } + switch(e.e.attr('type')){ + case'text': + if(e.o[e.switch]===1){ + e.e.addClass('text-success') + }else{ + e.e.removeClass('text-success') + } + break; + } + break; + case'cronStop': + $.ccio.cx({f:'cron',ff:'stop'}) + break; + case'cronRestart': + $.ccio.cx({f:'cron',ff:'restart'}) + break; + case'jpegToggle': + e.cx={f:'monitor',ff:'jpeg_on'}; + if($.ccio.op().jpeg_on===true){ + e.cx.ff='jpeg_off'; + } + $.ccio.cx(e.cx) + break; + } + }) + .on('click','[class_toggle]',function(e){ + e.e=$(this); + e.n=e.e.attr('data-target'); + e.v=e.e.attr('class_toggle'); + e.o=$.ccio.op().class_toggle; + if($(e.n).hasClass(e.v)){e.t=0}else{e.t=1} + if(!e.o)e.o={}; + e.o[e.n]=[e.v,e.t]; + $.ccio.op('class_toggle',e.o) + $(e.n).toggleClass(e.v); + }) + .on('change','[dropdown_toggle]',function(e){ + e.e=$(this); + e.n=e.e.attr('dropdown_toggle'); + e.v=e.e.val(); + e.o=$.ccio.op().dropdown_toggle; + if(!e.o)e.o={}; + e.o[e.n]=e.v; + $.ccio.op('dropdown_toggle',e.o) + }) + //monitor functions + .on('click','[monitor]',function(){ + var e={}; + e.e=$(this), + e.a=e.e.attr('monitor'),//the function + e.p=e.e.parents('[mid]'),//the parent element for monitor item + e.ke=e.p.attr('ke'),//group key + e.mid=e.p.attr('mid'),//monitor id + e.auth=e.p.attr('auth'),//authkey + e.mon=$.ccio.mon[e.ke+e.mid+e.auth];//monitor configuration + var user + if($.users[e.auth]){user=$.users[e.auth]}else{user=$user} + if(!user){ + user=$user + } + switch(e.a){ + case'show_data': + e.p.toggleClass('show_data') + var dataBlocks = e.p.find('.stream-block,.mdl-data_window') + if(e.p.hasClass('show_data')){ + dataBlocks.addClass('col-md-6').removeClass('col-md-12') + }else{ + dataBlocks.addClass('col-md-12').removeClass('col-md-6') + } + break; + case'motion': + if(!e.mon.motionDetectionRunning){ + $.ccio.init('streamMotionDetectOn',e,user) + }else{ + $.ccio.init('streamMotionDetectOff',e,user) + } + break; + case'pop': + e.fin=function(img){ + if($.ccio.mon[e.ke+e.mid+user.auth_token].popOut){ + $.ccio.mon[e.ke+e.mid+user.auth_token].popOut.close() + } + $.ccio.mon[e.ke+e.mid+user.auth_token].popOut = window.open($.ccio.init('location',user)+user.auth_token+'/embed/'+e.ke+'/'+e.mid+'/fullscreen|jquery|relative|gui','pop_'+e.mid+user.auth_token,'height='+img.height+',width='+img.width); + } + if(e.mon.watch===1){ + $.ccio.snapshot(e,function(url){ + $('#temp').html('') + var img=$('#temp img')[0] + img.onload=function(){ + e.fin(img) + } + img.src=url + }) + }else{ + var img={height:720,width:1280} + e.fin(img) + } + break; + case'mode': + e.mode=e.e.attr('mode') + if(e.mode){ + $.getJSON($.ccio.init('location',user)+user.auth_token+'/monitor/'+e.ke+'/'+e.mid+'/'+e.mode,function(d){ + $.ccio.log(d) + }) + } + break; + case'timelapse': + $.timelapse.e.modal('show') + $.timelapse.monitors.find('.monitor').remove() + $.each($.ccio.mon,function(n,v){ + $.timelapse.monitors.append('') + }) + e.e=$.timelapse.monitors.find('.monitor').prop('selected',false) + if(e.mid!==''){ + e.e=$.timelapse.monitors.find('.monitor[value="'+e.mid+'"]') + } + e.e.first().prop('selected',true) + $.timelapse.f.submit() + break; + case'powerview': + $.pwrvid.e.modal('show') + $.pwrvid.m.empty() + $.each($.ccio.mon,function(n,v){ + $.pwrvid.m.append('') + }) + e.e=$.pwrvid.m.find('option').prop('selected',false) + if(e.mid!==''){ + e.e=$.pwrvid.m.find('[value="'+e.mid+'"]') + } + e.e.first().prop('selected',true) + $.pwrvid.f.submit() + break; + case'region': + if(!e.mon){ + $.ccio.init('note',{title:lang['Unable to Launch'],text:lang.UnabletoLaunchText,type:'error'}); + return; + } + e.d=JSON.parse(e.mon.details); + e.width=$.aM.e.find('[detail="detector_scale_x"]'); + e.height=$.aM.e.find('[detail="detector_scale_y"]'); + e.d.cords=$.aM.e.find('[detail="cords"]').val(); + if(e.width.val()===''){ + e.d.detector_scale_x=320; + e.d.detector_scale_y=240; + $.aM.e.find('[detail="detector_scale_x"]').val(e.d.detector_scale_x); + $.aM.e.find('[detail="detector_scale_y"]').val(e.d.detector_scale_y); + }else{ + e.d.detector_scale_x=e.width.val(); + e.d.detector_scale_y=e.height.val(); + } + + $.zO.e.modal('show'); + $.zO.o().attr('width',e.d.detector_scale_x).attr('height',e.d.detector_scale_y); + $.zO.c.css({width:e.d.detector_scale_x,height:e.d.detector_scale_y}); + if(e.d.cords&&(e.d.cords instanceof Object)===false){ + try{e.d.cords=JSON.parse(e.d.cords);}catch(er){} + } + if(!e.d.cords||e.d.cords===''){ + e.d.cords={ + red:{ name:"red",sensitivity:0.0005, max_sensitivity:"",color_threshold:"",points:[[0,0],[0,100],[100,0]] }, + } + } + $.zO.regionViewerDetails=e.d; + $.zO.initRegionList() + break; + case'detector_filters': + $.detectorFilters.e.modal('show'); + break; + case'snapshot': + $.ccio.snapshot(e,function(url){ + $('#temp').html('a').find('a')[0].click(); + }); + break; + case'control': + e.a=e.e.attr('control') + $.ccio.cx({f:'monitor',ff:'control',direction:e.a,mid:e.mid,ke:e.ke},user) + break; + case'videos_table':case'calendar':case'video_grid'://call videos table or calendar or video grid + $.vidview.launcher=$(this); + e.limit=$.vidview.limit.val(); + if(!$.vidview.current_mid||$.vidview.current_mid!==e.mid){ + $.vidview.current_mid=e.mid + $.vidview.current_page=1; + if(e.limit.replace(/ /g,'')===''){ + e.limit='100'; + } + if(e.limit.indexOf(',')===-1){ + e.limit='0,'+e.limit + }else{ + e.limit='0,'+e.limit.split(',')[1] + } + if(e.limit=='0,0'){ + e.limit='0' + } + $.vidview.limit.val(e.limit) + } + e.dateRange=$('#videos_viewer_daterange').data('daterangepicker'); + var videoSet = 'videos' + switch($.vidview.set.val()){ + case'cloud': + videoSet = 'cloudVideos' + break; + } + e.videoURL=$.ccio.init('location',user)+user.auth_token+'/'+videoSet+'/'+e.ke+'/'+e.mid+'?limit='+e.limit+'&start='+$.ccio.init('th',e.dateRange.startDate)+'&end='+$.ccio.init('th',e.dateRange.endDate); + $.getJSON(e.videoURL,function(d){ + d.pages=d.total/100; + $('.video_viewer_total').text(d.total) + if(d.pages+''.indexOf('.')>-1){++d.pages} + $.vidview.page_count=d.pages; + d.count=1 + $.vidview.pages.empty() + d.fn=function(drawOne){ + if(d.count<=$.vidview.page_count){ + $.vidview.pages.append(''+d.count+' ') + ++d.count; + d.fn() + } + } + d.fn() + $.vidview.pages.find('[page="'+$.vidview.current_page+'"]').addClass('active') + e.v=$.vidview.e; + $.vidview.loadedVideos = {} + e.b=e.v.modal('show').find('.modal-body .contents'); + e.t=e.v.find('.modal-title i'); + switch(e.a){ + case'calendar': + $.vidview.e.removeClass('dark') + e.t.attr('class','fa fa-calendar') + e.ar=[]; + if(d.videos[0]){ + $.each(d.videos,function(n,v){ + if(v.status !== 0){ + $.vidview.loadedVideos[v.filename] = Object.assign(v,{}) + var n=$.ccio.mon[v.ke+v.mid+user.auth_token]; + if(n){v.title=n.name+' - '+(parseInt(v.size)/1000000).toFixed(2)+'mb';} + v.start=v.time; + // v.filename=$.ccio.init('tf',v.time)+'.'+v.ext; + e.ar.push(v); + } + }) + e.b.html('') + try{e.b.fullCalendar('destroy')}catch(er){} + e.b.fullCalendar({ + header: { + left: 'prev,next today', + center: 'title', + right: 'month,agendaWeek,agendaDay,listWeek' + }, + defaultDate: $.ccio.timeObject(d.videos[0].time).format('YYYY-MM-DD'), + navLinks: true, + eventLimit: true, + events:e.ar, + eventClick:function(f){ + $('#temp').html('
      ').find('[video="launch"]').click(); + $(this).css('border-color', 'red'); + } + }); + setTimeout(function(){e.b.fullCalendar('changeView','month');e.b.find('.fc-scroller').css('height','auto')},500) + }else{ + e.b.html('
      '+lang.NoVideosFoundForDateRange+'
      ') + } + break; + case'video_grid': + $.vidview.e.addClass('dark') + var tmp = ''; + $.each(d.videos,function(n,v){ + var href = $.ccio.init('videoUrlBuild',v) + v.mon = $.ccio.mon[v.ke+v.mid+user.auth_token] + var parentTag = 'ke="'+v.ke+'" status="'+v.status+'" mid="'+v.mid+'" file="'+v.filename+'" auth="'+v.mon.user.auth_token+'"' + tmp += '
      ' + tmp += '
      ' + tmp += '
      '+$.ccio.timeObject(v.time).format('h:mm:ss A, MMMM Do YYYY')+'
      ' + tmp += '
      ' + tmp += '
      ' + tmp += '  ' + tmp += '  ' + tmp += '  ' + tmp += '
      ' + tmp += '
      ' + tmp += '
      ' + tmp += '
      ' + }) + tmp += '
      ' + e.b.html(tmp) + var i = 0 + var getThumbnail = function(){ + var v = d.videos[i] + if(v){ + tool.getVideoImage($.ccio.init('videoUrlBuild',v),0,function(err,base64){ + if(base64){ + $('[ke="'+v.ke+'"][mid="'+v.mid+'"][file="'+v.filename+'"] .thumb').css('background-image','url('+base64+')') + } + ++i + getThumbnail() + }) + } + } + getThumbnail() + break; + case'videos_table': + var showThumbnail = $.ccio.op().showThumbnail === '1' + $.vidview.e.removeClass('dark') + e.t.attr('class','fa fa-film') + var tmp = ''; + tmp+=''; + tmp+=''; + tmp+=''; + if(showThumbnail)tmp+=''; + tmp+=''; + tmp+=''; + tmp+=''; + tmp+=''; + tmp+=''; + tmp+=''; + tmp+=''; + tmp+=''; + tmp+=''; + tmp+=''; + // tmp+=''; + tmp+=''; + tmp+=''; + tmp+=''; + $.each(d.videos,function(n,v){ + if(v.status!==0){ + $.vidview.loadedVideos[v.filename] = Object.assign(v,{}) + var href = $.ccio.init('videoUrlBuild',v) + v.mon=$.ccio.mon[v.ke+v.mid+user.auth_token]; + v.start=v.time; + // v.filename=$.ccio.init('tf',v.time)+'.'+v.ext; + tmp+=''; + tmp+=''; + if(showThumbnail)tmp+=''; + tmp+=''; + tmp+=''; + tmp+=''; + tmp+=''; + tmp+=''; + tmp+=''; + tmp+=''; + tmp+=''; + tmp+=''; + tmp+=''; + // tmp+=''; + tmp+=''; + } + }) + tmp+=''; + tmp+='
      '+lang.Thumbnail+''+lang.Closed+''+lang.Ended+''+lang.Started+''+lang.Monitor+''+lang.Filename+''+lang['Size (mb)']+''+lang.Preview+''+lang.Watch+''+lang.Download+''+lang.Delete+''+lang.Fix+'
      '+$.ccio.timeObject(v.end).format('h:mm:ss A, MMMM Do YYYY')+''+$.ccio.timeObject(v.time).format('h:mm:ss A, MMMM Do YYYY')+''+v.mon.name+''+v.filename+''+(parseInt(v.size)/1000000).toFixed(2)+'          
      '; + e.b.html(tmp) + if(showThumbnail){ + var i = 0 + var getThumbnail = function(){ + var v = d.videos[i] + if(v){ + tool.getVideoImage($.ccio.init('videoUrlBuild',v),0,function(err,base64){ + if(base64){ + $('[data-ke="'+v.ke+'"][data-mid="'+v.mid+'"][data-file="'+v.filename+'"] .thumbnail')[0].src = base64 + } + ++i + getThumbnail() + }) + } + } + getThumbnail() + } + $.ccio.init('ls'); + $.vidview.e.find('table').bootstrapTable(); + break; + } + }) + break; + case'fullscreen': + e.e=e.e.parents('.monitor_item'); + e.e.addClass('fullscreen') + e.vid=e.e.find('.stream-element') + if(e.vid.is('canvas')){ + e.doc=$('body') + e.vid.attr('height',e.doc.height()) + e.vid.attr('width',e.doc.width()) + } + $.ccio.init('fullscreen',e.vid[0]) + break; + case'watch_on': + $.ccio.cx({f:'monitor',ff:'watch_on',id:e.mid},user) + break; + case'control_toggle': + e.e=e.p.find('.PTZ_controls'); + if(e.e.length>0){ + e.e.remove() + }else{ + var html = '
      ' + html += '
      ' + html += '
      ' + html += '
      ' + html += '
      ' + html += '
      ' + html += '
      ' + html += '
      ' + html += '
      ' + html += '' + html += '' + html += '
      ' + html += '
      ' + html += '' + html += '' + html += '
      ' + html += '
      ' + e.p.append(html) + } + break; + case'watch': + if($("#monitor_live_"+e.mid+user.auth_token).length===0||$.ccio.mon[e.ke+e.mid+user.auth_token].watch!==1){ + $.ccio.cx({f:'monitor',ff:'watch_on',id:e.mid},user) + }else{ + $("#main_canvas").animate({scrollTop:$("#monitor_live_"+e.mid+user.auth_token).offset().top-($('#main_header').height()+10)},500); + } + break; + case'watch_off': + $.ccio.cx({f:'monitor',ff:'watch_off',id:e.mid},user) + break; + case'delete': + e.m=$('#confirm_window').modal('show');e.f=e.e.attr('file'); + $.confirm.title.text(lang['Delete Monitor']+' : '+e.mon.name) + e.html=lang.DeleteMonitorText + e.html+=''; + $.each($.ccio.init('cleanMon',e.mon),function(n,v,g){ + if(n==='host'&&v.indexOf('@')>-1){g=v.split('@')[1]}else{g=v}; + try{JSON.parse(g);return}catch(err){} + e.html+=''; + }) + e.html+='
      '+n+''+g+'
      '; + $.confirm.body.html(e.html) + $.confirm.click([ + { + title:'Delete Monitor', + class:'btn-danger', + callback:function(){ + $.get($.ccio.init('location',user)+user.auth_token+'/configureMonitor/'+user.ke+'/'+e.mon.mid+'/delete',function(d){ + $.ccio.log(d) + }) + } + }, + { + title:'Delete Monitor and Files', + class:'btn-danger', + callback:function(){ + $.get($.ccio.init('location',user)+user.auth_token+'/configureMonitor/'+user.ke+'/'+e.mon.mid+'/delete?deleteFiles=true',function(d){ + $.ccio.log(d) + }) + } + } + ]) + break; + case'edit': + e.p=$('#add_monitor'),e.mt=e.p.find('.modal-title') + e.p.find('.am_notice').hide() + e.p.find('[detailcontainer="detector_cascades"]').prop('checked',false).parents('.mdl-js-switch').removeClass('is-checked') + if(!$.ccio.mon[e.ke+e.mid+user.auth_token]){ + e.p.find('.am_notice_new').show() + //new monitor + e.p.find('[monitor="delete"]').hide() + e.mt.find('span').text('Add'),e.mt.find('i').attr('class','fa fa-plus'); + //default values + e.values=$.aM.generateDefaultMonitorSettings(); + }else{ + e.p.find('.am_notice_edit').show() + //edit monitor + e.p.find('[monitor="delete"]').show() + e.mt.find('span').text(lang.Edit); + e.mt.find('i').attr('class','fa fa-wrench'); + e.values=$.ccio.mon[e.ke+e.mid+user.auth_token]; + } + $.aM.selected=e.values; + // e.openTabs=$.ccio.op().tabsOpen + // if(e.openTabs[e.mid]){ + // e.values=e.openTabs[e.mid] + // } + $.aM.import(e) + $('#add_monitor').modal('show') + break; + } + }) + .on('dblclick','[type="password"],.password_field',function(){ + var _this = $(this) + var type = 'password' + _this.addClass('password_field') + if(_this.attr('type') === 'password'){ + type = 'text' + } + _this.attr('type',type) + }) + + $('.modal').on('hidden.bs.modal',function(){ + $(this).find('video').remove(); + $(this).find('iframe').attr('src','about:blank'); + }); + $('.modal').on('shown.bs.modal',function(){ + e={e:$(this).find('.flex-container-modal-body')} + if(e.e.length>0){ + e.e.resize() + } + }); + + $('body') + .on('click','.scrollTo',function(ee){ + ee.preventDefault() + var e = {e:$(this)}; + e.parent=e.e.attr('scrollToParent') + if(!e.parent){ + e.parent='body,html' + } + $(e.parent).animate({ + scrollTop: $(e.e.attr('href')).position().top + }, 400); + }) + .on('resize','.flex-container-modal-body',function(e){ + e=$(this) + e.find('.flex-modal-block').css('height',e.height()) + }) + .on('resize','#monitors_live .monitor_item',function(e){ + e.e=$(this).find('.stream-block'); + e.c=e.e.find('canvas'); + e.c.attr('height',e.e.height()); + e.c.attr('width',e.e.width()); + }) + .on('keyup','.search-parent .search-controller',function(){ + _this = this; + $.each($(".search-parent .search-body .search-row"), function() { + if($(this).text().toLowerCase().indexOf($(_this).val().toLowerCase()) === -1) + $(this).hide(); + else + $(this).show(); + }); + }) + .on('dblclick','.stream-hud',function(){ + $(this).parents('[mid]').find('[monitor="fullscreen"]').click(); + }) + //.on('mousemove',".magnifyStream",$.ccio.magnifyStream) + //.on('touchmove',".magnifyStream",$.ccio.magnifyStream); +}) diff --git a/web/libs/js/dash2.gridstack.js b/web/libs/js/dash2.gridstack.js new file mode 100644 index 00000000..878bc919 --- /dev/null +++ b/web/libs/js/dash2.gridstack.js @@ -0,0 +1,61 @@ +$(document).ready(function(e){ +//monitor grid +$.grid={e:$('#monitors_live')} +$.grid.data = function(){ + return $.grid.e.data('gridstack') +} +$.grid.getMonitorsPerRow = function(){ + var x + switch($.ccio.op().montage){ + case'1': + x = '12' + break; + case'2': + x = '6' + break; + case'3': + x = '4' + break; + case'4': + x = '3' + break; + case'5': + x = '5' + break; + case'6': + x = '2' + break; + default://3 + x = '4' + break; + } + return x +} +$.grid.saveElementPositions = function() { + var monitors = {} + $.grid.e.find(" .monitor_item").each(function(n,v){ + var el = $(v) + var item = {} + item.ke = el.attr('ke') + item.mid = el.attr('mid') + item.x = el.attr('data-gs-x') + item.y = el.attr('data-gs-y') + item.height = el.attr('data-gs-height') + item.width = el.attr('data-gs-width') + monitors[item.ke+item.mid] = item + }) + $user.details.monitorOrder=monitors; + $.ccio.cx({f:'monitorOrder',monitorOrder:monitors}) +} +$.grid.e +.gridstack({ + cellHeight: 80, + verticalMargin: 0, +}) +.on('dragstop', function(event,ui){ + setTimeout(function(){ + $.grid.saveElementPositions() + },700) +}) +.on('gsresizestop', $.grid.saveElementPositions); +}) diff --git a/web/libs/js/dash2.init.js b/web/libs/js/dash2.init.js new file mode 100644 index 00000000..da42d6a2 --- /dev/null +++ b/web/libs/js/dash2.init.js @@ -0,0 +1,612 @@ +$.ccio.init=function(x,d,user,k){ + if(!k){k={}};k.tmp=''; + if(d&&d.user){ + user=d.user + } + if(!user){ + user=$user + } + switch(x){ + case'cleanMon': + var acceptedFields = [ + 'mid', + 'ke', + 'name', + 'shto', + 'shfr', + 'details', + 'type', + 'ext', + 'protocol', + 'host', + 'path', + 'port', + 'fps', + 'mode', + 'width', + 'height' + ] + var row = {}; + $.each(d,function(m,b){ + if(acceptedFields.indexOf(m)>-1){ + row[m]=b; + } + }) + return row + break; + case'cleanMons': + if(d==='object'){ + var arr={} + }else{ + var arr=[] + } + $.each($.ccio.mon,function(n,v){ + var row = $.ccio.init('cleanMon',v) + if(d==='object'){ + arr[n]=row + }else{ + arr.push(row) + } + }) + return arr; + break; + case'location': + var url + if(d&&d.info&&d.info.URL){ + url=d.info.URL + if(url.charAt(url.length-1)!=='/'){ + url=url+'/' + } + }else{ + url = $.ccio.libURL + } + return url + break; + case'videoUrlBuild': + var url + if(d.href){ + url = d.href + }else if(!d.href && d.hrefNoAuth){ + url = $.ccio.init('location',user)+user.auth_token+d.hrefNoAuth + } + if(user!==$user&&url.charAt(0)==='/'){ + url = $.ccio.init('location',user)+d.href.substring(1) + } + return url + break; + case'videoHrefToDelete': + var urlSplit = d.split('?') + var url = urlSplit[0]+'/delete' + if(urlSplit[1])url += '?' + urlSplit[1] + return url + break; + case'videoHrefToUnread': + var urlSplit = d.split('?') + var url = urlSplit[0]+'/status/1' + if(urlSplit[1])url += '?' + urlSplit[1] + return url + break; + case'videoHrefToRead': + var urlSplit = d.split('?') + var url = urlSplit[0]+'/status/2' + if(urlSplit[1])url += '?' + urlSplit[1] + return url + break; +// case'streamWindow': +// return $('.monitor_item[mid="'+d.id+'"][ke="'+d.ke+'"][auth="'+user.auth_token+'"]') +// break; + case'streamMotionDetectRestart': + $.ccio.init('streamMotionDetectOff',d,user) + $.ccio.init('streamMotionDetectOn',d,user) + break; + case'streamMotionDetectOff': + d.mon.motionDetectionRunning = false + $('.monitor_item[mid="'+d.mid+'"][ke="'+d.ke+'"][auth="'+user.auth_token+'"]').find('.stream-detected-object,.zoomGlass').remove() + clearInterval(d.mon.motionDetector) + break; + case'streamMotionDetectOn': + switch(JSON.parse(d.mon.details).stream_type){ + case'hls':case'flv':case'mp4': + //pass + break; + default: + return $.ccio.init('note',{title:'Client-side Detector',text:'Could not be started. Only FLV and HLS can use this feature.',type:'error'}); + break; + + } + d.mon.motionDetectorNextDraw = true + d.mon.motionDetectionRunning = true + $.ccio.snapshot(d,function(url){ + $('#temp').html('') + var img=$('#temp img')[0] + img.onload=function(){ + var frameNumber = 0, + mainWindow = $('.monitor_item[mid="'+d.mid+'"][ke="'+d.ke+'"][auth="'+user.auth_token+'"]'), + blenderCanvas = mainWindow.find(".blenderCanvas"), + motionVision = mainWindow.find(".motionVision"), + streamElement = mainWindow.find('.stream-element'), + streamElementTag = streamElement[0], + lastURL = null, + currentImage = null, + f = [], + drawMatrices = { + e:mainWindow, + monitorDetails:JSON.parse(d.mon.details), + stream:streamElement, + streamObjects:mainWindow.find('.stream-objects'), + details:{ + name:'clientSideDetection', + } + }; + widthRatio = streamElement.width() / img.width + heightRatio = streamElement.height() / img.height + drawMatrices.monitorDetails.detector_scale_x = img.width; + drawMatrices.monitorDetails.detector_scale_y = img.height; + function checkForMotion() { + blenderCanvas.width = img.width; + blenderCanvas.height = img.height; + blenderCanvasContext.drawImage(streamElementTag, 0, 0); + f[frameNumber] = blenderCanvasContext.getImageData(0, 0, blenderCanvas.width, blenderCanvas.height); + frameNumber = 0 == frameNumber ? 1 : 0; + currentImage = blenderCanvasContext.getImageData(0, 0, blenderCanvas.width, blenderCanvas.height); + foundPixels = []; + for (var currentImageLength = currentImage.data.length * 0.25, b = 0; b < currentImageLength;){ + var pos = b * 4 + currentImage.data[pos] = .5 * (255 - currentImage.data[pos]) + .5 * f[frameNumber].data[pos]; + currentImage.data[pos + 1] = .5 * (255 - currentImage.data[pos + 1]) + .5 * f[frameNumber].data[pos + 1]; + currentImage.data[pos + 2] = .5 * (255 - currentImage.data[pos + 2]) + .5 * f[frameNumber].data[pos + 2]; + currentImage.data[pos + 3] = 255; + var score = (currentImage.data[pos] + currentImage.data[pos + 1] + currentImage.data[pos + 2]) / 3; + if(score>170){ + var x = (pos / 4) % img.width; + var y = Math.floor((pos / 4) / img.width); + foundPixels.push([x,y]) + } + b += 4; + } + var groupedPoints = Object.assign({},Cluster); + groupedPoints.iterations(25); + groupedPoints.data(foundPixels); + var groupedPoints = groupedPoints.clusters() + drawMatrices.details.matrices=[] + var mostHeight = 0; + var mostWidth = 0; + var mostWithMotion = null; + groupedPoints.forEach(function(v,n){ + var matrix = { + topLeft:[img.width,img.height], + topRight:[0,img.height], + bottomRight:[0,0], + bottomLeft:[img.width,0], + } + v.points.forEach(function(b){ + var x = b[0] + var y = b[1] + if(xmatrix.topRight[0])matrix.topRight[0]=x; + if(ymatrix.bottomRight[0])matrix.bottomRight[0]=x; + if(y>matrix.bottomRight[1])matrix.bottomRight[1]=y; + //Bottom Left point + if(xmatrix.bottomLeft[1])matrix.bottomLeft[1]=y; + }) + matrix.x = matrix.topLeft[0]; + matrix.y = matrix.topLeft[1]; + matrix.width = matrix.topRight[0] - matrix.topLeft[0] + matrix.height = matrix.bottomLeft[1] - matrix.topLeft[1] + + if(matrix.width>mostWidth&&matrix.height>mostHeight){ + mostWidth = matrix.width; + mostHeight = matrix.height; + mostWithMotion = matrix; + } + + drawMatrices.details.matrices.push(matrix) + }) + $.ccio.magnifyStream({ + p:mainWindow, + useCanvas:true, + zoomAmount:1, + auto:true, + animate:true, + pageX:((mostWithMotion.width / 2) + mostWithMotion.x) * widthRatio, + pageY:((mostWithMotion.height / 2) + mostWithMotion.y) * heightRatio + }) + $.ccio.init('drawMatrices',drawMatrices) + if(d.mon.motionDetectorNextDraw===true){ + clearTimeout(d.mon.motionDetectorNextDrawTimeout) + d.mon.motionDetectorNextDrawTimeout=setTimeout(function(){ + d.mon.motionDetectorNextDraw = true; + },1000) + d.mon.motionDetectorNextDraw = false; +// console.log({ +// p:mainWindow, +// pageX:((matrix.width / 2) + matrix.x) * widthRatio, +// pageY:((matrix.height / 2) + matrix.y) * heightRatio +// }) + } + return drawMatrices.details.matrices; + } + if(blenderCanvas.length === 0){ + mainWindow.append('
      ') + blenderCanvas = mainWindow.find(".blenderCanvas") + } + blenderCanvas = blenderCanvas[0]; + var blenderCanvasContext = blenderCanvas.getContext("2d"); + clearInterval(d.mon.motionDetector) + d.mon.motionDetector = setInterval(checkForMotion,2000) + } + img.src=url + }) + break; + case'streamURL': + var streamURL + switch(JSON.parse(d.details).stream_type){ + case'jpeg': + streamURL=$.ccio.init('location',user)+user.auth_token+'/jpeg/'+d.ke+'/'+d.mid+'/s.jpg' + break; + case'mjpeg': + streamURL=$.ccio.init('location',user)+user.auth_token+'/mjpeg/'+d.ke+'/'+d.mid + break; + case'hls': + streamURL=$.ccio.init('location',user)+user.auth_token+'/hls/'+d.ke+'/'+d.mid+'/s.m3u8' + break; + case'flv': + streamURL=$.ccio.init('location',user)+user.auth_token+'/flv/'+d.ke+'/'+d.mid+'/s.flv' + break; + case'h265': + streamURL=$.ccio.init('location',user)+user.auth_token+'/h265/'+d.ke+'/'+d.mid+'/s.hevc' + break; + case'mp4': + streamURL=$.ccio.init('location',user)+user.auth_token+'/mp4/'+d.ke+'/'+d.mid+'/s.mp4' + break; + case'b64': + streamURL='Websocket' + break; + case'pam': + streamURL='Websocket' + break; + } + return streamURL + break; + case'humanReadMode': + switch(d){ + case'idle': + k.mode=lang['Idle'] + break; + case'stop': + k.mode=lang['Disabled'] + break; + case'record': + k.mode=lang['Record'] + break; + case'start': + k.mode=lang['Watch Only'] + break; + } + return k.mode + break; + case'monitorInfo': + d.e=$('.glM'+d.mon.mid+user.auth_token); + if(JSON.parse(d.mon.details).vcodec!=='copy'&&d.mon.mode=='record'){ + d.e.find('.monitor_not_record_copy').show() + }else{ + d.e.find('.monitor_not_record_copy').hide() + } + d.e.find('.monitor_name').text(d.mon.name) + d.e.find('.monitor_mid').text(d.mon.mid) + d.e.find('.monitor_ext').text(d.mon.ext); + d.mode=$.ccio.init('humanReadMode',d.mon.mode,user) + d.e.find('.monitor_mode').text(d.mode) + d.e.find('.monitor_status').text(d.status) + d.e.attr('mode',d.mode) + d.e.find('.lamp').attr('title',d.mode) + break; + case'fullscreen': + if (d.requestFullscreen) { + d.requestFullscreen(); + } else if (d.mozRequestFullScreen) { + d.mozRequestFullScreen(); + } else if (d.webkitRequestFullscreen) { + d.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT); + } + break; + case'drawPoints': + d.height=d.stream.height() + d.width=d.stream.width() + if(d.monitorDetails.detector_scale_x===''){d.monitorDetails.detector_scale_x=320} + if(d.monitorDetails.detector_scale_y===''){d.monitorDetails.detector_scale_y=240} + + d.widthRatio=d.width/d.monitorDetails.detector_scale_x + d.heightRatio=d.height/d.monitorDetails.detector_scale_y + + d.streamObjects.find('.stream-detected-point[name="'+d.details.name+'"]').remove() + d.tmp='' + $.each(d.details.points,function(n,v){ + d.tmp+='
      ' + if(v.tag){d.tmp+=''+v.tag+''} + d.tmp+='
      ' + }) + d.streamObjects.append(d.tmp) + break; + case'drawMatrices': + d.height=d.stream.height() + d.width=d.stream.width() + if(d.monitorDetails.detector_scale_x===''){d.monitorDetails.detector_scale_x=320} + if(d.monitorDetails.detector_scale_y===''){d.monitorDetails.detector_scale_y=240} + + d.widthRatio=d.width/d.monitorDetails.detector_scale_x + d.heightRatio=d.height/d.monitorDetails.detector_scale_y + + d.streamObjects.find('.stream-detected-object[name="'+d.details.name+'"]').remove() + d.tmp='' + $.each(d.details.matrices,function(n,v){ + d.tmp+='
      ' + if(v.tag){d.tmp+=''+v.tag+''} + d.tmp+='
      ' + }) + d.streamObjects.append(d.tmp) + break; + case'clearTimers': + if(!d.mid){d.mid=d.id} + if($.ccio.mon[d.ke+d.mid+user.auth_token]){ + clearTimeout($.ccio.mon[d.ke+d.mid+user.auth_token]._signal); + clearInterval($.ccio.mon[d.ke+d.mid+user.auth_token].hlsGarbageCollectorTimer) + clearTimeout($.ccio.mon[d.ke+d.mid+user.auth_token].jpegInterval); + clearInterval($.ccio.mon[d.ke+d.mid+user.auth_token].signal); + clearInterval($.ccio.mon[d.ke+d.mid+user.auth_token].m3uCheck); + if($.ccio.mon[d.ke+d.mid+user.auth_token].Base64 && $.ccio.mon[d.ke+d.mid+user.auth_token].Base64.connected){ + $.ccio.mon[d.ke+d.mid+user.auth_token].Base64.disconnect() + } + if($.ccio.mon[d.ke+d.mid+user.auth_token].Poseidon){ + $.ccio.mon[d.ke+d.mid+user.auth_token].Poseidon.stop() + } + } + break; + case'note': + k.o=$.ccio.op().switches + if(k.o&&k.o.notifyHide!==1){ + new PNotify(d) + if(user.details.audio_note && user.details.audio_note !== ''){ + var audio = new Audio('libs/audio/'+user.details.audio_note); + audio.play() + } + } + break; + case'monGroup': + $.ccio.mon_groups={}; + $.each($.ccio.mon,function(n,v,x){ + if(typeof v.details==='string'){ + k.d=JSON.parse(v.details) + }else{ + k.d=v.details + } + try{ + k.groups=JSON.parse(k.d.groups) + $.each(k.groups,function(m,b){ + if(!$.ccio.mon_groups[b])$.ccio.mon_groups[b]={} + $.ccio.mon_groups[b][v.mid]=v; + }) + }catch(er){ + + } + }) + return $.ccio.mon_groups; + break; + case'closeVideo': + var el = $('#monitor_live_'+d.mid+user.auth_token) + var video = el.find('video') + if(video.length === 1){ + if(!video[0].paused){ + video[0].onerror = function(){} + video[0].pause() + } + video.prop('src',''); + video.find('source').remove(); + video.remove(); + } + break; + case'jpegModeStop': + clearTimeout($.ccio.mon[d.ke+d.mid+user.auth_token].jpegInterval); + delete($.ccio.mon[d.ke+d.mid+user.auth_token].jpegInterval); + $('#monitor_live_'+d.mid+user.auth_token+' .stream-element').unbind('load') + break; + case'jpegMode': + if(d.watch===1){ + k=JSON.parse(d.details); + k.jpegInterval=parseFloat(k.jpegInterval); + if(!k.jpegInterval||k.jpegInterval===''||isNaN(k.jpegInterval)){k.jpegInterval=1} + $.ccio.tm('stream-element',$.ccio.mon[d.ke+d.mid+user.auth_token]); + k.e=$('#monitor_live_'+d.mid+user.auth_token+' .stream-element'); + $.ccio.init('jpegModeStop',d,user); + k.run=function(){ + k.e.attr('src',$.ccio.init('location',user)+user.auth_token+'/jpeg/'+d.ke+'/'+d.mid+'/s.jpg?time='+(new Date()).getTime()) + } + k.e.load(function(){ + $.ccio.mon[d.ke+d.mid+user.auth_token].jpegInterval=setTimeout(k.run,1000/k.jpegInterval); + }).error(function(){ + $.ccio.mon[d.ke+d.mid+user.auth_token].jpegInterval=setTimeout(k.run,1000/k.jpegInterval); + }) + k.run() + }; + break; + case'jpegModeAll': + $.each($.ccio.mon,function(n,v){ + $.ccio.init('jpegMode',v,user) + }); + break; + case'getLocation': + var l = document.createElement("a"); + l.href = d; + return l; + break; + case 'ls'://livestamp all + g={e:jQuery('.livestamp')}; + g.e.each(function(){g.v=jQuery(this),g.t=g.v.attr('title');if(!g.t){return};g.v.toggleClass('livestamp livestamped').attr('title',$.ccio.init('t',g.t,user)).livestamp(g.t);}) + return g.e + break; + case't'://format time + if(!d){d=new Date();} + return $.ccio.timeObject(d).format('YYYY-MM-DD HH:mm:ss') + break; + case'th'://format time hy + if(!d){d=new Date();} + return $.ccio.timeObject(d).format('YYYY-MM-DDTHH:mm:ss') + break; + case'tf'://time to filename + if(!d){d=new Date();} + return $.ccio.timeObject(d).format('YYYY-MM-DDTHH-mm-ss') + break; + case'fn'://row to filename + return $.ccio.init('tf',d.time,user)+'.'+d.ext + break; + case'filters': + k.tmp=''; + $.each(user.details.filters,function(n,v){ + k.tmp+='' + }); + $('#saved_filters').html(k.tmp) + break; + case'id': + $('.usermail').html(d.mail) + try{k.d=JSON.parse(d.details);}catch(er){k.d=d.details;} + try{user.mon_groups=JSON.parse(k.d.mon_groups);}catch(er){} + if(!user.mon_groups)user.mon_groups={}; + $.sM.reDrawMonGroups() + $.each(user,function(n,v){$.sM.e.find('[name="'+n+'"]').val(v).change()}) + $.each(k.d,function(n,v){$.sM.e.find('[detail="'+n+'"]').val(v).change()}) + $.gR.drawList(); + $.ccio.pm('link-set',k.d.links,null,user) + break; + case'jsontoblock'://draw json as block + if(d instanceof Object){ + $.each(d,function(n,v){ + k.tmp+='
      '; + k.tmp+=''+n+' : '+$.ccio.init('jsontoblock',v,user); + k.tmp+='
      '; + }) + }else{ + k.tmp+=''; + k.tmp+=d; + k.tmp+=''; + } + break; + case'url': + var porty + if(d.port && d.port !== ''){ + porty = ':' + d.port + }else{ + porty = '' + } + d.url = d.protocol + '://' + d.host + porty + return d.url + break; + case'data-video': + if(!d){ + $('[data-mid]').each(function(n,v){ + v=$(v);v.attr('mid',v.attr('data-mid')) + }); + $('[data-ke]').each(function(n,v){ + v=$(v);v.attr('ke',v.attr('data-ke')) + }); + $('[data-file]').each(function(n,v){ + v=$(v);v.attr('file',v.attr('data-file')) + }); + $('[data-status]').each(function(n,v){ + v=$(v);v.attr('status',v.attr('data-status')) + }); + $('[data-auth]').each(function(n,v){ + v=$(v);v.attr('auth',v.attr('data-auth')) + }); + }else{ + $('[data-ke="'+d.ke+'"][data-mid="'+d.mid+'"][data-file="'+d.filename+'"][auth="'+user.auth_token+'"]').attr('mid',d.mid).attr('ke',d.ke).attr('status',d.status).attr('file',d.filename).attr('auth',user.auth_token); + } + break; + case'signal': + d.mon=$.ccio.mon[d.ke+d.id+user.auth_token];d.e=$('#monitor_live_'+d.id+user.auth_token+' .signal').addClass('btn-success').removeClass('btn-danger');d.signal=parseFloat(JSON.parse(d.mon.details).signal_check); + if(!d.signal||d.signal==NaN){d.signal=10;};d.signal=d.signal*1000*60; + clearTimeout($.ccio.mon[d.ke+d.id+user.auth_token]._signal);$.ccio.mon[d.ke+d.id+user.auth_token]._signal=setTimeout(function(){d.e.addClass('btn-danger').removeClass('btn-success');},d.signal) + break; + case'signal-check': + try{ + d.mon=$.ccio.mon[d.ke+d.id+user.auth_token];d.p=$('#monitor_live_'+d.id+user.auth_token); + try{d.d=JSON.parse(d.mon.details)}catch(er){d.d=d.mon.details;} + d.check={c:0}; + d.fn=function(){ + if(!d.speed){d.speed=1000} + switch(d.d.stream_type){ + case'b64':case'h265': + d.p.resize() + break; + case'hls':case'flv':case'mp4': + if(d.p.find('video')[0].paused){ + if(d.d.signal_check_log==1){ + d.log={type:'Stream Check',msg:lang.clientStreamFailedattemptingReconnect} + $.ccio.tm(4,d,'#logs,.monitor_item[mid="'+d.id+'"][ke="'+d.ke+'"][auth="'+user.auth_token+'"] .logs') + } + $.ccio.cx({f:'monitor',ff:'watch_on',id:d.id},user); + }else{ + if(d.d.signal_check_log==1){ + d.log={type:'Stream Check',msg:'Success'} + $.ccio.tm(4,d,'#logs,.monitor_item[mid="'+d.id+'"][ke="'+d.ke+'"][auth="'+user.auth_token+'"] .logs') + } + $.ccio.init('signal',d,user); + } + break; + default: + if($.ccio.op().jpeg_on===true){return} + $.ccio.snapshot(d,function(url){ + d.check.f=url; + setTimeout(function(){ + $.ccio.snapshot(d,function(url){ + if(d.check.f===url){ + if(d.check.c<3){ + ++d.check.c; + setTimeout(function(){ + d.fn(); + },d.speed) + }else{ + if(d.d.signal_check_log==1){ + d.log={type:'Stream Check',msg:'Client side ctream check failed, attempting reconnect.'} + $.ccio.tm(4,d,'#logs,.monitor_item[mid="'+d.id+'"][ke="'+d.ke+'"][auth="'+user.auth_token+'"] .logs') + } + delete(d.check) + $.ccio.cx({f:'monitor',ff:'watch_on',id:d.id},user); + } + }else{ + if(d.d.signal_check_log==1){ + d.log={type:'Stream Check',msg:'Success'} + $.ccio.tm(4,d,'#logs,.monitor_item[mid="'+d.id+'"][ke="'+d.ke+'"][auth="'+user.auth_token+'"] .logs') + } + delete(d.check) + $.ccio.init('signal',d,user); + } + }); + },d.speed) + }); + break; + } + } + d.fn(); + }catch(er){ + er=er.stack; + d.in=function(x){return er.indexOf(x)>-1} + switch(true){ + case d.in("The HTMLImageElement provided is in the 'broken' state."): + delete(d.check) + $.ccio.cx({f:'monitor',ff:'watch_on',id:d.id},user); + break; + default: + $.ccio.log('signal-check',er) + break; + } + clearInterval($.ccio.mon[d.ke+d.id+user.auth_token].signal);delete($.ccio.mon[d.ke+d.id+user.auth_token].signal); + } + break; + } + return k.tmp; +} diff --git a/web/libs/js/dash2.logviewer.js b/web/libs/js/dash2.logviewer.js new file mode 100644 index 00000000..40ec0eb7 --- /dev/null +++ b/web/libs/js/dash2.logviewer.js @@ -0,0 +1,66 @@ +$(document).ready(function(e){ +//log viewer +$.log = { + e : $('#logs_modal'), + lm : $('#log_monitors'), + dateRange : $('#logs_daterange'), + loaded : {} +} +$.log.dateRange.daterangepicker({ + startDate:$.ccio.timeObject().subtract(moment.duration("5:00:00")), + endDate:$.ccio.timeObject().add(moment.duration("24:00:00")), + timePicker: true, + timePicker24Hour: true, + timePickerSeconds: true, + timePickerIncrement: 30, + locale: { + format: 'MM/DD/YYYY h:mm A' + } +},function(start, end, label){ + //change daterange + $.log.lm.change() +}); +$.log.table = $.log.e.find('table') +$.log.e.on('shown.bs.modal', function () { + $.log.lm.find('option:not(.hard)').remove() + $.each($.ccio.mon,function(n,v){ + v.id = v.mid + $.ccio.tm('option',v,'#log_monitors') + }) + $.log.lm.change() +}) +$.log.lm.change(function(){ + e = {} + e.v = $(this).val(); + e.urlSelector = e.v+''; + if(e.v === 'all'){ + e.urlSelector = '' + } + e.dateRange = $.log.dateRange.data('daterangepicker'); + $.log.loaded.startDate = e.dateRange.startDate + $.log.loaded.endDate = e.dateRange.endDate + var url = $.ccio.init('location',$user)+$user.auth_token+'/logs/'+$user.ke+'/'+e.urlSelector+'?start='+$.ccio.init('th',$.log.loaded.startDate)+'&end='+$.ccio.init('th',$.log.loaded.endDate) + $.get(url,function(d){ + $.log.loaded.url = url + $.log.loaded.query = e.v + $.log.loaded.rows = d + e.tmp=''; + if(d.length === 0){ + e.tmp = ''+lang.NoLogsFoundForDateRange+'' + }else{ + $.each(d,function(n,v){ + e.tmp+=''+v.time+''+v.mid+''+$.ccio.init('jsontoblock',v.info)+'' + }) + } + $.log.table.find('tbody').html(e.tmp) +// $.log.table.bootstrapTable() + $.ccio.init('ls') + }) +}) +$.log.e.find('[download]').click(function(){ + $.ccio.downloadJSON($.log.loaded,'Shinobi_Logs_'+(new Date())+'.json',{ + title : 'No Logs Found', + text : 'No file will be downloaded.', + }) +}) +}) diff --git a/web/libs/js/dash2.monitoredit.js b/web/libs/js/dash2.monitoredit.js new file mode 100644 index 00000000..cedc2dbc --- /dev/null +++ b/web/libs/js/dash2.monitoredit.js @@ -0,0 +1,773 @@ +$(document).ready(function(e){ + +//Monitor Editor +$.aM={e:$('#add_monitor'),monitorsForCopy:$('#copy_settings_monitors')}; +$.aM.f=$.aM.e.find('form') +$.aM.channels=$('#monSectionStreamChannels') +$.aM.maps=$('#monSectionInputMaps') +$.aM.e.find('.follow-list ul').affix(); +$.each($.ccio.definitions["Monitor Settings"].blocks,function(n,v){ + $.each(v.info,function(m,b){ + if(!b.name){ + console.log(b) + return + } + if(b.name.indexOf('detail=')>-1){ + b.name=b.name.replace('detail=','') + v.element=$.aM.e.find('[detail="'+b.name+'"]') + }else{ + v.element=$.aM.e.find('[name="'+b.name+'"]') + } + v.parent=v.element.parents('.form-group').find('label div:first-child span') + v.parent.find('small').remove() + v.parent.append(''+b.description+'') + }) +}) +$.aM.generateDefaultMonitorSettings=function(){ + return { + "mode": "start", + "mid": $.ccio.gid(), + "name": "Some Stream", + "type": "h264", + "protocol": "rtsp", + "host": "", + "port": "", + "path": "", + "ext": "mp4", + "fps": "1", + "width": "640", + "height": "480", + "details": JSON.stringify({ + "fatal_max": "0", + "notes": "", + "dir": "", + "auto_host_enable": "1", + "auto_host": "", + "rtsp_transport": "tcp", + "muser": "", + "mpass": "", + "port_force": "0", + "aduration": "1000000", + "probesize": "1000000", + "stream_loop": "0", + "sfps": "", + "accelerator": "0", + "hwaccel": "auto", + "hwaccel_vcodec": "", + "hwaccel_device": "", + "stream_type": "mp4", + "stream_flv_type": "ws", + "stream_mjpeg_clients": "", + "stream_vcodec": "copy", + "stream_acodec": "no", + "hls_time": "2", + "preset_stream": "ultrafast", + "hls_list_size": "3", + "signal_check": "10", + "signal_check_log": "0", + "stream_quality": "15", + "stream_fps": "2", + "stream_scale_x": "", + "stream_scale_y": "", + "rotate_stream": "no", + "svf": "", + "rtmp_vcodec": "h264", + "rtmp_acodec": "aac", + "stream_timestamp": "0", + "stream_timestamp_font": "", + "stream_timestamp_font_size": "", + "stream_timestamp_color": "", + "stream_timestamp_box_color": "", + "stream_timestamp_x": "", + "stream_timestamp_y": "", + "stream_watermark": "0", + "stream_watermark_location": "", + "stream_watermark_position": "tr", + "snap": "0", + "snap_fps": "", + "snap_scale_x": "", + "snap_scale_y": "", + "snap_vf": "", + "rawh264": "0", + "rawh264_vcodec": "copy", + "rawh264_acodec": "", + "rawh264_fps": "", + "rawh264_scale_x": "", + "rawh264_scale_y": "", + "rawh264_crf": "", + "rawh264_vf": "", + "vcodec": "copy", + "crf": "1", + "preset_record": "", + "acodec": "no", + "dqf": "0", + "cutoff": "15", + "rotate_record": "no", + "vf": "", + "timestamp": "0", + "timestamp_font": "", + "timestamp_font_size": "10", + "timestamp_color": "white", + "timestamp_box_color": "0x00000000@1", + "timestamp_x": "(w-tw)/2", + "timestamp_y": "0", + "watermark": "0", + "watermark_location": "", + "watermark_position": "tr", + "cust_input": "", + "cust_snap": "", + "cust_rawh264": "", + "cust_detect": "", + "cust_stream": "", + "cust_stream_server": "", + "cust_record": "", + "custom_output": "", + "detector": "0", + "detector_pam": "1", + "detector_webhook": "0", + "detector_webhook_url": "", + "detector_command_enable": "0", + "detector_command": "", + "detector_command_timeout": "", + "detector_lock_timeout": "", + "detector_save": "0", + "detector_frame_save": "0", + "detector_mail": "0", + "detector_mail_timeout": "", + "detector_record_method": "sip", + "detector_trigger": "1", + "detector_trigger_record_fps": "", + "detector_timeout": "10", + "watchdog_reset": "0", + "detector_delete_motionless_videos": "0", + "detector_send_frames": "1", + "detector_region_of_interest": "0", + "detector_fps": "", + "detector_scale_x": "640", + "detector_scale_y": "480", + "detector_use_motion": "1", + "detector_use_detect_object": "0", + "detector_frame": "0", + "detector_sensitivity": "", + "detector_max_sensitivity": "", + "detector_threshold": "1", + "detector_color_threshold": "", + "cords": "[]", + "detector_buffer_vcodec": "auto", + "detector_buffer_fps": "", + "detector_buffer_hls_time": "", + "detector_buffer_hls_list_size": "", + "detector_buffer_start_number": "", + "detector_buffer_live_start_index": "", + "detector_lisence_plate": "0", + "detector_lisence_plate_country": "us", + "detector_notrigger": "0", + "detector_notrigger_mail": "0", + "detector_notrigger_timeout": "", + "control": "0", + "control_base_url": "", + "control_stop": "0", + "control_url_stop_timeout": "", + "control_url_center": "", + "control_url_left": "", + "control_url_left_stop": "", + "control_url_right": "", + "control_url_right_stop": "", + "control_url_up": "", + "control_url_up_stop": "", + "control_url_down": "", + "control_url_down_stop": "", + "control_url_enable_nv": "", + "control_url_disable_nv": "", + "control_url_zoom_out": "", + "control_url_zoom_out_stop": "", + "control_url_zoom_in": "", + "control_url_zoom_in_stop": "", + "tv_channel": "0", + "groups": "[]", + "loglevel": "warning", + "sqllog": "0", + "detector_cascades": "" + }), + "shto": "[]", + "shfr": "[]" +} +} +$.aM.drawList=function(){ + e={list:$.aM.e.find('.follow-list ul'),html:''} + $.aM.e.find('[section]:visible').each(function(n,v){ + e.e=$(v) + e.id = e.e.attr('id'); + e.title = e.e.find('h4').first().html(); + var div = document.createElement('div'); + div.innerHTML = e.title; + var elements = div.getElementsByTagName('a'); + while (elements[0]) + elements[0].parentNode.removeChild(elements[0]) + var elements = div.getElementsByTagName('small'); + while (elements[0]) + elements[0].parentNode.removeChild(elements[0]) + var repl = div.innerHTML; + e.html += '
    • '+repl+'
    • ' + }) + e.list.html(e.html) +} +$.aM.import=function(e){ + $.get($.ccio.init('location',$user)+$user.auth_token+'/hls/'+e.values.ke+'/'+e.values.mid+'/detectorStream.m3u8',function(data){ + $('#monEditBufferPreview').html(data) + }) + $.aM.e.find('.edit_id').text(e.values.mid); + $.aM.e.attr('mid',e.values.mid).attr('ke',e.values.ke).attr('auth',e.auth) + $.each(e.values,function(n,v){ + $.aM.e.find('[name="'+n+'"]').val(v).change() + }) + e.ss=JSON.parse(e.values.details); + //get maps + $.aM.maps.empty() + if(e.ss.input_maps&&e.ss.input_maps!==''){ + var input_maps + try{ + input_maps = JSON.parse(e.ss.input_maps) + }catch(er){ + input_maps = e.ss.input_maps; + } + var mapContainers = $('[input-mapping]') + if(input_maps.length>0){ + mapContainers.show() + $.each(input_maps,function(n,v){ + var tempID = $.ccio.tm('input-map') + var parent = $('#monSectionMap'+tempID) + $.each(v,function(m,b){ + parent.find('[map-detail="'+m+'"]').val(b).change() + }) + }) + }else{ + mapContainers.hide() + } + } + //get channels + $.aM.channels.empty() + if(e.ss.stream_channels&&e.ss.stream_channels!==''){ + var stream_channels + try{ + stream_channels = JSON.parse(e.ss.stream_channels) + }catch(er){ + stream_channels = e.ss.stream_channels; + } + $.each(stream_channels,function(n,v){ + var tempID = $.ccio.tm('stream-channel') + var parent = $('#monSectionChannel'+tempID) + $.each(v,function(m,b){ + parent.find('[channel-detail="'+m+'"]').val(b) + }) + }) + } + //get map choices for outputs + $('[input-mapping] .choices').empty() + if(e.ss.input_map_choices&&e.ss.input_map_choices!==''){ + var input_map_choices + try{ + input_map_choices = JSON.parse(e.ss.input_map_choices) + }catch(er){ + input_map_choices = e.ss.input_map_choices; + } + $.each(input_map_choices,function(n,v){ + $.each(v,function(m,b){ + var parent = $('[input-mapping="'+n+'"] .choices') + $.ccio.tm('input-map-selector',b,parent) + }) + }) + } + $.aM.e.find('[detail]').each(function(n,v){ + v=$(v).attr('detail');if(!e.ss[v]){e.ss[v]=''} + }) + $.each(e.ss,function(n,v){ + var theVal = v; + if(v instanceof Object){ + theVal = JSON.stringify(v); + } + $.aM.e.find('[detail="'+n+'"]').val(theVal).change(); + }); + $.each(e.ss,function(n,v){ + try{ + var variable=JSON.parse(v) + }catch(err){ + var variable=v + } + if(variable instanceof Object){ + $('[detailContainer="'+n+'"][detailObject]').prop('checked',false) + $('[detailContainer="'+n+'"][detailObject]').parents('.mdl-js-switch').removeClass('is-checked') + if(variable instanceof Array){ + $.each(variable,function(m,b,parentOfObject){ + $('[detailContainer="'+n+'"][detailObject="'+b+'"]').prop('checked',true) + parentOfObject=$('[detailContainer="'+n+'"][detailObject="'+b+'"]').parents('.mdl-js-switch') + parentOfObject.addClass('is-checked') + }) + }else{ + $.each(variable,function(m,b){ + if(typeof b ==='string'){ + $('[detailContainer="'+n+'"][detailObject="'+m+'"]').val(b).change() + }else{ + $('[detailContainer="'+n+'"][detailObject="'+m+'"]').prop('checked',true) + parentOfObject=$('[detailContainer="'+n+'"][detailObject="'+m+'"]').parents('.mdl-js-switch') + parentOfObject.addClass('is-checked') + } + }) + } + } + }); + try{ + $.each(['groups','group_detector'],function(m,b){ + var tmp='' + $.each($user.mon_groups,function(n,v){ + tmp+='
    • '; + tmp+=''; + tmp+=v.name; + tmp+=''; + tmp+=''; + tmp+=''; + tmp+=''; + tmp+='
    • '; + }) + $('#monitor_'+b).html(tmp) + }) + componentHandler.upgradeAllRegistered() + }catch(er){ + console.log(er) + //no group, this 'try' will be removed in future. + }; + $('#copy_settings').val('0').change() + var tmp = ''; + $.each($.ccio.mon,function(n,v){ + if(v.ke === $user.ke){ + tmp += $.ccio.tm('option',{auth_token:$user.auth_token,id:v.mid,name:v.name},null,$user); + } + }) + $.aM.monitorsForCopy.find('optgroup').html(tmp) + setTimeout(function(){$.aM.drawList()},1000) +} +//parse "Automatic" field in "Input" Section +$.aM.e.on('change','.auto_host_fill input,.auto_host_fill select',function(e){ + var theSwitch = $.aM.e.find('[detail="auto_host_enable"]').val() + if(!theSwitch||theSwitch===''){ + theSwitch='1' + } + if(theSwitch==='1'){ + return + } + if($.aM.e.find('[name="host"]').val() !== ''){ + $.aM.e.find('[detail="auto_host"]').val($.aM.buildMonitorURL()) + } +}) +$.aM.e.on('change','[detail="auto_host"]',function(e){ + var isRTSP = false + var inputType = $.aM.e.find('[name="type"]').val() + var url = $(this).val() + var theSwitch = $.aM.e.find('[detail="auto_host_enable"]') + var disabled = theSwitch.val() + if(!disabled||disabled===''){ + //if no value, then probably old version of monitor config. Set to Manual to avoid confusion. + disabled='0' + theSwitch.val('0').change() + } + if(disabled==='0'){ + return + } + if(inputType === 'local'){ + $.aM.e.find('[name="path"]').val(url).change() + }else{ + var urlSplitByDots = url.split('.') + var has = function(query,searchIn){if(!searchIn){searchIn=url;};return url.indexOf(query)>-1} + var protocol = url.split('://')[0] + console.log(url.split('://')) + //switch RTSP, RTMP and RTMPS to parse URL + if(has('rtsp://')){ + isRTSP = true; + url = url.replace('rtsp://','http://') + } + if(has('rtmp://')){ + isRTMP = true; + url = url.replace('rtmp://','http://') + } + if(has('rtmps://')){ + isRTMPS = true; + url = url.replace('rtmps://','http://') + } + //parse URL + var parsedURL = document.createElement('a'); + parsedURL.href = url; + var pathname = parsedURL.pathname + if(url.indexOf('?') > -1){ + pathname += '?'+url.split('?')[1] + } + $.aM.e.find('[name="protocol"]').val(protocol).change() + if(isRTSP){ + $.aM.e.find('[detail="rtsp_transport"]').val('tcp').change() + $.aM.e.find('[detail="aduration"]').val(1000000).change() + $.aM.e.find('[detail="probesize"]').val(1000000).change() + } + $.aM.e.find('[detail="muser"]').val(parsedURL.username).change() + $.aM.e.find('[detail="mpass"]').val(parsedURL.password).change() + $.aM.e.find('[name="host"]').val(parsedURL.hostname).change() + $.aM.e.find('[name="port"]').val(parsedURL.port).change() + $.aM.e.find('[name="path"]').val(pathname).change() + delete(parsedURL) + } +}) +$.aM.e.find('.refresh_cascades').click(function(e){ + $.ccio.cx({f:'ocv_in',data:{f:'refreshPlugins',ke:$user.ke}}) +}) +$.aM.f.submit(function(ee){ + ee.preventDefault(); + e={e:$(this)}; + e.s=e.e.serializeObject(); + e.er=[]; + $.each(e.s,function(n,v){e.s[n]=v.trim()}); + e.s.mid=e.s.mid.replace(/[^\w\s]/gi,'').replace(/ /g,'') + if(e.s.mid.length<3){e.er.push('Monitor ID too short')} + if(e.s.port==''){ + if(e.s.protocol === 'https'){ + e.s.port = 443 + }else{ + e.s.port = 80 + } + } + if(e.s.name==''){e.er.push('Monitor Name cannot be blank')} +// if(e.s.protocol=='rtsp'){e.s.ext='mp4',e.s.type='rtsp'} + if(e.er.length>0){ + $.sM.e.find('.msg').html(e.er.join('
      ')); + $.ccio.init('note',{title:'Configuration Invalid',text:e.er.join('
      '),type:'error'}); + return; + } + $.post($.ccio.init('location',$user)+$user.auth_token+'/configureMonitor/'+$user.ke+'/'+e.s.mid,{data:JSON.stringify(e.s)},function(d){ + $.ccio.log(d) + }) + // + if($('#copy_settings').val() === '1'){ + e.s.details = JSON.parse(e.s.details); + var copyMonitors = $.aM.monitorsForCopy.val(); + var chosenSections = []; + var chosenMonitors = {}; + + if(!copyMonitors||copyMonitors.length===0){ + $.ccio.init('note',{title:lang['No Monitors Selected'],text:lang.monSavedButNotCopied}) + return + } + + $.aM.e.find('[copy]').each(function(n,v){ + var el = $(v) + if(el.val() === '1'){ + chosenSections.push(el.attr('copy')) + } + }) + var alterSettings = function(settingsToAlter,monitor){ + monitor.details = JSON.parse(monitor.details); + $.aM.e.find(settingsToAlter).find('input,select,textarea').each(function(n,v){ + var el = $(v); + var name = el.attr('name') + var detail = el.attr('detail') + var value + switch(true){ + case !!name: + var value = e.s[name] + monitor[name] = value; + break; + case !!detail: + detail = detail.replace('"','') + var value = e.s.details[detail] + monitor.details[detail] = value; + break; + } + }) + monitor.details = JSON.stringify(monitor.details); + return monitor; + } + $.each(copyMonitors,function(n,id){ + var monitor + if(id === '$New'){ + monitor = $.aM.generateDefaultMonitorSettings(); + //connection + monitor.name = e.s.name+' - '+monitor.mid + monitor.type = e.s.type + monitor.protocol = e.s.protocol + monitor.host = e.s.host + monitor.port = e.s.port + monitor.path = e.s.path + monitor.details.fatal_max = e.s.details.fatal_max + monitor.details.port_force = e.s.details.port_force + monitor.details.muser = e.s.details.muser + monitor.details.password = e.s.details.password + monitor.details.rtsp_transport = e.s.details.rtsp_transport + monitor.details.auto_host = e.s.details.auto_host + monitor.details.auto_host_enable = e.s.details.auto_host_enable + //input + monitor.details.aduration = e.s.details.aduration + monitor.details.probesize = e.s.details.probesize + monitor.details.stream_loop = e.s.details.stream_loop + monitor.details.sfps = e.s.details.sfps + monitor.details.accelerator = e.s.details.accelerator + monitor.details.hwaccel = e.s.details.hwaccel + monitor.details.hwaccel_vcodec = e.s.details.hwaccel_vcodec + monitor.details.hwaccel_device = e.s.details.hwaccel_device + }else{ + monitor = Object.assign({},$.ccio.init('cleanMon',$.ccio.mon[$user.ke+id+$user.auth_token])); + } + $.each(chosenSections,function(n,section){ + monitor = alterSettings(section,monitor) + }) + console.log(monitor) + $.post($.ccio.init('location',$user)+$user.auth_token+'/configureMonitor/'+$user.ke+'/'+monitor.mid,{data:JSON.stringify(monitor)},function(d){ + $.ccio.log(d) + }) + chosenMonitors[monitor.mid] = monitor; + }) + console.log(chosenMonitors) + } + + $.aM.e.modal('hide') + return false; +}); +////////////////// +//Input Map (Feed) +$.aM.mapPlacementInit = function(){ + $('.input-map').each(function(n,v){ + var _this = $(this) + _this.find('.place').text(n+1) + }) +} +$.aM.mapSave = function(){ + var e={}; + var mapContainers = $('[input-mapping]'); + var stringForSave={} + mapContainers.each(function(q,t){ + var mapRowElement = $(t).find('.map-row'); + var mapRow = [] + mapRowElement.each(function(n,v){ + var map={} + $.each($(v).find('[map-input]'),function(m,b){ + map[$(b).attr('map-input')]=$(b).val() + }); + mapRow.push(map) + }); + stringForSave[$(t).attr('input-mapping')] = mapRow; + }); + $.aM.e.find('[detail="input_map_choices"]').val(JSON.stringify(stringForSave)).change(); +} +$.aM.maps.on('click','.delete',function(){ + $(this).parents('.input-map').remove() + var inputs = $('[map-detail]') + var mapContainers = $('[input-mapping]'); + if(inputs.length===0){ + $.aM.e.find('[detail="input_maps"]').val('[]').change() + mapContainers.hide(); + }else{ + inputs.first().change() + mapContainers.show(); + } + $.aM.mapPlacementInit() +}) +$.aM.e.on('change','[map-detail]',function(){ + var e={}; + e.e=$.aM.maps.find('.input-map') + e.s=[] + e.e.each(function(n,v){ + var map={} + $.each($(v).find('[map-detail]'),function(m,b){ + map[$(b).attr('map-detail')]=$(b).val() + }); + e.s.push(map) + }); + $.aM.e.find('[detail="input_maps"]').val(JSON.stringify(e.s)).change() +}) +$.aM.e.on('click','[input-mapping] .add_map_row',function(){ + $.ccio.tm('input-map-selector',{},$(this).parents('[input-mapping]').find('.choices')) + $.aM.mapSave() +}) +$.aM.e.on('click','[input-mapping] .delete_map_row',function(){ + $(this).parents('.map-row').remove() + $.aM.mapSave() +}) +$.aM.e.on('change','[map-input]',function(){ + $.aM.mapSave() +}) +////////////////// +//Stream Channels +$.aM.channelSave = function(){ + var e={}; + e.e=$.aM.channels.find('.stream-channel') + e.s=[] + e.e.each(function(n,v){ + var channel={} + $.each($(v).find('[channel-detail]'),function(m,b){ + channel[$(b).attr('channel-detail')]=$(b).val() + }); + e.s.push(channel) + }); + $.aM.e.find('[detail="stream_channels"]').val(JSON.stringify(e.s)).change() +} +$.aM.channelPlacementInit = function(){ + $('.stream-channel').each(function(n,v){ + var _this = $(this) + _this.attr('stream-channel',n) + _this.find('.place').text(n) + _this.find('[input-mapping]').attr('input-mapping','stream_channel-'+n) + $.aM.mapSave() + }) +} +$.aM.buildMonitorURL = function(){ + var e={}; + e.user=$.aM.e.find('[detail="muser"]').val(); + e.pass=$.aM.e.find('[detail="mpass"]').val(); + e.host=$.aM.e.find('[name="host"]').val(); + e.protocol=$.aM.e.find('[name="protocol"]').val(); + e.port=$.aM.e.find('[name="port"]').val(); + e.path=$.aM.e.find('[name="path"]').val(); + if($.aM.e.find('[name="type"]').val()==='local'){ + e.url=e.path; + }else{ + if(e.host.indexOf('@')===-1&&e.user!==''){ + e.host=e.user+':'+e.pass+'@'+e.host; + } + e.url=$.ccio.init('url',e)+e.path; + } + return e.url +} +$.aM.channels.on('click','.delete',function(){ + $(this).parents('.stream-channel').remove() + $.aM.channelSave() + $.aM.channelPlacementInit() +}) +$.aM.e.on('change','[channel-detail]',function(){ + $.aM.channelSave() +}) +////////////////// +$.aM.e.on('change','[groups]',function(){ + var e={}; + e.e=$.aM.e.find('[groups]:checked'); + e.s=[]; + e.e.each(function(n,v){ + e.s.push($(v).val()) + }); + $.aM.e.find('[detail="groups"]').val(JSON.stringify(e.s)).change() +}) +$.aM.e.on('change','.detector_cascade_selection',function(){ + var e={}; + e.e=$.aM.e.find('.detector_cascade_selection:checked'); + e.s={}; + e.e.each(function(n,v){ + e.s[$(v).val()]={} + }); + $.aM.e.find('[detail="detector_cascades"]').val(JSON.stringify(e.s)).change() +}) +//$.aM.e.on('change','.detector_cascade_selection',function(){ +// var e={}; +// e.details=$.aM.e.find('[name="details"]') +// try{ +// e.detailsVal=JSON.parse(e.details.val()) +// }catch(err){ +// e.detailsVal={} +// } +// e.detailsVal.detector_cascades=[]; +// e.e=$.aM.e.find('.detector_cascade_selection:checked'); +// e.e.each(function(n,v){ +// e.detailsVal.detector_cascades.push($(v).val()) +// }); +// e.details.val(JSON.stringify(e.detailsVal)) +//}) +$.aM.e.find('.probe_config').click(function(){ + $.pB.e.find('[name="url"]').val($.aM.buildMonitorURL()); + $.pB.f.submit(); + $.pB.e.modal('show'); +}) +$.aM.e.find('.import_config').click(function(e){ + var e={};e.e=$(this);e.mid=e.e.parents('[mid]').attr('mid'); + $.confirm.e.modal('show'); + $.confirm.title.text(lang['Import Monitor Configuration']) + e.html=lang.ImportMonitorConfigurationText+'
      '; + $.confirm.body.html(e.html) + $.confirm.e.find('.upload').change(function(e){ + var files = e.target.files; // FileList object + f = files[0]; + var reader = new FileReader(); + reader.onload = function(ee) { + $.confirm.e.find('textarea').val(ee.target.result); + } + reader.readAsText(f); + }); + $.confirm.click({title:'Import',class:'btn-primary'},function(){ + try{ + e.values=JSON.parse($.confirm.e.find('textarea').val()); + $.aM.import(e) + $.aM.e.modal('show') + }catch(err){ + $.ccio.log(err) + $.ccio.init('note',{title:lang['Invalid JSON'],text:lang.InvalidJSONText,type:'error'}) + } + }); +}); +$.aM.e.find('.save_config').click(function(e){ + var e={};e.e=$(this);e.mid=e.e.parents('[mid]').attr('mid');e.s=$.aM.f.serializeObject(); + if(!e.mid||e.mid===''){ + e.mid='NewMonitor' + } + e.dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(e.s)); + $('#temp').html('') + .find('a') + .attr('href',e.dataStr) + .attr('download','Shinobi_'+e.mid+'_config.json') + [0].click() +}); +$.aM.e.find('.add_map').click(function(e){ + $('[input-mapping]').show() + $.ccio.tm('input-map') +}); +$.aM.e.find('.add_channel').click(function(e){ + $.ccio.tm('stream-channel') +}); +$.aM.f.find('[detail="stream_type"]').change(function(e){ + e.e=$(this); + if(e.e.val()==='jpeg'){$.aM.f.find('[detail="snap"]').val('1').change()} +}) +$.aM.f.find('[name="type"]').change(function(e){ + e.e=$(this); + if(e.e.val()==='h264'){$.aM.f.find('[name="protocol"]').val('rtsp').change()} +}) +$.aM.md=$.aM.f.find('[detail]'); +$.aM.md.change($.ccio.form.details) +$.aM.f.on('change','[selector]',function(){ + e={e:$(this)} + e.v=e.e.val(); + e.a=e.e.attr('selector') + e.triggerChange=e.e.attr('triggerchange') + e.triggerChangeIgnore=e.e.attr('triggerChangeIgnore') + $.aM.f.find('.'+e.a+'_input').hide() + $.aM.f.find('.'+e.a+'_'+e.v).show(); + $.aM.f.find('.'+e.a+'_text').text($(this).find('option:selected').text()) + if(e.triggerChange && e.triggerChange !== '' && !e.triggerChangeIgnore || (e.triggerChangeIgnore && e.triggerChangeIgnore.split(',').indexOf(e.v) === -1)){ + console.log(e.triggerChange) + $(e.triggerChange).trigger('change') + } + $.aM.drawList() +}); +$.aM.f.find('[name="type"]').change(function(e){ + e.e=$(this); + e.v=e.e.val(); + e.h=$.aM.f.find('[name="path"]'); + e.p=e.e.parents('.form-group'); + switch(e.v){ + case'local':case'socket': + e.h.attr('placeholder','/dev/video0') + break; + default: + e.h.attr('placeholder','/videostream.cgi?1') + break; + } +}); +}) diff --git a/web/libs/js/dash2.multimon.js b/web/libs/js/dash2.multimon.js new file mode 100644 index 00000000..669d4fab --- /dev/null +++ b/web/libs/js/dash2.multimon.js @@ -0,0 +1,192 @@ +$(document).ready(function(e){ +//multi monitor manager +$.multimon={e:$('#multi_mon')}; +$.multimon.table=$.multimon.e.find('.tableData tbody'); +$.multimon.f=$.multimon.e.find('form'); +$.multimon.f.on('change','#multimon_select_all',function(e){ + e.e=$(this); + e.p=e.e.prop('checked') + e.a=$.multimon.f.find('input[type=checkbox][name]') + if(e.p===true){ + e.a.prop('checked',true) + }else{ + e.a.prop('checked',false) + } +}) +$.multimon.e.find('.import_config').click(function(){ + var e={};e.e=$(this);e.mid=e.e.parents('[mid]').attr('mid'); + $.confirm.e.modal('show'); + $.confirm.title.text(lang['Import Monitor Configuration']) + e.html=lang.ImportMultiMonitorConfigurationText+'
      '; + $.confirm.body.html(e.html) + $.confirm.e.find('.upload').change(function(e){ + var files = e.target.files; // FileList object + f = files[0]; + var reader = new FileReader(); + reader.onload = function(ee) { + $.confirm.e.find('textarea').val(ee.target.result); + } + reader.readAsText(f); + }); + $.confirm.click({title:'Import',class:'btn-primary'},function(){ +// setTimeout(function(){ +// $.confirm.e.modal('show'); +// },1000) +// $.confirm.title.text(lang['Are you sure?']) +// $.confirm.body.html(lang.ImportMultiMonitorConfigurationText) +// $.confirm.click({title:'Save Set',class:'btn-danger'},function(){ + try{ + var postMonitor = function(v){ + $.post($.ccio.init('location',$user)+$user.auth_token+'/configureMonitor/'+$user.ke+'/'+v.mid,{data:JSON.stringify(v,null,3)},function(d){ + $.ccio.log(d) + }) + } + var parseZmMonitor = function(Monitor){ + console.log(Monitor) + var newMon = $.aM.generateDefaultMonitorSettings() + newMon.details = JSON.parse(newMon.details) + newMon.details.stream_type = 'jpeg' + switch(Monitor.Type.toLowerCase()){ + case'ffmpeg':case'libvlc': + newMon.details.auto_host_enable = '1' + newMon.details.auto_host = Monitor.Path + if(newMon.auto_host.indexOf('rtsp://') > -1 || newMon.auto_host.indexOf('rtmp://') > -1 || newMon.auto_host.indexOf('rtmps://') > -1){ + newMon.type = 'h264' + }else{ + $.ccio.init('note',{title:lang['Please Check Your Settings'],text:lang.migrateText1,type:'error'}) + } + break; + case'local': + newMon.details.auto_host = Monitor.Device + break; + case'remote': + + break; + } + newMon.details = JSON.stringify(newMon.details) + console.log(newMon) + return newMon + } + parsedData=JSON.parse($.confirm.e.find('textarea').val()); + //zoneminder one monitor + if(parsedData.monitor){ + $.aM.import({ + values : parseZmMonitor(parsedData.monitor.Monitor) + }) + $.aM.e.modal('show') + }else + //zoneminder multiple monitors + if(parsedData.monitors){ + $.each(parsedData.monitors,function(n,v){ + $.aM.import({ + values : parseZmMonitor(parsedData.Monitor) + }) + parseZmMonitor(v.Monitor) + }) + }else + //shinobi one monitor + if(parsedData.mid){ + postMonitor(parsedData) + }else + //shinobi multiple monitors + if(parsedData[0] && parsedData[0].mid){ + $.each(parsedData,function(n,v){ + postMonitor(v) + }) + } + }catch(err){ + $.ccio.log(err) + $.ccio.init('note',{title:lang['Invalid JSON'],text:lang.InvalidJSONText,type:'error'}) + } +// }); + }); +}) +$.multimon.getSelectedMonitors = function(unclean){ + var arr=[]; + if(unclean === true){ + var monitors = $.ccio.mon + }else{ + var monitors = $.ccio.init('cleanMons','object') + } + $.each($.multimon.f.serializeObject(),function(n,v){ + arr.push(monitors[n]) + }) + return arr; +} +$.multimon.e.find('.delete').click(function(){ + var arr=$.multimon.getSelectedMonitors(true); + if(arr.length===0){ + $.ccio.init('note',{title:'No Monitors Selected',text:'Select atleast one monitor to delete.',type:'error'}); + return + } + $.confirm.e.modal('show'); + $.confirm.title.text(lang['Delete']+' '+lang['Monitors']) + e.html='

      '+lang.DeleteMonitorsText+'

      '; + $.confirm.body.html(e.html) + $.confirm.click([ + { + title:'Delete Monitors', + class:'btn-danger', + callback:function(){ + $.each(arr,function(n,v){ + $.get($.ccio.init('location',$user)+v.user.auth_token+'/configureMonitor/'+v.ke+'/'+v.mid+'/delete',function(data){ + console.log(data) + }) + }) + } + }, + { + title:'Delete Monitors and Files', + class:'btn-danger', + callback:function(){ + $.each(arr,function(n,v){ + $.get($.ccio.init('location',$user)+v.user.auth_token+'/configureMonitor/'+v.ke+'/'+v.mid+'/delete?deleteFiles=true',function(data){ + console.log(data) + }) + }) + } + } + ]); +}) +//$.multimon.e.find('.edit_all').click(function(){ +// var arr=$.multimon.getSelectedMonitors(); +// var arrObject={} +// if(arr.length===0){ +// $.ccio.init('note',{title:'No Monitors Selected',text:'Select atleast one monitor to delete.',type:'error'}); +// return +// } +// $.multimonedit.selectedList = arr; +// $.multimonedit.e.modal('show') +//}) +$.multimon.e.find('.save_config').click(function(){ + var e={};e.e=$(this); + var arr=$.multimon.getSelectedMonitors(); + if(arr.length===0){ + $.ccio.init('note',{title:'No Monitors Selected',text:'Select atleast one monitor to delete.',type:'error'}); + return + } + e.dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(arr)); + $('#temp').html('') + .find('a') + .attr('href',e.dataStr) + .attr('download','Shinobi_Monitors_'+(new Date())+'.json') + [0].click() +}) +$.multimon.e.on('shown.bs.modal',function() { + var tmp='' + $.each($.ccio.mon,function(n,v){ + var streamURL = $.ccio.init('streamURL',v) + if(streamURL!=='Websocket'&&v.mode!==('idle'&&'stop')){ + streamURL=''+streamURL+'' + } + var img = $('#left_menu [mid="'+v.mid+'"][auth="'+v.user.auth_token+'"] [monitor="watch"]').attr('src') + tmp+='' + tmp+='
      ' + tmp+=''+v.name+'
      '+v.mid+''+v.status+''+streamURL+'' + //buttons + tmp+=' ' + tmp+='' + }) + $.multimon.table.html(tmp) +}) +}) diff --git a/web/libs/js/dash2.onvifscanner.js b/web/libs/js/dash2.onvifscanner.js new file mode 100644 index 00000000..a9ce1520 --- /dev/null +++ b/web/libs/js/dash2.onvifscanner.js @@ -0,0 +1,60 @@ +$(document).ready(function(e){ +//onvif probe +$.oB={ + e:$('#onvif_probe'), + v:$('#onvif_video'), +}; +$.oB.f=$.oB.e.find('form');$.oB.o=$.oB.e.find('.output_data'); +$.oB.f.submit(function(ee){ + ee.preventDefault(); + e={}; + $.oB.foundMonitors={} + e.e=$(this),e.s=e.e.serializeObject(); + $.oB.o.empty(); + $.oB.e.find('._loading').show() + $.oB.e.find('[type="submit"]').prop('disabled',true) + $.ccio.cx({f:'onvif',ip:e.s.ip,port:e.s.port,user:e.s.user,pass:e.s.pass}) + clearTimeout($.oB.checkTimeout) + $.oB.checkTimeout=setTimeout(function(){ + if($.oB.o.find('tr').length===0){ + $.oB.e.find('._loading').hide() + $.oB.e.find('[type="submit"]').prop('disabled',false) + $.oB.o.append('Sorry, nothing was found.') + } + },5000) + return false; +}); +$.oB.e.on('click','.copy',function(){ + $('.hidden-xs [monitor="edit"]').click(); + e={}; + e.e = $(this).parents('[onvif_row]'); + var id = e.e.attr('onvif_row'); + var onvifRecord = $.oB.foundMonitors[id]; + var streamURL = onvifRecord.uri; + if($.oB.e.find('[name="user"]').val()!==''){ + streamURL = streamURL.split('://') + streamURL = streamURL[0]+'://'+$.oB.e.find('[name="user"]').val()+':'+$.oB.e.find('[name="pass"]').val()+'@'+streamURL[1]; + } + $.aM.e.find('[detail="auto_host"]').val(streamURL).change() + $.aM.e.find('[name="mode"]').val('start') + $.oB.e.modal('hide') +}) +$.oB.e.find('[name="ip"]').change(function(e){ + $.ccio.op('onvif_probe_ip',$(this).val()); +}) +if($.ccio.op().onvif_probe_ip){ + $.oB.e.find('[name="ip"]').val($.ccio.op().onvif_probe_ip) +} +$.oB.e.find('[name="port"]').change(function(e){ + $.ccio.op('onvif_probe_port',$(this).val()); +}) +if($.ccio.op().onvif_probe_port){ + $.oB.e.find('[name="port"]').val($.ccio.op().onvif_probe_port) +} +$.oB.e.find('[name="user"]').change(function(e){ + $.ccio.op('onvif_probe_user',$(this).val()); +}) +if($.ccio.op().onvif_probe_user){ + $.oB.e.find('[name="user"]').val($.ccio.op().onvif_probe_user) +} +}) diff --git a/web/libs/js/dash2.powervideo.js b/web/libs/js/dash2.powervideo.js new file mode 100644 index 00000000..ecd4e159 --- /dev/null +++ b/web/libs/js/dash2.powervideo.js @@ -0,0 +1,298 @@ +$(document).ready(function(e){ +//POWER videos window +$.pwrvid={e:$('#pvideo_viewer')}; +$.pwrvid.f=$.pwrvid.e.find('form'), +$.pwrvid.d=$('#vis_pwrvideo'), +$.pwrvid.mL=$('#motion_list'), +$.pwrvid.m=$('#vis_monitors'), +$.pwrvid.lv=$('#live_view'), +$.pwrvid.dr=$('#pvideo_daterange'), +$.pwrvid.vp=$('#video_preview'), +$.pwrvid.seekBar=$('#pwrvid_seekBar'), +$.pwrvid.seekBarProgress=$.pwrvid.seekBar.find('.progress-bar'), +$.pwrvid.playRate = 1; +$.pwrvid.dr.daterangepicker({ + startDate:$.ccio.timeObject().subtract(moment.duration("24:00:00")), + endDate:$.ccio.timeObject().add(moment.duration("24:00:00")), + timePicker: true, + timePicker24Hour: true, + timePickerSeconds: true, + timePickerIncrement: 30, + locale: { + format: 'MM/DD/YYYY h:mm A' + } +},function(start, end, label){ + $.pwrvid.drawTimeline() + $.pwrvid.dr.focus() +}); +$('#pvideo_show_events').change(function(){ + $.pwrvid.drawTimeline() +}) +$.pwrvid.e.on('click','[preview]',function(e){ + e.e=$(this); + e.video=$.pwrvid.vp.find('video')[0]; + if(e.video){ + e.duration=e.video.duration; + e.now=e.video.currentTime; + } + if($.pwrvid.video){ + clearInterval($.pwrvid.video.interval); + } + switch(e.e.attr('preview')){ + case'fullscreen': + $.ccio.init('fullscreen',e.video) + break; + case'mute': + e.video.muted = !e.video.muted + e.e.find('i').toggleClass('fa-volume-off fa-volume-up') + e.e.toggleClass('btn-danger') + break; + case'play': + e.video.playbackRate = 1; + $.pwrvid.vpOnPlayPause(1) + break; + case'stepFrontFront': + e.add=e.e.attr('add') + e.stepFrontFront=parseInt(e.e.attr('stepFrontFront')) + if(!e.stepFrontFront||isNaN(e.stepFrontFront)){e.stepFrontFront = 5} + if(e.add==="0"){ + $.pwrvid.playRate = e.stepFrontFront + }else{ + $.pwrvid.playRate += e.stepFrontFront + } + e.video.playbackRate = $.pwrvid.playRate; + e.video.play() + break; + case'stepFront': + e.video.currentTime += 1; + e.video.pause() + break; + case'stepBackBack': + $.pwrvid.video.interval = setInterval(function(){ + e.video.playbackRate = 1.0; + if(e.video.currentTime == 0){ + clearInterval($.pwrvid.video.interval); + e.video.pause(); + } + else{ + e.video.currentTime += -.2; + } + },30); + break; + case'stepBack': + e.video.currentTime += -1; + e.video.pause() + break; + case'video': +// e.preventDefault(); + e.p=e.e.parents('[mid]'); + e.filename=e.p.attr('file'); + $.pwrvid.vp.find('h3').text(e.filename) + e.href=e.e.attr('href'); + e.status=e.p.attr('status'); + e.mon=$.ccio.mon[e.p.attr('ke')+e.p.attr('mid')+$user.auth_token]; + $.pwrvid.vp.find('.holder').html(''); + $.pwrvid.vp + .attr('mid',e.mon.mid) + .attr('mid',e.mon.user.auth_token) + .attr('ke',e.mon.ke) + .attr('status',e.status) + .attr('file',e.filename) + .find('[download],[video="download"]') + .attr('download',e.filename) + .attr('href',e.href) + $.pwrvid.vp.find('video').off('loadeddata').on('loadeddata',function(){ + $.pwrvid.vp.find('.stream-objects .stream-detected-object').remove() + }) + if(e.status==1){ + $.get($.ccio.init('videoHrefToRead',e.href),function(d){ + + }) + } + var labels=[] + var Dataset1=[] + var events=$.pwrvid.currentDataObject[e.filename].motion + var eventsLabeledByTime={} + $.each(events,function(n,v){ + if(!v.details.confidence){v.details.confidence=0} + var time=$.ccio.timeObject(v.time).format('MM/DD/YYYY HH:mm:ss') + labels.push(time) + Dataset1.push(v.details.confidence) + eventsLabeledByTime[time]=v; + }) + if(events.length>0){ + $.pwrvid.mL.html("") + var timeFormat = 'MM/DD/YYYY HH:mm:ss'; + var color = Chart.helpers.color; + Chart.defaults.global.defaultFontColor = '#fff'; + var config = { + type: 'bar', + data: { + labels: labels, + datasets: [{ + type: 'line', + label: 'Motion Confidence', + backgroundColor: color(window.chartColors.red).alpha(0.2).rgbString(), + borderColor: window.chartColors.red, + data: Dataset1, + }] + }, + options: { + maintainAspectRatio: false, + title: { + fontColor: "white", + text:"Events in this video" + }, + scales: { + xAxes: [{ + type: "time", + display: true, + time: { + format: timeFormat, + // round: 'day' + } + }], + }, + } + }; + var ctx = $.pwrvid.mL.find('canvas')[0].getContext("2d"); + $.pwrvid.miniChart = new Chart(ctx, config); + $.pwrvid.mL.find('canvas').click(function(f) { + var target = $.pwrvid.miniChart.getElementsAtEvent(f)[0]; + if(!target){return false} + var video = $.pwrvid.currentDataObject[e.filename]; + var event = video.motion[target._index]; + var video1 = $('#video_preview video')[0]; + video1.currentTime=$.ccio.timeObject(event.time).diff($.ccio.timeObject(video.row.time),'seconds') + video1.play() + }); + var colorNames = Object.keys(window.chartColors); + + }else{ + $.pwrvid.mL.html('
      '+lang['No Events found for this video']+'
      ') + } + $.pwrvid.video={filename:e.filename,href:e.href,mid:e.mon.mid,ke:e.mon.ke} + $.pwrvid.vpOnPlayPause=function(x,e){ + var e={} + e.video=$.pwrvid.vp.find('video')[0] + e.i=$.pwrvid.vp.find('[preview="play"]').find('i') + if(e.video.paused===true){ + e.i.removeClass('fa-pause').addClass('fa-play') + if(x==1)e.video.play(); + }else{ + e.i.removeClass('fa-play').addClass('fa-pause') + if(x==1)e.video.pause(); + } + } + var videoElement=$.pwrvid.vp.find('video')[0] + $.pwrvid.vp.find('video') + .off('loadeddata').on('loadeddata', function() { + this.playbackRate = $.pwrvid.playRate; + this.play() + }) + .off("pause").on("pause",$.pwrvid.vpOnPlayPause) + .off("play").on("play",$.pwrvid.vpOnPlayPause) + .off("timeupdate").on("timeupdate",function(){ + var video = $.pwrvid.currentDataObject[e.filename]; + var videoTime=$.ccio.timeObject(video.row.time).add(parseInt(videoElement.currentTime),'seconds').format('MM/DD/YYYY HH:mm:ss'); + var event = eventsLabeledByTime[videoTime]; + if(event){ + if(event.details.plates){ + console.log('licensePlateVideo',event) + } + if(event.details.matrices){ + event.monitorDetails=JSON.parse(e.mon.details) + event.stream=$(videoElement) + event.streamObjects=$.pwrvid.vp.find('.stream-objects') + $.ccio.init('drawMatrices',event) + } + if(event.details.confidence){ + $.pwrvid.vp.find('.motion-meter .progress-bar').css('width',event.details.confidence+'px').find('span').text(event.details.confidence) + } + } + var value= (( videoElement.currentTime / videoElement.duration ) * 100)+"%" + $.pwrvid.seekBarProgress.css("width",value); + }) + $.pwrvid.seekBar.off("click").on("click", function(seek){ + var offset = $(this).offset(); + var left = (seek.pageX - offset.left); + var totalWidth = $.pwrvid.seekBar.width(); + var percentage = ( left / totalWidth ); + var vidTime = videoElement.duration * percentage; + videoElement.currentTime = vidTime; + }); + break; + } +}) +$.pwrvid.drawTimeline=function(getData){ + var e={}; + $.pwrvid.e.find('.nodata').hide() + if(getData===undefined){getData=true} + var mid=$.pwrvid.m.val(); + $.pwrvid.e.find('.loading').show() + e.live_header=$.pwrvid.lv.find('h3 span'); + e.live=$.pwrvid.lv.find('iframe'); + e.dateRange=$.pwrvid.dr.data('daterangepicker'); + e.videoLimit = $('#pvideo_video_limit').val(); + e.eventLimit = $('#pvideo_event_limit').val(); + if(e.eventLimit===''||isNaN(e.eventLimit)){e.eventLimit=500} + if(e.videoLimit===''||isNaN(e.videoLimit)){e.videoLimit=0} + + var getTheData = function(){ + e.live_header.text($.ccio.mon[$user.ke+mid+$user.auth_token].name) + e.live.attr('src',$.ccio.init('location',$user)+$user.auth_token+'/embed/'+$user.ke+'/'+mid+'/fullscreen|jquery|relative|gui') + + var pulseLoading = function(){ + var loading = $.pwrvid.e.find('.loading') + var currentColor = loading.css('color') + loading.animate('color','red') + setTimeout(function(){ + loading.css('color',currentColor) + },500) + } + if(getData===true){ + $.ccio.cx({ + f:'monitor', + ff:'get', + fff:'videos&events', + videoLimit:e.videoLimit, + eventLimit:e.eventLimit, + startDate:$.ccio.init('th',e.dateRange.startDate), + endDate:$.ccio.init('th',e.dateRange.endDate), + ke:e.ke, + mid:mid + }); + }else{ + $.pwrvid.e.find('.loading').hide() + e.next($.pwrvid.currentVideos,$.pwrvid.currentEvents) + } + } + if(parseInt(e.eventLimit) >= 1000){ + $.confirm.e.modal('show'); + $.confirm.title.text(lang['Warning']+'!') + e.html=lang.powerVideoEventLimit + $.confirm.body.html(e.html) + $.confirm.click({title:lang.Request,class:'btn-primary'},function(){ + getTheData() + }); + }else{ + getTheData() + } +} +$('#vis_monitors,#pvideo_event_limit,#pvideo_video_limit').change(function(){ + $.pwrvid.f.submit() +}) +$.pwrvid.f.submit(function(e){ + e.preventDefault(); + $.pwrvid.drawTimeline() + return false; +}) +$.pwrvid.e.on('hidden.bs.modal',function(e){ + $(this).find('iframe').attr('src','about:blank') + $.pwrvid.vp.find('.holder').empty() + delete($.pwrvid.currentDataObject) + delete($.pwrvid.currentData) + $.pwrvid.mL.empty() + $.pwrvid.d.empty() +}) +}) diff --git a/web/libs/js/dash2.probe.js b/web/libs/js/dash2.probe.js new file mode 100644 index 00000000..e7ad267e --- /dev/null +++ b/web/libs/js/dash2.probe.js @@ -0,0 +1,48 @@ +$(document).ready(function(e){ +//probe +$.pB={e:$('#probe')};$.pB.f=$.pB.e.find('form');$.pB.o=$.pB.e.find('.output_data'); +$.pB.f.submit(function(e){ + + $.pB.e.find('._loading').show() + $.pB.o.empty(); + $.pB.e.find('.stop').show(); + $.pB.e.find('[type="submit"]').hide(); + + e.preventDefault();e.e=$(this),e.s=e.e.serializeObject(); + e.s.url=e.s.url.trim(); + var flags = ''; + switch(e.s.mode){ + case'json': + flags = '-v quiet -print_format json -show_format -show_streams'; + break; + } +// if(e.s.url.indexOf('{{JSON}}')>-1){ +// e.s.url='-v quiet -print_format json -show_format -show_streams '+e.s.url +// } + $.get($.ccio.init('location',$user)+$user.auth_token+'/probe/'+$user.ke+'?url='+e.s.url+'&flags='+flags,function(data){ + if(data.ok===true){ + var html + try{ + html = $.ccio.init('jsontoblock',JSON.parse(data.result)) + }catch(err){ + html = data.result + } + $.pB.o.append(html) + }else{ + $.ccio.init('note',{title:'Failed to Probe',text:data.error,type:'error'}); + } + $.pB.e.find('._loading').hide() + $.pB.o.append('
      END
      '); + $.pB.e.find('.stop').hide(); + $.pB.e.find('[type="submit"]').show(); + }) + return false; +}); +$.pB.e.on('hidden.bs.modal',function(){ + $.pB.o.empty() +}) +$.pB.e.find('.stop').click(function(e){ + e.e=$(this); +// $.ccio.cx({f:'ffprobe',ff:'stop'}) +}); +}) diff --git a/web/libs/js/dash2.regioneditor.js b/web/libs/js/dash2.regioneditor.js new file mode 100644 index 00000000..2ae99b57 --- /dev/null +++ b/web/libs/js/dash2.regioneditor.js @@ -0,0 +1,200 @@ +$(document).ready(function(e){ +//Region Editor +$.zO={e:$('#region_editor')}; +$.zO.f=$.zO.e.find('form'); +$.zO.o=function(){return $.zO.e.find('canvas')}; +$.zO.c=$.zO.e.find('.canvas_holder'); +$.zO.name=$.zO.e.find('[name="name"]'); +$.zO.rl=$('#regions_list'); +$.zO.rp=$('#regions_points'); +$.zO.ca=$('#regions_canvas'); +$.zO.saveCoords=function(){ + $.aM.e.find('[detail="cords"]').val(JSON.stringify($.zO.regionViewerDetails.cords)).change() +} +$.zO.initRegionList=function(){ + $('#regions_list,#region_points').empty(); + $.each($.zO.regionViewerDetails.cords,function(n,v){ + if(v&&v.name){ + $.zO.rl.append('') + } + }); + $.zO.rl.change(); +} +$.zO.rl.change(function(e){ + $.zO.initCanvas(); +}) +$.zO.initLiveStream=function(e){ + var e={} + e.re=$('#region_editor_live'); + e.re.find('iframe,img').attr('src','about:blank').hide() + if($('#region_still_image').is(':checked')){ + e.re=e.re.find('img') + e.choice='jpeg' + }else{ + e.re=e.re.find('iframe') + e.choice='embed' + } + e.src=$.ccio.init('location',$user)+$user.auth_token+'/'+e.choice+'/'+$user.ke+'/'+$.aM.selected.mid + if(e.choice=='embed'){ + e.src+='/fullscreen|jquery|relative' + }else{ + e.src+='/s.jpg' + } + if(e.re.attr('src')!==e.src){ + e.re.attr('src',e.src).show() + } + e.re.attr('width',$.zO.regionViewerDetails.detector_scale_x) + e.re.attr('height',$.zO.regionViewerDetails.detector_scale_y) +} +$('#region_still_image').change(function(e){ + e.o=$.ccio.op().switches + if(!e.o){e.o={}} + if($(this).is(':checked')){ + e.o.regionStillImage=1 + }else{ + e.o.regionStillImage="0" + } + $.ccio.op('switches',e.o) + $.zO.initLiveStream() +}).ready(function(e){ + e.switches=$.ccio.op().switches + if(e.switches&&e.switches.regionStillImage===1){ + $('#region_still_image').prop('checked',true) + } +}) +$.zO.initCanvas=function(){ + var e={}; + e.ar=[]; + e.val=$.zO.rl.val(); + if(!e.val){ + $.zO.f.find('[name="name"]').val('') + $.zO.f.find('[name="sensitivity"]').val('') + $.zO.f.find('[name="max_sensitivity"]').val('') + $.zO.f.find('[name="threshold"]').val('') + $.zO.f.find('[name="color_threshold"]').val('') + $.zO.rp.empty() + }else{ + e.cord=$.zO.regionViewerDetails.cords[e.val]; + if(!e.cord.points){e.cord.points=[[0,0],[0,100],[100,0]]} + $.each(e.cord.points,function(n,v){ + e.ar=e.ar.concat(v) + }); + if(isNaN(e.cord.sensitivity)){ + e.cord.sensitivity=$.zO.regionViewerDetails.detector_sensitivity; + } + $.zO.f.find('[name="name"]').val(e.val) + $.zO.e.find('.cord_name').text(e.val) + $.zO.f.find('[name="sensitivity"]').val(e.cord.sensitivity) + $.zO.f.find('[name="max_sensitivity"]').val(e.cord.max_sensitivity) + $.zO.f.find('[name="threshold"]').val(e.cord.threshold) + $.zO.f.find('[name="color_threshold"]').val(e.cord.color_threshold) + $.zO.e.find('.canvas_holder canvas').remove(); + + $.zO.initLiveStream() + e.e=$.zO.ca.val(e.ar.join(',')) + e.e.canvasAreaDraw({ + imageUrl:placeholder.getData(placeholder.plcimg({ + bgcolor:'transparent', + text:' ', + size:$.zO.regionViewerDetails.detector_scale_x+'x'+$.zO.regionViewerDetails.detector_scale_y + })) + }); + e.e.change(); + } +} +$.zO.e.on('change','[name]:not([name="name"])',function(){ + var el = $(this) + var val = el.val() + var key = el.attr('name') + $.zO.regionViewerDetails.cords[$.zO.rl.val()][key] = val + $.zO.saveCoords() +}) +$.zO.e.on('change','[name="name"]',function(e){ + e.old=$.zO.rl.val(); + e.new=$.zO.name.val(); + $.zO.regionViewerDetails.cords[e.new]=$.zO.regionViewerDetails.cords[e.old]; + delete($.zO.regionViewerDetails.cords[e.old]); + $.zO.rl.find('option[value="'+e.old+'"]').attr('value',e.new).text(e.new) + $.zO.saveCoords() +}) +$.zO.e.on('change','[point]',function(e){ + e.points=[]; + $('[points]').each(function(n,v){ + v=$(v); + n=v.find('[point="x"]').val(); + if(n){ + e.points.push([n,v.find('[point="y"]').val()]) + } + }) + $.zO.regionViewerDetails.cords[$.zO.name.val()].points=e.points; + $.zO.initCanvas(); +}) +$.zO.e.find('.erase').click(function(e){ + e.arr=[] + $.each($.zO.regionViewerDetails.cords,function(n,v){ + if(v&&v!==$.zO.regionViewerDetails.cords[$.zO.rl.val()]){ + e.arr.push(v) + } + }) + $.zO.regionViewerDetails.cords=e.arr.concat([]); + if(Object.keys($.zO.regionViewerDetails.cords).length>0){ + $.zO.initRegionList(); + }else{ + $.zO.f.find('input').prop('disabled',true) + $('#regions_points tbody').empty() + $('#regions_list [value="'+$.zO.rl.val()+'"]').remove() + $.aM.e.find('[detail="cords"]').val('[]') + } +}); +//$.zO.e.find('.new').click(function(e){ +// $.zO.regionViewerDetails.cords[$.zO.rl.val()] +// $.zO.initRegionList(); +//}) +$.zO.e.on('changed','#regions_canvas',function(e){ + e.val=$(this).val().replace(/(,[^,]*),/g, '$1;').split(';'); + e.ar=[]; + $.each(e.val,function(n,v){ + v=v.split(',') + if(v[1]){ + e.ar.push([v[0],v[1]]) + } + }) + $.zO.regionViewerDetails.cords[$.zO.rl.val()].points=e.ar; + e.selected=$.zO.regionViewerDetails.cords[$.zO.rl.val()]; + e.e=$('#regions_points tbody').empty(); + $.each($.zO.regionViewerDetails.cords[$.zO.rl.val()].points,function(n,v){ + if(isNaN(v[0])){v[0]=20} + if(isNaN(v[1])){v[1]=20} + e.e.append('') + }); + $.zO.saveCoords() +}) +$.zO.f.submit(function(e){ + e.preventDefault();e.e=$(this),e.s=e.e.serializeObject(); + + return false; +}); +$('#regions_points') +.on('click','.delete',function(e){ + e.p=$(this).parents('tr'),e.row=e.p.attr('points'); + delete($.zO.regionViewerDetails.cords[$.zO.rl.val()].points[e.row]) + $.zO.saveCoords() + e.p.remove(); + $.zO.rl.change(); +}) +$.zO.e.on('click','.add',function(e){ + $.zO.f.find('input').prop('disabled',false) + e.gid=$.ccio.gid(5); + e.save={}; + $.each($.zO.regionViewerDetails.cords,function(n,v){ + if(v&&v!==null&&v!=='null'){ + e.save[n]=v; + } + }) + $.zO.regionViewerDetails.cords=e.save; + $.zO.regionViewerDetails.cords[e.gid]={name:e.gid,sensitivity:0.0005,max_sensitivity:'',threshold:1,color_threshold:9,points:[[0,0],[0,100],[100,0]]}; + $.zO.rl.append(''); + $.zO.rl.val(e.gid) + $.zO.rl.change(); +}); +}) diff --git a/web/libs/js/dash2.socketio.js b/web/libs/js/dash2.socketio.js new file mode 100644 index 00000000..53761ca8 --- /dev/null +++ b/web/libs/js/dash2.socketio.js @@ -0,0 +1,933 @@ +$(document).ready(function(e){ +//websocket functions +$.users = {} +$.ccio.cx=function(x,user){ + if(!user){user=$user} + if(!x.ke){x.ke=user.ke;}; + if(!x.uid){x.uid=user.uid;}; + return user.ws.emit('f',x) +} +$.ccio.globalWebsocket=function(d,user){ + if(d.f!=='monitor_frame'&&d.f!=='os'&&d.f!=='video_delete'&&d.f!=='detector_trigger'&&d.f!=='detector_record_timeout_start'&&d.f!=='log'){$.ccio.log(d);} + if(!user){ + user=$user + } + if(d.viewers){ + $('[ke="'+d.ke+'"][mid="'+d.id+'"][auth="'+user.auth_token+'"] .viewers').html(d.viewers); + } + switch(d.f){ + case'note': + $.ccio.init('note',d.note,user); + break; + case'monitor_status': + console.log(d) + $('[ke="'+d.ke+'"][mid="'+d.id+'"][auth="'+user.auth_token+'"] .monitor_status').html(d.status); + break; + case'detector_trigger': + d.e=$('.monitor_item[ke="'+d.ke+'"][mid="'+d.id+'"][auth="'+user.auth_token+'"]') + if($.ccio.mon[d.ke+d.id+user.auth_token]&&d.e.length>0){ + if(d.doObjectDetection === true){ + d.e.addClass('doObjectDetection') + clearTimeout($.ccio.mon[d.ke+d.id+user.auth_token].detector_trigger_doObjectDetection_timeout) + $.ccio.mon[d.ke+d.id+user.auth_token].detector_trigger_doObjectDetection_timeout = setTimeout(function(){ + d.e.removeClass('doObjectDetection') + },3000) + }else{ + d.e.removeClass('doObjectDetection') + } + if(d.details.plates&&d.details.plates.length>0){ + console.log('licensePlateStream',d.id,d) + } + if(d.details.matrices&&d.details.matrices.length>0){ + d.monitorDetails=JSON.parse($.ccio.mon[d.ke+d.id+user.auth_token].details) + d.stream=d.e.find('.stream-element') + d.streamObjects=d.e.find('.stream-objects') + $.ccio.init('drawMatrices',d) + } + if(d.details.points&&Object.keys(d.details.points).length>0){ + d.monitorDetails=JSON.parse($.ccio.mon[d.ke+d.id+user.auth_token].details) + d.stream=d.e.find('.stream-element') + d.streamObjects=d.e.find('.stream-objects') + $.ccio.init('drawPoints',d) + } + if(d.details.confidence){ + d.tt=d.details.confidence; + if (d.tt > 100) { d.tt = 100 } + d.e.find('.indifference .progress-bar').css('width',d.tt + '%').find('span').html(d.details.confidence+'% change in '+d.details.name+'') + } + d.e.addClass('detector_triggered') + clearTimeout($.ccio.mon[d.ke+d.id+user.auth_token].detector_trigger_timeout); + $.ccio.mon[d.ke+d.id+user.auth_token].detector_trigger_timeout=setTimeout(function(){ + $('.monitor_item[ke="'+d.ke+'"][mid="'+d.id+'"][auth="'+user.auth_token+'"]').removeClass('detector_triggered').find('.stream-detected-object,.stream-detected-point').remove() + },5000); + //noise alert + if(user.details.audio_alert && user.details.audio_alert !== '' && $.ccio.soundAlarmed !== true){ + $.ccio.soundAlarmed = true + var audio = new Audio('libs/audio/'+user.details.audio_alert); + audio.onended = function(){ + setTimeout(function(){ + $.ccio.soundAlarmed = false + },user.details.audio_delay * 1000) + } + if($.ccio.windowFocus = true){ + audio.play() + }else{ + clearInterval($.ccio.soundAlarmInterval) + if(!user.details.audio_delay || user.details.audio_delay === ''){ + user.details.audio_delay = 1 + }else{ + user.details.audio_delay = parseFloat(user.details.audio_delay) + } + $.ccio.soundAlarmInterval = setInterval(function(){ + audio.play() + },user.details.audio_delay * 1000) + } + } + if(user.details.event_mon_pop === '1' && (!$.ccio.mon[d.ke+d.id+user.auth_token].popOut || $.ccio.mon[d.ke+d.id+user.auth_token].popOut.closed === true)){ + d.e.find('[monitor="pop"]').click() + } + } + break; + case'init_success': + $('#monitors_list .link-monitors-list[auth="'+user.auth_token+'"][ke="'+user.ke+'"]').empty(); + if(user===$user){ + d.chosen_set='watch_on' + }else{ + d.chosen_set='watch_on_links' + } + d.o=$.ccio.op()[d.chosen_set]; + if(!d.o){d.o={}}; + $.getJSON($.ccio.init('location',user)+user.auth_token+'/monitor/'+user.ke,function(f,g){ + g=function(n,v){ + $.ccio.mon[v.ke+v.mid+user.auth_token]=v; + v.user=user; + $.ccio.tm(1,v,null,user) + if(d.o[v.ke]&&d.o[v.ke][v.mid]===1){ + $.ccio.cx({f:'monitor',ff:'watch_on',id:v.mid},user) + } + } + if(f.mid){ + g(null,f) + }else{ + $.each(f,g); + } + if($.ccio.op().jpeg_on===true){ + $.ccio.cx({f:'monitor',ff:'jpeg_on'},user) + } + $.gR.drawList(); + }) + $.ccio.pm(3,d.apis,null,user); + $('.os_platform').html(d.os.platform) + $('.os_cpuCount').html(d.os.cpuCount) + $('.os_totalmem').html((d.os.totalmem/1000000).toFixed(2)) + if(d.os.cpuCount>1){ + $('.os_cpuCount_trailer').html('s') + } + break; + case'get_videos': + $.ccio.pm(0,d,null,user) + break; + case'log': + var attr = '[mid="'+d.mid+'"][ke="'+d.ke+'"][auth="'+user.auth_token+'"]' + $.ccio.tm(4,d,'#logs,'+attr+'.monitor_item .logs:visible,'+attr+'#add_monitor:visible .logs',user) + break; + case'camera_cpu_usage': + var el = $('.monitor_item[auth="'+user.auth_token+'"][ke="'+d.ke+'"][mid="'+d.id+'"] .camera_cpu_usage') + .attr('title',d.percent + '% ' + lang['CPU used by this stream']) + el.find('.progress-bar').css('width',d.percent) + el.find('.percent').html(d.percent + '%') + break; + case'os'://indicator + //cpu + d.cpu=parseFloat(d.cpu).toFixed(0)+'%'; + $('.cpu_load .progress-bar').css('width',d.cpu); + $('.cpu_load .percent').html(d.cpu); + //ram + d.ram=(100-parseFloat(d.ram)).toFixed(0)+'%'; + $('.ram_load .progress-bar').css('width',d.ram); + $('.ram_load .percent').html(d.ram); + break; + case'diskUsed': + if(!d.limit||d.limit===''){d.limit=10000} + d.percent=parseInt((d.size/d.limit)*100)+'%'; + d.human=parseFloat(d.size) + if(d.human>1000){d.human=(d.human/1000).toFixed(2)+' GB'}else{d.human=d.human.toFixed(2)+' MB'} + $('.diskUsed .value').html(d.human) + $('.diskUsed .percent').html(d.percent) + $('.diskUsed .progress-bar').css('width',d.percent) + break; + case'video_fix_success':case'video_fix_start': + switch(d.f){ + case'video_fix_success': + d.addClass='fa-wrench' + d.removeClass='fa-pulse fa-spinner' + break; + case'video_fix_start': + d.removeClass='fa-wrench' + d.addClass='fa-pulse fa-spinner' + break; + } + $('[mid="'+d.mid+'"][ke="'+d.ke+'"][file="'+d.filename+'"][auth="'+user.auth_token+'"] [video="fix"] i,[data-mid="'+d.mid+'"][data-ke="'+d.ke+'"][data-file="'+d.filename+'"][data-auth="'+user.auth_token+'"] [video="fix"] i').addClass(d.addClass).removeClass(d.removeClass) + break; + case'video_edit':case'video_archive': + $.ccio.init('data-video',d) + d.e=$('[file="'+d.filename+'"][mid="'+d.mid+'"][ke="'+d.ke+'"][auth="'+user.auth_token+'"],[data-file="'+d.filename+'"][data-mid="'+d.mid+'"][data-ke="'+d.ke+'"][data-auth="'+user.auth_token+'"]'); + d.e.attr('status',d.status),d.e.attr('data-status',d.status); + console.log(d) + + break; + case'video_delete': +// if($('.modal[mid="'+d.mid+'"][auth="'+user.auth_token+'"]').length>0){$('#video_viewer[mid="'+d.mid+'"]').attr('file',null).attr('ke',null).attr('mid',null).attr('auth',null).modal('hide')} + $('[file="'+d.filename+'"][mid="'+d.mid+'"][ke="'+d.ke+'"][auth="'+user.auth_token+'"]:not(.modal)').remove(); + $('[data-file="'+d.filename+'"][data-mid="'+d.mid+'"][data-ke="'+d.ke+'"][data-auth="'+user.auth_token+'"]:not(.modal)').remove(); + if($.pwrvid.currentDataObject&&$.pwrvid.currentDataObject[d.filename]){ + delete($.timelapse.currentVideos[$.pwrvid.currentDataObject[d.filename].position]) + $.pwrvid.drawTimeline(false) + } + if($.timelapse.currentVideos&&$.timelapse.currentVideos[d.filename]){ + delete($.timelapse.currentVideosArray.videos[$.timelapse.currentVideos[d.filename].position]) + $.timelapse.drawTimeline(false) + } + if($.vidview.loadedVideos && $.vidview.loadedVideos[d.filename])delete($.vidview.loadedVideos[d.filename]) + break; + case'video_build_success': + if(!d.mid){d.mid=d.id;};d.status=1; + d.e='.glM'+d.mid+user.auth_token+'.videos_list ul,.glM'+d.mid+user.auth_token+'.videos_monitor_list ul';$(d.e).find('.notice.novideos').remove(); + $.ccio.tm(0,d,d.e,user) + break; + case'monitor_snapshot': + setTimeout(function(){ + var snapElement = $('[mid="'+d.mid+'"][ke="'+d.ke+'"][auth="'+user.auth_token+'"] .snapshot') + switch(d.snapshot_format){ + case'plc': + snapElement.attr('src',placeholder.getData(placeholder.plcimg(d.snapshot))) + break; + case'ab': + d.reader = new FileReader(); + d.reader.addEventListener("loadend",function(){snapElement.attr('src',d.reader.result)}); + d.reader.readAsDataURL(new Blob([d.snapshot],{type:"image/jpeg"})); + break; + case'b64': + snapElement.attr('src','data:image/jpeg;base64,'+d.snapshot) + break; + } + },1000) + break; + case'monitor_delete': + $('[mid="'+d.mid+'"][ke="'+d.ke+'"][auth="'+user.auth_token+'"]:not(.modal)').remove(); + $.ccio.init('clearTimers',d) + delete($.ccio.mon[d.ke+d.mid+user.auth_token]); + break; + case'monitor_watch_off':case'monitor_stopping': + if(user===$user){ + d.chosen_set='watch_on' + }else{ + d.chosen_set='watch_on_links' + } + d.o=$.ccio.op()[d.chosen_set]; + if(!d.o[d.ke]){d.o[d.ke]={}};d.o[d.ke][d.id]=0;$.ccio.op(d.chosen_set,d.o); + $.ccio.destroyStream(d,user,(d.f === 'monitor_watch_off')) + break; + case'monitor_watch_on': + if(user===$user){ + d.chosen_set='watch_on' + }else{ + d.chosen_set='watch_on_links' + } + d.o=$.ccio.op()[d.chosen_set]; + if(!d.o){d.o={}};if(!d.o[d.ke]){d.o[d.ke]={}};d.o[d.ke][d.id]=1;$.ccio.op(d.chosen_set,d.o); + $.ccio.mon[d.ke+d.id+user.auth_token].watch=1; + delete($.ccio.mon[d.ke+d.id+user.auth_token].image) + delete($.ccio.mon[d.ke+d.id+user.auth_token].ctx) + d.e=$('#monitor_live_'+d.id+user.auth_token); + d.e.find('.stream-detected-object').remove() + $.ccio.init('clearTimers',d) + if(d.e.length === 1){ + $.ccio.init('closeVideo',{mid:d.id,ke:d.ke},user); + } + if(d.e.length === 0){ + $.ccio.tm(2,$.ccio.mon[d.ke+d.id+user.auth_token],'#monitors_live',user); + } + d.d=JSON.parse($.ccio.mon[d.ke+d.id+user.auth_token].details); + $.ccio.tm('stream-element',$.ccio.mon[d.ke+d.id+user.auth_token],null,user); + if($.ccio.op().jpeg_on===true){ + $.ccio.init('jpegMode',$.ccio.mon[d.ke+d.id+user.auth_token]); + }else{ + var path = tool.checkCorrectPathEnding(location.pathname)+'socket.io' + switch(d.d.stream_type){ + case'jpeg': + $.ccio.init('jpegMode',$.ccio.mon[d.ke+d.id+user.auth_token]); + break; + case'b64': + if($.ccio.mon[d.ke+d.id+user.auth_token].Base64 && $.ccio.mon[d.ke+d.id+user.auth_token].Base64.connected){ + $.ccio.mon[d.ke+d.id+user.auth_token].Base64.disconnect() + } + $.ccio.mon[d.ke+d.id+user.auth_token].Base64 = io(location.origin,{ path: path, transports: ['websocket'], forceNew: false}) + var ws = $.ccio.mon[d.ke+d.id+user.auth_token].Base64 + var buffer + ws.on('diconnect',function(){ + console.log('Base64 Stream Disconnected') + }) + ws.on('connect',function(){ + ws.emit('Base64',{ + auth: user.auth_token, + uid: user.uid, + ke: d.ke, + id: d.id, +// channel: channel + }) + if(!$.ccio.mon[d.ke+d.id+user.auth_token].ctx||$.ccio.mon[d.ke+d.id+user.auth_token].ctx.length===0){ + $.ccio.mon[d.ke+d.id+user.auth_token].ctx = $('#monitor_live_'+d.id+user.auth_token+' canvas'); + } + var ctx = $.ccio.mon[d.ke+d.id+user.auth_token].ctx[0] + var ctx2d = ctx.getContext("2d") + $.ccio.mon[d.ke+d.id+user.auth_token].image = new Image() + var image = $.ccio.mon[d.ke+d.id+user.auth_token].image + image.onload = function() { + $.ccio.mon[d.ke+d.id+user.auth_token].imageLoading = false + d.x = 0 + d.y = 0 + // d.ratio = Math.min(ctx.width/image.width,ctx.height/image.height) + // d.height = image.height * d.ratio + // d.width = image.width * d.ratio + // if(d.width < ctx.width){ + // d.x = (ctx.width / 2) - (d.width / 2) + // } + // if(d.height < ctx.height){ + // d.y = (ctx.height / 2) - (d.height / 2) + // } + // ctx.getContext("2d").drawImage(image,d.x,d.y,d.width,d.height) + ctx.getContext("2d").drawImage(image,d.x,d.y,ctx.width,ctx.height) + URL.revokeObjectURL($.ccio.mon[d.ke+d.id+user.auth_token].imageUrl) + } + ws.on('data',function(imageData){ + try{ + if($.ccio.mon[d.ke+d.id+user.auth_token].imageLoading === true)return console.log('drop'); +// var base64Frame = 'data:image/jpeg;base64,'+$.ccio.base64ArrayBuffer(imageData) + $.ccio.mon[d.ke+d.id+user.auth_token].imageLoading = true +// $.ccio.mon[d.ke+d.id+user.auth_token].image.src = base64Frame + var arrayBufferView = new Uint8Array(imageData); + var blob = new Blob( [ arrayBufferView ], { type: "image/jpeg" } ); + $.ccio.mon[d.ke+d.id+user.auth_token].imageUrl = URL.createObjectURL( blob ); + $.ccio.mon[d.ke+d.id+user.auth_token].image.src = $.ccio.mon[d.ke+d.id+user.auth_token].imageUrl + $.ccio.mon[d.ke+d.id+user.auth_token].last_frame = 'data:image/jpeg;base64,'+$.ccio.base64ArrayBuffer(imageData) + }catch(er){ + console.log(er) + $.ccio.log('base64 frame') + } + $.ccio.init('signal',d); + }) + }) + break; + case'mp4': + setTimeout(function(){ + var stream = d.e.find('.stream-element'); + var onPoseidonError = function(){ + // setTimeout(function(){ + // $.ccio.cx({f:'monitor',ff:'watch_on',id:d.id},user) + // },5000) + } + if(!$.ccio.mon[d.ke+d.id+user.auth_token].PoseidonErrorCount)$.ccio.mon[d.ke+d.id+user.auth_token].PoseidonErrorCount = 0 + if($.ccio.mon[d.ke+d.id+user.auth_token].PoseidonErrorCount >= 5)return + if(d.d.stream_flv_type==='ws'){ + if($.ccio.mon[d.ke+d.id+user.auth_token].Poseidon){ + $.ccio.mon[d.ke+d.id+user.auth_token].Poseidon.stop() + } + try{ + $.ccio.mon[d.ke+d.id+user.auth_token].Poseidon = new Poseidon({ + video: stream[0], + auth_token:user.auth_token, + ke:d.ke, + uid:user.uid, + id:d.id, + url: location.origin, + path: path, + onError : onPoseidonError + }) + $.ccio.mon[d.ke+d.id+user.auth_token].Poseidon.start(); + }catch(err){ + // onPoseidonError() + console.log('onTryPoseidonError',err) + } + }else{ + stream.attr('src',$.ccio.init('location',user)+user.auth_token+'/mp4/'+d.ke+'/'+d.id+'/s.mp4') + stream[0].onerror = function(err){ + console.error(err) + } + } + },2000) + break; + case'flv': + if (flvjs.isSupported()) { + if($.ccio.mon[d.ke+d.id+user.auth_token].flv){ + $.ccio.mon[d.ke+d.id+user.auth_token].flv.destroy() + } + var options = {}; + if(d.d.stream_flv_type==='ws'){ + if(d.d.stream_flv_maxLatency&&d.d.stream_flv_maxLatency!==''){ + d.d.stream_flv_maxLatency = parseInt(d.d.stream_flv_maxLatency) + }else{ + d.d.stream_flv_maxLatency = 20000; + } + options = { + type: 'flv', + isLive: true, + auth_token:user.auth_token, + ke:d.ke, + uid:user.uid, + id:d.id, + maxLatency:d.d.stream_flv_maxLatency, + hasAudio:false, + url: location.origin, + path: path + } + }else{ + options = { + type: 'flv', + isLive: true, + url: $.ccio.init('location',user)+user.auth_token+'/flv/'+d.ke+'/'+d.id+'/s.flv' + } + } + $.ccio.mon[d.ke+d.id+user.auth_token].flv = flvjs.createPlayer(options); + $.ccio.mon[d.ke+d.id+user.auth_token].flv.attachMediaElement($('#monitor_live_'+d.id+user.auth_token+' .stream-element')[0]); + $.ccio.mon[d.ke+d.id+user.auth_token].flv.on('error',function(err){ + console.log(err) + }); + $.ccio.mon[d.ke+d.id+user.auth_token].flv.load(); + $.ccio.mon[d.ke+d.id+user.auth_token].flv.play(); + }else{ + $.ccio.init('note',{title:'Stream cannot be started',text:'FLV.js is not supported on this browser. Try another stream type.',type:'error'}); + } + break; + case'hls': + d.fn=function(){ + clearTimeout($.ccio.mon[d.ke+d.id+user.auth_token].m3uCheck) + d.url=$.ccio.init('location',user)+user.auth_token+'/hls/'+d.ke+'/'+d.id+'/s.m3u8'; + $.get(d.url,function(m3u){ + if(m3u=='File Not Found'){ + $.ccio.mon[d.ke+d.id+user.auth_token].m3uCheck=setTimeout(function(){ + d.fn() + },2000) + }else{ + var video = $('#monitor_live_'+d.id+user.auth_token+' .stream-element')[0]; + if (navigator.userAgent.match(/(iPod|iPhone|iPad)/)||(navigator.userAgent.match(/(Safari)/)&&!navigator.userAgent.match('Chrome'))) { + video.src=d.url; + if (video.paused) { + video.play(); + } + }else{ + $.ccio.mon[d.ke+d.id+user.auth_token].hlsGarbageCollector=function(){ + if($.ccio.mon[d.ke+d.id+user.auth_token].hls){$.ccio.mon[d.ke+d.id+user.auth_token].hls.destroy();URL.revokeObjectURL(video.src)} + $.ccio.mon[d.ke+d.id+user.auth_token].hls = new Hls(); + $.ccio.mon[d.ke+d.id+user.auth_token].hls.loadSource(d.url); + $.ccio.mon[d.ke+d.id+user.auth_token].hls.attachMedia(video); + $.ccio.mon[d.ke+d.id+user.auth_token].hls.on(Hls.Events.MANIFEST_PARSED,function() { + if (video.paused) { + video.play(); + } + }); + } + $.ccio.mon[d.ke+d.id+user.auth_token].hlsGarbageCollector() + $.ccio.mon[d.ke+d.id+user.auth_token].hlsGarbageCollectorTimer=setInterval($.ccio.mon[d.ke+d.id+user.auth_token].hlsGarbageCollector,1000*60*20) + } + } + }) + } + d.fn() + break; + case'mjpeg': + $('#monitor_live_'+d.id+user.auth_token+' .stream-element').attr('src',$.ccio.init('location',user)+user.auth_token+'/mjpeg/'+d.ke+'/'+d.id+'/?full=true') + break; + case'h265': + var player = $.ccio.mon[d.ke+d.id+user.auth_token].h265Player + var video = $('#monitor_live_'+d.id+user.auth_token+' .stream-element')[0] + if (player) { + player.stop() + } + $.ccio.mon[d.ke+d.id+user.auth_token].h265Player = new libde265.RawPlayer(video) + var player = $.ccio.mon[d.ke+d.id+user.auth_token].h265Player + player.set_status_callback(function(msg, fps) { + }) + player.launch() + if($.ccio.mon[d.ke+d.id+user.auth_token].h265Socket && $.ccio.mon[d.ke+d.id+user.auth_token].h265Socket.connected){ + $.ccio.mon[d.ke+d.id+user.auth_token].h265Socket.disconnect() + } + if($.ccio.mon[d.ke+d.id+user.auth_token].h265HttpStream && $.ccio.mon[d.ke+d.id+user.auth_token].abort){ + $.ccio.mon[d.ke+d.id+user.auth_token].h265HttpStream.abort() + } + if(d.d.stream_flv_type==='ws'){ + $.ccio.mon[d.ke+d.id+user.auth_token].h265Socket = io(location.origin,{ path: path, transports: ['websocket'], forceNew: false}) + var ws = $.ccio.mon[d.ke+d.id+user.auth_token].h265Socket + ws.on('diconnect',function(){ + console.log('h265Socket Stream Disconnected') + }) + ws.on('connect',function(){ + ws.emit('h265',{ + auth: user.auth_token, + uid: user.uid, + ke: d.ke, + id: d.id, + // channel: channel + }) + ws.on('data',function(imageData){ + player._handle_onChunk(imageData) + }) + }) + }else{ + var url = $.ccio.init('location',user)+user.auth_token+'/h265/'+d.ke+'/'+d.id+'/s.hevc'; + $.ccio.mon[d.ke+d.id+user.auth_token].h265HttpStream = player.createHttpStream(url) + } + break; + } + } + d.signal=parseFloat(d.d.signal_check); + if(!d.signal||d.signal==NaN){d.signal=10;};d.signal=d.signal*1000*60; + if(d.signal>0){ + $.ccio.mon[d.ke+d.id+user.auth_token].signal=setInterval(function(){$.ccio.init('signal-check',{id:d.id,ke:d.ke})},d.signal); + } + d.e=$('.monitor_item[mid="'+d.id+'"][ke="'+d.ke+'"][auth="'+user.auth_token+'"]').resize() + if(d.e.find('.videos_monitor_list li').length===0){ + d.dr=$('#videos_viewer_daterange').data('daterangepicker'); + $.getJSON($.ccio.init('location',user)+user.auth_token+'/videos/'+d.ke+'/'+d.id+'?limit=10',function(f){ + $.ccio.pm(0,{videos:f.videos,ke:d.ke,mid:d.id},null,user) + }) + } + setTimeout(function(){ + if($.ccio.mon[d.ke+d.id+user.auth_token].motionDetectionRunning===true){ + $.ccio.init('streamMotionDetectRestart',{mid:d.id,ke:d.ke,mon:$.ccio.mon[d.ke+d.id+user.auth_token]},user); + } + },3000) + break; + case'pam_frame': + if(!$.ccio.mon[d.ke+d.id+user.auth_token].ctx||$.ccio.mon[d.ke+d.id+user.auth_token].ctx.length===0){ + $.ccio.mon[d.ke+d.id+user.auth_token].ctx = $('#monitor_live_'+d.id+user.auth_token+' canvas'); + $.ccio.mon[d.ke+d.id+user.auth_token].ctxContext = $.ccio.mon[d.ke+d.id+user.auth_token].ctx[0].getContext('2d'); + } + var ctx = $.ccio.mon[d.ke+d.id+user.auth_token].ctxContext; + d.x = 0,d.y = 0; + d.ratio = Math.min($.ccio.mon[d.ke+d.id+user.auth_token].ctx.width()/d.imageData.width,$.ccio.mon[d.ke+d.id+user.auth_token].ctx.height()/d.imageData.height); + d.height = d.imageData.height*d.ratio; + d.width = d.imageData.width*d.ratio; + if( d.width < $.ccio.mon[d.ke+d.id+user.auth_token].ctx.width() ) + d.x = ($.ccio.mon[d.ke+d.id+user.auth_token].ctx.width() / 2) - (d.width / 2); + if( d.height < $.ccio.mon[d.ke+d.id+user.auth_token].ctx.height() ) + d.y = ($.ccio.mon[d.ke+d.id+user.auth_token].ctx.height() / 2) - (d.height / 2); + var imageData = ctx.createImageData(d.width,d.height) + imageData.data.set(new Uint8ClampedArray(d.imageData.data)) + console.log(imageData) + ctx.putImageData(imageData, 0, 0); + break; + case'monitor_frame': + try{ + if($.ccio.mon[d.ke+d.id+user.auth_token].imageLoading === true)return + if(!$.ccio.mon[d.ke+d.id+user.auth_token].ctx||$.ccio.mon[d.ke+d.id+user.auth_token].ctx.length===0){ + $.ccio.mon[d.ke+d.id+user.auth_token].ctx = $('#monitor_live_'+d.id+user.auth_token+' canvas'); + } + var ctx = $.ccio.mon[d.ke+d.id+user.auth_token].ctx[0] + if(!$.ccio.mon[d.ke+d.id+user.auth_token].image){ + $.ccio.mon[d.ke+d.id+user.auth_token].image = new Image() + var image = $.ccio.mon[d.ke+d.id+user.auth_token].image + image.onload = function() { + $.ccio.mon[d.ke+d.id+user.auth_token].imageLoading = false + d.x = 0 + d.y = 0 +// d.ratio = Math.min(ctx.width/image.width,ctx.height/image.height) +// d.height = image.height * d.ratio +// d.width = image.width * d.ratio +// if(d.width < ctx.width){ +// d.x = (ctx.width / 2) - (d.width / 2) +// } +// if(d.height < ctx.height){ +// d.y = (ctx.height / 2) - (d.height / 2) +// } +// ctx.getContext("2d").drawImage(image,d.x,d.y,d.width,d.height) + ctx.getContext("2d").drawImage(image,d.x,d.y,ctx.width,ctx.height) + } + } + var base64Frame = 'data:image/jpeg;base64,'+d.frame + $.ccio.mon[d.ke+d.id+user.auth_token].imageLoading = true + $.ccio.mon[d.ke+d.id+user.auth_token].image.src = base64Frame + $.ccio.mon[d.ke+d.id+user.auth_token].last_frame = base64Frame + }catch(er){ + console.log(er) + $.ccio.log('base64 frame') + } + $.ccio.init('signal',d); + break; + case'monitor_edit': + $.ccio.init('clearTimers',d) + d.e=$('[mid="'+d.mon.mid+'"][ke="'+d.mon.ke+'"][auth="'+user.auth_token+'"]'); + d.e=$('#monitor_live_'+d.mid+user.auth_token); + d.e.find('.stream-detected-object').remove() + if(d.mon.details.control=="1"){d.e.find('[monitor="control_toggle"]').show()}else{d.e.find('.pad').remove();d.e.find('[monitor="control_toggle"]').hide()} + if(user===$user){ + d.chosen_set='watch_on' + }else{ + d.chosen_set='watch_on_links' + } + d.o=$.ccio.op()[d.chosen_set]; + if(!d.o){d.o={}} + if(d.mon.details.cords instanceof Object){d.mon.details.cords=JSON.stringify(d.mon.details.cords);} + d.mon.details=JSON.stringify(d.mon.details); + if(!$.ccio.mon[d.ke+d.mid+user.auth_token]){$.ccio.mon[d.ke+d.mid+user.auth_token]={}} + $.ccio.init('jpegModeStop',d); + $.ccio.mon[d.ke+d.mid+user.auth_token].previousStreamType=d.mon.details.stream_type + $.each(d.mon,function(n,v){ + $.ccio.mon[d.ke+d.mid+user.auth_token][n]=v; + }); + $.ccio.mon[d.ke+d.mid+user.auth_token].user=user + if(d.new===true){$.ccio.tm(1,d.mon,null,user)} + switch(d.mon.mode){ + case'start':case'record': + if(d.o[d.ke]&&d.o[d.ke][d.mid]===1){ + $.ccio.cx({f:'monitor',ff:'watch_on',id:d.mid},user) + } + break; + } + $.ccio.init('monitorInfo',d) + $.gR.drawList(); + if(!d.silenceNote){ + $.ccio.init('note',{title:'Monitor Saved',text:''+d.mon.name+' '+d.mon.mid+' has been saved.',type:'success'}); + } + break; + case'monitor_starting': +// switch(d.mode){case'start':d.mode='Watch';break;case'record':d.mode='Record';break;} +// $.ccio.init('note',{title:'Monitor Starting',text:'Monitor '+d.mid+' is now running in mode '+d.mode+'',type:'success'}); + d.e=$('#monitor_live_'+d.mid+user.auth_token) + if(d.e.length>0){$.ccio.cx({f:'monitor',ff:'watch_on',id:d.mid},user)} + break; + case'mode_jpeg_off': + $.ccio.op('jpeg_on',"0"); + $.each($.ccio.mon,function(n,v,x){ + $.ccio.init('jpegModeStop',v); + if(v.watch===1){ + $.ccio.cx({f:'monitor',ff:'watch_on',id:v.mid},user) + } + }); + $('body').removeClass('jpegMode') + break; + case'mode_jpeg_on': + $.ccio.op('jpeg_on',true); + $.ccio.init('jpegModeAll'); + $('body').addClass('jpegMode') + break; + case'drawPowerVideoMainTimeLine': + var videos = d.videos; + var events = d.events; +// $.pwrvid.currentlyLoading = false + $.pwrvid.currentVideos=videos + $.pwrvid.currentEvents=events + $.pwrvid.e.find('.loading').hide() + $.pwrvid.e.find('.nodata').hide() + //$.pwrvid.drawTimeLine + if($.pwrvid.t&&$.pwrvid.t.destroy){$.pwrvid.t.destroy()} + data={}; + $.each(videos.videos,function(n,v){ + if(!v||!v.mid){return} + v.mon=$.ccio.mon[v.ke+v.mid+$user.auth_token]; +// v.filename=$.ccio.init('tf',v.time)+'.'+v.ext; + if(v.status>0){ + // data.push({src:v,x:v.time,y:$.ccio.timeObject(v.time).diff($.ccio.timeObject(v.end),'minutes')/-1}) + data[v.filename]={ + filename:v.filename, + time:v.time, + timeFormatted:$.ccio.timeObject(v.time).format('MM/DD/YYYY HH:mm'), + endTime:v.end, + close:$.ccio.timeObject(v.time).diff($.ccio.timeObject(v.end),'minutes')/-1, + motion:[], + row:v, + position:n + } + } + }); + + var eventsToCheck = Object.assign({},events) + $.each(data,function(m,b){ + startTimeFormatted = $.ccio.timeObject(b.time).format('YYYY-MM-DD HH:mm:ss'); + startTime = $.ccio.timeObject(b.time).format(); + endTime = $.ccio.timeObject(b.endTime).format(); + var newSetOfEventsWithoutChecked = {}; + var eventTime + $.each(eventsToCheck,function(n,v){ + try{ + if(v.details.videoTime.indexOf('T') > -1){ + eventTime = v.details.videoTime.split('T'); + }else{ + eventTime = v.details.videoTime.split(' '); + } + }catch(err){ + if(v.time.indexOf('T') > -1){ + eventTime = v.time.split('T'); + }else{ + eventTime = v.time.split(' '); + } + } + eventTime[1] = eventTime[1].replace(/-/g,':'),eventTime = eventTime.join(' '); + if(eventTime === startTimeFormatted){ + data[m].motion.push(v) + }else if ($.ccio.timeObject(v.time).isBetween(startTime,$.ccio.timeObject(b.endTime).format())) { + data[m].motion.push(v) + }else{ + newSetOfEventsWithoutChecked[n] = v; + } + }) + eventsToCheck = newSetOfEventsWithoutChecked; + }); + $.pwrvid.currentDataObject=data; + if($.pwrvid.chart){ + $.pwrvid.d.empty() + delete($.pwrvid.chart) + } + $.pwrvid.currentData=Object.values(data); + if($.pwrvid.currentData.length>0){ + var labels=[] + var Dataset1=[] + var Dataset2=[] + $.each(data,function(n,v){ + labels.push(v.timeFormatted) + Dataset1.push(v.close) + Dataset2.push(v.motion.length) + }) + $.pwrvid.d.html("") + var timeFormat = 'MM/DD/YYYY HH:mm'; + var color = Chart.helpers.color; + Chart.defaults.global.defaultFontColor = '#fff'; + var config = { + type: 'bar', + data: { + labels: labels, + datasets: [{ + type: 'line', + label: lang['Video and Time Span (Minutes)'], + backgroundColor: color(window.chartColors.blue).alpha(0.2).rgbString(), + borderColor: window.chartColors.blue, + data: Dataset1, + }, { + type: 'bar', + showTooltip: false, + label: lang['Counts of Motion'], + backgroundColor: color(window.chartColors.red).alpha(0.5).rgbString(), + borderColor: window.chartColors.red, + data:Dataset2, + }, ] + }, + options: { + maintainAspectRatio: false, + title: { + fontColor: "white", + text: lang['Video Length (minutes) and Motion Count per video'] + }, + tooltips: { + callbacks: { + + }, + }, + scales: { + xAxes: [{ + type: "time", + display: true, + time: { + format: timeFormat, + // round: 'day' + } + }], + }, + } + }; + + var ctx = $.pwrvid.d.find('canvas')[0].getContext("2d"); + $.pwrvid.chart = new Chart(ctx, config); + $.pwrvid.d.find('canvas').click(function(e) { + var target = $.pwrvid.chart.getElementsAtEvent(e)[0]; + if(!target){return false} + target = $.pwrvid.currentData[target._index]; + $.pwrvid.e.find('.temp').html('
    • ').find('a').click() + }); + var colorNames = Object.keys(window.chartColors); + }else{ + $.pwrvid.e.find('.nodata').show() + } + break; + } +} +$user.ws=io(location.origin,{ + path : tool.checkCorrectPathEnding(location.pathname)+'socket.io' +}); +$user.ws.on('connect',function (d){ + $(document).ready(function(e){ + $.ccio.init('id',$user); + $.ccio.cx({f:'init',ke:$user.ke,auth:$user.auth_token,uid:$user.uid}) + if($user.details&&$user.details.links){ + $.each($user.details.links,function(n,v){ + if(v.secure==='0'){ + v.protocol='http' + }else{ + v.protocol='https' + } + if(v.host.indexOf('://')>-1){ + v.URL=v.protocol+'://'+v.host.split('://')[1] + }else{ + v.URL=v.protocol+'://'+v.host + } + $.get(v.URL+'/'+v.api+'/userInfo/'+v.ke,function(e){ + if(e.ok===true){ + e.user.auth_token=v.api + $.users[v.api]=e.user + $.users[v.api].info=v + $.users[v.api].ws=io(v.host) + $.users[v.api].ws.on('ping', function(d){ + $.users[v.api].ws.emit('pong',{beat:1}); + }); + $.users[v.api].ws.on('connect',function (d){ + console.log(v.host,'connected') + $.ccio.cx({f:'init',ke:e.user.ke,auth:v.api,uid:e.user.uid},$.users[v.api]) + }) + $.users[v.api].ws.on('f',function (d){ + $.ccio.globalWebsocket(d,$.users[v.api]) + }) + } + }) + }) + } + }) +}) +PNotify.prototype.options.styling = "fontawesome"; +$user.ws.on('ping', function(d){ + $user.ws.emit('pong',{beat:1}); +}); +$user.ws.on('f',function (d){ + $.ccio.globalWebsocket(d) + switch(d.f){ + case'api_key_deleted': + if($user.uid === d.uid){ + $.ccio.init('note',{title:lang['API Key Deleted'],text:lang.APIKeyDeletedText,type:'notice'}); + $('[api_key="'+d.form.code+'"]').remove() + } + break; + case'api_key_added': + if($user.uid === d.uid){ + $.ccio.init('note',{title:lang['API Key Added'],text:lang.FiltersUpdatedText,type:'success'}); + $.ccio.tm(3,d.form,'#api_list') + } + break; + case'filters_change': + $.ccio.init('note',{title:lang['Filters Updated'],text:lang.FiltersUpdatedText,type:'success'}); + $user.details.filters=d.filters; + $.ccio.init('filters'); + break; + case'user_settings_change': + $.ccio.init('note',{title:lang['Settings Changed'],text:lang.SettingsChangedText,type:'success'}); + $.ccio.init('id',d.form); + d.form.details=JSON.parse(d.form.details) + $('#custom_css').append(d.form.details.css) + if(d.form.details){ + $user.details=d.form.details + } + break; + case'users_online': + $.ccio.pm('user-row',d.users); + break; + case'user_status_change': + if(d.status===1){ + $.ccio.tm('user-row',d.user,null) + }else{ + $('.user-row[uid="'+d.uid+'"][ke="'+d.ke+'"]').remove() + } + break; + case'ffprobe_stop': + $.pB.e.find('._loading').hide() + $.pB.o.append('
      END
      '); + $.pB.e.find('.stop').hide(); + $.pB.e.find('[type="submit"]').show(); + break; + case'ffprobe_start': + $.pB.e.find('._loading').show() + $.pB.o.empty(); + $.pB.e.find('.stop').show(); + $.pB.e.find('[type="submit"]').hide(); + break; + case'ffprobe_data': + $.pB.results=JSON.parse(d.data) + $.pB.o.append($.ccio.init('jsontoblock',$.pB.results)) + break; + case'detector_cascade_list': + d.tmp='' + $.each(d.cascades,function(n,v){ + d.tmp+='
    • '; + d.tmp+=''; + d.tmp+=v; + d.tmp+=''; + d.tmp+=''; + d.tmp+=''; + d.tmp+=''; + d.tmp+='
    • '; + }) + $('#detector_cascade_list').html(d.tmp) + componentHandler.upgradeAllRegistered() + //add auto select for preferences + d.currentlyEditing=$.aM.e.attr('mid') + if(d.currentlyEditing&&d.currentlyEditing!==''){ + d.currentlyEditing=JSON.parse(JSON.parse($.ccio.mon[d.currentlyEditing].details).detector_cascades) + $.each(d.currentlyEditing,function(m,b){ + d.e=$('.detector_cascade_selection[value="'+m+'"]').prop('checked',true) + d.p=d.e.parents('.mdl-js-switch') + if(d.p.length>0){ + d.p.addClass('is-checked') + } + }) + } + break; + case'detector_plugged': + if(!d.notice){d.notice=''} + $('.shinobi-detector').show() + $('.shinobi-detector-msg').html(d.notice) + $('.shinobi-detector_name').text(d.plug) + $('.shinobi-detector-'+d.plug).show() + $('.shinobi-detector-invert').hide() + $.aM.drawList() + break; + case'detector_unplugged': + $('.stream-objects .stream-detected-object').remove() + $('.shinobi-detector').hide() + $('.shinobi-detector-msg').empty() + $('.shinobi-detector_name').empty() + $('.shinobi-detector_plug').hide() + $('.shinobi-detector-invert').show() + $.aM.drawList() + break; + case'monitor_edit_failed': + d.pnote={title:'Monitor Not Saved',text:''+d.mon.name+' '+d.mon.mid+' has not been saved.',type:'error'} + switch(d.ff){ + case'max_reached': + d.pnote.text+=' '+lang.monitorEditFailedMaxReached + break; + } + $.ccio.init('note',d.pnote); + break; +// case'onvif_end': +// if(Object.keys($.oB.foundMonitorsCount).length===0){ +// $.oB.e.find('._loading').hide() +// $.oB.e.find('[type="submit"]').prop('disabled',false) +// $.oB.o.append('Sorry, nothing was found.') +// } +// break; + case'onvif': + var tempID = $.ccio.gid(); + $.oB.foundMonitors[tempID] = Object.assign({},d); + $.oB.e.find('._loading').hide() + $.oB.e.find('._notfound').remove() + $.oB.e.find('[type="submit"]').prop('disabled',false) + d.info=$.ccio.init('jsontoblock',d.info) + if(d.uri){ + d.stream=d.uri + }else{ + d.stream='URL not Found' + } + $('#onvif_probe .output_data').append('  '+d.ip+''+d.port+''+$.ccio.init('jsontoblock',d.info)+''+d.stream+'') + break; + } + delete(d); +}); +}) diff --git a/web/libs/js/dash2.timelapse.js b/web/libs/js/dash2.timelapse.js new file mode 100644 index 00000000..0334d9e8 --- /dev/null +++ b/web/libs/js/dash2.timelapse.js @@ -0,0 +1,334 @@ +$(document).ready(function(e){ + //Timelapse Window + $.timelapse={e:$('#timelapse')} + $.timelapse.f=$.timelapse.e.find('form'), + $.timelapse.meter=$.timelapse.e.find('.motion-meter'), + $.timelapse.line=$('#timelapse_video_line'), + $.timelapse.display=$('#timelapse_video_display'), + $.timelapse.seekBar=$('#timelapse_seekBar'), + $.timelapse.seekBarProgress=$.timelapse.seekBar.find('.progress-bar'), + $.timelapse.dr=$('#timelapse_daterange'), + $.timelapse.mL=$.timelapse.e.find('.motion_list'), + $.timelapse.monitors=$.timelapse.e.find('.monitors_list'); + $.timelapse.playDirection='videoAfter' + $.timelapse.playRate=15 + $.timelapse.placeholder=placeholder.getData(placeholder.plcimg({bgcolor:'#b57d00',text:'...'})) + $.timelapse.dr.daterangepicker({ + startDate:$.ccio.timeObject().subtract(moment.duration("24:00:00")), + endDate:$.ccio.timeObject().add(moment.duration("24:00:00")), + timePicker: true, + timePicker24Hour: true, + timePickerSeconds: true, + timePickerIncrement: 30, + locale: { + format: 'MM/DD/YYYY h:mm A' + } + },function(start, end, label){ + $.timelapse.drawTimeline() + $.timelapse.dr.focus() + }); + $.timelapse.f.find('input,select').change(function(){ + $.timelapse.f.submit() + }) + $.timelapse.f.submit(function(e){ + e.preventDefault(); + $.timelapse.drawTimeline() + return false; + }) + $.timelapse.drawTimeline=function(getData){ + var e={}; + if(getData===undefined){getData=true} + var mid = $.timelapse.monitors.val() + e.dateRange=$.timelapse.dr.data('daterangepicker'); + e.dateRange={startDate:e.dateRange.startDate,endDate:e.dateRange.endDate} + e.videoURL=$.ccio.init('location',$user)+$user.auth_token+'/videos/'+$user.ke+'/'+mid; + e.videoURL+='?limit=100&start='+$.ccio.init('th',e.dateRange.startDate)+'&end='+$.ccio.init('th',e.dateRange.endDate); + e.next=function(videos){ + $.timelapse.currentVideos={} + e.tmp='' + $.each(videos.videos,function(n,v){ + if(!v||!v.time){return} + // v.filename=$.ccio.init('tf',v.time)+'.'+v.ext; + v.videoBefore=videos.videos[n-1]; + v.videoAfter=videos.videos[n+1]; + // if(v.href.charAt(0)==='/'){ + // v.href=$.ccio.init('location',user)+(v.href.substring(1)) + // v.videoURL=$.ccio.init('location',user)+(v.videoURL.substring(1)) + // } + v.position=n; + $.timelapse.currentVideos[v.filename]=v; + e.tmp+='
    • ' + e.tmp+='
      ' + e.tmp+='
      ' + e.tmp+='
      '+v.filename+'
      ' + e.tmp+='' + e.tmp+='
      ' + e.tmp+='
      ' + e.tmp+='
      ' + e.tmp+='
      ' + e.tmp+='
    • ' + }) + $.timelapse.line.html(e.tmp) + $.ccio.init('ls') + if(getData===true){ + e.timeout=50 + }else{ + e.timeout=2000 + } + setTimeout(function(){ + if($.timelapse.e.find('.timelapse_video.active').length===0){ + $.timelapse.e.find('[timelapse="video"]').first().click() + } + },e.timeout) + } + if(getData===true){ + $.getJSON(e.videoURL,function(videos){ + videos.videos=videos.videos.reverse() + $.timelapse.currentVideosArray=videos + e.next(videos) + }) + }else{ + e.next($.timelapse.currentVideosArray) + } + } + $.timelapse.playButtonIcon = $.timelapse.e.find('[timelapse="play"]').find('i') + $.timelapse.timelapseSpeedUseBasicSwitch = $('#timelapseSpeedUseBasic') + $.timelapse.timelapseSpeedUseBasicSwitch.on('change',function(){ + var el = $.timelapse.e.find('.timelapseSpeedUseBasicSwitch') + if($(this).is(':checked')){ + el.hide() + }else{ + el.show() + } + $.timelapse.play() + }) + $.timelapse.getUseBasicStatus = function(){return $.timelapse.timelapseSpeedUseBasicSwitch.prop('checked')} + $.timelapse.onPlayPause = function(toggleGui,secondWind){ + if($.timelapse.paused === true){ + $.timelapse.paused = false + if(toggleGui === true)$.timelapse.play(); + }else{ + $.timelapse.paused = true + if(toggleGui === true)$.timelapse.pause(secondWind); + } + } + $.timelapse.pause = function(secondWind){ + //secondWind is used because sometimes pause can be pressed just as a video ends and the pause command does not register on the next video. + var videoNow = $.timelapse.display.find('video.videoNow')[0] + var pause = function(){ + if(videoNow.paused == false)videoNow.pause() + clearInterval($.timelapse.interval) + $.timelapse.playButtonIcon.removeClass('fa-pause').addClass('fa-play') + } + pause() + if(secondWind === true)setTimeout(pause,250); + } + $.timelapse.play = function(x){ + var videoNow = $.timelapse.display.find('video.videoNow')[0] + $.timelapse.pause() + clearInterval($.timelapse.interval) + if($.timelapse.getUseBasicStatus()){ + videoNow.playbackRate = $.timelapse.playRate + if(videoNow.paused)videoNow.play() + }else{ + videoNow.playbackRate = 1.0 + $.timelapse.interval = setInterval(function(){ + if(videoNow.currentTime >= videoNow.duration - .2){ + clearInterval($.timelapse.interval) + videoNow.currentTime = videoNow.duration + }else{ + videoNow.currentTime += .5 + } + },500 / $.timelapse.playRate) + } + $.timelapse.playButtonIcon.removeClass('fa-play').addClass('fa-pause') + } + $.timelapse.rewind = function(e){ + var videoNow = $.timelapse.display.find('video.videoNow')[0] + $.timelapse.pause() + videoNow.playbackRate = 1.0 + clearInterval($.timelapse.interval) + $.timelapse.interval = setInterval(function(){ + if(videoNow.currentTime <= 0.2){ + clearInterval($.timelapse.interval) + videoNow.currentTime = 0 + $('[timelapse][href="'+e.videoCurrentBefore.attr('video')+'"]').click() + var videoNowNew = $.timelapse.display.find('video.videoNow')[0] + videoNowNew.pause() + videoNowNew.currentTime = videoNowNew.duration - 0.1 + $.timelapse.e.find('[timelapse="stepBackBack"]').click() + }else{ + videoNow.currentTime += -.5 + } + },500 / $.timelapse.playRate) + $.timelapse.playButtonIcon.removeClass('fa-play').addClass('fa-pause') + } + $.timelapse.e.on('click','[timelapse]',function(){ + var e={} + e.e=$(this) + e.videoCurrentNow=$.timelapse.display.find('.videoNow') + e.videoCurrentAfter=$.timelapse.display.find('.videoAfter') + e.videoCurrentBefore=$.timelapse.display.find('.videoBefore') + if($.timelapse.videoInterval){ + clearInterval($.timelapse.videoInterval); + } + switch(e.e.attr('timelapse')){ + case'download': + $.timelapse.line.find('.active [download]').click() + break; + case'mute': + e.videoCurrentNow[0].muted = !e.videoCurrentNow[0].muted + $.timelapse.videoNowIsMuted = e.videoCurrentNow[0].muted + e.e.find('i').toggleClass('fa-volume-off fa-volume-up') + e.e.toggleClass('btn-danger') + break; + case'play': + e.videoCurrentNow[0].playbackRate = $.timelapse.playRate; + $.timelapse.onPlayPause(true,true) + break; + case'setPlayBackRate': + $.timelapse.pause() + $.timelapse.playRate = parseFloat(e.e.attr('playRate')) + $.timelapse.play() + break; + case'stepFrontFront': + e.add=e.e.attr('add') + e.stepFrontFront=parseInt(e.e.attr('stepFrontFront')) + if(!e.stepFrontFront||isNaN(e.stepFrontFront)){e.stepFrontFront = 5} + if(e.add==="0"){ + $.timelapse.playRate = e.stepFrontFront + }else{ + $.timelapse.playRate += e.stepFrontFront + } + e.videoCurrentNow[0].playbackRate = $.timelapse.playRate; + e.videoCurrentNow[0].play() + break; + case'stepFront': + e.videoCurrentNow[0].currentTime += 5; + e.videoCurrentNow[0].pause() + break; + case'stepBackBack': + // e.videoCurrentNow=$.timelapse.display.find('.videoNow') + // e.videoCurrentAfter=$.timelapse.display.find('.videoAfter') + // e.videoCurrentBefore=$.timelapse.display.find('.videoBefore') + $.timelapse.rewind(e) + break; + case'stepBack': + e.videoCurrentNow[0].currentTime += -5; + e.videoCurrentNow[0].pause() + break; + case'video': + $.timelapse.e.find('video').each(function(n,v){ + v.pause() + }) + e.drawVideoHTML=function(position){ + var video + var exisitingElement=$.timelapse.display.find('.'+position) + if(position){ + video=e.video[position] + }else{ + position='videoNow' + video=e.video + } + if(video){ + $.timelapse.display.append('') + } + } + e.filename=e.e.attr('file') + e.video=$.timelapse.currentVideos[e.filename] + e.videoIsSame=(e.video.href==e.videoCurrentNow.attr('video')) + e.videoIsAfter=(e.video.href==e.videoCurrentAfter.attr('video')) + e.videoIsBefore=(e.video.href==e.videoCurrentBefore.attr('video')) + if(e.videoIsSame||e.videoIsAfter||e.videoIsBefore){ + switch(true){ + case e.videoIsSame: + $.ccio.log('$.timelapse','videoIsSame') + e.videoNow=$.timelapse.display.find('video.videoNow') + if(e.videoNow[0].paused===true){ + e.videoNow[0].play() + }else{ + e.videoNow[0].pause() + } + return + break; + case e.videoIsAfter: + $.ccio.log('$.timelapse','videoIsAfter') + e.videoCurrentBefore.remove() + e.videoCurrentAfter.removeClass('videoAfter').addClass('videoNow') + e.videoCurrentNow.removeClass('videoNow').addClass('videoBefore') + e.drawVideoHTML('videoAfter') + break; + case e.videoIsBefore: + $.ccio.log('$.timelapse','videoIsBefore') + e.videoCurrentAfter.remove() + e.videoCurrentBefore.removeClass('videoBefore').addClass('videoNow') + e.videoCurrentNow.removeClass('videoNow').addClass('videoAfter') + e.drawVideoHTML('videoBefore') + break; + } + }else{ + $.ccio.log('$.timelapse','newSetOf3') + $.timelapse.display.empty() + e.drawVideoHTML()//videoNow + e.drawVideoHTML('videoBefore') + e.drawVideoHTML('videoAfter') + } + $.timelapse.display.find('video').each(function(n,v){ + v.addEventListener('loadeddata', function() { + e.videoCurrentAfterPreview=$('.timelapse_video[href="'+$(v).attr('video')+'"] .frame') + if(e.videoCurrentAfterPreview.attr('set')!=='1'){ + $.ccio.snapshotVideo(v,function(url,buffer){ + e.videoCurrentAfterPreview.attr('set','1').css('background-image','url('+url+')') + if($(v).hasClass('videoAfter')){ + v.currentTime=0 + v.pause() + } + }) + } + }, false); + }) + e.videoNow=$.timelapse.display.find('video.videoNow')[0] + if($.timelapse.videoNowIsMuted){ + e.videoNow.muted=true + } + $.timelapse.playButtonIcon.removeClass('fa-pause').addClass('fa-play') + $.timelapse.onended = function() { + $.timelapse.line.find('[file="'+e.video[$.timelapse.playDirection].filename+'"]').click() + }; + e.videoNow.onended = $.timelapse.onended + e.videoNow.onerror = $.timelapse.onended + // + $(e.videoNow) + .off('play').on('play',$.timelapse.play) + .off('pause').on('pause',$.timelapse.onPlayPause) + .off('timeupdate').on('timeupdate',function(){ + var value= (( e.videoNow.currentTime / e.videoNow.duration ) * 100)+"%" + $.timelapse.seekBarProgress.css("width",value); + $.timelapse.e.find('.timelapse_video[file="'+e.filename+'"] .progress-bar').css("width",value); + }) + $.timelapse.play() + $.timelapse.seekBar.off("click").on("click", function(seek){ + var offset = $(this).offset(); + var left = (seek.pageX - offset.left); + var totalWidth = $.timelapse.seekBar.width(); + var percentage = ( left / totalWidth ); + var vidTime = e.videoNow.duration * percentage; + e.videoNow.currentTime = vidTime; + }); + + $.ccio.log('$.timelapse',e.video) + $.timelapse.line.find('.timelapse_video').removeClass('active') + e.videoCurrentNow=$.timelapse.display.find('.videoNow') + e.e.addClass('active') + if ($('#timelapse_video_line:hover').length === 0) { + $.timelapse.line.animate({scrollTop:$.timelapse.line.scrollTop() + e.e.position().top - $.timelapse.line.height()/2 + e.e.height()/2 - 40}); + } + break; + } + $.timelapse.e.find('.timelapse_playRate').text('x'+$.timelapse.playRate) + }) + $.timelapse.e.on('hidden.bs.modal',function(e){ + delete($.timelapse.currentVideos) + delete($.timelapse.currentVideosArray) + }) +}) diff --git a/web/libs/js/dash2.usersettings.js b/web/libs/js/dash2.usersettings.js new file mode 100644 index 00000000..388f6dbb --- /dev/null +++ b/web/libs/js/dash2.usersettings.js @@ -0,0 +1,101 @@ +$(document).ready(function(e){ +//settings window +$.sM={e:$('#settings')}; +$.sM.f=$.sM.e.find('form'); +$.sM.links=$('#linkShinobi'); +$.sM.g=$('#settings_mon_groups'); +$.sM.md=$.sM.f.find('[detail]'); +$.sM.md.change($.ccio.form.details); +$.sM.f.find('[selector]').change(function(e){ + e.v=$(this).val();e.a=$(this).attr('selector') + $.sM.f.find('.'+e.a+'_input').hide() + $.sM.f.find('.'+e.a+'_'+e.v).show(); + $.sM.f.find('.'+e.a+'_text').text($(this).find('option:selected').text()) +}); +$.sM.writewMonGroups=function(){ + $.sM.f.find('[detail="mon_groups"]').val(JSON.stringify($user.mon_groups)).change() +} +$.sM.reDrawMonGroups=function(){ + $.sM.g.empty(); + $.ccio.pm('option',$user.mon_groups,'#settings_mon_groups') + $.sM.g.change(); +}; +$.sM.f.submit(function(e){ + e.preventDefault(); + $.sM.writewMonGroups() + $.sM.linkChange() + e.e=$(this),e.s=e.e.serializeObject(); + e.er=[]; + if(e.s.pass!==''&&e.password_again===e.s.pass){e.er.push(lang['Passwords don\'t match'])}; + if(e.er.length>0){$.sM.e.find('.msg').html(e.er.join('
      '));return;} + $.each(e.s,function(n,v){e.s[n]=v.trim()}) + $.ccio.cx({f:'settings',ff:'edit',form:e.s}) + $.sM.e.modal('hide') +}); +$.sM.e.on('shown.bs.modal',function(){ + $.sM.reDrawMonGroups() +}) +$.sM.g.change(function(e){ + e.v=$(this).val(); + e.group=$user.mon_groups[e.v]; + if(!e.group){return} + $.sM.selectedMonGroup=e.group; + $.each(e.group,function(n,v){ + $.sM.f.find('[group="'+n+'"]').val(v) + }) +}); +$.sM.f.find('[group]').change(function(){ + e = {} + e.v = $.sM.g.val() + if(!e.v||e.v==''){ + e.e = $.sM.f.find('[group="name"]') + e.name = e.e.val() + $('.mon_groups .add').click(); + e.v = $.sM.g.val() + e.e.val(e.name) + } + e.group=$user.mon_groups[e.v]; + $.sM.f.find('[group]').each(function(n,v){ + v=$(v) + e.group[v.attr('group')]=v.val() + }); + $user.mon_groups[e.v]=e.group; + $.sM.g.find('option[value="'+$.sM.g.val()+'"]').text(e.group.name) + $.sM.writewMonGroups() +}) +$.sM.f.on('click','.mon_groups .delete',function(e){ + e.v=$.sM.g.val(); + delete($user.mon_groups[e.v]); + $.sM.reDrawMonGroups() +}) +$.sM.f.on('click','.mon_groups .add',function(e){ + e.gid=$.ccio.gid(5); + $user.mon_groups[e.gid]={id:e.gid,name:e.gid}; + $.sM.g.append($.ccio.tm('option',$user.mon_groups[e.gid])); + $.sM.g.val(e.gid) + $.sM.g.change(); +}); +$.sM.linkChange=function(){ + var e={}; + e.e=$.sM.e.find('[name="details"]') + e.details=JSON.parse(e.e.val()) + e.details.links=[] + $.sM.links.find('.linksGroup').each(function(n,v){ + var arr={} + $(v).find('[link]').each(function(m,b){ + arr[$(b).attr('link')]=$(b).val() + }) + e.details.links.push(arr) + }) + e.e.val(JSON.stringify(e.details)) +} +$.sM.f.on('change','[link]',$.sM.linkChange) +$.sM.e.on('click','.linkShinobi .delete',function(){ + $(this).parents('.linksGroup').remove() + $.sM.linkChange() +}) +$.sM.e.find('.linkShinobi .add').click(function(){ + $.ccio.tm('link-set',{},'#linkShinobi') + $.sM.linkChange() +}) +}) diff --git a/web/libs/js/dash2.vidview.js b/web/libs/js/dash2.vidview.js new file mode 100644 index 00000000..9eb35383 --- /dev/null +++ b/web/libs/js/dash2.vidview.js @@ -0,0 +1,147 @@ +$(document).ready(function(e){ +//videos window +$.vidview={ + e:$('#videos_viewer'), + pages:$('#videos_viewer_pages'), + limit:$('#videos_viewer_limit'), + dr:$('#videos_viewer_daterange'), + preview:$('#videos_viewer_preview'), + set:$('#videos_viewer_set') +} +$.vidview.set.change(function(){ + var el = $(this) + var isCloud = (el.val() === 'cloud') + var zipDlButton = $.vidview.e.find('.export_selected') + if(isCloud){ + zipDlButton.hide() + }else{ + zipDlButton.show() + } + +}) +$.vidview.f=$.vidview.e.find('form') +$.vidview.dr.daterangepicker({ + startDate:$.ccio.timeObject().subtract(moment.duration("24:00:00")), + endDate:$.ccio.timeObject().add(moment.duration("24:00:00")), + timePicker: true, + timePicker24Hour: true, + timePickerSeconds: true, + timePickerIncrement: 30, + locale: { + format: 'MM/DD/YYYY h:mm A' + } +},function(start, end, label){ + $.vidview.launcher.click() + $.vidview.dr.focus() +}); +$.vidview.e.on('change','#videos_select_all',function(e){ + e.e=$(this); + e.p=e.e.prop('checked') + e.a=$.vidview.e.find('input[type=checkbox][name]') + if(e.p===true){ + e.a.prop('checked',true) + }else{ + e.a.prop('checked',false) + } +}) +$.vidview.f.submit(function(e){ + e.preventDefault(); + $.vidview.launcher.click() + return false; +}) +$('#videos_viewer_limit,#videos_viewer_daterange,#videos_viewer_set').change(function(){ + $.vidview.f.submit() +}) +$.vidview.getSelected = function(getArray){ + var arr = {} + if(getArray){ + arr = [] + } + $.vidview.f.find('[data-ke] input:checked').each(function(n,v){ + v=$(v).parents('tr') + if(getArray){ + arr.push({filename:v.attr('data-file'),mid:v.attr('data-mid'),auth:v.attr('data-auth')}) + }else{ + arr[v.attr('data-file')]={mid:v.attr('data-mid'),auth:v.attr('data-auth')} + } + }) + return arr +} +$.vidview.e.find('.delete_selected').click(function(){ + e = {} + e.s = $.vidview.getSelected() + if(Object.keys(e.s).length === 0){ + $.ccio.init('note',{ + title:'No Videos Selected', + text:'You must choose at least one video.', + type:'error' + },$user); + return + } + $.confirm.e.modal('show'); + $.confirm.title.text(lang['Delete Selected Videos']) + e.html=lang.DeleteSelectedVideosMsg+'
      ' + var deleteLinks = [] + $.each(e.s,function(n,v){ + e.html+=n+'
      '; + if($.vidview.loadedVideos[n])deleteLinks.push($.vidview.loadedVideos[n].links.deleteVideo) + }) + $.confirm.body.html(e.html) + $.confirm.click({title:'Delete Video',class:'btn-danger'},function(){ + $.each(deleteLinks,function(n,link){ + $.getJSON(link,function(d){ + $.ccio.log(d) + }) + }) + }); +}) +$.vidview.e.find('.export_selected').click(function(){ + e = {} + var videos = $.vidview.getSelected(true) + if(videos.length === 0){ + $.ccio.init('note',{ + title:'No Videos Selected', + text:'You must choose at least one video.', + type:'error' + },$user); + return + } + $.confirm.e.modal('show'); + $.confirm.title.text(lang['Export Selected Videos']) + var html = lang.ExportSelectedVideosMsg+'
      ' + $.each(videos,function(n,v){ + html+=v.filename+'
      '; + }) + $.confirm.body.html(html) + $.confirm.click({title:'Export Video',class:'btn-danger'},function(){ + var queryVariables = [] + queryVariables.push('videos='+JSON.stringify(videos)) + if($.ccio.useUTC === true){ + queryVariables.push('isUTC=true') + } + console.log(queryVariables) + var downloadZip = $.ccio.init('location',$user)+$user.auth_token+'/zipVideos/'+$user.ke+'?'+queryVariables.join('&') + $('#temp').html('').find('iframe').attr('src',downloadZip); + }); +}) +$.vidview.pages.on('click','[page]',function(e){ + e.limit=$.vidview.limit.val(); + e.page=$(this).attr('page'); + $.vidview.current_page=e.page; + if(e.limit.replace(/ /g,'')===''){ + e.limit='100'; + } + if(e.limit.indexOf(',')>-1){ + e.limit=parseInt(e.limit.split(',')[1]) + }else{ + e.limit=parseInt(e.limit) + } + $.vidview.limit.val((parseInt(e.page)-1)+'00,'+e.limit) + $.vidview.launcher.click() +}) +$.vidview.e.on('click','.preview',function(e){ + e.preventDefault() + e=$(this) + $.vidview.preview.html('') +}) +}) diff --git a/web/libs/js/main.dash2.js b/web/libs/js/main.dash2.js index 7aed2ad2..7b7e7c93 100644 --- a/web/libs/js/main.dash2.js +++ b/web/libs/js/main.dash2.js @@ -1156,7 +1156,9 @@ switch($user.details.lang){ tmp+=''; break; case'option': - tmp+='' + var selected = '' + if(d.selected === true){selected = ' selected'} + tmp+='' break; case'stream-element': try{k.d=JSON.parse(d.details);}catch(er){k.d=d.details} diff --git a/web/pages/admin.ejs b/web/pages/admin.ejs index 0a29f86c..5298e019 100644 --- a/web/pages/admin.ejs +++ b/web/pages/admin.ejs @@ -12,6 +12,9 @@ +<% customAutoLoad.adminLibsCss.forEach(function(lib){ %> + +<% }) %>
      @@ -80,8 +83,14 @@
      + <% include blocks/confirm.ejs %> <% include blocks/subpermissions.ejs %> +<% customAutoLoad.adminPageBlocks.forEach(function(block){ %> + <%- include(block) %> +<% }) %> @@ -275,3 +284,6 @@ $('body') localStorage.removeItem('ShinobiLogin_'+location.host);location.href=location.href; }) +<% customAutoLoad.adminLibsJs.forEach(function(lib){ %> + +<% }) %> diff --git a/web/pages/blocks/api.ejs b/web/pages/blocks/api.ejs index 2fdbb4e6..a6543459 100644 --- a/web/pages/blocks/api.ejs +++ b/web/pages/blocks/api.ejs @@ -93,3 +93,4 @@ + diff --git a/web/pages/blocks/detectorfilters.ejs b/web/pages/blocks/detectorfilters.ejs index d5d7e2a0..e76291b8 100644 --- a/web/pages/blocks/detectorfilters.ejs +++ b/web/pages/blocks/detectorfilters.ejs @@ -259,3 +259,4 @@ + diff --git a/web/pages/blocks/filters.ejs b/web/pages/blocks/filters.ejs index f29892b2..0350d594 100644 --- a/web/pages/blocks/filters.ejs +++ b/web/pages/blocks/filters.ejs @@ -28,8 +28,8 @@

      <%- lang['Find Where'] %>
      -    -    +    +   

      @@ -39,20 +39,20 @@
      @@ -68,8 +68,8 @@
      @@ -77,24 +77,24 @@
      @@ -115,4 +115,5 @@
      - \ No newline at end of file + + diff --git a/web/pages/blocks/logs.ejs b/web/pages/blocks/logs.ejs index d2870a63..cddecdf3 100644 --- a/web/pages/blocks/logs.ejs +++ b/web/pages/blocks/logs.ejs @@ -35,7 +35,7 @@
      - +
      @@ -48,4 +48,5 @@ - \ No newline at end of file + + diff --git a/web/pages/blocks/mainpermissions.ejs b/web/pages/blocks/mainpermissions.ejs index 2f31428d..349e7ab8 100644 --- a/web/pages/blocks/mainpermissions.ejs +++ b/web/pages/blocks/mainpermissions.ejs @@ -185,7 +185,7 @@ diff --git a/web/pages/blocks/monitoredit.ejs b/web/pages/blocks/monitoredit.ejs index c83a5fda..190d0539 100644 --- a/web/pages/blocks/monitoredit.ejs +++ b/web/pages/blocks/monitoredit.ejs @@ -169,7 +169,7 @@
      @@ -284,6 +284,14 @@
      +
      + +
      @@ -842,6 +850,11 @@
      +
      -
      - -
      -

      <%-lang['"No Motion" Detector']%>

      @@ -1149,6 +1144,29 @@
      + +
      +

      <%-lang['Audio Detector']%>

      +
      + +
      +
      + +
      +
      + +
      +
      +

      <%-lang['Object Detection']%> <%-lang['Plugin']%> : <%-lang['Not Connected']%>

      @@ -1159,6 +1177,14 @@
      +
      + +
      +
      +
      +

      <%- lang['Schedule'] %> +
      + +
      +

      +
      + +
      +
      + +
      +
      + +
      +
      + +
      +
      + +
      +
      + + + + + + diff --git a/web/pages/blocks/settings.ejs b/web/pages/blocks/settings.ejs index 76711c92..df8e5a0d 100644 --- a/web/pages/blocks/settings.ejs +++ b/web/pages/blocks/settings.ejs @@ -503,3 +503,4 @@ + diff --git a/web/pages/blocks/timelapse.ejs b/web/pages/blocks/timelapse.ejs index 730e70f5..d844fb42 100644 --- a/web/pages/blocks/timelapse.ejs +++ b/web/pages/blocks/timelapse.ejs @@ -77,4 +77,5 @@ - \ No newline at end of file + + diff --git a/web/pages/blocks/videoview.ejs b/web/pages/blocks/videoview.ejs index 6baa5f18..4113316f 100644 --- a/web/pages/blocks/videoview.ejs +++ b/web/pages/blocks/videoview.ejs @@ -77,3 +77,4 @@ + diff --git a/web/pages/home.ejs b/web/pages/home.ejs index 1e928b58..7a765373 100644 --- a/web/pages/home.ejs +++ b/web/pages/home.ejs @@ -14,7 +14,22 @@ + + +<% customAutoLoad.LibsCss.forEach(function(lib){ %> + +<% }) %> @@ -121,6 +136,8 @@ <% if(!details.sub){ %>
    • <%- lang.ONVIF %>
    • <%- lang.FFprobe %>
    • +
    • <%- lang['Monitor States'] %>
    • +
    • <%- lang['Schedules'] %>
    • <%- lang.Filters %>
    • <% } %>
    • <%- lang.Logs %>
    • @@ -167,7 +184,12 @@ <% include blocks/probe.ejs %> <% include blocks/region.ejs %> <% include blocks/detectorfilters.ejs %> +<% include blocks/monitorStates.ejs %> +<% include blocks/schedules.ejs %> <% include blocks/confirm.ejs %> +<% customAutoLoad.PageBlocks.forEach(function(block){ %> + <%- include(block) %> +<% }) %> <% if(config.DropboxAppKey){ %> @@ -198,5 +220,15 @@ - + + + + + + + + +<% customAutoLoad.LibsJs.forEach(function(lib){ %> + +<% }) %> <% include blocks/help.ejs %> diff --git a/web/pages/super.ejs b/web/pages/super.ejs index 2796365d..fa825928 100644 --- a/web/pages/super.ejs +++ b/web/pages/super.ejs @@ -27,6 +27,9 @@ .list-group li .form-group {margin:0} a {cursor:pointer} + <% customAutoLoad.superLibsCss.forEach(function(lib){ %> + + <% }) %> @@ -62,7 +65,7 @@
      -
      + + <% include blocks/confirm.ejs %> +<% customAutoLoad.superPageBlocks.forEach(function(block){ %> + <%- include(block) %> +<% }) %> @@ -178,6 +188,7 @@ <% include blocks/mainpermissions.ejs %> +<% customAutoLoad.superLibsJs.forEach(function(lib){ %> + +<% }) %>