Merge branch 'dev' into 'master'

Zom Be Gone

See merge request Shinobi-Systems/Shinobi!318
cron-addstorage-fix
Moe 2021-08-02 17:17:46 +00:00
commit 04a7baed2c
28 changed files with 4338 additions and 109 deletions

View File

@ -87,22 +87,8 @@ docker run -d --name='Shinobi' -p '8080:8080/tcp' -v "/dev/shm/Shinobi/streams":
### Environment Variables
| Environment Variable | Description | Default |
|----------------------|----------------------------------------------------------------------|--------------------|
| SUBSCRIPTION_ID | **THIS IS NOT REQUIRED**. If you are a subscriber to any of the Shinobi services you may use that key as the value for this parameter. If you have donated by PayPal you may use your Transaction ID to activate the license as well. | *None* |
| DB_USER | Username that the Shinobi process will connect to the database with. | majesticflame |
| DB_PASSWORD | Password that the Shinobi process will connect to the database with. | *None* |
| DB_HOST | Address that the Shinobi process will connect to the database with. | localhost |
| DB_DATABASE | Database that the Shinobi process will interact with. | ccio |
| DB_DISABLE_INCLUDED | Disable included database to use your own. Set to `true` to disable.| false |
| PLUGIN_KEYS | The object containing connection keys for plugins running in client mode (non-host, default). | {} |
| SSL_ENABLED | Enable or Disable SSL. | false |
| SSL_COUNTRY | Country Code for SSL. | CA |
| SSL_STATE | Province/State Code for SSL. | BC |
| SSL_LOCATION | Location of where SSL key is being used. | Vancouver |
| SSL_ORGANIZATION | Company Name associated to key. | Shinobi Systems |
| SSL_ORGANIZATION_UNIT | Department associated to key. | IT Department |
| SSL_COMMON_NAME | Common Name associated to key. | nvr.ninja |
> Environment Variables have been disabled. You must now make changes in the conf.json itself when it is mounted on the host.
> If conf.json does not exist inside the mounted folder then you may create it and Shinobi will use that on next container start.
> You must add (to the docker container) `/config/ssl/server.key` and `/config/ssl/server.cert`. The `/config` folder is mapped to `$HOME/Shinobi/config` on the host by default with the quick run methods. Place `key` and `cert` in `$HOME/Shinobi/config/ssl`. If `SSL_ENABLED=true` and these files don't exist they will be generated with `openssl`.

View File

@ -84,7 +84,7 @@ elif [ ! -e "./conf.json" ]; then
cp conf.sample.json conf.json
fi
sudo sed -i -e 's/change_this_to_something_very_random__just_anything_other_than_this/'"$cronKey"'/g' conf.json
node tools/modifyConfiguration.js cpuUsageMarker=CPU subscriptionId=$SUBSCRIPTION_ID thisIsDocker=true pluginKeys="$PLUGIN_KEYS" db="$DATABASE_CONFIG" ssl="$SSL_CONFIG"
# node tools/modifyConfiguration.js cpuUsageMarker=CPU subscriptionId=$SUBSCRIPTION_ID thisIsDocker=true pluginKeys="$PLUGIN_KEYS" db="$DATABASE_CONFIG" ssl="$SSL_CONFIG"
echo "============="

View File

@ -104,7 +104,7 @@ VOLUME ["/config"]
VOLUME ["/customAutoLoad"]
VOLUME ["/var/lib/mysql"]
EXPOSE 8080
EXPOSE 8080 443 21 25
ENTRYPOINT ["sh","/home/Shinobi/Docker/init.sh"]

View File

@ -2261,7 +2261,7 @@ module.exports = function(s,config,lang){
hidden: true,
"name": "detail=detector_trigger",
"field": lang['Trigger Record'],
"description": "This will order the camera to record if it is set to \"Watch-Only\" when a motion even is detected.",
"description": "This will order the camera to record if it is set to \"Watch-Only\" when an Event is detected.",
"default": "0",
"example": "",
"form-group-class": "h_det_input h_det_1",
@ -4012,6 +4012,23 @@ module.exports = function(s,config,lang){
}
]
},
{
"name": "detail=notify_useRawSnapshot",
"field": lang['Use Raw Snapshot'],
"default": "0",
"example": "1",
"fieldType": "select",
"possible": [
{
"name": lang.No,
"value": "0"
},
{
"name": lang.Yes,
"value": "1"
}
]
},
]
},
"Logging": {

View File

@ -18,6 +18,7 @@
"deleteSubAccountText": "Do you want to delete this Sub-Account? You cannot recover it.",
"Turn Speed": "Turn Speed",
"Session Key": "Session Key",
"Use Raw Snapshot": "Use Raw Snapshot",
"Login": "Login",
"API Key Action Failed": "API Key Action Failed",
"Authenticate": "Authenticate",

View File

@ -179,6 +179,8 @@ module.exports = async (s,config,lang,app,io) => {
const moduleName = shinobiModule.name
s.customAutoLoadModules[moduleName] = {}
var customModulePath = modulesBasePath + '/' + moduleName
s.debugLog(customModulePath)
s.debugLog(JSON.stringify(shinobiModule,null,3))
if(shinobiModule.isIgnitor){
s.customAutoLoadModules[moduleName].type = 'file'
try{
@ -329,6 +331,7 @@ module.exports = async (s,config,lang,app,io) => {
adminLibsCss: [],
superPageBlocks: [],
superLibsJs: [],
superRawJs: [],
superLibsCss: []
}
fs.readdir(modulesBasePath,function(err,folderContents){

View File

@ -18,6 +18,9 @@ module.exports = (s,config,lang,app,io) => {
const {
moveCameraPtzToMatrix
} = require('../control/ptz.js')(s,config,lang)
const {
cutVideoLength
} = require('../video/utils.js')(s,config,lang)
async function saveImageFromEvent(options,frameBuffer){
const monitorId = options.mid || options.id
const groupKey = options.ke
@ -425,13 +428,24 @@ module.exports = (s,config,lang,app,io) => {
if(activeMonitor && activeMonitor.eventBasedRecording && activeMonitor.eventBasedRecording.process){
const eventBasedRecording = activeMonitor.eventBasedRecording
const monitorConfig = s.group[groupKey].rawMonitorConfigurations[monitorId]
const videoLength = monitorConfig.details.detector_send_video_length
const recordingDirectory = s.getVideoDirectory(monitorConfig)
const fileTime = eventBasedRecording.lastFileTime
const filename = `${fileTime}.mp4`
response.filename = `${filename}`
response.filePath = `${recordingDirectory}${filename}`
eventBasedRecording.process.on('close',function(){
setTimeout(() => {
setTimeout(async () => {
if(!isNaN(videoLength)){
const cutResponse = await cutVideoLength({
ke: groupKey,
mid: monitorId,
filePath: response.filePath,
cutLength: parseInt(videoLength),
})
response.filename = cutResponse.filename
response.filePath = cutResponse.filePath
}
resolve(response)
},1000)
})

View File

@ -24,6 +24,7 @@ module.exports = function(s,config,lang){
splitForFFPMEG,
} = require('./ffmpeg/utils.js')(s,config,lang)
const {
processKill,
cameraDestroy,
monitorConfigurationMigrator,
} = require('./monitor/utils.js')(s,config,lang)
@ -202,14 +203,14 @@ module.exports = function(s,config,lang){
})
snapProcess.on('error', (data) => {
console.log(data)
snapProcess.terminate()
processKill(snapProcess)
})
snapProcess.on('exit', (code) => {
clearTimeout(snapProcessTimeout)
sendTempImage()
})
var snapProcessTimeout = setTimeout(function(){
snapProcess.terminate()
processKill(snapProcess)
},dynamicTimeout)
}catch(err){
console.log(err)

View File

@ -6,6 +6,40 @@ module.exports = (s,config,lang) => {
splitForFFPMEG,
} = require('../ffmpeg/utils.js')(s,config,lang)
const getUpdateableFields = require('./updatedFields.js')
const processKill = (proc) => {
const response = {ok: true}
return new Promise((resolve,reject) => {
function sendError(err){
response.ok = false
response.err = err
resolve(response)
}
try{
proc.stdin.write("q\r\n")
setTimeout(() => {
if(proc && proc.kill){
if(s.isWin){
spawn("taskkill", ["/pid", proc.pid, '/t'])
}else{
proc.kill('SIGTERM')
}
setTimeout(function(){
try{
proc.kill()
resolve(response)
}catch(err){
s.debugLog(err)
sendError(err)
}
},1000)
}
},1000)
}catch(err){
s.debugLog(err)
sendError(err)
}
})
}
const cameraDestroy = function(e,p){
if(
s.group[e.ke] &&
@ -72,27 +106,9 @@ module.exports = (s,config,lang) => {
if(activeMonitor.childNode){
s.cx({f:'kill',d:s.cleanMonitorObject(e)},activeMonitor.childNodeId)
}else{
try{
proc.stdin.write("q\r\n")
setTimeout(() => {
if(proc && proc.kill){
if(s.isWin){
spawn("taskkill", ["/pid", proc.pid, '/t'])
}else{
proc.kill('SIGTERM')
}
setTimeout(function(){
try{
proc.kill()
}catch(err){
s.debugLog(err)
}
},1000)
}
},1000)
}catch(err){
s.debugLog(err)
}
processKill(proc).then((response) => {
s.debugLog(`cameraDestroy`,response)
})
}
}
}
@ -130,21 +146,11 @@ module.exports = (s,config,lang) => {
completeRequest()
})
var snapProcessTimeout = setTimeout(function(){
var pid = snapProcess.pid
if(s.isWin){
spawn("taskkill", ["/pid", pid, '/t'])
}else{
process.kill(-pid, 'SIGTERM')
}
setTimeout(function(){
if(s.isWin === false){
treekill(pid)
}else{
snapProcess.kill()
}
processKill(snapProcess).then((response) => {
s.debugLog(`createSnapshot-snapProcessTimeout`,response)
completeRequest()
},10000)
},30000)
})
},5000)
})
}
const addCredentialsToStreamLink = (options) => {
@ -192,6 +198,7 @@ module.exports = (s,config,lang) => {
return {
cameraDestroy: cameraDestroy,
createSnapshot: createSnapshot,
processKill: processKill,
addCredentialsToStreamLink: addCredentialsToStreamLink,
monitorConfigurationMigrator: monitorConfigurationMigrator,
}

View File

@ -1,6 +1,16 @@
var fs = require("fs")
module.exports = function(s,config,lang){
require('./notifications/email.js')(s,config,lang)
require('./notifications/discordBot.js')(s,config,lang)
require('./notifications/telegram.js')(s,config,lang)
async function getSnapshot(d,monitorConfig){
d.screenshotBuffer = d.screenshotBuffer || d.frame
if(!d.screenshotBuffer || (monitorConfig.details.notify_useRawSnapshot === '1' && !d.usingRawSnapshotBuffer)){
d.usingRawSnapshotBuffer = true
const { screenShot, isStaticFile } = await s.getRawSnapshotFromMonitor(monitorConfig,{
secondsInward: monitorConfig.details.snap_seconds_inward
})
d.screenshotBuffer = screenShot
}
}
require('./notifications/email.js')(s,config,lang,getSnapshot)
require('./notifications/discordBot.js')(s,config,lang,getSnapshot)
require('./notifications/telegram.js')(s,config,lang,getSnapshot)
}

View File

@ -1,6 +1,6 @@
var fs = require("fs")
var Discord = require("discord.js")
module.exports = function(s,config,lang){
module.exports = function(s,config,lang,getSnapshot){
const {
getEventBasedRecordingUponCompletion,
} = require('../events/utils.js')(s,config,lang)
@ -81,7 +81,6 @@ module.exports = function(s,config,lang){
videoPath = siftedVideoFileFromRam.filePath
videoName = siftedVideoFileFromRam.filename
}
console.log(videoPath,videoName)
if(videoPath){
sendMessage({
author: {
@ -103,13 +102,7 @@ module.exports = function(s,config,lang){
],d.ke)
}
}
d.screenshotBuffer = d.screenshotBuffer || d.frame
if(!d.screenshotBuffer){
const { screenShot, isStaticFile } = await s.getRawSnapshotFromMonitor(monitorConfig,{
secondsInward: monitorConfig.details.snap_seconds_inward
})
d.screenshotBuffer = screenShot
}
await getSnapshot(d,monitorConfig)
if(d.screenshotBuffer){
sendMessage({
author: {

View File

@ -3,7 +3,7 @@ const {
template,
checkEmail,
} = require("./emailUtils.js")
module.exports = function(s,config,lang){
module.exports = function(s,config,lang,getSnapshot){
const {
getEventBasedRecordingUponCompletion,
} = require('../events/utils.js')(s,config,lang)
@ -193,13 +193,7 @@ module.exports = function(s,config,lang){
})
}
}
d.screenshotBuffer = d.screenshotBuffer || d.frame
if(!d.screenshotBuffer){
const {screenShot, isStaticFile} = await s.getRawSnapshotFromMonitor(monitorConfig,{
secondsInward: monitorConfig.details.snap_seconds_inward
})
if(screenShot)d.screenshotBuffer = screenShot
}
await getSnapshot(d,monitorConfig)
sendMail([
{
filename: d.screenshotName + '.jpg',

View File

@ -1,5 +1,5 @@
var fs = require("fs")
module.exports = function(s,config,lang){
module.exports = function(s,config,lang,getSnapshot){
const {
getEventBasedRecordingUponCompletion,
} = require('../events/utils.js')(s,config,lang)
@ -87,13 +87,7 @@ module.exports = function(s,config,lang){
],d.ke)
}
}
d.screenshotBuffer = d.screenshotBuffer || d.frame
if(!d.screenshotBuffer){
const { screenShot, isStaticFile } = await s.getRawSnapshotFromMonitor(monitorConfig,{
secondsInward: monitorConfig.details.snap_seconds_inward
})
d.screenshotBuffer = screenShot
}
await getSnapshot(d,monitorConfig)
if(d.screenshotBuffer){
sendMessage({
title: lang.Event+' - '+d.screenshotName,

View File

@ -4,14 +4,14 @@ module.exports = (config) => {
var currentlyUpdating = false
return {
getSystemInfo: (s) => {
return {
const response = {
"Time Started": s.timeStarted,
"Time Ready": s.timeReady,
Versions: {
"Shinobi": s.currentVersion,
"Node.js": process.version,
"FFmpeg": s.ffmpegVersion,
"isActivated": config.userHasSubscribed,
"isActivated": config.userHasSubscribed
},
Machine: {
"CPU Core Count": s.coreCount,
@ -19,6 +19,8 @@ module.exports = (config) => {
"Operating System Platform": s.platform,
},
}
if(s.expiryDate)response.Versions["License Expires On"] = s.expiryDate
return response
},
getConfiguration: () => {
return new Promise((resolve,reject) => {
@ -32,11 +34,7 @@ module.exports = (config) => {
try{
if(config.thisIsDocker){
const dockerConfigFile = '/config/conf.json'
fs.stat(dockerConfigFile,(err) => {
if(!err){
fs.writeFile(dockerConfigFile,JSON.stringify(postBody,null,3),function(){})
}
})
fs.writeFileSync(dockerConfigFile,JSON.stringify(postBody,null,3))
}
}catch(err){
console.log(err)

View File

@ -176,8 +176,42 @@ module.exports = (s,config,lang) => {
finish()
}
}
function cutVideoLength(options){
return new Promise((resolve,reject) => {
const response = {ok: false}
const inputFilePath = options.filePath
const monitorId = options.mid
const groupKey = options.ke
const cutLength = options.cutLength || 10
const tempDirectory = s.getStreamsDirectory(options)
let fileExt = inputFilePath.split('.')
fileExt = fileExt[fileExt.length -1]
const filename = `${s.gid(10)}.${fileExt}`
const videoOutPath = `${tempDirectory}`
const cuttingProcess = spawn(config.ffmpegDir,['-loglevel','warning','-i', inputFilePath, '-c','copy','-t',`${cutLength}`,videoOutPath])
cuttingProcess.stderr.on('data',(data) => {
const err = data.toString()
s.debugLog('cutVideoLength',options,err)
})
cuttingProcess.on('close',(data) => {
fs.stat(videoOutPath,(err) => {
if(!err){
response.filename = filename
response.filePath = videoOutPath
setTimeout(() => {
s.file('delete',videoOutPath)
},1000 * 60 * 3)
}else{
s.debugLog('cutVideoLength:readFile',options,err)
}
resolve(response)
})
})
})
}
return {
orphanedVideoCheck: orphanedVideoCheck,
scanForOrphanedVideos: scanForOrphanedVideos,
cutVideoLength: cutVideoLength,
}
}

View File

@ -1482,7 +1482,7 @@ module.exports = function(s,config,lang,app,io){
let video = req.files.video;
var time = new Date(parseInt(video.name.split('.')[0]))
var filename = s.formattedTime(time) + '.' + monitor.ext
video.mv(s.getVideoDirectory(monitor) + filename,function(){
video.mv(s.getVideoDirectory(monitor) + filename,function(){
s.insertCompletedVideo(monitor,{
file: filename,
events: s.group[groupKey].activeMonitors[monitorId].detector_motion_count,

View File

@ -254,11 +254,7 @@ module.exports = function(s,config,lang,app){
try{
if(config.thisIsDocker){
const dockerSuperFile = '/config/super.json'
fs.stat(dockerSuperFile,(err) => {
if(!err){
fs.writeFile(dockerSuperFile,JSON.stringify(currentSuperUserList,null,3),function(){})
}
})
fs.writeFileSync(dockerSuperFile,JSON.stringify(currentSuperUserList,null,3))
}
}catch(err){
console.log(err)

4
plugins/platerecognizer/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
conf.json
dist
models
node_modules

View File

@ -0,0 +1,19 @@
#!/bin/bash
DIR=$(dirname $0)
echo "Removing existing Node.js modules..."
rm -rf $DIR/node_modules
nonInteractiveFlag=false
if [ ! -e "$DIR/conf.json" ]; then
dontCreateKeyFlag=false
echo "Creating conf.json"
sudo cp $DIR/conf.sample.json $DIR/conf.json
else
echo "conf.json already exists..."
fi
if [ "$dontCreateKeyFlag" = false ]; then
echo "Adding Random Plugin Key to Main Configuration"
node $DIR/../../tools/modifyConfigurationForPlugin.js platerecognizer key=$(head -c 64 < /dev/urandom | sha256sum | awk '{print substr($1,1,60)}')
fi

View File

@ -0,0 +1,78 @@
# PlateRecognizer
> PlateRecognizer is a cloud-based service. You must sign up at http://platerecognizer.com to use this plugin. **In your purchase notes to http://platerecognizer.com be sure to indicate it will be used with Shinobi.**
1. Go to the Shinobi directory. **/home/Shinobi** is the default directory.
```
cd /home/Shinobi/plugins/platerecognizer
```
2. Install
```
sh INSTALL.sh
```
3. Start the plugin.
```
pm2 start shinobi-platerecognizer.js
```
4. Save to startup list. **OPTIONAL**
```
pm2 save
```
Doing this will reveal options in the monitor configuration. Shinobi does not need to be restarted when a plugin is initiated or stopped.
## 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.
Edit your plugins configuration file. Set the `hostPort` **to be different** than the `listening port for camera.js`.
```
nano conf.json
```
Here is a sample of a Host configuration for the plugin.
- `platerecognizerApiKey` is your API Key given by http://platerecognizer.com.
- `plug` is the name of the plugin corresponding in the main configuration file.
- `https` choose if you want to use SSL or not. Default is `false`.
- `hostPort` can be any available port number. **Don't make this the same port number as Shinobi.** Default is `8082`.
- `type` tells the main application (Shinobi) what kind of plugin it is. In this case it is a detector.
```
{
"plug":"PlateRecognizer",
"platerecognizerApiKey": "11111111111111111",
"hostPort":8082,
"key":"1234567890",
"mode":"host",
"type":"detector"
}
```
Now modify the **main configuration file** located in the main directory of Shinobi.
```
nano conf.json
```
Add the `plugins` array if you don't already have it. Add the following *object inside the array*.
```
"plugins":[
{
"id" : "PlateRecognizer",
"https" : false,
"host" : "localhost",
"port" : 8082,
"key" : "1234567890",
"mode" : "host",
"type" : "detector"
}
],
```

View File

@ -0,0 +1,10 @@
{
"plug": "PlateRecognizer",
"host": "localhost",
"platerecognizerApiKey": "11111111111111111",
"port": 8080,
"hostPort": 58084,
"key": "1234567890",
"mode": "client",
"type": "detector"
}

3737
plugins/platerecognizer/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,26 @@
{
"name": "shinobi-platerecognizer",
"author": "Moe Alam",
"version": "1.0.0",
"description": "Object Detection plugin for DeepStack",
"main": "shinobi-platerecognizer.js",
"dependencies": {
"request": "^2.88.0",
"express": "^4.16.2",
"moment": "^2.19.2",
"socket.io": "^2.3.0",
"socket.io-client": "^2.3.0"
},
"devDependencies": {},
"bin": "shinobi-platerecognizer.js",
"pkg": {
"targets": [
"node12"
],
"scripts": [
"../pluginBase.js"
],
"assets": []
},
"disabled": true
}

View File

@ -0,0 +1,192 @@
//
// Shinobi - Tensorflow Plugin
// Copyright (C) 2016-2025 Elad Bar, Moe Alam
//
// Base Init >>
const fs = require('fs');
const config = require('./conf.json')
const fetch = require('node-fetch');
const FormData = require('form-data');
var s
const {
workerData
} = require('worker_threads');
if(workerData && workerData.ok === true){
try{
s = require('../pluginWorkerBase.js')(__dirname,config)
}catch(err){
console.log(err)
try{
s = require('./pluginWorkerBase.js')(__dirname,config)
}catch(err){
console.log(err)
return console.log(config.plug,'WORKER : Plugin start has failed. pluginBase.js was not found.')
}
}
}else{
try{
s = require('../pluginBase.js')(__dirname,config)
}catch(err){
console.log(err)
try{
s = require('./pluginBase.js')(__dirname,config)
}catch(err){
console.log(err)
return console.log(config.plug,'Plugin start has failed. pluginBase.js was not found.')
}
}
try{
s = require('../pluginBase.js')(__dirname,config)
}catch(err){
console.log(err)
try{
const {
haltMessage,
checkStartTime,
setStartTime,
} = require('../pluginCheck.js')
if(!checkStartTime()){
console.log(haltMessage,new Date())
s.disconnectWebSocket()
return
}
setStartTime()
}catch(err){
console.log(`pluginCheck failed`)
}
}
}
// Base Init />>
const licensePlateRegion = config.licensePlateRegion || 'us'
const platerecognizerApiKey = config.platerecognizerApiKey || '111111111111111111'
if(!config.platerecognizerApiKey){
console.log('No Plate Recognizer API Key set.')
console.log('set conf.json value for `platerecognizerApiKey`')
return process.exit()
}
const baseUrl = config.platerecognizerEndpoint || "https://api.platerecognizer.com/v1/plate-reader/"
function platerecognizerRequest(d,frameBuffer){
return new Promise((resolve,reject) => {
try{
let body = new FormData();
frameBufferToPath(d,frameBuffer).then((filePath) => {
body.append('upload', fs.createReadStream(filePath));
// Or body.append('upload', base64Image);
body.append('regions', licensePlateRegion); // Change to your country
fetch(baseUrl, {
method: 'POST',
headers: {
"Authorization": `Token ${platerecognizerApiKey}`
},
body: body
}).then(res => res.json())
.then((json) => {
let predictions = []
try{
const response = json || {results: []}
predictions = response["results"] || []
}catch(err){
console.log(json)
console.log(err)
console.log(body)
}
resolve(predictions);
fs.unlink(filePath,function(){
})
})
.catch((err) => {
console.log(err);
});
})
}catch(err){
resolve([])
console.log(err)
}
})
}
function addVehicleMatrix(v,mats){
const label = v.vehicle["type"]
const confidence = v.vehicle["score"]
const y_min = v.vehicle["ymin"]
const x_min = v.vehicle["xmin"]
const y_max = v.vehicle["ymax"]
const x_max = v.vehicle["xmax"]
const vehicleWidth = x_max - x_min
const vehicleHeight = y_max - y_min
mats.push({
x: x_min,
y: y_min,
width: vehicleWidth,
height: vehicleHeight,
tag: label,
confidence: confidence,
})
}
function frameBufferToPath(d,buffer){
return new Promise((resolve,reject) => {
const tmpFile = s.gid(5)+'.jpg'
if(!fs.existsSync(s.dir.streams)){
fs.mkdirSync(s.dir.streams);
}
frameDirectory = s.dir.streams+d.ke+'/'+d.id+'/'
fs.writeFile(frameDirectory+tmpFile,buffer,function(err){
if(err) return s.systemLog(err);
try{
resolve(frameDirectory+tmpFile)
}catch(error){
console.error('Catch: ' + error);
}
})
})
}
s.detectObject = async function(frameBuffer,d,tx,frameLocation,callback){
const timeStart = new Date()
const predictions = await platerecognizerRequest(d,frameBuffer)
if(predictions.length > 0) {
const mats = []
predictions.forEach(function(v){
const label = v["plate"]
const confidence = v["score"]
const y_min = v.box["ymin"]
const x_min = v.box["xmin"]
const y_max = v.box["ymax"]
const x_max = v.box["xmax"]
const width = x_max - x_min
const height = y_max - y_min
mats.push({
x: x_min,
y: y_min,
width: width,
height: height,
tag: label,
confidence: confidence,
})
addVehicleMatrix(v,mats)
})
const isObjectDetectionSeparate = d.mon.detector_pam === '1' && d.mon.detector_use_detect_object === '1'
const width = parseFloat(isObjectDetectionSeparate && d.mon.detector_scale_y_object ? d.mon.detector_scale_y_object : d.mon.detector_scale_y)
const height = parseFloat(isObjectDetectionSeparate && d.mon.detector_scale_x_object ? d.mon.detector_scale_x_object : d.mon.detector_scale_x)
tx({
f:'trigger',
id:d.id,
ke:d.ke,
details:{
plug: config.plug,
name: `PlateRecognizer`,
reason: 'object',
matrices: mats,
imgHeight: width,
imgWidth: height,
},
frame: frameBuffer
})
}
callback()
}

View File

@ -0,0 +1,117 @@
<script src="https://cdn.shinobi.video/js/socket.io.js"></script>
<script src="https://cdn.shinobi.video/js/jquery.min.js"></script>
<!-- my messy GUI -->
<style>
body {position:relative;}
.shinobi_stream{position:absolute;width:100%;height:100%;top:0;left:0;}
iframe.stream-element{border:0;}
.stream-objects{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10}
.stream-objects .tag{position:absolute;top:5px;left:5px;background:#d9534f;color:#fff;font-family:monospace;font-size:80%;border-radius: 15px;padding:3px 5px;line-height: 1}
.stream-objects .stream-detected-object{position:absolute;top:0;left:0;border:3px dotted 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%}
</style>
<!-- Full Screen -->
<style>
body,html{overflow: hidden;height:100%}
*{margin:0;padding:0;border:0}
.stream-element,.shinobi_stream{position:absolute;top:0;left:0;height:100%}
.shinobi_stream video{object-fit: fill}
</style>
<!-- Full Screen /-->
<div class="shinobi_stream" id="stream_container">
<div class="stream-objects"></div>
<img class="stream-element">
</div>
<!-- the juice, i mean js -->
<script>
function getQueryString(){
var theObject = {}
location.search.substring(1).split('&').forEach(function(string){
var parts = string.split('=')
theObject[parts[0]] = parts[1]
})
return theObject
}
const bodyWidth = $('body').width()
const bodyHeight = $('body').height()
const queryStringValues = getQueryString()
let shinobiOrigin = queryStringValues.shinobiOrigin
const monitorId = queryStringValues.monitorId
const groupKey = queryStringValues.groupKey
const apiKey = queryStringValues.apiKey
let loadedWebsocketConnection = null
const streamObjectsContainer = $('.stream-objects')
if(shinobiOrigin.charAt(shinobiOrigin.length - 1) === '/'){
shinobiOrigin = shinobiOrigin.slice(0, -1)
}
function initMonitorStream(d){
$('#stream_container .stream-element').attr('src',`${shinobiOrigin}/${apiKey}/mjpeg/${groupKey}/${monitorId}`)
$(window).resize();
}
function drawMatrices(event){
var theContainer = streamObjectsContainer
var height = bodyHeight
var width = bodyWidth
var widthRatio = width / event.details.imgWidth
var heightRatio = height / event.details.imgHeight
var objectTagGroup = event.details.reason === 'motion' ? 'motion' : event.details.name
theContainer.find(`.stream-detected-object[name="${objectTagGroup}"]`).remove()
var html = ''
$.each(event.details.matrices,function(n,matrix){
html += `<div class="stream-detected-object" name="${objectTagGroup}" style="height:${heightRatio * matrix.height}px;width:${widthRatio * matrix.width}px;top:${heightRatio * matrix.y}px;left:${widthRatio * matrix.x}px;">`
if(matrix.tag)html += `<span class="tag">${matrix.tag}</span>`
html += '</div>'
})
theContainer.append(html)
}
$(document).ready(function(){
$.get(`${shinobiOrigin}/${apiKey}/monitor/${groupKey}/${monitorId}`,function(data){
if(
!loadedWebsocketConnection ||
loadedWebsocketConnection.connected === false
){
loadedWebsocketConnection = io(shinobiOrigin);
loadedWebsocketConnection.on('f',function (d){
switch(d.f){
case'monitor_watch_off':
case'monitor_watch_on':
initMonitorStream(d)
break;
case'detector_trigger':
if(d.id !== monitorId)return false;
if(
d.details.matrices &&
d.details.matrices.length > 0
){
drawMatrices(d)
}
console.log({
ke: groupKey,
mid: monitorId,
log: {
type: 'Event Occurred',
msg: d.details,
}
})
break;
}
})
}
loadedWebsocketConnection.emit('e',{
f: 'init',
auth: apiKey,
id: monitorId,
ke: groupKey
})
})
$(window).resize(function(){
$('.stream-element').attr('width',bodyWidth)
$('.stream-element').attr('height',bodyHeight)
})
})
</script>

View File

@ -59,11 +59,7 @@ processArgv.forEach(function(val) {
try{
if(config.thisIsDocker){
const dockerConfigFile = '/config/conf.json'
fs.stat(dockerConfigFile,(err) => {
if(!err){
fs.writeFile(dockerConfigFile,JSON.stringify(config,null,3),function(){})
}
})
fs.writeFileSync(dockerConfigFile,JSON.stringify(config,null,3))
}
}catch(err){
console.log(err)

View File

@ -1,10 +1,10 @@
<form id="hey-activate" class="card shadow mb-3">
<div class="card-header">
<%- lang['Not Activated'] %>
<%- config.hasSubcribed ? lang['Activated'] : lang['Not Activated'] %>
</div>
<div class="card-body" style="min-height:auto">
<div class="form-group">
<input name="subscriptionId" id="pass" tabindex="2" class="form-control wide-text" placeholder="License Key / Subscription ID">
<input name="subscriptionId" tabindex="2" class="form-control wide-text" placeholder="License Key / Subscription ID">
</div>
<div class="form-group mb-0">
<button class="btn btn-sm btn-round btn-block btn-success" type="submit"><%- lang.Save %></button>
@ -42,10 +42,11 @@
noticeTitle = lang.Activated
noticeText = lang.activatedText
noticeType = 'success'
heyActivateCard.remove()
heyActivateCard.find('[name="subscriptionId"]').val('')
}else{
heyActivateCardSubmit.html(lang.Save)
}
heyActivateCard.find('.card-header').html(noticeTitle)
new PNotify({
title: noticeTitle,
text: noticeText,

View File

@ -103,9 +103,7 @@
<div class="tab-pane active" id="accounts" role="tabpanel">
<div class="row">
<div class="col-md-5 text-left">
<% if(!config.userHasSubscribed){ %>
<% include blocks/heyActivate.ejs %>
<% } %>
<% include blocks/heyActivate.ejs %>
<div class="card shadow mb-3">
<pre class="super-system-info card-body mb-0">
@ -349,4 +347,7 @@ $('body')
<% customAutoLoad.superLibsJs.forEach(function(lib){ %>
<script src="<%-window.libURL%>libs/js/<%-lib%>"></script>
<% }) %>
<% customAutoLoad.superRawJs.forEach(function(scriptData){ %>
<script><%- scriptData %></script>
<% }) %>
</html>