diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 00000000..599b3e2a --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,93 @@ +# This file is a template, and might need editing before it works on your project. +# To contribute improvements to CI/CD templates, please follow the Development guide at: +# https://docs.gitlab.com/ee/development/cicd/templates.html +# This specific template is located at: +# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Docker.gitlab-ci.yml + +# Build a Docker image with CI/CD and push to the GitLab registry. +# Docker-in-Docker documentation: https://docs.gitlab.com/ee/ci/docker/using_docker_build.html +# +# This template uses one generic job with conditional builds +# for the default branch and all other (MR) branches. + +docker-latest-build: + # Use the official docker image. + image: docker:latest + stage: build + services: + - docker:dind + before_script: + - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY + # Default branch leaves tag empty (= latest tag) + # All other branches are tagged with the escaped branch name (commit ref slug) + script: + - | + if [[ "$CI_COMMIT_BRANCH" == "$CI_DEFAULT_BRANCH" ]]; then + tag="" + echo "Running on default branch '$CI_DEFAULT_BRANCH': tag = 'latest'" + else + tag=":$CI_COMMIT_REF_SLUG" + echo "Running on branch '$CI_COMMIT_BRANCH': tag = $tag" + fi + - docker build --pull -t "$CI_REGISTRY_IMAGE${tag}" . + - docker push "$CI_REGISTRY_IMAGE${tag}" + # Run this job in a branch where a Dockerfile exists + rules: + - if: $CI_COMMIT_BRANCH + exists: + - Dockerfile + +docker-arm32v7-build: + # Use the official docker image. + image: docker:latest + stage: build + services: + - docker:dind + before_script: + - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY + # Default branch leaves tag empty (= latest tag) + # All other branches are tagged with the escaped branch name (commit ref slug) + script: + - | + if [[ "$CI_COMMIT_BRANCH" == "$CI_DEFAULT_BRANCH" ]]; then + tag=":arm32v7" + echo "Running on default branch '$CI_DEFAULT_BRANCH': tag = 'arm32v7'" + else + tag=":arm32v7-$CI_COMMIT_REF_SLUG" + echo "Running on branch '$CI_COMMIT_BRANCH': tag = $tag" + fi + - docker build --pull -t "$CI_REGISTRY_IMAGE${tag}" . -f Dockerfile.nvidia + - docker push "$CI_REGISTRY_IMAGE${tag}" + # Run this job in a branch where a Dockerfile exists + rules: + - if: $CI_COMMIT_BRANCH + exists: + - Dockerfile.arm32v7 + + +docker-nvidia-build: + # Use the official docker image. + image: docker:latest + stage: build + services: + - docker:dind + before_script: + - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY + # Default branch leaves tag empty (= latest tag) + # All other branches are tagged with the escaped branch name (commit ref slug) + script: + - | + if [[ "$CI_COMMIT_BRANCH" == "$CI_DEFAULT_BRANCH" ]]; then + tag=":nvidia" + echo "Running on default branch '$CI_DEFAULT_BRANCH': tag = 'nvidia'" + else + tag=":nvidia-$CI_COMMIT_REF_SLUG" + echo "Running on branch '$CI_COMMIT_BRANCH': tag = $tag" + fi + - docker build --pull -t "$CI_REGISTRY_IMAGE${tag}" . -f Dockerfile.nvidia + - docker push "$CI_REGISTRY_IMAGE${tag}" + # Run this job in a branch where a Dockerfile exists + rules: + - if: $CI_COMMIT_BRANCH + exists: + - Dockerfile.nvidia diff --git a/Docker/init.sh b/Docker/init.sh index 31a18864..64b6c1db 100644 --- a/Docker/init.sh +++ b/Docker/init.sh @@ -69,7 +69,7 @@ if [ "$DB_DISABLE_INCLUDED" = "false" ]; then else echo "Create database schema if it does not exists ..." - mysql -u "$DB_USER" -h "$DB_HOST" -p"$DB_PASSWORD" --port="$DB_PORT" --database="$DB_DATABASE" -e "source /home/Shinobi/sql/framework.sql" || true + mysql -u "$DB_USER" -h "$DB_HOST" -p"$DB_PASSWORD" --port="$DB_PORT" --database="$DB_DATABASE" -e "source /home/Shinobi/sql/framework1.sql" || true fi DATABASE_CONFIG='{"host": "'$DB_HOST'","user": "'$DB_USER'","password": "'$DB_PASSWORD'","database": "'$DB_DATABASE'","port":'$DB_PORT'}' diff --git a/Dockerfile b/Dockerfile index 853f97ed..e0b40bde 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:12.21.0-buster-slim +FROM node:16.13-buster-slim ENV DB_USER=majesticflame \ DB_PASSWORD='' \ @@ -100,12 +100,12 @@ RUN sed -i -e 's/\r//g' /home/Shinobi/Docker/init.sh VOLUME ["/home/Shinobi/videos"] VOLUME ["/home/Shinobi/plugins"] +VOLUME ["/home/Shinobi/libs/customAutoLoad"] VOLUME ["/config"] -VOLUME ["/customAutoLoad"] VOLUME ["/var/lib/mysql"] EXPOSE 8080 443 21 25 ENTRYPOINT ["sh","/home/Shinobi/Docker/init.sh"] -CMD [ "pm2-docker", "pm2.yml" ] +CMD [ "pm2-docker", "/home/Shinobi/Docker/pm2.yml" ] diff --git a/Dockerfile.arm32v7 b/Dockerfile.arm32v7 index e7bd2698..c85896f6 100644 --- a/Dockerfile.arm32v7 +++ b/Dockerfile.arm32v7 @@ -98,8 +98,8 @@ RUN chmod -f +x /home/Shinobi/Docker/init.sh VOLUME ["/home/Shinobi/videos"] VOLUME ["/home/Shinobi/plugins"] +VOLUME ["/home/Shinobi/libs/customAutoLoad"] VOLUME ["/config"] -VOLUME ["/customAutoLoad"] VOLUME ["/var/lib/mysql"] EXPOSE 8080 diff --git a/Dockerfile.nvidia b/Dockerfile.nvidia new file mode 100644 index 00000000..10ac76ef --- /dev/null +++ b/Dockerfile.nvidia @@ -0,0 +1,118 @@ +FROM nvidia/cuda:11.4.2-cudnn8-runtime-ubuntu20.04 + +ENV DB_USER=majesticflame \ + DB_PASSWORD='' \ + DB_HOST='localhost' \ + DB_DATABASE=ccio \ + DB_PORT=3306 \ + SUBSCRIPTION_ID=sub_XXXXXXXXXXXX \ + PLUGIN_KEYS='{}' \ + SSL_ENABLED='false' \ + SSL_COUNTRY='CA' \ + SSL_STATE='BC' \ + SSL_LOCATION='Vancouver' \ + SSL_ORGANIZATION='Shinobi Systems' \ + SSL_ORGANIZATION_UNIT='IT Department' \ + SSL_COMMON_NAME='nvr.ninja' \ + DB_DISABLE_INCLUDED=false +ARG DEBIAN_FRONTEND=noninteractive + +RUN mkdir -p /home/Shinobi /config /var/lib/mysql + +RUN apt update -y +RUN apt install wget curl net-tools -y + +# Additional packages to support NodeJS as the base build is NVidia CUDA +RUN curl -sL https://deb.nodesource.com/setup_16.x | bash - +RUN apt install nodejs -y + +RUN node -v +RUN npm -v + +# Install MariaDB server... the debian way +RUN if [ "$DB_DISABLE_INCLUDED" = "false" ] ; then set -ex; \ + { \ + echo "mariadb-server" mysql-server/root_password password '${DB_ROOT_PASSWORD}'; \ + echo "mariadb-server" mysql-server/root_password_again password '${DB_ROOT_PASSWORD}'; \ + } | debconf-set-selections; \ + apt-get update; \ + apt-get install -y \ + "mariadb-server" \ + socat \ + ; \ + find /etc/mysql/ -name '*.cnf' -print0 \ + | xargs -0 grep -lZE '^(bind-address|log)' \ + | xargs -rt -0 sed -Ei 's/^(bind-address|log)/#&/'; fi + +RUN if [ "$DB_DISABLE_INCLUDED" = "false" ] ; then sed -ie "s/^bind-address\s*=\s*127\.0\.0\.1$/#bind-address = 0.0.0.0/" /etc/mysql/my.cnf; fi + +# Install FFmpeg + +RUN apt update --fix-missing +RUN apt install -y software-properties-common \ + libfreetype6-dev \ + libgnutls28-dev \ + libmp3lame-dev \ + libass-dev \ + libogg-dev \ + libtheora-dev \ + libvorbis-dev \ + libvpx-dev \ + libwebp-dev \ + libssh2-1-dev \ + libopus-dev \ + librtmp-dev \ + libx264-dev \ + libx265-dev \ + yasm +RUN apt install -y \ + build-essential \ + bzip2 \ + coreutils \ + procps \ + gnutls-bin \ + nasm \ + tar \ + x264 + +RUN apt install -y \ + ffmpeg \ + git \ + make \ + g++ \ + gcc \ + pkg-config \ + python3 \ + wget \ + tar \ + sudo \ + xz-utils + + +WORKDIR /home/Shinobi +COPY . . +#RUN rm -rf /home/Shinobi/plugins +COPY ./plugins /home/Shinobi/plugins +RUN chmod -R 777 /home/Shinobi/plugins +RUN npm i npm@latest -g && \ + npm install --unsafe-perm && \ + npm install pm2 -g +COPY ./Docker/pm2.yml ./ + +# Copy default configuration files +# COPY ./config/conf.json ./config/super.json /home/Shinobi/ +RUN chmod -f +x /home/Shinobi/Docker/init.sh +RUN sed -i -e 's/\r//g' /home/Shinobi/Docker/init.sh +# RUN chmod -f +x /home/Shinobi/shinobi + +VOLUME ["/home/Shinobi/videos"] +VOLUME ["/home/Shinobi/plugins"] +VOLUME ["/home/Shinobi/libs/customAutoLoad"] +VOLUME ["/config"] +VOLUME ["/var/lib/mysql"] + +EXPOSE 8080 443 21 25 + +ENTRYPOINT ["sh","/home/Shinobi/Docker/init.sh"] + +CMD [ "pm2-docker", "/home/Shinobi/Docker/pm2.yml" ] diff --git a/languages/pt_BR.json b/languages/pt_BR.json index 34b64f42..14367af0 100644 --- a/languages/pt_BR.json +++ b/languages/pt_BR.json @@ -51,7 +51,7 @@ "Enable Nightvision": "Ativar Visão Noturna", "Disable Nightvision": "Desativar Visão Noturna", "Current": "Atual", - "Monitors": "Monitors", + "Monitors": "Monitores", "Video": "Video", "Videos": "Videos", "Events": "Eventos", @@ -122,12 +122,12 @@ "Execute Command": "Executar Comando", "for Global Access": "para Acesso Global", "Help": "Ajuda", - "Don't show this anymore": "Não mostre isso mais", + "Don't show this anymore": "Não me mostre isso mais", "Chat on Discord": "Conversar no Discord", "Documentation": "Documentação", "All Monitors": "Todos Monitores", - "Motion Meter": "Medidos de Movimento", - "FFmpegTip": "FFprobe is a simple multimedia streams analyzer. You can use it to output all kinds of information about an input including duration, frame rate, frame size, etc.", + "Motion Meter": "Medição de movimentos", + "FFmpegTip": "FFprobe é um simples analisador de streams. Você pode usá-lo para extrar todos os tipos de informação sobre uma entrada incluindo duração, taxa de frames por segundo, tamanho do frame, etc;", "Complete Stream URL": "URL de transmissão completa", "ONVIF Scanner": "ONVIF Scanner", "Scan Settings": "Configurações de digitalização", @@ -162,7 +162,7 @@ "Browser Console Log": "Navegador de logs", "All Monitors and Privileges": "Todos monitores e privilégios", "Permissions": "Permissões", - "Time-lapse Tool": "Ferramenta Time-laps", + "Time-lapse Tool": "Time-lapse", "total": "total", "MB": "MB", "Calendar": "Calendário", @@ -187,11 +187,11 @@ "Add New": "Adicionar novo", "Delete Selected Videos": "Excluir vídeos selecionados", "DeleteSelectedVideosMsg": "Deseja excluir esses vídeos? Você não poderá recuperá-los.", - "clientStreamFailedattemptingReconnect": "A verificação ctream do lado do cliente falhou, tentando reconectar.", + "clientStreamFailedattemptingReconnect": "A verificação do stream falhou, tentando se reconectar.", "Delete Filter": "Excluir filtro", "confirmDeleteFilter": "Deseja excluir este filtro? Você não poderá recuperá-lo.", "Fix Video": "Corrigir Vídeo", - "FixVideoMsg": "Você deseja corrigir esse vídeo? Você não poderá desfazer essa ação..", + "FixVideoMsg": "Você deseja corrigir o vídeo? Você não poderá desfazer essa ação..", "DeleteVideoMsg": "Deseja excluir este vídeo? Você não poderá recuperá-lo.", "dropBoxSuccess": "Sucesso! Arquivos salvos em seu Dropbox.", "API Key Deleted": "Chave da API excluída", @@ -246,13 +246,13 @@ "Connected": "Conectado", "Not Connected": "Não conectado", "Lisence Plate Detector": "Detector de placas", - "OpenCV Cascades": "OpenCV Cascatas", + "OpenCV Cascades": "Cascatas OpenCV ", "Refresh List of Cascades": "Atualizar Lista de Cascatas", "\"No Motion\" Detector": "\"Sem movimento\" Detector", "Control": "Controle", "Grouping": "Agrupando   Adicione grupos em Configurações", "Logging": "Logging", - "IdentityText1": "É assim que o sistema irá identificar os dados para este fluxo. Você não pode alterar o ID do Monitor uma vez que você pressionou salvar. Se você quiser, você pode fazer o ID do Monitor mais legível para humanos antes de continuar.", + "IdentityText1": "É assim que o sistema irá identificar os dados para este fluxo. Você não pode alterar o ID do Monitor uma vez que você o salvou. Se você quiser, você pode fazer o ID do Monitor mais legível para humanos antes de continuar.", "IdentityText2": "Você pode duplicar um monitor modificando o ID do Monitor e depois pressionando salvar. Você não pode usar o ID de um monitor que já existe ou ele economizará sobre as informações do banco de dados desse monitor.", "noSpecialCharacters": "Sem espaços ou caracteres especiais.", "NotesPlacholder": "Comentários que você quer deixar para as configurações desta câmera.", @@ -273,7 +273,7 @@ "Path": "Caminho", "Monitor Capture Rate": "Taxa de captura do monitor (FPS)", "Analyzation Duration": "Duração da análise", - "Probe Size": "Probe Size", + "Probe Size": "Tamanho da sonda", "Stream Type": "Tipo de transmissão", "# of Allow MJPEG Clients": "# para permitir clientes MJPEG 0 para infinito", "HLS Video Encoder": "Codificador de vídeo HLS", @@ -281,9 +281,9 @@ "HLS Segment Length": "Comprimento do segmento HLS em segundos", "HLS Preset": "Pré-definição HLS", "HLS List Size": "Tamanho da lista HLS", - "Check Signal Interval": "Verifique o intervalo do sinal em minutos", + "Check Signal Interval": "Verificar o intervalo do sinal em minutos", "Log Signal Event": "Evento de sinal de registro Apenas cliente", - "Quality": "Qualidade 1 para alta, 23 para Low", + "Quality": "Qualidade 1 para alta, 23 para baixa", "Rate": "Taxa (FPS)", "Width": "Largura", "Height": "Altura", @@ -444,13 +444,13 @@ "Process Unexpected Exit": "Saída inesperado do processo", "Process Crashed for Monitor": "Processo de Monitor quebrado", "FFmpegCantStart": "FFmpeg não pôde iniciar", - "FFmpegCantStartText": "O mecanismo de gravação para esta câmera não pôde começar. Pode haver algo errado com a configuração da sua câmera. Se houver algum registro diferente deste, por favor, coloque-os em Problemas no Github.", + "FFmpegCantStartText": "O FFMpeg não pode inicializar. Pode haver algo de errado com a configuração da sua câmera. Se houver alguma configuração diferente, por favor, reporte no Gitlab.", "JPEG Error": "Erro JPEG", "JPEGErrorText": "Houve um problema ao obter dados da sua câmera.", "Fatal Maximum Reached": "Máximo atingido, parando câmera.", - "FatalMaximumReachedText": "Erro JPEG fatal.", + "FatalMaximumReachedText": "Erro fatal de JPEG", "Incorrect Settings Chosen": "Configuração incorreta escolhida", - "Can't Connect": "Não pode conectar", + "Can't Connect": "Não foi possível conectar", "Video Finished": "Vídeo finalizado", "No Monitor Found, Ignoring Request": "Monitor não encontrado, ignorando requisição", "Event": "Evento", diff --git a/libs/cameraThread/libs/detectorUtils.js b/libs/cameraThread/libs/detectorUtils.js index 5bcae2e4..ed7f2764 100644 --- a/libs/cameraThread/libs/detectorUtils.js +++ b/libs/cameraThread/libs/detectorUtils.js @@ -1,6 +1,7 @@ const P2P = require('pipe2pam') -const PamDiff = require('pam-diff') -module.exports = function(jsonData,pamDiffResponder){ +let PamDiff = require('pam-diff') +module.exports = function(jsonData,pamDiffResponder,alternatePamDiff){ + if(alternatePamDiff)PamDiff = alternatePamDiff; const noiseFilterArray = {}; const config = jsonData.globalInfo.config const completeMonitorConfig = jsonData.rawMonitorConfig @@ -390,5 +391,6 @@ module.exports = function(jsonData,pamDiffResponder){ regionConfidenceMaximums, regionTriggerThresholds, mergeTriggers, + pamDiff, } } diff --git a/libs/childNode.js b/libs/childNode.js index e7207d65..eaf3ba4a 100644 --- a/libs/childNode.js +++ b/libs/childNode.js @@ -38,7 +38,7 @@ module.exports = function(s,config,lang,app,io){ ipAddress = cn.request.connection.remoteAddress.replace('::ffff:','')+':'+d.port cn.ip = ipAddress cn.shinobi_child = 1 - tx = function(z){ + cn.tx = function(z){ cn.emit('c',z) } if(!s.childNodes[cn.ip]){ @@ -52,7 +52,7 @@ module.exports = function(s,config,lang,app,io){ d.availableHWAccels.forEach(function(accel){ if(config.availableHWAccels.indexOf(accel) === -1)config.availableHWAccels.push(accel) }) - tx({ + cn.tx({ f : 'init_success', childNodes : s.childNodes }) @@ -103,7 +103,7 @@ module.exports = function(s,config,lang,app,io){ return console.log('FILE NOT EXIST') } s.group[d.ke].activeMonitors[d.mid].childNodeStreamWriters[d.filename].end() - tx({ + cn.tx({ f: 'deleteTimelapseFrame', file: d.filename, currentDate: d.currentDate, @@ -118,6 +118,9 @@ module.exports = function(s,config,lang,app,io){ case'created_file_chunk': if(!s.group[d.ke].activeMonitors[d.mid].childNodeStreamWriters[d.filename]){ d.dir = s.getVideoDirectory(s.group[d.ke].rawMonitorConfigurations[d.mid]) + if (!fs.existsSync(d.dir)) { + fs.mkdirSync(d.dir, {recursive: true}, (err) => {s.debugLog(err)}) + } s.group[d.ke].activeMonitors[d.mid].childNodeStreamWriters[d.filename] = fs.createWriteStream(d.dir+d.filename) } s.group[d.ke].activeMonitors[d.mid].childNodeStreamWriters[d.filename].write(d.chunk) @@ -127,7 +130,7 @@ module.exports = function(s,config,lang,app,io){ return console.log('FILE NOT EXIST') } s.group[d.ke].activeMonitors[d.mid].childNodeStreamWriters[d.filename].end(); - tx({ + cn.tx({ f:'delete', file:d.filename, ke:d.ke, diff --git a/libs/commander.js b/libs/commander.js index 7ed6d364..96f8b519 100644 --- a/libs/commander.js +++ b/libs/commander.js @@ -1,85 +1,76 @@ const { Worker } = require('worker_threads'); module.exports = function(s,config,lang,app){ + const fetch = require('node-fetch') const { modifyConfiguration, getConfiguration } = require('./system/utils.js')(config) + let customerServerList = !!config.p2pServerList; var runningWorker; config.machineId = config.p2pApiKey + '' + config.p2pGroupId config.p2pTargetAuth = config.p2pTargetAuth || s.gid(30) - if(!config.p2pServerList)config.p2pServerList = { - "vancouver-1": { - name: 'Vancouver-1', - host: 'p2p-vancouver-1.shinobi.cloud', - p2pPort: '8084', - webPort: '8000', - maxNetworkSpeed: { - up: 5000, - down: 5000, - shared: true + if(!config.workerStreamOutHandlers){ + config.workerStreamOutHandlers = [ + 'Base64', + 'FLV', + 'MP4', + ]; + } + if(!customerServerList){ + config.p2pServerList = { + "vancouver-1": { + name: 'Vancouver-1', + host: 'p2p-vancouver-1.shinobi.cloud', + p2pPort: '8084', + webPort: '8000', + maxNetworkSpeed: { + up: 5000, + down: 5000, + shared: true + }, + location: { + lat: 49.284966, + lon: -123.1140607 + } }, - location: { - lat: 49.284966, - lon: -123.1140607 - } - }, - "vancouver-2": { - name: 'Vancouver-2', - host: 'p2p-vancouver-2.shinobi.cloud', - p2pPort: '8084', - webPort: '8000', - maxNetworkSpeed: { - up: 400, - down: 1000, - shared: true + "toronto-1": { + name: 'Toronto-1', + host: 'p2p-toronto-1.shinobi.cloud', + p2pPort: '8084', + webPort: '8000', + maxNetworkSpeed: { + up: 5000, + down: 5000, + shared: true + }, + location: { + lat: 43.644773, + lon: -79.3862837 + } }, - location: { - lat: 49.284966, - lon: -123.1140607 + "paris-1": { + name: 'Paris-1', + host: 'p2p-paris-1.shinobi.cloud', + p2pPort: '8084', + webPort: '8000', + maxNetworkSpeed: { + up: 200, + down: 200, + shared: true + }, + location: { + lat: 48.873877, + lon: 2.295533 + } } - }, - "toronto-1": { - name: 'Toronto-1', - host: 'p2p-toronto-1.shinobi.cloud', - p2pPort: '8084', - webPort: '8000', - maxNetworkSpeed: { - up: 5000, - down: 5000, - shared: true - }, - location: { - lat: 43.644773, - lon: -79.3862837 - } - }, - "paris-1": { - name: 'Paris-1', - host: 'p2p-paris-1.shinobi.cloud', - p2pPort: '8084', - webPort: '8000', - maxNetworkSpeed: { - up: 200, - down: 200, - shared: true - }, - location: { - lat: 48.873877, - lon: 2.295533 - } - }, - "amsterdam-1": { - name: 'Amsterdam-1', - host: 'p2p-amsterdam-1.shinobi.cloud', - p2pPort: '8084', - webPort: '8000', - maxNetworkSpeed: { - up: 500, - down: 500, - shared: true - }, - location: { - lat: 52.348773, - lon: 4.8846043 - } - }, + } + // get latest + fetch('https://cdn.shinobi.video/configs/p2pServers.js') + .then(res => res.text()) + .then((text) => { + try{ + eval(`config.p2pServerList = ` + text) + }catch(err){ + s.debugLog(err) + } + }); } if(!config.p2pHostSelected)config.p2pHostSelected = 'paris-1' const stopWorker = () => { diff --git a/libs/control/onvif.js b/libs/control/onvif.js index 745a1f3e..77703fe4 100644 --- a/libs/control/onvif.js +++ b/libs/control/onvif.js @@ -2,6 +2,10 @@ var os = require('os'); var exec = require('child_process').exec; const onvif = require("shinobi-onvif"); module.exports = function(s,config,lang,app,io){ + const { + createSnapshot, + addCredentialsToStreamLink, + } = require('../monitor/utils.js')(s,config,lang) const createOnvifDevice = async (onvifAuth) => { var response = {ok: false} const monitorConfig = s.group[onvifAuth.ke].rawMonitorConfigurations[onvifAuth.id] @@ -118,6 +122,16 @@ module.exports = function(s,config,lang,app,io){ doAction(s.group[onvifAuth.ke].activeMonitors[onvifAuth.id].onvifConnection) } } + async function getSnapshotFromOnvif(onvifOptions){ + return await createSnapshot({ + output: ['-s 400x400'], + url: addCredentialsToStreamLink({ + username: onvifOptions.username, + password: onvifOptions.password, + url: onvifOptions.uri + }), + }) + } /** * API : ONVIF Method Controller */ @@ -140,6 +154,7 @@ module.exports = function(s,config,lang,app,io){ }) },res,req); }) + s.getSnapshotFromOnvif = getSnapshotFromOnvif s.createOnvifDevice = createOnvifDevice s.runOnvifMethod = runOnvifMethod } diff --git a/libs/monitor.js b/libs/monitor.js index c8a476cd..0d57dbb4 100644 --- a/libs/monitor.js +++ b/libs/monitor.js @@ -186,6 +186,11 @@ module.exports = function(s,config,lang){ var temporaryImageFile = streamDir + s.gid(5) + '.jpg' var iconImageFile = streamDir + 'icon.jpg' var ffmpegCmd = splitForFFPMEG(`-loglevel warning -re -probesize 100000 -analyzeduration 100000 ${inputOptions.join(' ')} -i "${url}" ${outputOptions.join(' ')} -f image2 -an -vf "fps=1" -vframes 1 "${temporaryImageFile}"`) + checkExists(streamDir, function(success) { + if (success === false) { + fs.mkdirSync(streamDir, {recursive: true}, (err) => {s.debugLog(err)}) + } + }) const snapProcess = new Worker(__dirname + '/cameraThread/snapshot.js', { workerData: { jsonData: { @@ -495,9 +500,12 @@ module.exports = function(s,config,lang){ s.checkDetails(e) if(e.ke && config.doSnapshot === true){ if(s.group[e.ke] && s.group[e.ke].rawMonitorConfigurations && s.group[e.ke].rawMonitorConfigurations[e.mid] && s.group[e.ke].rawMonitorConfigurations[e.mid].mode !== 'stop'){ - var pathDir = s.dir.streams+e.ke+'/'+e.mid+'/' - const {screenShot, isStaticFile} = await s.getRawSnapshotFromMonitor(s.group[e.ke].rawMonitorConfigurations[e.mid],options) - if(screenShot){ + if(s.group[e.ke].activeMonitors[e.mid].onvifConnection){ + const screenShot = await s.getSnapshotFromOnvif({ + username: onvifUsername, + password: onvifPassword, + uri: cameraResponse.uri, + }); s.tx({ f: 'monitor_snapshot', snapshot: screenShot.toString('base64'), @@ -506,9 +514,21 @@ module.exports = function(s,config,lang){ ke: e.ke },'GRP_'+e.ke) }else{ - s.debugLog('Damaged Snapshot Data') - s.tx({f:'monitor_snapshot',snapshot:e.mon.name,snapshot_format:'plc',mid:e.mid,ke:e.ke},'GRP_'+e.ke) - } + var pathDir = s.dir.streams+e.ke+'/'+e.mid+'/' + const {screenShot, isStaticFile} = await s.getRawSnapshotFromMonitor(s.group[e.ke].rawMonitorConfigurations[e.mid],options) + if(screenShot){ + s.tx({ + f: 'monitor_snapshot', + snapshot: screenShot.toString('base64'), + snapshot_format: 'b64', + mid: e.mid, + ke: e.ke + },'GRP_'+e.ke) + }else{ + s.debugLog('Damaged Snapshot Data') + s.tx({f:'monitor_snapshot',snapshot:e.mon.name,snapshot_format:'plc',mid:e.mid,ke:e.ke},'GRP_'+e.ke) + } + } }else{ s.tx({f:'monitor_snapshot',snapshot:'Disabled',snapshot_format:'plc',mid:e.mid,ke:e.ke},'GRP_'+e.ke) } @@ -930,11 +950,15 @@ module.exports = function(s,config,lang){ console.log(err) } }) - if(e.details.detector_use_detect_object === '1'){ + if(e.details.detector_use_detect_object === '1' && e.details.detector_use_motion === '1' ){ s.group[e.ke].activeMonitors[e.id].spawn.stdio[4].on('data',function(data){ onDetectorJpegOutputSecondary(e,data) }) - } + }else{ + s.group[e.ke].activeMonitors[e.id].spawn.stdio[4].on('data',function(data){ + onDetectorJpegOutputAlone(e,data) + }) + } }else if(e.details.detector_use_detect_object === '1' && e.details.detector_send_frames !== '1'){ s.group[e.ke].activeMonitors[e.id].spawn.stdio[4].on('data',function(data){ onDetectorJpegOutputSecondary(e,data) diff --git a/libs/monitor/utils.js b/libs/monitor/utils.js index ea2b5698..cfcf8446 100644 --- a/libs/monitor/utils.js +++ b/libs/monitor/utils.js @@ -136,7 +136,7 @@ module.exports = (s,config,lang) => { }) } const temporaryImageFile = streamDir + s.gid(5) + '.jpg' - const ffmpegCmd = splitForFFPMEG(`-loglevel warning -re -probesize 100000 -analyzeduration 100000 ${inputOptions.join(' ')} -i "${url}" ${outputOptions.join(' ')} -f image2 -an -vf "fps=1" -vframes 1 "${temporaryImageFile}"`) + const ffmpegCmd = splitForFFPMEG(`-loglevel warning -re -stimeout 30000000 -probesize 100000 -analyzeduration 100000 ${inputOptions.join(' ')} -i "${url}" ${outputOptions.join(' ')} -f image2 -an -vf "fps=1" -vframes 1 "${temporaryImageFile}"`) const snapProcess = spawn('ffmpeg',ffmpegCmd,{detached: true}) snapProcess.stderr.on('data',function(data){ // s.debugLog(data.toString()) diff --git a/libs/scanners/utils.js b/libs/scanners/utils.js index 764ffb95..4cd4f8f2 100644 --- a/libs/scanners/utils.js +++ b/libs/scanners/utils.js @@ -4,10 +4,6 @@ const { stringContains, } = require('../common.js') module.exports = (s,config,lang) => { - const { - createSnapshot, - addCredentialsToStreamLink, - } = require('../monitor/utils.js')(s,config,lang) const ipRange = (start_ip, end_ip) => { var startLong = toLong(start_ip); var endLong = toLong(end_ip); @@ -148,13 +144,10 @@ module.exports = (s,config,lang) => { var imageSnap if(cameraResponse.uri){ try{ - imageSnap = (await createSnapshot({ - output: ['-s 400x400'], - url: addCredentialsToStreamLink({ - username: onvifUsername, - password: onvifPassword, - url: cameraResponse.uri - }), + imageSnap = (await s.getSnapshotFromOnvif({ + username: onvifUsername, + password: onvifPassword, + uri: cameraResponse.uri, })).toString('base64'); }catch(err){ s.debugLog(err) diff --git a/libs/uploaders/backblazeB2.js b/libs/uploaders/backblazeB2.js index 0ca101b5..9bc432d4 100644 --- a/libs/uploaders/backblazeB2.js +++ b/libs/uploaders/backblazeB2.js @@ -53,9 +53,9 @@ module.exports = function(s,config,lang){ applicationKey: userDetails.bb_b2_applicationKey }) b2.authorize().then(function(resp){ - s.group[e.ke].bb_b2_downloadUrl = resp.data.downloadUrl + s.group[e.ke].bb_b2_downloadUrl = resp.downloadUrl b2.listBuckets().then(function(resp){ - var buckets = resp.data.buckets + var buckets = resp.buckets var bucketN = -2 buckets.forEach(function(item,n){ if(item.bucketName === userDetails.bb_b2_bucket){ @@ -69,7 +69,7 @@ module.exports = function(s,config,lang){ userDetails.bb_b2_bucket, 'allPublic' ).then(function(resp){ - s.group[e.ke].bb_b2_bucketId = resp.data.bucketId + s.group[e.ke].bb_b2_bucketId = resp.bucketId }).catch(backblazeErr) } }).catch(backblazeErr) @@ -98,7 +98,7 @@ module.exports = function(s,config,lang){ fileId: videoDetails.fileId, fileName: videoDetails.fileName }).then(function(resp){ - // console.log('deleteFileVersion',resp.data) + // console.log('deleteFileVersion',resp) }).catch(function(err){ console.log('deleteFileVersion',err) }) @@ -117,7 +117,7 @@ module.exports = function(s,config,lang){ var backblazeSavePath = s.group[e.ke].init.bb_b2_dir+e.ke+'/'+e.mid+'/'+k.filename var getUploadUrl = function(bucketId,callback){ s.group[e.ke].bb_b2.getUploadUrl(bucketId).then(function(resp){ - callback(resp.data) + callback(resp) }).catch(backblazeErr) } getUploadUrl(s.group[e.ke].bb_b2_bucketId,function(req){ @@ -128,7 +128,7 @@ module.exports = function(s,config,lang){ data: data, onUploadProgress: null }).then(function(resp){ - if(s.group[e.ke].init.bb_b2_log === '1' && resp.data.fileId){ + if(s.group[e.ke].init.bb_b2_log === '1' && resp.fileId){ var backblazeDownloadUrl = s.group[e.ke].bb_b2_downloadUrl + '/file/' + s.group[e.ke].init.bb_b2_bucket + '/' + backblazeSavePath s.knexQuery({ action: "insert", @@ -140,9 +140,9 @@ module.exports = function(s,config,lang){ status: 1, details: s.s({ type : 'b2', - bucketId : resp.data.bucketId, - fileId : resp.data.fileId, - fileName : resp.data.fileName + bucketId : resp.bucketId, + fileId : resp.fileId, + fileName : resp.fileName }), size: k.filesize, end: k.endTime, diff --git a/libs/webServerAdminPaths.js b/libs/webServerAdminPaths.js index 18ebfb1f..55f78259 100644 --- a/libs/webServerAdminPaths.js +++ b/libs/webServerAdminPaths.js @@ -302,7 +302,13 @@ module.exports = function(s,config,lang,app){ } }else{ if(!user.details.sub || user.details.allmonitors === '1' || user.details.monitor_edit.indexOf(req.params.id) > -1 || hasRestrictions && user.details.monitor_create === '1'){ - s.userLog(s.group[req.params.ke].rawMonitorConfigurations[req.params.id],{type:'Monitor Deleted',msg:'by user : '+user.uid}); + s.userLog({ + ke: req.params.ke, + mid: req.params.id + },{ + type: 'Monitor Deleted', + msg: 'by user : '+user.uid + }); req.params.delete=1;s.camera('stop',req.params); s.tx({f:'monitor_delete',uid:user.uid,mid:req.params.id,ke:req.params.ke},'GRP_'+req.params.ke); s.knexQuery({ diff --git a/web/libs/js/dash2.onvifscanner.js b/web/libs/js/dash2.onvifscanner.js index 0a44a16e..adc85f63 100644 --- a/web/libs/js/dash2.onvifscanner.js +++ b/web/libs/js/dash2.onvifscanner.js @@ -53,7 +53,7 @@ $(document).ready(function(e){ mid: tempID + `${options.port}`, host: pathLocation.hostname, port: pathLocation.port, - path: pathLocation.pathname, + path: pathLocation.pathname + (pathLocation.search && pathLocation.search !== '?' ? pathLocation.search : ''), protocol: theLocation.protocol, details: { auto_host: addCredentialsToUri(streamUrl,currentUsername,currentPassword),