Some Updates and Fixes

- Add Branding Engine
- Lock PM2 install to 3.0.0 (newer versions cannot catch `uncaughtException`.
- Update macOS installer
- Update OpenALPR (CPU) installer
- Add Portuguese Language
- Fix Language Selector
- Lay-In ability to create Matrix from Pam-Diff detection blob
- createEventBasedRecording called for Traditional Recording labelled 5 seconds before real time to capture event
- fix NaN for new user created in Superuser
- count space used for Timelapse Frames and Files tables (does not purge Timelapse data yet)
- Update TimelapseJpeg, lay-in MP4 builder, many fixes, additonal page
- add `impervious` to gitignore in `web/libs` for custom static files
- Fix delete button for quick video list
- Fix Super form appearance
merge-requests/60/head
Moe 2019-04-02 20:47:03 -07:00
parent 9877ac480f
commit 89a70b6438
38 changed files with 1497 additions and 140 deletions

View File

@ -113,7 +113,7 @@ sudo npm install --unsafe-perm
sudo npm audit fix --force
echo "============="
echo "Shinobi - Install PM2"
sudo npm install pm2 -g
sudo npm install pm2@3.0.0 -g
echo "Shinobi - Finished"
sudo chmod -R 755 .
touch INSTALL/installed.txt

View File

@ -3,12 +3,32 @@ echo "------------------------------------------"
echo "-- Installing CUDA Toolkit and CUDA DNN --"
echo "------------------------------------------"
# Install CUDA Drivers and Toolkit
wget https://cdn.shinobi.video/installers/cuda-repo-ubuntu1710_9.2.148-1_amd64.deb -O cuda.deb
sudo dpkg -i cuda.deb
sudo apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1710/x86_64/7fa2af80.pub
echo "============="
echo " Detecting Ubuntu Version"
echo "============="
getubuntuversion=$(lsb_release -r | awk '{print $2}' | cut -d . -f1)
echo "============="
echo " Ubuntu Version: $getubuntuversion"
echo "============="
if [ "$getubuntuversion" = "17" ] || [ "$getubuntuversion" > "17" ]; then
wget https://cdn.shinobi.video/installers/cuda-repo-ubuntu1710_9.2.148-1_amd64.deb -O cuda.deb
sudo apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1710/x86_64/7fa2af80.pub
sudo dpkg -i cuda.deb
fi
if [ "$getubuntuversion" = "16" ]; then
wget https://cdn.shinobi.video/installers/cuda-repo-ubuntu1604_9.2.148-1_amd64.deb -O cuda.deb
sudo apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1604/x86_64/7fa2af80.pub
sudo dpkg -i cuda.deb
fi
sudo apt-get update -y
sudo apt-get -o Dpkg::Options::="--force-overwrite" install cuda -y
sudo apt-get -o Dpkg::Options::="--force-overwrite" install --fix-broken -y
if [ "$getubuntuversion" = "17" ] || [ "$getubuntuversion" > "17" ]; then
sudo apt-get -o Dpkg::Options::="--force-overwrite" install cuda -y --no-install-recommends
sudo apt-get -o Dpkg::Options::="--force-overwrite" install --fix-broken -y
fi
if [ "$getubuntuversion" = "16" ]; then
sudo apt-get install libcuda1-384 -y --no-install-recommends
sudo apt-get install nvidia-cuda-toolkit -y
fi
# Install CUDA DNN
wget https://cdn.shinobi.video/installers/libcudnn7_7.2.1.38-1+cuda9.2_amd64.deb -O cuda-dnn.deb
sudo dpkg -i cuda-dnn.deb

View File

@ -46,7 +46,7 @@ npm install --unsafe-perm
sudo npm audit fix --force
echo "============="
echo "Shinobi - Install PM2"
npm install pm2 -g
npm install pm2@3.0.0 -g
if (! -e "./conf.json" ) then
cp conf.sample.json conf.json
endif

View File

@ -21,7 +21,7 @@ npm i npm -g
#There are some errors in here that I don't want you to see. Redirecting to dev null :D
npm install --unsafe-perm > & /dev/null
npm audit fix --force > & /dev/null
npm install pm2 -g
npm install pm2@3.0.0 -g
cp conf.sample.json conf.json
cp super.sample.json super.json
pm2 start camera.js

30
INSTALL/getDistro.sh Normal file
View File

@ -0,0 +1,30 @@
if [ -f /etc/os-release ]; then
# freedesktop.org and systemd
. /etc/os-release
OS=$NAME
VER=$VERSION_ID
elif type lsb_release >/dev/null 2>&1; then
# linuxbase.org
OS=$(lsb_release -si)
VER=$(lsb_release -sr)
elif [ -f /etc/lsb-release ]; then
# For some versions of Debian/Ubuntu without lsb_release command
. /etc/lsb-release
OS=$DISTRIB_ID
VER=$DISTRIB_RELEASE
elif [ -f /etc/debian_version ]; then
# Older Debian/Ubuntu/etc.
OS=Debian
VER=$(cat /etc/debian_version)
elif [ -f /etc/SuSe-release ]; then
# Older SuSE/etc.
...
elif [ -f /etc/redhat-release ]; then
# Older Red Hat, CentOS, etc.
...
else
# Fall back to uname, e.g. "Linux <version>", also works for BSD, etc.
OS=$(uname -s)
VER=$(uname -r)
fi
echo $OS

View File

@ -9,13 +9,8 @@ echo "(y)es or (N)o"
read mysqlagreeData
if [ "$mysqlagreeData" = "y" ]; then
echo "Shinobi will now use root for database installation..."
echo "What is your SQL Username?"
read sqluser
echo "What is your SQL Password?"
read sqlpass
echo "You may now be asked for your Administator (root for Mac OS, not MySQL) password"
sudo mysql -u $sqluser -p$sqlpass -e "source sql/user.sql" || true
sudo mysql -u $sqluser -p$sqlpass -e "source sql/framework.sql" || true
sudo mysql -e "source sql/user.sql" || true
sudo mysql -e "source sql/framework.sql" || true
fi
echo "============="
echo "Shinobi - Install NPM Libraries"
@ -24,7 +19,7 @@ sudo npm install --unsafe-perm
sudo npm audit fix --unsafe-perm
echo "============="
echo "Shinobi - Install PM2"
sudo npm install pm2 -g
sudo npm install pm2@3.0.0 -g
if [ ! -e "./conf.json" ]; then
sudo cp conf.sample.json conf.json
fi
@ -35,6 +30,8 @@ if [ ! -e "./super.json" ]; then
fi
echo "Shinobi - Finished"
touch INSTALL/installed.txt
dos2unix /home/Shinobi/INSTALL/shinobi
ln -s /home/Shinobi/INSTALL/shinobi /usr/bin/shinobi
sudo chmod -R 755 .
echo "=====================================" > INSTALL/installed.txt
echo "======= Login Credentials =======" >> INSTALL/installed.txt

View File

@ -24,27 +24,59 @@ if [ "$ffmpeginstall" = "y" ]; then
curl -o ffmpeg.zip https://cdn.shinobi.video/installers/ffmpeg-3.4.1-macos.zip
sudo unzip ffmpeg.zip
sudo rm ffmpeg.zip
sudo mv ffmpeg-3.4.1-macos/ffmpeg /usr/bin/ffmpeg
sudo mv ffmpeg-3.4.1-macos/ffplay /usr/bin/ffplay
sudo mv ffmpeg-3.4.1-macos/ffprobe /usr/bin/ffprobe
sudo mv ffmpeg-3.4.1-macos/ffserver /usr/bin/ffserver
sudo mv ffmpeg-3.4.1-macos/ffmpeg /usr/local/bin/ffmpeg
sudo mv ffmpeg-3.4.1-macos/ffplay /usr/local/bin/ffplay
sudo mv ffmpeg-3.4.1-macos/ffprobe /usr/local/bin/ffprobe
sudo mv ffmpeg-3.4.1-macos/ffserver /usr/local/bin/ffserver
sudo chmod +x /usr/local/bin/ffmpeg
sudo chmod +x /usr/local/bin/ffplay
sudo chmod +x /usr/local/bin/ffprobe
sudo chmod +x /usr/local/bin/ffserver
fi
echo "============="
echo "Shinobi - Do you want to Install MySQL? Choose No if you have MySQL or MySQL already."
echo "(y)es or (N)o"
read mysqlagree
if [ "$mysqlagree" = "y" ]; then
echo "Shinobi - Installing MySQL"
bash <(curl -Ls http://git.io/eUx7rg)
if [ ! -e "./shinobi.sqlite" ]; then
sudo npm install jsonfile
sudo cp sql/shinobi.sample.sqlite shinobi.sqlite
sudo node tools/modifyConfiguration.js databaseType=sqlite3
fi
echo "Shinobi - Install NPM Libraries"
sudo npm i npm -g
sudo npm install --unsafe-perm
sudo npm audit fix --unsafe-perm
echo "============="
echo "============="
echo "You must now close this terminal window and reopen it."
echo "Reopen the Shinobi folder and run"
echo "sudo sh INSTALL/macos-part2.sh"
echo "============="
echo "============="
echo "Shinobi - Install PM2"
sudo npm install pm2@3.0.0 -g
if [ ! -e "./conf.json" ]; then
sudo cp conf.sample.json conf.json
fi
if [ ! -e "./super.json" ]; then
echo "Default Superuser : admin@shinobi.video"
echo "Default Password : admin"
sudo cp super.sample.json super.json
fi
echo "Shinobi - Finished"
sudo chmod -R 755 .
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
echo "Shinobi - Start Shinobi and set to start on boot?"
echo "(y)es or (N)o"
read startShinobi
if [ "$startShinobi" = "y" ]; then
pm2 start camera.js
pm2 startup
pm2 save
pm2 list
fi
echo "details written to INSTALL/installed.txt"
echo "====================================="
echo "======= Login Credentials ======="
echo "|| Username : $userEmail"
echo "|| Password : $userPasswordPlain"
echo "|| API Key : $apiKey"
echo "====================================="
echo "====================================="

View File

@ -9,6 +9,9 @@ if ! [ -x "$(command -v opencv_version)" ]; then
else
echo "OpenCV found... : $(opencv_version)"
fi
# get tesseract repo because ubuntu repo is serving a broken version
sudo add-apt-repository ppa:alex-p/tesseract-ocr -y
sudo apt-get update -y
# 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

View File

@ -11,7 +11,7 @@ else
fi
# get tesseract repo because ubuntu repo is serving a broken version
sudo add-apt-repository ppa:alex-p/tesseract-ocr -y
sudo apt-get update
sudo apt-get update -y
# 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

View File

@ -109,7 +109,7 @@ npm install --unsafe-perm
sudo npm audit fix --force
echo "============="
echo "Shinobi - Install PM2"
sudo npm install pm2 -g
sudo npm install pm2@3.0.0 -g
echo "Shinobi - Finished"
sudo chmod -R 755 .
touch INSTALL/installed.txt

View File

@ -154,7 +154,7 @@ echo "============="
#Install PM2
echo "Shinobi - Install PM2"
sudo npm install pm2 -g
sudo npm install pm2@3.0.0 -g
if [ ! -e "./conf.json" ]; then
cp conf.sample.json conf.json
fi

View File

@ -18,6 +18,9 @@ if [ "$getubuntuversion" = "18" ] || [ "$getubuntuversion" > "18" ]; then
sudo apt install -y software-properties-common
sudo add-apt-repository universe -y
fi
if [ "$getubuntuversion" = "16" ]; then
apt install gnupg-curl -y
fi
#create conf.json
if [ ! -e "./conf.json" ]; then
sudo cp conf.sample.json conf.json
@ -121,7 +124,7 @@ sudo npm install --unsafe-perm
sudo npm audit fix --force
echo "============="
echo "Shinobi - Install PM2"
sudo npm install pm2 -g
sudo npm install pm2@3.0.0 -g
echo "Shinobi - Finished"
sudo chmod -R 755 .
touch INSTALL/installed.txt

View File

@ -73,6 +73,8 @@ loadLib('ffmpeg')(s,config,lang,function(ffmpeg){
loadLib('dropInEvents')(s,config,lang,app,io)
//form fields to drive the internals
loadLib('definitions')(s,config,lang,app,io)
//branding functions and config defaults
loadLib('branding')(s,config,lang,app,io)
//custom module loader
loadLib('customAutoLoad')(s,config,lang,app,io)
//scheduling engine

View File

@ -1481,7 +1481,7 @@ module.exports = function(s,config,lang){
"name": "height",
"field": lang["Record Height"],
"description": "Height of the stream image.",
"default": "480",
"default": "",
"example": "720",
"form-group-class": "h_vc_input h_vc_libvpx h_vc_libvpx-vp9 h_vc_libx264 h_vc_libx265 h_vc_hevc_nvenc h_vc_h264_nvenc h_vc_h264_vaapi h_vc_hevc_vaapi h_vc_h264_qsv h_vc_hevc_qsv h_vc_mpeg2_qsv h_vc_default h_vc_none",
"possible": ""
@ -1490,7 +1490,7 @@ module.exports = function(s,config,lang){
"name": "width",
"field": lang["Record Width"],
"description": "Width of the stream image.",
"default": "640",
"default": "",
"example": "1280",
"form-group-class": "h_vc_input h_vc_libvpx h_vc_libvpx-vp9 h_vc_libx264 h_vc_libx265 h_vc_hevc_nvenc h_vc_h264_nvenc h_vc_h264_vaapi h_vc_hevc_vaapi h_vc_h264_qsv h_vc_hevc_qsv h_vc_mpeg2_qsv h_vc_default h_vc_none",
"possible": ""
@ -1768,6 +1768,10 @@ module.exports = function(s,config,lang){
"name": `5 ${lang.minutes}`,
"value": "300"
},
{
"name": `10 ${lang.minutes}`,
"value": "600"
},
{
"name": `15 ${lang.minutes}`,
"value": "900"

View File

@ -77,6 +77,7 @@
"Zoom Out": "Zoom Out <small>URL Address</small>",
"Enable Nightvision": "Enable Nightvision",
"Disable Nightvision": "Disable Nightvision",
"Date": "Date",
"Current": "Current",
"Monitors": "Monitors",
"Video": "Video",
@ -93,6 +94,8 @@
"New Monitor": "New Monitor",
"Please Check Your Settings": "Please Check Your Settings",
"migrateText1": "<b>Input Type</b> could not be parsed. Please set it manually.",
"Building": "Building",
"Started Building": "Started Building",
"Add": "Add",
"Save": "Save",
"Close": "Close",
@ -391,6 +394,7 @@
"Logging": "Logging",
"Timelapse": "Timelapse",
"Nothing exists": "Nothing exists",
"Already exists": "Already exists",
"Creation Interval": "Creation Interval",
"Plugin": "Plugin",
"IdentityText1": "This is how the system will identify the data for this stream. You cannot change the <b>Monitor ID</b> once you have pressed save. If you want you can make the <b>Monitor ID</b> more human readable before you continue.",

909
languages/pt.json Normal file
View File

@ -0,0 +1,909 @@
{
"\"No Motion\" Detector": "\"Sem movimento\" Detector",
"# of Allow MJPEG Clients": "# para permitir clientes MJPEG <small>0 para infinito</small>",
"180 Degrees": "180 Graus",
"2-Factor Authentication": "Autenticação de 2 fatores",
"90 Clockwise": "90 no sentido horário",
"90 Clockwise and Vertical Flip": "90 no sentido horário e vertical",
"90 Counter Clockwise and Vertical Flip (default)": "90 no sentido anti-horário e vertical (Padrão)",
"AND": "E",
"API": "API",
"API Key": "Chave de API",
"API Key Added": "Chave da API adicionada",
"API Key Deleted": "Chave da API excluída",
"API Keys": "Chaves da API",
"APIKeyAddedText": "Você pode usar essa chave agora",
"APIKeyDeletedText": "A chave foi excluída. Não funcionará mais.",
"ASC": "ASC",
"Accelerator": "Acelerador",
"Account Info": "Informações da conta",
"AccountEditText1": "Não foi possível editar. Atualize a página se o problema continuar",
"Accounts": "Contas",
"Action for Selected": "Ação para Selecionado",
"Add": "Adicionar",
"Add Channel": "Adicionar Canal",
"Add Input Feed": "Adicionar Entrada De Alimentação",
"Add Map": "Adicionar O Mapa",
"Add Monitor": "Adicionar monitor",
"Add New": "Adicionar novo",
"Admin": "Admin",
"Advanced": "Avançado",
"Again": "De novo",
"Age": "Idade",
"Alert Sound": "Som De Alerta",
"Alert Sound Delay": "Som De Alerta Para Atraso",
"All Logs": "Todos logs",
"All Monitors": "Todos Monitores",
"All Monitors and Privileges": "Todos monitores e privilégios",
"All Privileges": "Todos Privilégios",
"All Warnings": "Todos avisos",
"All streams in first feed": "Todos os fluxos no primeiro feed",
"Allow Next Command": "Habilitar próximo comando <small>em minutos</small>",
"Allow Next Discord Alert": "Permitir Junto a Discórdia Alerta <small>em Minutos</small>",
"Allow Next Email": "Permitir próximo e-mail <small>em minutos</small>",
"Allow Next Trigger": "Habilitar próxima ação <small>em milissegundos</small>",
"Allowed IPs": "IPs Permitidos",
"Amazon S3": "O Amazon S3",
"Amazon S3 Upload Error": "O Amazon S3 Erro De Upload",
"Analyzation Duration": "Duração da análise",
"Archive": "Arquivar",
"Are you sure?": "Tem a certeza?",
"Attach Video Clip": "Anexar Vídeo Clip",
"Audio": "Áudio",
"Audio Bit Rate": "Taxa De Bits De Áudio",
"Audio Codec": "Codec De Áudio",
"Audio Detection": "Detecção De Áudio",
"Audio Detector": "Detector De Áudio",
"Audio stream only from first feed": "Fluxo de áudio somente a partir de primeiro de alimentação",
"Audio streams only": "Fluxos de áudio apenas",
"Authenticate": "Autenticar",
"Authentication Failed": "Autenticação falhou",
"Auto": "Auto",
"Automatic": "Automática",
"Autosave": "Auto salvar",
"Backblaze B2": "Backblaze B2",
"Backblaze Error": "Backblaze Erro",
"Base64 over Websocket": "Base64 sobre Websocket",
"Basic Authentication": "Autenticação básica",
"Batch": "Lote",
"Bind Credentials": "Vincular Credenciais (Senha)",
"Blank for No Change": "Em branco para Não Alterar",
"Bottom Left": "Inferior esquerdo",
"Bottom Right": "Inferior direito",
"Browser Console Log": "Navegador de logs",
"Bucket": "Balde",
"Buffer Preview": "Buffer De Pré-Visualização",
"CPU": "CPU",
"CPU indicator will not work. Continuing...": "Indicador de CPU não irá funcionar. Continuando...",
"CPU used by this stream": "CPU usada por este fluxo",
"CSS": "CSS <small>Personalize seu painel de controle.</small>",
"Calendar": "Calendário",
"Call Method": "Método De Chamada",
"Camera Password": "Senha da câmera",
"Camera Username": "Usuário da câmera",
"Camera is not recording": "Câmera não está gravando",
"Camera is not running": "A câmera não está em execução",
"Camera is not streaming": "Câmera não está transmitindo",
"CameraNotRecordingText": "As configurações podem ser incompatíveis. Verifique os encoders. Reiniciando...",
"Can Authenticate Websocket": "Pode Autenticar Websocket",
"Can Change User Settings": "Pode Alterar As Definições De Utilizador",
"Can Control Monitors": "Pode Controlar Monitores",
"Can Create and Delete Monitors": "Pode Criar e Excluir Monitores",
"Can Delete Videos": "Pode Deletar Vídeos",
"Can Delete Videos and Events": "Pode Deletar Vídeos e Eventos",
"Can Edit Monitor": "Pode Editar Monitores",
"Can Get Logs": "Pode Obter Logs",
"Can Get Monitors": "Pode Obter Monitores",
"Can View Logs": "Pode Ver Os Logs",
"Can View Monitor": "Pode Visualizar Monitores",
"Can View Snapshots": "Pode Visualizar Snapshots",
"Can View Streams": "Pode Visualizar Transmissões",
"Can View Videos": "Pode Visualizar Vídeos",
"Can View Videos and Events": "Pode Visualizar Vídeos e Eventos",
"Can edit Max Days": "Pode editar Máximo de Dias",
"Can edit Max Storage": "Pode editar Armazenamento Máximo",
"Can edit how long to keep Events": "Pode editar quanto tempo manter eventos",
"Can edit how long to keep Logs": "Pode editar por quanto tempo maneter logs",
"Can use Admin Panel": "Pode usar Painel Administrativo",
"Can use Amazon S3": "Pode usar o Amazon S3",
"Can use Discord Bot": "Pode usar a Discórdia Bot",
"Can use LDAP": "Pode usar LDAP",
"Can use SFTP": "Pode usar SFTP",
"Can use Wasabi Hot Cloud Storage": "Pode usar Wasabi Quente de Armazenamento em Nuvem",
"Can use WebDAV": "Pode usar WebDAV",
"Can't Connect": "Não pode conectar",
"Cannot watch a monitor that isn't running.": "Não pode assistir um monitor que não está rodando.",
"Cards": "Cartões",
"Center": "Centro <small>URL Address</small>",
"Channel": "Canal",
"Channel ID": "ID do canal",
"Chat on Discord": "Conversar no Discord",
"Check": "Verificar",
"Check Signal Interval": "Verifique o intervalo do sinal <small>em minutos</small>",
"Check for Motion First": "Verifique primeiro o movimento",
"Clear Recorder Process": "Claro Gravador De Processo",
"Close": "Fechar",
"Closed": "Fechado",
"Color Threshold": "Limiar De Cores",
"Command": "Comando",
"Command on Trigger": "Comando quando acionado",
"Complete Stream URL": "URL de transmissão completa",
"Confidence": "Confiança",
"Confidence of Detection": "A confiança de Detecção de",
"Configuration": "Configuração",
"Confirm": "Confirmar",
"Connected": "Conectado",
"Connection": "Conexão",
"Connection Type": "Tipo de conexão",
"Connection timed out": "Conexão esgotado",
"Contains": "Contém",
"Control": "Controle",
"Control Error": "Erro de controle",
"ControlErrorText1": "Controle não está ativado",
"ControlErrorText2": "Verifique sua conexão detalhes. Você pode precisar ponto a URL Base em port 8000 ou 80. Verifique a sua informação de autenticação.",
"Controllable": "Controlável",
"Copy Connection Settings": "Copiar Configurações De Conexão",
"Copy Custom Settings": "Copiar Configurações Personalizadas",
"Copy Detector Settings": "Copiar As Configurações Do Detector",
"Copy Group Settings": "Copiar Configurações Do Grupo",
"Copy Input Settings": "Copiar As Configurações De Entrada De",
"Copy JPEG API Settings": "Cópia JPEG API de Configurações",
"Copy Logging Settings": "Copiar As Configurações De Log",
"Copy Mode": "Modo De Cópia",
"Copy Recording Settings": "Copiar Configurações De Gravação",
"Copy Settings": "Configurações De Cópia",
"Copy Stream Channel Settings": "Copiar Configurações De Canal Stream",
"Copy Stream Settings": "Copiar Definições Da Sequência",
"Copy to Settings": "Copiar Configurações",
"Could not create Bucket.": "Não foi possível criar o Balde.",
"Country of Plates": "País das placas",
"Counts of Motion": "Contagem de movimento",
"Create Sub-Accounts at /admin": "Criação de Sub-Contas em /admin",
"Creating New Account": "Criando Nova Conta",
"Creation Interval": "Intervalo De Criação De",
"Current": "Atual",
"Currently viewing": "Visualizando atualmente",
"Custom": "Customizado",
"Custom Base URL": "URL Customizada <small>Deixe em branco para usar URL do Host</small>",
"Custom Endpoint": "Ponto Final Personalizado",
"DB Lost.. Retrying..": "Banco de dados perdido... Repetindo...",
"DESC": "DESC",
"Dashboard": "Painel de Controle",
"Dashboard Language": "Idioma do Painel de Controle",
"Dashcam": "Dashcam",
"Dashcam (Streamer v2)": "Dashcam (Streamer v2)",
"Database": "Banco de dados",
"Database Not Found": "Banco De Dados Não Encontrado",
"Database row does not exist": "Linha de base de dados não existe",
"Date Range": "Intervalo de Data",
"Debug": "Debugar",
"Default": "Padrão",
"Delete": "Deletar",
"Delete Filter": "Excluir filtro",
"Delete Matches": "Deletar Combinações",
"Delete Monitor": "Deletar Monitor",
"Delete Monitor State?": "Apagar O Monitor De Estado",
"Delete Monitor States Preset": "Apagar O Monitor De Estados Predefinidos",
"Delete Motionless Video": "Deletar vídeos sem movimento",
"Delete Motionless Videos (Record)": "Eliminar vídeos sem movimento (Gravação)",
"Delete Selected Videos": "Excluir vídeos selecionados",
"Delete Video": "Deletar Vídeo",
"Delete selected": "Deletar Selecionado",
"DeleteMonitorText": "Deseja excluir este monitor? Você não pode recuperá-lo. Os arquivos para essa ID permanecerão no sistema de arquivos. Se você optar por recriar um monitor com a mesma identificação, os vídeos e eventos tornar-se-ão visíveis no painel de controle.",
"DeleteMonitorsText": "Você deseja excluir esses monitores? Você não pode recuperá-los. Você pode optar por manter os arquivos para essas Identificações no sistema de arquivos. Se você escolher para recriar um monitor com uma das Identificações vídeos e eventos tornam-se visíveis no painel de controle.",
"DeleteSelectedVideosMsg": "Deseja excluir esses vídeos? Você não poderá recuperá-los.",
"DeleteVideoMsg": "Deseja excluir este vídeo? Você não poderá recuperá-lo.",
"Deleted": "Deletado",
"Deleted Schedule Configuration": "Excluído Agenda De Configuração",
"Deleted State Configuration": "Excluído O Estado De Configuração",
"Detect Objects": "Detectar objetos <small class=\"\">Veja abaixo</small>",
"Detection Engine": "O Motor De Detecção De",
"Detector": "Detector de",
"Detector Buffer": "Detector De Buffer",
"Detector Filters": "Detector De Filtros",
"Detector Flags": "Detector de Flags",
"Detector Grouping": "Detector de Agrupamento <small>Adicionar grupos de <b>Configurações</b></small>",
"Detector Rate": "Taxa do detector <small>(FPS)</small>",
"Detector Recording Complete": "Detector De Gravação Completo",
"Detector Recording Process Exited Prematurely. Restarting.": "Detector Processo De Gravação Foi Encerrado Prematuramente. Reiniciar.",
"DetectorText": "<p>Quando as caixas de Largura e Altura são mostradas, você deve configurá-las para 640x480 ou abaixo. Isso otimizará a velocidade de leitura dos quadros.</p>",
"Died": "Morreu",
"Digest Authentication": "A Autenticação Digest",
"Disable Night Vision": "Desativar visão noturna <small>URL Address</small>",
"Disable Nightvision": "Desativar Visão Noturna",
"Disabled": "Desabilitado",
"Discord": "Discórdia",
"Discord Alert on Trigger": "A discórdia Alerta no Gatilho",
"Discord Bot": "A Discórdia Bot",
"DiscordErrorText": "Enviar a Discórdia causou um Erro",
"DiscordFailedText": "Enviar a Discórdia Falha",
"DiscordLoggedIn": "A Discórdia Bot Autenticado",
"DiscordNotEnabledText": "A discórdia Bot Não Habilitada, Habilite-o em suas Configurações de Conta.",
"Documentation": "Documentação",
"Does Not Contain": "Não Contém",
"Don't show this anymore": "Não mostre isso mais",
"Double Quote Directory": "Diretório com aspas duplas <small>Alguns diretórios têm espaços. Usar isso pode bloquear algumas câmeras.</small>",
"Down": "Baixo <small>URL Address</small>",
"Down Stop": "Baixo máximo <small>URL Address</small>",
"Download": "Baixar",
"EU": "UE",
"Edit": "Editar",
"Edit Selected": "Edit Selected",
"Edited Schedule Configuration": "Editado Agenda De Configuração",
"Edited State Configuration": "Editado Estado De Configuração",
"Email": "E-mail",
"Email Details": "Detalhes do E-mail",
"Email address is in use.": "Endereço de e-mail está em uso.",
"Email and Password fields cannot be empty": "E-mail e campos de palavra-Passe não pode ser vazio",
"Email on No Motion": "E-mail em \"Sem movimento\"",
"Email on Trigger": "E-mail quando acionado <small>Emails vão para o endereço de login do titular da conta principal.</small>",
"Emotion": "Emoção",
"Emotion Average": "Emoção Média",
"Enable": "Habilitar",
"Enable Night Vision": "Ativar visão noturna <small>URL Address</small>",
"Enable Nightvision": "Ativar Visão Noturna",
"Enabled": "Habilitado",
"End": "Finalizar",
"End Time": "Hora de Término",
"Ended": "Finalizado",
"Endpoint": "Ponto final",
"Endpoint Address": "Endereço De Ponto De Extremidade",
"Enlarge": "Prolongar",
"Enter this code to proceed": "Informe este código para prosseguir",
"Equal to": "Igual a",
"Error Connecting": "Erro ao conectar",
"Error While Decoding": "Erro Durante A Decodificação",
"ErrorWhileDecodingText": "Seu hardware pode ter uma conexão instável para a rede. Verifique suas conexões de rede.",
"Event": "Evento",
"Event Limit": "Limite de Evento",
"Event Webhook Error": "Evento Webhook Erro",
"EventText1": "Disparou um evento de movimento",
"EventText2": "Não foi possível enviar imagem de e-mail, o arquivo não foi acessado",
"Events": "Eventos",
"Example": "Exemplo",
"Execute Command": "Executar Comando",
"Executed": "Executado",
"Export": "Exportar",
"Export Selected Videos": "Exportar Vídeos Selecionados",
"Export Video": "Exportação De Vídeo",
"ExportSelectedVideosMsg": "Você deseja exportar esses vídeos? Pode levar algum tempo para zip e fazer o download.",
"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 <b> Problemas </b> no Github.",
"FFmpegTip": "FFprobe é um simples fluxos multimídia analyzer. Você pode usá-lo para a saída de todos os tipos de informações sobre uma entrada, incluindo duração, taxa de quadros, tamanho do quadro, etc.",
"FFprobe": "Sonda",
"FLV": "FLV",
"FLV Stream Type": "FLV Tipo de Fluxo",
"FactorAuthText1": "O código vai estar ativo somente por 15 minutos. Se você entrar novamente o temporizador será reiniciado para 15 minutos com o mesmo código",
"Fatal": "Fatal",
"Fatal Maximum Reached": "Máximo atingido, parando câmera.",
"FatalMaximumReachedText": "Erro JPEG fatal.",
"Feed-in Image Height": "Altura da imagem de entrada",
"Feed-in Image Width": "Largura da imagem de entrada",
"Female": "Feminino",
"Fields cannot be empty": "Campos não podem estar vazios",
"File Delete Error": "Excluir O Arquivo De Erro",
"File Not Exist": "Arquivo inexistente",
"File Not Found": "Arquivo não encontrado",
"File Not Found in Database": "Arquivo Não Encontrado no Banco de dados",
"File Not Found in Filesystem": "Arquivo Não Encontrado no sistema de arquivos",
"File Type": "Tipo de Arquivo",
"FileNotExistText": "Não é possível salvar o arquivo inexistente. Algo deu errado.",
"Filename": "Nome do Arquivo",
"Filesize": "Tamanho do Arquivo",
"Filter ID": "Filtrar ID",
"Filter Matches": "Combinações de filtro",
"Filter Name": "Filtrar Nome",
"Filter for Objects only": "Filtro para que somente os Objetos",
"FilterMatchesText1": "Este filtro atingiu as condições.",
"FilterMatchesText2": "vídeos encontrados.",
"Filters": "Filtros",
"Filters Updated": "Filtros atualizados",
"FiltersUpdatedText": "Suas alterações foram salvas e aplicadas.",
"Find Where": "Encontrar Onde",
"First stream in feed": "Primeiro fluxo de alimentação",
"Fix": "Corrigir",
"Fix Video": "Corrigir Vídeo",
"FixVideoMsg": "Você deseja corrigir esse vídeo? Você não poderá desfazer essa ação..",
"Flush PM2 Logs": "Flush Logs de PM2",
"Font Path": "Caminho da fonte",
"Font Size": "Tamanho da fonte",
"For Group": "Para grupo",
"Force Monitors Per Row": "Força De Monitores Por Linha",
"Force Port": "Forçar porta",
"Form Data Not Found": "Formulário De Dados Não Encontrado",
"Found Devices": "Dispositivos encontrados",
"Frame Rate": "Taxa de quadros <small>(FPS)</small>",
"Full Frame Detection": "Detecção de quadro completo",
"Full Stream URL": "Total URL de Fluxo",
"Full URL Path": "Caminho completo da URL",
"Fullscreen": "Tela cheia",
"Gender": "Sexo",
"Generate Subtitles": "Gerar legendas",
"Get Logs to Client": "Obter Logs do Cliente",
"Global Detector Settings": "Global As Configurações Do Detector",
"Greater Than": "Maior que",
"Greater Than or Equal to": "Maior ou Igual a",
"Group Key": "Chave do Grupo",
"Group Key is in use.": "Chave de grupo está em uso.",
"Group Name": "Nome do grupo",
"Grouping": "Agrupando &nbsp; <small>Adicione grupos em <b>Configurações</b></small>",
"H.264 / H.265 / H.265+": "H. 264 / H. 265 / H. 265 ",
"HEVC (H.265)": "HEVC (H. 265)",
"HLS (.m3u8)": "HLS (.m3u8)",
"HLS (includes Audio)": "HLS (inclui áudio)",
"HLS Audio Encoder": "Codificador de áudio HLS",
"HLS List Size": "Tamanho da lista HLS",
"HLS Live Start Index": "HLS ao Vivo Índice de Início",
"HLS Preset": "Pré-definição HLS",
"HLS Segment Length": "Comprimento do segmento HLS <small>em segundos</small>",
"HLS Start Number": "HLS Número de Início",
"HLS Video Encoder": "Codificador de vídeo HLS",
"HTTP": "HTTP",
"HTTPS": "HTTPS",
"Hardware Accelerated": "Aceleração De Hardware",
"Height": "Altura",
"Help": "Ajuda",
"Hide List": "Esconder Lista",
"Hide Notes": "Esconder Notas",
"Host": "Host",
"Host Type": "Tipo De Host",
"Hotswap Modes (Watch-Only)": "Hotswap Modes (Visualizar Apenas)",
"How to Record": "Como gravar",
"IP Address": "Endereço IP",
"Identity": "Identidade",
"IdentityText1": "É assim que o sistema irá identificar os dados para este fluxo. Você não pode alterar o <b>ID do Monitor</b> uma vez que você pressionou salvar. Se você quiser, você pode fazer o <b>ID do Monitor</b> mais legível para humanos antes de continuar.",
"IdentityText2": "Você pode duplicar um monitor modificando o <b>ID do Monitor</b> e depois pressionando salvar. Você <b>não pode</b> usar o ID de um monitor que já existe ou ele economizará sobre as informações do banco de dados desse monitor.",
"Idle": "Inativo",
"Image Height": "Altura da imagem",
"Image Location": "Localização da imagem <small>Caminho Absoluto ou deixar em branco para usar global</small>",
"Image Position": "Posição da imagem",
"Image Width": "Largura da imagem",
"Import": "Importar",
"Import Monitor Configuration": "Importar Configuração do Monitor",
"ImportMonitorConfigurationText": "Fazer isso substituirá todas as alterações atualmente não salvas. As mudanças importadas só serão aplicadas quando você pressionar <b>Salvar</b>.",
"ImportMultiMonitorConfigurationText": "Fazendo isso vai overrwrite qualquer monitores com IDs existentes no arquivo de importação.",
"In": "Em",
"Incorrect Settings Chosen": "Configuração incorreta escolhida",
"Indifference": "Indiferença",
"Input": "Entrada",
"Input Feed": "Entrada De Alimentação",
"Input Flags": "Flags de entrada",
"Input Selector": "Selector De Entrada",
"Input Settings": "Configurações De Entrada De",
"Input Type": "Tipo de entrada",
"InputText1": "Esta seção diz a Shinobi como consumir um fluxo. Para um desempenho ideal, tente ajustar as configurações internas da sua câmera. Encontre as seguintes opções e defina-as como mostrado. Para encontrar sua câmera, você pode usar o <b>ONVIF Scanner embarcado</b> do Shinobi. Algumas câmeras ONVIF requerem o uso de uma ferramenta de gerenciamento para modificar suas configurações internas. Se você não consegue encontrar suas câmeras, você pode tentar <a href=\"https://s3.amazonaws.com/cloudcamio/odm-v2.2.250.msi\">Gerenciador de dispositivos ONVIF para Windows</a>.",
"InputText2": "<ul><li><b>Taxa de quadros por segundo (FPS) :</b> High : 10 - 15 FPS, Low : 2-5 FPS</li><li><b>I-frame interval :</b> 80</li><li><b>Bit Rate Type :</b> CBR (Constant Bit Rate)</li><li><b>Bit Rate :</b> entre 256kbps - 500kbps</li></ul>",
"InputText3": "Se você precisar de ajuda para descobrir o tipo de entrada que sua câmera é, você pode dar uma olhada no <a href=\"http://shinobi.video/docs/cameras\" target=\"_blank\">Camera URLs List</a> na página do Shinobi.",
"Inserted Schedule Configuration": "Inserido Agenda De Configuração",
"Inserted State Configuration": "Inserido Estado De Configuração",
"Invalid Data": "Dados Inválidos",
"Invalid JSON": "JSON inválido",
"InvalidJSONText": "Certifique-se de que esta seja uma string JSON válida para a configuração do monitor Shinobi.",
"JPEG": "JPEG",
"JPEG (Auto Enables JPEG API)": "JPEG (Auto habilitado JPEG API)",
"JPEG API": "JPEG API <small>Snapshot (cgi-bin)</small>",
"JPEG Error": "Erro JPEG",
"JPEG Mode": "Modo JPEG",
"JPEGErrorText": "Houve um problema ao obter dados da sua câmera.",
"LDAP": "LDAP",
"LDAP Success": "LDAP Sucesso",
"LDAP User Authenticated": "Usuário LDAP Usuário Autenticado",
"LDAP User is New": "Usuário LDAP é Novo",
"Launch in New Window": "O lançamento na Nova Janela",
"Leave blank for random.": "Deixe em branco para aleatório.",
"Leave blank for unlimited": "Deixe em branco para ilimitado",
"Left": "Esquerda <small>URL Address</small>",
"Left Stop": "Esquerda máxima <small>URL Address</small>",
"Less Than": "Menor que",
"Less Than or Equal to": "Menor ou Igual a",
"License Plate Detector": "Placa De Licença Detector De",
"Like": "Como",
"Limited": "Limitado",
"Link Shinobi": "Link Shinobi",
"Lisence Plate Detector": "Detector de placas",
"List Toggle": "Mostrar lista",
"List of Videos Delete Error": "Lista de Vídeos Apagar Erro",
"Live Stream Toggle": "Mostrar transmissão ao vivo",
"Live View": "Visualização ao vivo",
"Local": "Local",
"Log Level": "Nível de registro",
"Log Signal Event": "Evento de sinal de registro <small>Apenas cliente</small>",
"Log Stream": "Fluxo De Log",
"Logging": "Registo",
"Login": "Entrar",
"Logout": "Sair",
"Logs": "Logs",
"Loop Stream": "Loop Stream",
"MB": "MB",
"MJPEG": "MJPEG",
"MP4 (copy, libx264, libx265)": "MP4 (copiar, libx264, libx265)",
"MPEG-4 (.mp4 / .ts)": "MPEG-4 (.mp4 / .ts)",
"MPEG-DASH (includes Audio)": "MPEG-DASH (inclui Áudio)",
"MailError": "MAIL ERROR : Não foi possível enviar e-mail, verifique conf.json. Ignorando qualquer recurso que dependa do envio",
"Main": "Principal",
"Male": "Masculino",
"Manual": "Manual",
"Map": "Mapa",
"Matches": "Combina",
"Max Indifference": "Max Indiferença",
"Max Latency": "Max Latência",
"Max Number of Cameras": "Número máximo de câmeras",
"Max Storage Amount": "Montante máximo de armazenamento <small>em Megabytes</small>",
"Maximum dB": "Máximo dB",
"Merge Selected Videos": "Mesclar Vídeos Selecionados",
"Merge Video": "Vídeo De Impressão",
"Merge and Download": "De mala e fazer o Download",
"MergeSelectedVideosMsg": "Você deseja mesclar esses vídeos? Pode levar algum tempo para mesclar e fazer o download. O momento em que a conexão é fechada, o arquivo será excluído. Certifique-se de manter o navegador aberto até que seja concluída.",
"Migrator": "Migrator",
"Minimum dB": "Mínimo dB",
"Mode": "Modo",
"Monitor": "Monitor",
"Monitor Added by user": "Monitor adicionado pelo usuário",
"Monitor Capture Rate": "Taxa de captura do monitor <small>(FPS)</small>",
"Monitor Groups": "Grupos de monitores",
"Monitor ID": "ID do Monitor",
"Monitor Idling": "Monitor ocioso",
"Monitor Name": "Nome do monitor",
"Monitor Settings": "Configurações do monitor",
"Monitor States": "Monitor De Estados",
"Monitor States and Schedules": "Monitor de Estados e Horários",
"Monitor Stopped": "Monitor parado",
"Monitor Updated by user": "Monitor atualizado pelo usuário",
"Monitor is now Disabled": "O Monitor é Desativado",
"Monitor is now Idle": "O Monitor está agora Inactiva",
"Monitor is now Recording": "O Monitor é agora de Gravação",
"Monitor is now Watching": "O Monitor está agora Assistindo",
"Monitor mode changed": "Modo de monitor mudou",
"Monitor mode is already": "Modo de monitor já está em uso",
"Monitor or Key does not exist.": "Monitor ou chave não existe",
"MonitorIdlingText": "A sessão do monitor foi ordenada para ocioso.",
"MonitorStoppedText": "A sessão do monitor foi ordenada a parar.",
"Monitors": "Monitores",
"Monitors per row": "Monitores por linha <small>para montagem</small>",
"Monitors to Copy to": "Monitores para Copiar para",
"Montage": "Montagem",
"Motion": "Movimento",
"Motion Detection": "Detecção De Movimento",
"Motion GUI": "GUI de Movimento",
"Motion Meter": "Medidos de Movimento",
"Mp4Frag": "Mp4Frag",
"Must be atleast one row": "Deve ser pelo menos uma linha",
"Mute Audio": "Silenciar Áudio",
"NVIDIA": "NVIDIA",
"Name": "Nome",
"Name cannot be empty.": "O nome não pode estar vazia.",
"New Authentication Token": "Novo token de autenticação",
"New Monitor": "Novo Monitor",
"No": "Não",
"No Audio": "Sem áudio",
"No Data": "Sem Dados",
"No Events found for this video": "Nenhum evento encontrado para este vídeo",
"No Group with this key exists": "Nenhum grupo com esta chave foi encontrado",
"No Monitor Exists with this ID.": "Não existem monitores com esse ID.",
"No Monitor Found, Ignoring Request": "Monitor não encontrado, ignorando requisição",
"No Monitor ID Present in Form": "Nenhum ID do Monitor Presente no Formulário",
"No Monitors Selected": "Não Há Monitores Selecionados",
"No Rotation": "Sem rotação",
"No Videos Found": "Nenhum Vídeo Encontrado",
"No such file": "Arquivo inexistente",
"NoLogsFoundForDateRange": "Sem Registos encontrados neste intervalo de data. Tente alargar o intervalo de data.",
"NoMotionEmailText1": "Nenhum movimento para",
"NoMotionEmailText2": "Não houve nenhum movimento detectado na câmera para",
"NoVideosFoundForDateRange": "Nenhum vídeo encontrado neste intervalo de datas. Tente definir a data de início mais adiante.",
"Noise Filter": "Filtro De Ruído",
"Noise Filter Range": "Filtro De Ruído Faixa De",
"Not Authorized": "Não autorizados",
"Not Connected": "Não conectado",
"Not Equal to": "Não Igual a",
"Not In": "Não Em",
"Not Matches": "Não Combina",
"Not Permitted": "Não permitidos",
"Not Saved": "Não Salvo",
"Not an Administrator Account": "Não é uma conta de Administrador",
"NotAuthorizedText1": "Não autorizado, envie o comando inicial com \"auth\",\"ke\", e \"uid\"",
"Notes": "Notas",
"NotesPlacholder": "Comentários que você quer deixar para as configurações desta câmera.",
"Nothing exists": "Nada existe",
"Notification Sound": "Som De Notificação",
"Notification Video Length": "Notificação De Duração Do Vídeo",
"Number of Days to keep": "Número de dias para manter",
"ONVIF": "ONVIF",
"ONVIF Port": "ONVIF Porta",
"ONVIF Scanner": "ONVIF Scanner",
"ONVIFEventsNotAvailable": "ONVIF Eventos não Disponível",
"ONVIFnotCompliantProfileT": "A câmera não está ONVIF Perfil T Complacente",
"ONVIFnote": "Descubra os dispositivos ONVIF em redes fora da sua ou deixe em branco para escanear sua rede atual. <br>O nome de usuário e a senha podem ser deixados em branco.",
"OR": "OU",
"Object": "Objeto",
"Object Detection": "A Detecção De Objetos",
"Object Tag": "Objeto Tag",
"OpenCV Cascades": "OpenCV Cascatas",
"Operating Hours": "Horário De Funcionamento",
"Options": "Opções",
"Order Streams": "Ordenar transmissões",
"Output Method": "Método de saída",
"Password": "Senha",
"Password Again": "Senha novamente",
"Passwords don't match": "As senhas não combinam",
"Paste JSON here.": "Cole o JSON aqui.",
"Path": "Caminho",
"Permissions": "Permissões",
"Ping Failed": "O Ping",
"Please Check Your Settings": "Por Favor Verifique As Suas Definições",
"Please Wait for Completion": "Por favor, Aguarde a Conclusão, Dependendo do número de arquivos selecionados isso pode levar algum tempo. Atualizar para verificar novamente.",
"Plugin": "Plugin",
"Points": "Pontos <small>Ao adicionar pontos, clique na borda do polígono.</small>",
"Pop": "Pop",
"Popout Monitor on Event": "Popout Monitor de Eventos",
"Port": "Porta",
"Pose": "Pose",
"Poseidon": "Poseidon",
"Position X": "Posição X",
"Position Y": "Posição Y",
"Power Video Viewer": "Ligar visualizador de vídeo",
"Power Viewer": "Ligar visualizador",
"Preferences": "Preferências",
"Preset": "Pré-definição",
"Presets": "Predefinições",
"Preview": "Visualização",
"Primary Engine": "Motor Principal",
"Primary Input": "Entrada Principal",
"Privileges": "Privilégios",
"Probe Size": "Sonda Tamanho",
"Process Crashed for Monitor": "Processo de Monitor quebrado",
"Process Started": "Processo Iniciado",
"Process Unexpected Exit": "Saída inesperado do processo",
"Processor": "Processador",
"Profile": "Perfil",
"Quality": "Qualidade <small>1 para alta, 23 para Low</small>",
"Query": "Pesquisa",
"Quick Sync Video": "Quick Sync Video",
"RAM": "RAM",
"RTMP": "RTMP",
"RTMP Stream": "RTMP Stream",
"RTMP Stream Flags": "RTMP Fluxo de Sinalizadores",
"RTMPS": "RTMPS",
"RTSP": "RTSP",
"RTSP Transport": "Transporte RTSP",
"Range or Single": "Intervalo ou Único",
"Raspberry Pi": "Raspberry Pi",
"Rate": "Taxa <small>(FPS)</small>",
"Raw": "Matérias",
"Raw H.264 Stream": "Matérias H. 264 Stream",
"Reason": "Razão",
"Recent Videos": "Vídeos Recentes",
"Recommended": "Recomendado",
"Record": "Gravar",
"Record File Type": "Tipo de arquivo de gravação",
"Record Height": "Altura da gravação",
"Record Video Filter": "Gravar filtro de vídeo",
"Record Width": "Largura da gravação",
"Recorded Buffer": "Gravado Buffer",
"Recording": "Gravando",
"Recording FPS": "Gravação de FPS",
"Recording FPS Change on Start": "Gravação de FPS Alterar em Iniciar",
"Recording Flags": "Gravando Flags",
"Recording Segment Interval": "Intervalo de Segmento de Gravação <small>em minutos</small>",
"Recording Timeout": "Gravando Timeout <small>em minutos</small>",
"Recording Timestamp": "Gravando horário",
"Recording Watermark": "Gravando marca d'água",
"RecordingText": "Recomenda-se que você defina <b>Tipo de arquivo de gravação</b> para <b class=\"h_t_input h_t_jpeg h_t_socket\">WebM</b><b class=\"h_t_input h_t_mjpeg h_t_h264 h_t_hls h_t_mp4 h_t_local\">MP4</b> e <b>Video Codec</b> para <b class=\"h_t_input h_t_jpeg h_t_socket\">libvpx</b><b class=\"h_t_input h_t_h264 h_t_hls h_t_mp4\">copie ou </b><b class=\"h_t_input h_t_mjpeg h_t_h264 h_t_hls h_t_mp4 h_t_local\">libx264</b> porque seu <b>Tipo de Entrada</b> está definido para <b class=\"h_t_text\"></b>.",
"Refresh List of Cascades": "Atualizar Lista de Cascatas",
"Region": "Região",
"Region Editor": "Editor de região",
"Region Name": "Nome da região",
"RegionNote": "Os pontos só são salvos quando você pressiona <b>Salvar</b> na janela de <b>Configurações do Monitor</b>.",
"Regions": "Regiões",
"Remember Me": "Lembrar",
"Request": "Pedido",
"Require Object to be in Region": "Exigir Objeto a ser na Região",
"Reset Timer": "Temporizador de reinicialização",
"Restart": "Reinicie",
"Restart CRON": "Reinicie o CRON",
"Restart Core": "Reinicie O Núcleo",
"Restarting": "Reiniciar",
"Restarting Process": "Reiniciado processo",
"Retry Connection": "Tentar novamente a conexão <small>Número de vezes permitido falhar</small>",
"Retrying...": "Repetindo...",
"Right": "Direita <small>URL Address</small>",
"Right Stop": "Direita máxima <small>URL Address</small>",
"Rotate": "Rotação",
"S3-Based Network Storage": "S3-Armazenamento Em Rede",
"SFTP": "SFTP",
"SFTP (SSH File Transfer)": "SFTP (SSH File Transfer)",
"SFTP Error": "SFTP Erro",
"Save": "Salvar",
"Save Directory": "Salvar diretório",
"Save Events to SQL": "Salvar eventos no SQL",
"Save Frames to Events": "Salvar Quadros para Eventos",
"Save Links to Database": "Salvar Links para Banco de dados",
"Save Log in SQL": "Salvar log em SQL <small>Isso pode encher rapidamente.</small>",
"Save as": "Salvar como",
"Saved Filters": "Filtros Salvos",
"Saved Presets": "Salvo Presets",
"Saved Schedules": "Salvo Horários",
"Scan Settings": "Configurações de digitalização",
"Schedule": "Programação",
"Schedule Configuration Not Found": "Agenda De Configuração Não Encontrado",
"Schedules": "Horários",
"Search": "Procurar",
"Search Base": "Base de Pesquisa",
"Search Filter": "Procurar Filtro",
"Search Images": "Procurar Imagens",
"Second stream in feed": "Segundo fluxo de alimentação",
"Secure": "Seguro",
"Send Frames": "Enviar quadros <small>Envie os quadros a serem analisados</small>",
"Separate with commas, no spaces": "Separar com vírgulas, sem espaços",
"Server URL": "URL do servidor",
"Set to Watch Only": "Definir para apenas visualizar",
"Settings": "Configurações",
"Settings Changed": "Configurações Alteradas",
"SettingsChangedText": "Suas configurações foram salvas e aplicadas.",
"Shinobi": "Shinobi",
"Shinobi Streamer": "Shinobi Streamer",
"Show Logs": "Mostrar logs",
"Show Matrices": "Mostrar Matrizes",
"Show Matrix": "Mostrar A Matriz",
"Show Regions of Interest": "Mostrar as Regiões de Interesse",
"Show Stream HUD": "Show Stream HUD",
"Show Thumbnails in Video List": "Mostrar Miniaturas na Lista de filmes",
"Silent": "Silencioso",
"Simple": "Simples",
"Size (mb)": "Tamanho (mb)",
"Skip Ping": "Pular Ping",
"Snapshot": "Instantâneo",
"Snapshot Flags": "Instantâneo Bandeiras",
"Snapshots": "Instantâneos",
"Sort By": "Ordenar Por",
"Space Used": "Espaço Utilizado",
"Start": "Iniciar",
"Start Recording": "Iniciar gravação",
"Start Time": "Hora de Início",
"Start Time cannot be empty.": "Horário de início não pode estar vazia.",
"Started": "Iniciado",
"Starting": "Partida",
"State Configuration Not Found": "Estado De Configuração Não Encontrado",
"State Configuration has no monitors associated": "Configuração de estado não tem monitores associados",
"Status Changed": "Status Alterado",
"Status Indicator": "Indicador de status",
"Stop": "Parar",
"Stop Command": "Comando De Parada",
"Stop URL": "Parar URL",
"Stopped": "Parado",
"Storage Location": "Local De Armazenamento",
"Stream": "Transmissão",
"Stream Channel": "Canal De Transmissão Em Sequência",
"Stream Flags": "Flags de Transmissão",
"Stream Key": "Chave De Fluxo",
"Stream Timestamp": "Transmitindo horário",
"Stream Type": "Tipo de transmissão",
"Stream Watermark": "Transmitindo marca d'água",
"Stream to YouTube": "Transmitir para o YouTube",
"Stream to YouTube Flags": "Transmitir para o YouTube Flags",
"StreamText": "<p>Esta seção designará o método do fluxo primário e as configurações. Este fluxo será exibido no painel. Se você optar por usar HLS, JPEG ou MJPEG, então você pode consumir o fluxo através de outros programas.</p><p class=\"h_st_input h_st_jpeg\">Usar o fluxo de JPEG essencialmente desativa o fluxo primário e usa o compartimento de instantâneos para obter quadros.</p>",
"Streamer": "Transmissor",
"Streams": "Transmissões",
"Subdivision": "Subdivisão",
"Success": "Sucesso",
"Superuser": "Superusuário",
"Superuser Logs": "Logs do Superusuário",
"Switch on for Still Image": "Ligar para manter imagem",
"System": "Sistema",
"TCP": "TCP",
"TV Channel": "TV Canal",
"TV Channel Group": "Canal de TV do Grupo",
"TV Channel ID": "TV Canal ID",
"Text Box Color": "Cor da caixa de texto",
"Text Color": "Cor do texto",
"Themes": "Temas",
"There are no monitors that you can view with this account.": "Não há monitores que você pode ver com esta conta.",
"Thumbnail": "Miniatura",
"Time": "Horário",
"Time Left": "Tempo Restante",
"Time Occurred": "Tempo Ocorreu",
"Time-lapse": "Time-lapse",
"Time-lapse Tool": "Ferramenta Time-laps",
"Timelapse": "Timelapse",
"Timeout": "Tempo de espera",
"Timeout Reset on Next Event": "Tempo limite de Reset no Próximo Evento",
"Timeout Reset on Next Motion": "Timeout reiniciará no próximo movimento",
"Toggle Sidebar": "Mostrar barra lateral",
"Token": "Token",
"Top Left": "Superior esquerdo",
"Top Right": "Superior direito",
"Traditional (Watch-Only, Includes Buffer)": "Tradicional (Veja Só, Inclui Buffer)",
"Traditional Recording": "Tradicional De Gravação",
"Traditional Recording Flags": "A Tradicional Gravação De Sinalizadores",
"Train": "De trem",
"TrainConfirm": "Tem certeza de que deseja para começar a treinar? Isto pode demorar mais de 12 horas, com mais de 500 imagens. Isto irá consumir uma grande quantidade de recursos, como memória RAM ou CPU.",
"Trainer Engine": "Treinador Do Motor",
"Trigger Camera Groups": "Disparador Da Câmera Grupos",
"Trigger Group to Record": "Gatilho do Grupo para Gravar",
"Trigger Record": "Acionar registro",
"Trigger Successful": "Disparou bem sucedido",
"Trigger Threshold": "Gatilho Limite",
"UDP": "UDP",
"URL": "URL",
"URL Stop Timeout": "URL timeout máximo <small>Parar URL depois de X milissegundos</small>",
"US": "EUA",
"Unable to Launch": "Incapaz de executar",
"UnabletoLaunchText": "Salve primeiro o monitor novo. Em seguida, tente iniciar o editor da região.",
"Uniform": "Uniforme",
"Up": "Cima <small>URL Address</small>",
"Up Stop": "Cima máximo <small>URL Address</small>",
"Update": "Atualização",
"Update to Development": "Actualização para o Desenvolvimento",
"Update to Master": "Atualização para o Mestre de",
"Upload File": "Upload Do Arquivo",
"Uploaders": "Uploaders",
"Use Built-In": "Use Built-In",
"Use Global Amazon S3 Video Storage": "Uso Global Do Amazon S3 Armazenamento De Vídeo",
"Use Global Backblaze B2 Video Storage": "Use Global Backblaze B2 Armazenamento De Vídeo",
"Use Global Wasabi Hot Cloud Storage Video Storage": "Uso Global Do Wasabi Quente De Armazenamento Em Nuvem De Armazenamento De Vídeo",
"Use Global WebDAV Video Storage": "Uso Global Do WebDAV Armazenamento De Vídeo",
"Use HTML5 Play Method": "Utilizar o HTML5 Método Play",
"Use Max Storage Amount": "Usar O Max Quantidade De Armazenamento",
"Use coProcessor": "O uso de co-processador",
"User Not Found": "Usuário Não Encontrado",
"Username": "Usuário",
"VA-API": "VA-API",
"Value": "Valor",
"Video": "Vídeo",
"Video Bit Rate": "Taxa De Bits De Vídeo",
"Video Codec": "Codec De Vídeo",
"Video Filter": "Filtro de vídeo",
"Video Finished": "Vídeo finalizado",
"Video Length (minutes) and Motion Count per video": "Comprimento do vídeo (minutos) e Contagem de movimento por vídeo",
"Video Limit": "Limite de vídeo",
"Video Record Rate": "Taxa de gravação de vídeo <small>(FPS)</small>",
"Video Set": "Conjunto De Vídeo",
"Video Status": "Status do Vídeo",
"Video and Time Span (Minutes)": "Vídeo e intervalo de tempo (minutos)",
"Video stream only from first feed": "Fluxo de vídeo somente a partir de primeiro de alimentação",
"Video streams only": "Fluxos de vídeo informativo",
"Videos": "Vídeos",
"Videos List": "Lista de vídeos",
"Videos Merge": "Vídeos De Mesclagem",
"Warning": "Aviso",
"Wasabi Hot Cloud Storage": "Wasabi Quente De Armazenamento Em Nuvem",
"Wasabi Hot Cloud Storage Upload Error": "Wasabi Quente De Armazenamento Na Nuvem, Erro De Upload",
"Watch": "Assistir",
"Watch Only": "Visualizar apenas",
"Watch-Only": "Assistir-Apenas",
"Watching": "Assistindo",
"Web Page": "Página Da Web",
"WebDAV": "WebDAV",
"WebM (libvpx)": "WebM (libvpx)",
"Webdav Error": "Erro Webdav",
"WebdavErrorText": "Não é possível salvar. Você criou os diretórios da câmera dentro do seu diretório de escolhido para isso?",
"WebdavErrorTextCreatingDir": "Não é possível criar diretório.",
"WebdavErrorTextTryCreatingDir": "Não é possível salvar. Tentando criar o directório.",
"Webhook": "Webhook",
"Webhook URL": "Webhook URL",
"Websocket": "Websocket",
"Websocket Connected": "Websocket conectado",
"Websocket Disconnected": "Websocket desconectado",
"Width": "Largura",
"X Point": "X Ponto",
"Y Point": "Y Ponto De",
"Yes": "Sim",
"Zip and Download": "Zip e Download",
"Zoom In": "Mais Zoom <small>URL Address</small>",
"Zoom In Stop": "Parar mais zoom <small>URL Address</small>",
"Zoom Out": "Menos Zoom <small>URL Address</small>",
"Zoom Out Stop": "Parar menos zoom <small>URL Address</small>",
"a day": "um dia",
"a few seconds": "alguns segundos",
"a minute": "um minuto",
"a month": "um mês",
"a year": "um ano",
"aac": "aac",
"aac (Default)": "aac (Padrão)",
"ac3": "ac3",
"accountId": "ID da conta",
"ago": "atrás",
"an hour": "uma hora",
"applicationKey": "Chave De Aplicativo",
"aws_accessKeyId": "Id Da Chave De Acesso",
"aws_secretAccessKey": "A Chave De Acesso Secreta",
"bindDN": "Vincular DN",
"blankPassword": "Deixe em branco para manter a mesma senha",
"clientStreamFailedattemptingReconnect": "A verificação ctream do lado do cliente falhou, tentando reconectar.",
"coProcess Crashed for Monitor": "coProcess Caiu para Monitor",
"coProcess Unexpected Exit": "coProcess Inesperado Sair",
"coProcessor": "co-processador",
"coProcessor Started": "co-processador matemático Começou",
"coProcessor Stopped": "co-processador Parado",
"coProcessorTextStarted": "co-processador matemático foi iniciado pela CPU, apenas saídas.",
"coProcessorTextStopped": "co-processador terminou.",
"confirmDeleteFilter": "Deseja excluir este filtro? Você não poderá recuperá-lo.",
"copy": "copiar",
"cuvid": "cuvid (NVIDIA NVENC)",
"days": "dias",
"deleteMonitorStateText1": "Você deseja excluir este Monitor Estados Preset?",
"deleteMonitorStateText2": "Você deseja excluir este Monitor do Preset?",
"drm": "DRM objeto de partilha",
"dropBoxSuccess": "Sucesso! Arquivos salvos em seu Dropbox.",
"dxva2": "dxva2 (Vídeo DirectX, Windows)",
"failedLoginText1": "Falhou o início de sessão muitas vezes. Você deve aguardar 15 minutos antes de tentar novamente.",
"failedLoginText2": "Por favor verifique as suas credenciais de início de sessão.",
"flv": "flv",
"for Global Access": "para Acesso Global",
"h264_cuvid": "H. 264 CUVID",
"h264_mmal": "H. 264 (Raspberry Pi)",
"h264_nvenc": "H. 264 NVENC (NVIDIA HARDWARE de Aceleração)",
"h264_omx": "H. 264 openMAX (Raspberry Pi)",
"h264_qsv": "H. 264 (Quick Sync Vídeo)",
"h264_vaapi": "H. 264 VA-API (Intel HARDWARE de Aceleração)",
"h265BrowserText1": "Se você estiver tentando reproduzir um H. 265 arquivo, você pode precisar para fazer o download e abri-lo em outro aplicativo, como o VLC.",
"hevc_cuvid": "H. 265 CUVID",
"hevc_nvenc": "H. 265 NVENC (NVIDIA HARDWARE de Aceleração)",
"hevc_qsv": "H. 265 (Quick Sync Vídeo)",
"hevc_vaapi": "H. 265 VA-API (Intel HARDWARE de Aceleração)",
"hours": "horas",
"hwaccel": "A Aceleração Do Motor",
"hwaccel_device": "HWAccel Dispositivo",
"hwaccel_vcodec": "O Decodificador De Vídeo",
"ifYouSeeNothingHereThenJustDownloadThisPackageOfCascadesDropThemIntoPluginsopencvcascadesThenPressRefresh": "Se você não vê nada aqui, então, basta baixar este pacote de <a href=\"https://cdn.shinobi.video/weights/cascades.zip\">cascatas</a>. Solte-os em <code>plugins/opencv/cascatas</code> , em seguida, pressione atualizar <i class=\"fa fa-retweet\"></i>.",
"in": "em",
"in Days": "em Dias",
"itIsRecommendedThatYouSetRecordFileTypeToWebmmp4AndVideoCodecToLibvpxcopyOrLibx264BecauseYourInputTypeIsSetTo": "É recomendável que você defina o <b>Registro de Tipo de Arquivo</b> para <b class=\"h_t_input h_t_jpeg h_t_socket\">WebM</b><b class=\"h_t_input h_t_mjpeg h_t_h264 h_t_hls h_t_mp4 h_t_local\">, MP4</b> e <b>Codec de Vídeo</b> para <b class=\"h_t_input h_t_jpeg h_t_socket\">libvpx</b><b class=\"h_t_input h_t_h264 h_t_hls h_t_mp4\">copiar ou </b><b class=\"h_t_input h_t_mjpeg h_t_h264 h_t_hls h_t_mp4 h_t_local\">libx264</b> porque o <b>Tipo de Entrada</b> é definida como <b class=\"h_t_text\"></b>.",
"libmp3lame": "libmp3lame",
"libopus": "libopus",
"libvorbis (Default)": "libvorbis (Padrão)",
"libvpx (Default)": "libvpx (Padrão)",
"libvpx-vp9": "libvpx-vp9",
"libx264": "libx264",
"libx264 (Default)": "libx264 (Padrão)",
"libx265": "libx265",
"migrateText1": "<b>Tipo de entrada</b> não pode ser analisada. Por favor configure-a manualmente.",
"minutes": "minutos",
"mjpeg_cuvid": "MJPEG CUVID",
"modifyVideoText1": "Método não existe. Verifique se o último valor da URL não está em branco",
"monSavedButNotCopied": "O monitor foi salvo, mas não é copiado para qualquer outro monitor.",
"monitorEditFailedMaxReached": "Sua conta atingiu o número máximo de câmeras que podem ser criadas. Fale com um administrador se você gostaria que isso mudasse.",
"monitorEditText1": "Dados inválidos. Verifique se esta é uma string de importação válida.",
"monitorEditText2": "String de detalhes inválida. Certifique-se de ser uma string JSON e não um objeto comum que está sendo informado",
"monitorGetText1": "Requisição incompleta. Remova a última barra da URL ou informe um valor válido",
"months": "meses",
"mpeg2_mmal": "MPEG-2 (Raspberry Pi)",
"mpeg2_qsv": "MPEG2 (Quick Sync Vídeo)",
"mpeg4_cuvid": "MPEG4 CUVID",
"mpeg4_mmal": "MPEG-4 (Raspberry Pi)",
"noSpecialCharacters": "Sem espaços ou caracteres especiais.",
"notPermitted1": "Esta ação não é permitido pelo administrador da sua conta.'",
"on": "ligado",
"on Error": "em Erro",
"opencvCascadesText": "Se você não vê nada aqui, então, basta baixar este pacote de <a href=\"https://cdn.shinobi.video/weights/cascades.zip\">cascatas</a>. Solte-os em <code>plugins/opencv/cascatas</code> , em seguida, pressione atualizar <i class=\"fa fa-retweet\"></i>.",
"possibleInternalError": "Possível Erro Interno",
"postDataBroken": "Verifique o formato JSON. Certifique-é stringified e definidos em 'dados'",
"powerVideoEventLimit": "Você tem de definir um alto limite de evento. Tem certeza de que quer fazer esse pedido?",
"privateKey": "Chave Privada",
"qsv": "qsv",
"sizePurgeLockedText": "O Tamanho de Purga de Bloqueio (deleteOverMax) parece ter falhado para desbloquear. Desbloqueio agora...",
"skipPingText1": "Tente definir \"Ignorar Ping\" para Sim.",
"startUpText0": "Verificação de tamanho para vídeos",
"startUpText1": "Verificação de fim de tamanho para vídeos",
"startUpText2": "Todos usuário verificados. Aguarde para fechar arquivos abertos e remover arquivos por limite de usuário",
"startUpText3": " Esperando dar uma verificação de vídeo inacabada algum tempo. 3 segundos",
"startUpText4": "Iniciando todos os monitores para assistir e gravar",
"startUpText5": "Shinobi está pronto.",
"startUpText6": "Órfão Vídeos Encontrados e Inserido",
"superAdminText": "\"super.json\" não existe. Por favor, renomeie \"super.sample.json\" para \"super.json\".",
"superAdminTitle": "Shinobi : Super Admin",
"thisIsHowTheSystemWillIdentifyTheDataForThisStreamYouCannotChangeTheMonitorIdOnceYouHavePressedSaveIfYouWantYouCanMakeTheMonitorIdMoreHumanReadableBeforeYouContinueyouCanDuplicateAMonitorByModifyingTheMonitorIdThenPressingSaveYouCannotUseTheIdOfAMonitorThatAlreadyExistsOrItWillSaveOverThatMonitorsDatabaseInformation": "<div class=\"am_notice am_notice_new\">Esta é a forma como o sistema irá identificar os dados para este fluxo. Você não pode alterar o <b>ID do Monitor</b> depois de ter pressionado salvar. Se você quiser você pode fazer o <b>Monitor de IDENTIFICAÇÃO</b> mais legível para humanos antes de continuar.</div><div class=\"am_notice am_notice_edit\">Você pode duplicar um monitor modificando o <b>Monitor de IDENTIFICAÇÃO</b> , em seguida, pressionando salvar. Você <b>não pode</b> usar o ID de um monitor que já existe, ou ele vai economizar mais que o monitor de informações do banco de dados.</div>",
"thisSectionTellsShinobiHowToConsumeAStreamForOptimalPerformanceTryTuningYourCamerasInternalSettingsFindTheFollowingOptionsAndSetThemAsShownToFindYourCameraYouCanUseTheBuiltInOnvifScannerOfShinobiSomeOnvifCamerasRequireTheUseOfAManagementToolToModifyTheirInternalSettingsIfYouCantFindYourCamerasYouCanTryOnvifDeviceManagerForWindowsFramerateFpsHigh1015FpsLow25FpsiframeInterval1bitRateTypeVbrVariableBitRatebitRateBetween256kbps1000kbpsIfYouNeedHelpFiguringOutWhatInputTypeYourCameraIsYouCanTakeALookInTheCameraUrlsListOnTheShinobiWebsite": "<p>Esta secção diz Shinobi como consumir um fluxo. Para melhor desempenho, tente afinar a sua câmera configurações internas. Encontrar as seguintes opções e defina-as como mostrado. Para encontrar a sua câmera, você pode usar o <b>construída em ONVIF Scanner</b> de Shinobi. Alguns ONVIF câmeras exigem o uso de uma ferramenta de gestão para modificar suas configurações internas. Se você não pode encontrar câmeras, você pode tentar <a href=\"https://s3.amazonaws.com/cloudcamio/odm-v2.2.250.msi\">ONVIF Gerenciador de Dispositivos do Windows</a>.</p> <ul><li><b>A taxa de quadros (FPS) :</b> Alta : de 10 a 15 FPS, Baixa : de 2 a 5 FPS</li><li><b>I-intervalo :</b> 1</li><li><b>A Taxa de bits do Tipo :</b> VBR (Taxa de Bit Variável)</li><li><b>Taxa de bits :</b> entre 256kbps - 1000kbps</li></ul> <p>Se você precisar de ajuda para descobrir que tipo de entrada de sua câmera é que você pode dar uma olhada na <a href=\"http://shinobi.video/docs/cameras\" target=\"_blank\">Câmera Lista de URLs</a> no Shinobi site.</p>",
"thisSectionWillDesignateThePrimaryMethodOfStreamingOutAndItsSettingsThisStreamWillBeDisplayedInTheDashboardIfYouChooseToUseHlsJpegOrMjpegThenYouCanConsumeTheStreamThroughOtherProgramsusingJpegStreamEssentiallyTurnsOffThePrimaryStreamAndUsesTheSnapshotBinToGetFrames": "<p>Esta seção irá designar o principal método de transmissão e suas configurações. Este fluxo será exibida no painel de controle. Se você escolher usar HLS, JPEG, ou MJPEG, em seguida, você pode consumir o fluxo através de outros programas.</p><p class=\"h_st_input h_st_jpeg\">Usando JPEG fluxo essencialmente desliga o fluxo principal e usa o instantâneo bin para obter quadros.</p>",
"total": "total",
"updateKeyText1": "\"updateKey\" faltando em \"conf.json\", não é possível atualizar dessa maneira até você adicionar",
"updateKeyText2": "\"updateKey\" incorreta",
"vaapi": "vaapi (VA-API)",
"vda": "vda (Apple VDA Aceleração de Hardware)",
"vdpau": "vdpau",
"videotoolbox": "videotoolbox",
"vp8_cuvid": "VP8 NVENC (NVIDIA HARDWARE de Aceleração)",
"vp8_qsv": "VP8 (Quick Sync Vídeo)",
"vp9_cuvid": "VP9 NVENC (NVIDIA HARDWARE de Aceleração)",
"whenTheWidthAndHeightBoxesAreShownYouShouldSetThemTo640x480OrBelowThisWillOptimizeTheReadSpeedOfFrames\n": "<p>Quando as caixas Largura e Altura são mostradas, você deve configurá-los para 640x480 ou abaixo. Isto irá otimizar a velocidade de leitura dos quadros.</p>\n<p class=\"shinobi-detector-msg\"></p>",
"years": "anos"
}

View File

@ -121,7 +121,9 @@ module.exports = function(s,config,lang){
var adminUsersSelected = null
try{
var success = function(){
var chosenConfig = config
if(req && res){
chosenConfig = s.getConfigWithBranding(req.hostname)
res.setHeader('Content-Type', 'application/json');
var ip = req.headers['cf-connecting-ip']||req.headers["CF-Connecting-IP"]||req.headers["'x-forwarded-for"]||req.connection.remoteAddress;
var resp = {
@ -143,7 +145,7 @@ module.exports = function(s,config,lang){
ip : ip,
$user:userSelected,
users:adminUsersSelected,
config:config,
config: chosenConfig,
lang:lang
})
}

20
libs/branding.js Normal file
View File

@ -0,0 +1,20 @@
module.exports = function(s,config,lang,app,io){
if(config.showPoweredByShinobi === undefined){config.showPoweredByShinobi=true}
if(config.poweredByShinobi === undefined){config.poweredByShinobi='Powered by Shinobi.Systems'}
if(config.poweredByShinobiClass === undefined){config.poweredByShinobiClass='margin:15px 0 0 0;text-align:center;color:#777;font-family: sans-serif;text-transform: uppercase;letter-spacing: 3;font-size: 8pt;'}
if(config.webPageTitle === undefined){config.webPageTitle='Shinobi'}
if(config.showLoginCardHeader === undefined){config.showLoginCardHeader=true}
if(config.webFavicon === undefined){config.webFavicon='libs/img/icon/favicon.ico'}
if(config.logoLocation76x76 === undefined){config.logoLocation76x76='libs/img/icon/apple-touch-icon-76x76.png'}
if(config.logoLocation76x76Link === undefined){config.logoLocation76x76Link='https://shinobi.video'}
if(config.logoLocation76x76Style === undefined){config.logoLocation76x76Style='border-radius:50%'}
if(config.showLoginSelector === undefined){config.showLoginSelector=true}
s.getConfigWithBranding = function(domain){
var configCopy = Object.assign({},config)
if(config.brandingConfig && config.brandingConfig[domain]){
return Object.assign(configCopy,config.brandingConfig[domain])
}
return config
}
}

View File

@ -9,14 +9,6 @@ module.exports = function(s){
config.productType = 'CE'
}
//config defaults
if(config.showPoweredByShinobi === undefined){config.showPoweredByShinobi=true}
if(config.poweredByShinobi === undefined){config.poweredByShinobi='Powered by Shinobi.Systems'}
if(config.poweredByShinobiClass === undefined){config.poweredByShinobiClass='margin:15px 0 0 0;text-align:center;color:#777;font-family: sans-serif;text-transform: uppercase;letter-spacing: 3;font-size: 8pt;'}
if(config.webPageTitle === undefined){config.webPageTitle='Shinobi'}
if(config.showLoginCardHeader === undefined){config.showLoginCardHeader=true}
if(config.logoLocation76x76 === undefined){config.logoLocation76x76='libs/img/icon/apple-touch-icon-76x76.png'}
if(config.logoLocation76x76Link === undefined){config.logoLocation76x76Link='https://shinobi.video'}
if(config.logoLocation76x76Style === undefined){config.logoLocation76x76Style='border-radius:50%'}
if(config.cpuUsageMarker === undefined){config.cpuUsageMarker='%Cpu'}
if(config.customCpuCommand === undefined){config.customCpuCommand=null}
if(config.autoDropCache === undefined){config.autoDropCache=true}

View File

@ -7,7 +7,13 @@ module.exports = function(s,config,lang,app,io){
}catch(er){
console.error(er)
console.log('There was an error loading your definition file.')
var definitions = require(s.location.definitions+'/en_CA.json');
try{
var definitions = require(s.location.definitions+'/en_CA.js')(s,config,lang)
}catch(er){
console.error(er)
console.log('There was an error loading your definition file.')
var definitions = require(s.location.definitions+'/en_CA.json');
}
}
//load defintions dynamically
s.copySystemDefaultDefinitions = function(){

View File

@ -57,12 +57,14 @@ module.exports = function(s,config){
e.triggerTimer = {}
var regions = s.createPamDiffRegionArray(regionJson,globalColorThreshold,globalSensitivity,fullFrame)
s.group[e.ke].mon[e.id].pamDiff = new PamDiff({
var pamDiffOptions = {
grayscale: 'luminosity',
regions : regions.forPam,
drawMatrix : e.details.detector_show_matrix
});
regions : regions.forPam
}
if(e.details.detector_show_matrix==='1'){
pamDiffOptions.response = 'bounds'
}
s.group[e.ke].mon[e.id].pamDiff = new PamDiff(pamDiffOptions);
s.group[e.ke].mon[e.id].p2p = new P2P()
var regionArray = Object.values(regionJson)
if(config.detectorMergePamRegionTriggers === true){
@ -94,7 +96,7 @@ module.exports = function(s,config){
++filteredCount
if(!err1 && !err2)++filteredCountSuccess
if(filteredCount === trigger.merged.length && filteredCountSuccess > 0){
detectorObject.doObjectDetection = (s.ocv && e.details.detector_use_detect_object === '1')
detectorObject.doObjectDetection = (s.isAtleatOneDetectorPluginConnected && e.details.detector_use_detect_object === '1')
s.triggerEvent(detectorObject)
}
})
@ -106,7 +108,7 @@ module.exports = function(s,config){
s.checkMaximumSensitivity(e, region, detectorObject, function(err1) {
s.checkTriggerThreshold(e, region, detectorObject, function(err2) {
if(!err1 && !err2){
detectorObject.doObjectDetection = (s.ocv && e.details.detector_use_detect_object === '1')
detectorObject.doObjectDetection = (s.isAtleatOneDetectorPluginConnected && e.details.detector_use_detect_object === '1')
s.triggerEvent(detectorObject)
}
})
@ -161,7 +163,7 @@ module.exports = function(s,config){
s.checkMaximumSensitivity(e, region, detectorObject, function(err1) {
s.checkTriggerThreshold(e, region, detectorObject, function(err2) {
if(!err1 && ! err2){
detectorObject.doObjectDetection = (s.ocv && e.details.detector_use_detect_object === '1')
detectorObject.doObjectDetection = (s.isAtleatOneDetectorPluginConnected && e.details.detector_use_detect_object === '1')
s.triggerEvent(detectorObject)
}
})
@ -176,13 +178,17 @@ module.exports = function(s,config){
s.group[e.ke].mon[e.id].pamDiff.on('diff', (data) => {
data.trigger.forEach(function(trigger){
s.filterTheNoise(e,noiseFilterArray,regions,trigger,function(){
s.createMatrixFromPamTrigger(trigger)
buildTriggerEvent(trigger)
})
})
})
}else{
s.group[e.ke].mon[e.id].pamDiff.on('diff', (data) => {
data.trigger.forEach(buildTriggerEvent)
data.trigger.forEach(function(trigger){
s.createMatrixFromPamTrigger(trigger)
buildTriggerEvent(trigger)
})
})
}
}
@ -305,6 +311,7 @@ module.exports = function(s,config){
name.push(trigger.name + ' ('+trigger.percent+'%)')
++n
sum += trigger.percent
s.createMatrixFromPamTrigger(trigger)
if(trigger.matrix)matrices.push(trigger.matrix)
})
var average = sum / n
@ -318,6 +325,7 @@ module.exports = function(s,config){
}
}else{
var trigger = data.trigger[0]
s.createMatrixFromPamTrigger(trigger)
trigger.matrices = [trigger.matrix]
}
return trigger
@ -357,4 +365,28 @@ module.exports = function(s,config){
if(callback)callback(foundInRegion,collisions)
return foundInRegion
}
s.createMatrixFromPamTrigger = function(trigger){
if(
trigger.minX &&
trigger.maxX &&
trigger.minY &&
trigger.maxY
){
var coordinates = [
{"x" : trigger.minX, "y" : trigger.minY},
{"x" : trigger.maxX, "y" : trigger.minY},
{"x" : trigger.maxX, "y" : trigger.maxY}
]
var width = Math.sqrt( Math.pow(coordinates[1].x - coordinates[0].x, 2) + Math.pow(coordinates[1].y - coordinates[0].y, 2));
var height = Math.sqrt( Math.pow(coordinates[2].x - coordinates[1].x, 2) + Math.pow(coordinates[2].y - coordinates[1].y, 2))
trigger.matrix = {
x: coordinates[0].x,
y: coordinates[0].y,
width: width,
height: height,
tag: trigger.name
}
}
return trigger
}
}

View File

@ -185,6 +185,7 @@ module.exports = function(s,config,lang){
d.details.matrices = reviewedMatrix
}
}
var eventTime = new Date()
//motion counter
if(filter.addToMotionCounter && filter.record){
if(!s.group[d.ke].mon[d.id].detector_motion_count){
@ -254,7 +255,7 @@ module.exports = function(s,config,lang){
}
//save this detection result in SQL, only coords. not image.
if(filter.save && currentConfig.detector_save === '1'){
s.sqlQuery('INSERT INTO Events (ke,mid,details,time) VALUES (?,?,?,?)',[d.ke,d.id,detailString,new Date()])
s.sqlQuery('INSERT INTO Events (ke,mid,details,time) VALUES (?,?,?,?)',[d.ke,d.id,detailString,eventTime])
}
if(currentConfig.detector_notrigger === '1'){
var detector_notrigger_timeout
@ -273,7 +274,7 @@ module.exports = function(s,config,lang){
detector_timeout = parseFloat(currentConfig.detector_timeout)
}
if(filter.record && d.mon.mode=='start'&&currentConfig.detector_trigger==='1'&&currentConfig.detector_record_method==='sip'){
s.createEventBasedRecording(d)
s.createEventBasedRecording(d,moment(eventTime).subtract(5,'seconds').format('YYYY-MM-DDTHH-mm-ss'))
}else if(filter.record && d.mon.mode!=='stop'&&currentConfig.detector_trigger=='1'&&currentConfig.detector_record_method==='hot'){
if(!d.auth){
d.auth=s.gid();
@ -331,7 +332,8 @@ module.exports = function(s,config,lang){
d.cx={f:'detector_trigger',id:d.id,ke:d.ke,details:d.details,doObjectDetection:d.doObjectDetection};
s.tx(d.cx,'DETECTOR_'+d.ke+d.id);
}
s.createEventBasedRecording = function(d){
s.createEventBasedRecording = function(d,fileTime){
if(!fileTime)fileTime = s.formattedTime()
d.mon = s.group[d.ke].mon_conf[d.id]
var currentConfig = s.group[d.ke].mon[d.id].details
if(currentConfig.detector !== '1'){
@ -355,7 +357,7 @@ module.exports = function(s,config,lang){
if(!s.group[d.ke].mon[d.id].eventBasedRecording.process){
s.group[d.ke].mon[d.id].eventBasedRecording.allowEnd = false;
var runRecord = function(){
var filename = s.formattedTime()+'.mp4'
var filename = fileTime+'.mp4'
s.userLog(d,{type:lang["Traditional Recording"],msg:lang["Started"]})
//-t 00:'+s.timeObject(new Date(detector_timeout * 1000 * 60)).format('mm:ss')+'
s.group[d.ke].mon[d.id].eventBasedRecording.process = spawn(config.ffmpegDir,s.splitForFFPMEG(('-loglevel warning -analyzeduration 1000000 -probesize 1000000 -re -i "'+s.dir.streams+'/'+d.ke+'/'+d.id+'/detectorStream.m3u8" -c:v copy -strftime 1 "'+s.getVideoDirectory(d.mon) + filename + '"')))

View File

@ -924,6 +924,7 @@ module.exports = function(s,config,lang){
}
var timeNow = new Date()
s.sqlQuery('INSERT INTO `Timelapse Frames` (ke,mid,details,filename,size,time) VALUES (?,?,?,?,?,?)',[e.ke,e.id,s.s(details),filename,fileStats.size,timeNow])
s.setDiskUsedForGroup(e,fileStats.size / 1000000)
})
s.group[e.ke].mon[e.id].recordTimelapseWriter = fileStream
}

View File

@ -77,13 +77,27 @@ module.exports = function(s,config,lang,io){
user.size = 0
user.limit = userDetails.size
s.sqlQuery('SELECT * FROM Videos WHERE ke=? AND status!=?',[user.ke,0],function(err,videos){
if(videos && videos[0]){
videos.forEach(function(video){
user.size += video.size
s.sqlQuery('SELECT * FROM `Timelapse Frames` WHERE ke=?',[user.ke],function(err,timelapseFrames){
s.sqlQuery('SELECT * FROM `Files` WHERE ke=?',[user.ke],function(err,files){
if(videos && videos[0]){
videos.forEach(function(video){
user.size += video.size
})
}
if(timelapseFrames && timelapseFrames[0]){
timelapseFrames.forEach(function(frame){
user.size += frame.size
})
}
if(files && files[0]){
files.forEach(function(file){
user.size += file.size
})
}
s.systemLog(user.mail+' : '+lang.startUpText1+' : '+videos.length,user.size)
callback()
})
}
s.systemLog(user.mail+' : '+lang.startUpText1+' : '+videos.length,user.size)
callback()
})
})
}
var loadCloudDiskUseForUser = function(user,callback){

View File

@ -131,9 +131,9 @@ module.exports = function(s,config){
if(!s.group[e.ke].sizePurgeQueue){s.group[e.ke].sizePurgeQueue=[]}
if(!e.limit||e.limit===''){e.limit=10000}else{e.limit=parseFloat(e.limit)}
//save global space limit for group key (mb)
s.group[e.ke].sizeLimit=e.limit;
s.group[e.ke].sizeLimit = e.limit
//save global used space as megabyte value
s.group[e.ke].usedSpace=e.size/1000000;
s.group[e.ke].usedSpace = (e.size || 0) / 1000000
//emit the changes to connected users
s.sendDiskUsedAmountToClients(e)
}

View File

@ -414,4 +414,80 @@ module.exports = function(s,config,lang){
file.pipe(res)
return file
}
s.createVideoFromTimelapse = function(timelapseFrames,callback){
var frames = timelapseFrames.reverse()
var ke = frames[0].ke
var mid = frames[0].mid
var finalFileName = frames[0].filename.split('T')[0]
var concatFiles = []
var createLocation
frames.forEach(function(frame,frameNumber){
var selectedDate = frame.filename.split('T')[0]
var fileLocationMid = `${frame.ke}/${frame.mid}_timelapse/${selectedDate}/`
frame.details = s.parseJSON(frame.details)
var fileLocation
if(frame.details.dir){
fileLocation = `${s.checkCorrectPathEnding(frame.details.dir)}`
}else{
fileLocation = `${s.dir.videos}`
}
fileLocation = `${fileLocation}${fileLocationMid}${frame.filename}`
concatFiles.push(fileLocation)
if(frameNumber === 0){
createLocation = fileLocationMid
}
})
var commandTempLocation = `${s.dir.streams}${ke}/${mid}/mergeJpegs_${finalFileName}.sh`
var finalMp4OutputLocation = `${s.dir.fileBin}${ke}/${mid}/${finalFileName}.mp4`
if(!s.group[ke].mon[mid].buildingTimelapseVideo){
if(!fs.existsSync(finalMp4OutputLocation)){
var currentFile = 0
var completionTimeout
var commandString = `ffmpeg -y -pattern_type glob -f image2pipe -vcodec mjpeg -r 2 -i - -q:v 1 -c:v libx264 -r 2 "${finalMp4OutputLocation}"`
fs.writeFileSync(commandTempLocation,commandString)
var videoBuildProcess = spawn('sh',[commandTempLocation])
videoBuildProcess.stderr.on('data',function(data){
console.log(data.toString())
clearTimeout(completionTimeout)
completionTimeout = setTimeout(function(){
if(currentFile === concatFiles.length - 1){
videoBuildProcess.kill('SIGTERM')
}
},4000)
})
videoBuildProcess.on('exit',function(data){
var timeNow = new Date()
var fileStats = fs.statSync(finalMp4OutputLocation)
var details = {}
s.sqlQuery('INSERT INTO `Files` (ke,mid,details,name,size,time) VALUES (?,?,?,?,?,?)',[ke,mid,s.s(details),finalFileName + '.mp4',fileStats.size,timeNow])
s.setDiskUsedForGroup({ke: ke},fileStats.size / 1000000)
fs.unlink(commandTempLocation,function(){
})
delete(s.group[ke].mon[mid].buildingTimelapseVideo)
})
var readFile = function(){
var filePath = concatFiles[currentFile]
console.log(filePath,currentFile,'/',concatFiles.length - 1)
videoBuildProcess.stdin.write(fs.readFileSync(filePath))
if(currentFile === concatFiles.length - 1){
//is last
}else{
setTimeout(function(){
++currentFile
readFile()
},500)
}
}
readFile()
s.group[ke].mon[mid].buildingTimelapseVideo = videoBuildProcess
callback({ok: true, fileExists: false, fileLocation: finalMp4OutputLocation, msg: lang['Started Building']})
}else{
callback({ok: false, fileExists: true, fileLocation: finalMp4OutputLocation, msg: lang['Already exists']})
}
}else{
callback({ok: false, fileExists: false, fileLocation: finalMp4OutputLocation, msg: lang.Building})
}
}
}

View File

@ -44,6 +44,8 @@ module.exports = function(s,config,lang,io){
if(config.renderPaths.dashcam === undefined){config.renderPaths.dashcam='pages/dashcam'}
//embeddable widget page
if(config.renderPaths.embed === undefined){config.renderPaths.embed='pages/embed'}
//timelapse page (not modal)
if(config.renderPaths.timelapse === undefined){config.renderPaths.timelapse='pages/timelapse'}
//mjpeg full screen page
if(config.renderPaths.mjpeg === undefined){config.renderPaths.mjpeg='pages/mjpeg'}
//gridstack only page

View File

@ -19,7 +19,7 @@ module.exports = function(s,config,lang,app,io){
s.renderPage = function(req,res,paths,passables,callback){
passables.window = {}
passables.originalURL = s.getOriginalUrl(req)
passables.config = config
passables.config = s.getConfigWithBranding(req.hostname)
res.render(paths,passables,callback)
}
//child node proxy check
@ -94,7 +94,7 @@ module.exports = function(s,config,lang,app,io){
* Page : Login Screen
*/
app.get(config.webPaths.home, function (req,res){
s.renderPage(req,res,config.renderPaths.index,{lang:lang,config:config,screen:'dashboard'},function(err,html){
s.renderPage(req,res,config.renderPaths.index,{lang:lang,config: s.getConfigWithBranding(req.hostname),screen:'dashboard'},function(err,html){
if(err){
s.systemLog(err)
}
@ -105,7 +105,7 @@ module.exports = function(s,config,lang,app,io){
* Page : Administrator Login Screen
*/
app.get(config.webPaths.admin, function (req,res){
s.renderPage(req,res,config.renderPaths.index,{lang:lang,config:config,screen:'admin'},function(err,html){
s.renderPage(req,res,config.renderPaths.index,{lang:lang,config: s.getConfigWithBranding(req.hostname),screen:'admin'},function(err,html){
if(err){
s.systemLog(err)
}
@ -117,7 +117,7 @@ module.exports = function(s,config,lang,app,io){
*/
app.get(config.webPaths.super, function (req,res){
s.renderPage(req,res,config.renderPaths.index,{lang:lang,config:config,screen:'super'},function(err,html){
s.renderPage(req,res,config.renderPaths.index,{lang:lang,config: s.getConfigWithBranding(req.hostname),screen:'super'},function(err,html){
if(err){
s.systemLog(err)
}
@ -183,7 +183,7 @@ module.exports = function(s,config,lang,app,io){
failedLogin: true,
message: lang.failedLoginText1,
lang: s.copySystemDefaultLanguage(),
config: config,
config: s.getConfigWithBranding(req.hostname),
screen: screenChooser(req.params.screen)
},function(err,html){
if(err){
@ -241,7 +241,7 @@ module.exports = function(s,config,lang,app,io){
failedLogin: true,
message: lang.failedLoginText2,
lang: s.copySystemDefaultLanguage(),
config: config,
config: s.getConfigWithBranding(req.hostname),
screen: screenChooser(req.params.screen)
},function(err,html){
if(err){
@ -284,7 +284,7 @@ module.exports = function(s,config,lang,app,io){
s.sqlQuery('SELECT * FROM Monitors WHERE ke=? AND type=?',[r.ke,"dashcam"],function(err,rr){
req.resp.mons=rr;
renderPage(config.renderPaths.dashcam,{
// config: config,
// config: s.getConfigWithBranding(req.hostname),
$user: req.resp,
lang: r.lang,
define: s.getDefinitonFile(r.details.lang),
@ -296,7 +296,7 @@ module.exports = function(s,config,lang,app,io){
s.sqlQuery('SELECT * FROM Monitors WHERE ke=? AND type=?',[r.ke,"socket"],function(err,rr){
req.resp.mons=rr;
renderPage(config.renderPaths.streamer,{
// config: config,
// config: s.getConfigWithBranding(req.hostname),
$user: req.resp,
lang: r.lang,
define: s.getDefinitonFile(r.details.lang),
@ -309,7 +309,7 @@ module.exports = function(s,config,lang,app,io){
s.sqlQuery('SELECT uid,mail,details FROM Users WHERE ke=? AND details LIKE \'%"sub"%\'',[r.ke],function(err,rr) {
s.sqlQuery('SELECT * FROM Monitors WHERE ke=?',[r.ke],function(err,rrr) {
renderPage(config.renderPaths.admin,{
config: config,
config: s.getConfigWithBranding(req.hostname),
$user: req.resp,
$subs: rr,
$mons: rrr,
@ -323,7 +323,7 @@ module.exports = function(s,config,lang,app,io){
//not admin user
renderPage(config.renderPaths.home,{
$user:req.resp,
config:config,
config: s.getConfigWithBranding(req.hostname),
lang:r.lang,
define:s.getDefinitonFile(r.details.lang),
addStorage:s.dir.addStorage,
@ -336,7 +336,7 @@ module.exports = function(s,config,lang,app,io){
default:
renderPage(config.renderPaths.home,{
$user:req.resp,
config:config,
config: s.getConfigWithBranding(req.hostname),
lang:r.lang,
define:s.getDefinitonFile(r.details.lang),
addStorage:s.dir.addStorage,
@ -689,7 +689,7 @@ module.exports = function(s,config,lang,app,io){
s.renderPage(req,res,page,{
data:Object.assign(req.params,req.query),
baseUrl:req.protocol+'://'+req.hostname,
config:config,
config: s.getConfigWithBranding(req.hostname),
lang:user.lang,
$user:user,
monitors:r,
@ -1137,7 +1137,13 @@ module.exports = function(s,config,lang,app,io){
if(req.params.date.indexOf('-') === -1 && !isNaN(req.params.date)){
req.params.date = parseInt(req.params.date)
}
var selectedDate = new Date(req.params.date)
var isMp4Call = false
var selectedDate = req.params.date
if(typeof req.params.date === 'string' && req.params.date.indexOf('.') > -1){
isMp4Call = true
selectedDate = req.params.date.split('.')[0]
}
selectedDate = new Date(selectedDate)
var utcSelectedDate = new Date(selectedDate.getTime() + selectedDate.getTimezoneOffset() * 60000)
req.query.start = moment(utcSelectedDate).format('YYYY-MM-DD HH:mm:ss')
var dayAfter = utcSelectedDate
@ -1166,13 +1172,33 @@ module.exports = function(s,config,lang,app,io){
if(!req.query.limit||req.query.limit==''){req.query.limit=288}
req.sql+=' ORDER BY `time` DESC LIMIT '+req.query.limit+'';
s.sqlQuery(req.sql,req.ar,function(err,r){
if(r && r[0]){
r.forEach(function(file){
file.details = s.parseJSON(file.details)
})
res.end(s.prettyPrint(r))
if(isMp4Call){
if(r && r[0]){
s.createVideoFromTimelapse(r,function(response){
if(response.fileExists){
res.setHeader('Content-Type', 'video/mp4')
s.streamMp4FileOverHttp(response.fileLocation,req,res)
}else{
res.setHeader('Content-Type', 'application/json')
res.end(s.prettyPrint({
ok : response.ok,
msg : response.msg,
}))
}
})
}else{
res.setHeader('Content-Type', 'application/json');
res.end(s.prettyPrint([]))
}
}else{
res.end(s.prettyPrint([]))
if(r && r[0]){
r.forEach(function(file){
file.details = s.parseJSON(file.details)
})
res.end(s.prettyPrint(r))
}else{
res.end(s.prettyPrint([]))
}
}
})
},res,req);
@ -1247,6 +1273,30 @@ module.exports = function(s,config,lang,app,io){
},res,req);
});
/**
* Page : Get Timelapse Page (Not Modal)
*/
process.on('uncaughtException', function (err) {
console.error('uncaughtExceptioasdasdasdasdasdn',err);
});
app.get(config.webPaths.apiPrefix+':auth/timelapsePage/:ke', function (req,res){
req.params.protocol=req.protocol;
s.auth(req.params,function(user){
// if(user.permissions.watch_stream==="0"||user.details.sub&&user.details.allmonitors!=='1'&&user.details.monitors.indexOf(req.params.id)===-1){
// res.end(user.lang['Not Permitted'])
// return
// }
req.params.uid = user.uid
s.renderPage(req,res,config.renderPaths.timelapse,{
$user: user,
data: req.params,
baseUrl: req.protocol+'://'+req.hostname,
config: s.getConfigWithBranding(req.hostname),
lang: user.lang,
originalURL: s.getOriginalUrl(req)
})
},res,req);
});
/**
* API : Get Events
*/
app.get([config.webPaths.apiPrefix+':auth/events/:ke',config.webPaths.apiPrefix+':auth/events/:ke/:id',config.webPaths.apiPrefix+':auth/events/:ke/:id/:limit',config.webPaths.apiPrefix+':auth/events/:ke/:id/:limit/:start',config.webPaths.apiPrefix+':auth/events/:ke/:id/:limit/:start/:end'], function (req,res){

View File

@ -25,7 +25,7 @@ module.exports = function(s,config,lang,app){
if(s.group[req.params.ke]&&s.group[req.params.ke].mon[req.params.id]){
if(s.group[req.params.ke].mon[req.params.id].isStarted === true){
req.params.uid=user.uid;
s.renderPage(req,res,config.renderPaths.embed,{data:req.params,baseUrl:req.protocol+'://'+req.hostname,config:config,lang:user.lang,mon:CircularJSON.parse(CircularJSON.stringify(s.group[req.params.ke].mon_conf[req.params.id])),originalURL:s.getOriginalUrl(req)});
s.renderPage(req,res,config.renderPaths.embed,{data:req.params,baseUrl:req.protocol+'://'+req.hostname,config: s.getConfigWithBranding(req.hostname),lang:user.lang,mon:CircularJSON.parse(CircularJSON.stringify(s.group[req.params.ke].mon_conf[req.params.id])),originalURL:s.getOriginalUrl(req)});
res.end()
}else{
res.end(user.lang['Cannot watch a monitor that isn\'t running.'])

2
web/libs/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
demo
impervious

View File

@ -76,3 +76,39 @@
top:0;
left:0;
}
#timelapsejpeg .playBackView img{
width:calc(100% - 40px)
}
/* stand alone */
#timelapsejpeg.standalone {
background: #2f2f2f;
color:#fff;
}
#timelapsejpeg.standalone .fieldHolder{
padding-top: 20px;
}
.scroll-style-6::-webkit-scrollbar-track
{
-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
background-color: #333;
border-radius: 15px;
}
.scroll-style-6::-webkit-scrollbar
{
width: 10px;
background-color: #333;
}
.scroll-style-6::-webkit-scrollbar-thumb
{
background-color: #555;
background-image: -webkit-linear-gradient(45deg,
rgba(255, 255, 255, .2) 25%,
transparent 25%,
transparent 50%,
rgba(255, 255, 255, .2) 50%,
rgba(255, 255, 255, .2) 75%,
transparent 75%,
transparent)
}

View File

@ -20,8 +20,13 @@ $.ccio.tm=function(x,d,z,user){
d.per=parseInt(d.hr/24*100);
d.circle='<div title="at '+d.hr+' hours of '+d.startMoment.format('MMMM DD')+'" '+href+' video="launch" class="progress-circle progress-'+d.per+'"><span>'+d.hr+'</span></div>'
tmp+='<li class="video-item glM'+d.mid+user.auth_token+'" auth="'+user.auth_token+'" mid="'+d.mid+'" ke="'+d.ke+'" status="'+d.status+'" status="'+d.status+'" file="'+d.filename+'">'+d.circle+'<div><span title="'+d.endMoment.format()+'" class="livestamp"></span></div><div><div class="small"><b>'+lang.Start+'</b> : '+d.startMoment.format('h:mm:ss , MMMM Do YYYY')+'</div><div class="small"><b>'+lang.End+'</b> : '+d.endMoment.format('h:mm:ss , MMMM Do YYYY')+'</div></div><div><span class="pull-right">'+(parseInt(d.size)/1000000).toFixed(2)+'mb</span><div class="controls btn-group"><a class="btn btn-sm btn-primary" video="launch" '+href+'><i class="fa fa-play-circle"></i></a> <a download="'+d.dlname+'" '+href+' class="btn btn-sm btn-default"><i class="fa fa-download"></i></a>'
if($.ccio.DropboxAppKey){ tmp+='<a video="download" host="dropbox" download="'+d.dlname+'" '+href+' class="btn btn-sm btn-default"><i class="fa fa-dropbox"></i></a>' }
tmp+='<a title="'+lang['Delete Video']+'" video="delete" href="'+$.ccio.init('videoHrefToDelete',url)+'" class="btn btn-sm btn-danger permission_video_delete"><i class="fa fa-trash"></i></a></div></div></li>';
if($.ccio.DropboxAppKey){
tmp+='<a video="download" host="dropbox" download="'+d.dlname+'" '+href+' class="btn btn-sm btn-default"><i class="fa fa-dropbox"></i></a>'
}
if($.ccio.permissionCheck('video_delete',d.mid)){
tmp += '<a title="'+lang['Delete Video']+'" video="delete" href="'+$.ccio.init('videoHrefToDelete',url)+'" class="btn btn-sm btn-danger"><i class="fa fa-trash"></i></a>'
}
tmp += '</div></div></li>';
$(z).each(function(n,v){
v=$(v);
if(v.find('.video-item').length>10){v.find('.video-item:last').remove()}

View File

@ -10,10 +10,14 @@ $(document).ready(function(e){
$.timelapseJpeg.frameStripContainer = $.timelapseJpeg.e.find('.frameStripContainer')
$.timelapseJpeg.playBackViewImg = $.timelapseJpeg.e.find('.playBackView img')
$.timelapseJpeg.monitors=$.timelapseJpeg.e.find('.monitors_list')
$.timelapseJpeg.pointer = $.ccio.init('location',$user)
setTimeout(function(){
$.timelapseJpeg.datepicker.val(moment().subtract(1, 'days').format('YYYY-MM-DD')).change()
},5000)
$.timelapseJpeg.datepicker.daterangepicker({
singleDatePicker: true,
showDropdowns: true,
// startDate: moment().subtract(1, 'days'),
// startDate: moment().subtract(2, 'days'),
locale: {
format: 'YYYY-MM-DD'
}
@ -26,7 +30,7 @@ $(document).ready(function(e){
var frameStripHtml = ''
var frameIconsHtml = ''
var selectedMonitor = $.timelapseJpeg.monitors.val()
var apiURL = $.ccio.init('location',$user)+$user.auth_token+'/timelapse/'+$user.ke+'/'+selectedMonitor+'/'+selectedDate
var apiURL = $.timelapseJpeg.pointer+$user.auth_token+'/timelapse/'+$user.ke+'/'+selectedMonitor+'/'+selectedDate
$.timelapseJpeg.frameStripHrefPrefix = apiURL + '/'
$.getJSON(apiURL,function(data){
if(data && data[0]){
@ -37,26 +41,27 @@ $(document).ready(function(e){
$.each(data.reverse(),function(n,fileInfo){
fileInfo.href = apiURL + '/' + fileInfo.filename
fileInfo.number = n
// frameStripHtml += '<div class="frame" data-filename="' + fileInfo.filename + '"><img src="' + fileInfo.href + '"></div>'
frameStripHtml += '<div class="frame" data-filename="' + fileInfo.filename + '"><img src="' + fileInfo.href + '"></div>'
frameIconsHtml += '<div class="col-md-4"><div class="frame" data-filename="' + fileInfo.filename + '" style="background-image:url(\'' + fileInfo.href + '\')"><div class="shade">' + moment(fileInfo.time).format('YYYY-MM-DD HH:mm:ss') + '</div></div></div>'
$.timelapseJpeg.playlist[fileInfo.filename] = fileInfo
})
$.timelapseJpeg.playlistArray = data
// $.timelapseJpeg.frameStrip.html(frameStripHtml)
$.timelapseJpeg.frameStrip.html(frameStripHtml)
$.timelapseJpeg.frameIcons.html(frameIconsHtml)
$.timelapseJpeg.resetFilmStripPositions()
}else{
html = lang['No Data']
$.timelapseJpeg.frameStrip.html(html)
frameIconsHtml = lang['No Data']
$.timelapseJpeg.frameIcons.html(frameIconsHtml)
}
})
}
$.timelapseJpeg.fieldHolderCssHeightModifier = 0
$.timelapseJpeg.resetFilmStripPositions = function(){
// var numberOfFrames = Object.keys($.timelapseJpeg.playlist).length
// var frameStripWidth = $.timelapseJpeg.frameStrip.width()
// var widthForEachFrame = frameStripWidth / numberOfFrames
// $.timelapseJpeg.frameStrip.find('.frame').css('width',widthForEachFrame)
var fieldHolderHeight = $.timelapseJpeg.fieldHolder.height()
var numberOfFrames = Object.keys($.timelapseJpeg.playlist).length
var frameStripWidth = $.timelapseJpeg.frameStrip.width()
var widthForEachFrame = frameStripWidth / numberOfFrames
$.timelapseJpeg.frameStrip.find('.frame').css('width',widthForEachFrame)
var fieldHolderHeight = $.timelapseJpeg.fieldHolder.height() + $.timelapseJpeg.fieldHolderCssHeightModifier
console.log("calc(100% - " + fieldHolderHeight + "px)")
$.timelapseJpeg.frameIcons.css({height:"calc(100% - " + fieldHolderHeight + "px)"})
}
@ -68,8 +73,8 @@ $(document).ready(function(e){
var selectedFrame = $.timelapseJpeg.playlist[$.timelapseJpeg.frameSelected]
var selectedFrameNumber = $.timelapseJpeg.playlist[$.timelapseJpeg.frameSelected].number
$.timelapseJpeg.setPlayBackFrame(selectedFrame.href)
// $.timelapseJpeg.frameStrip.find(`.frameStrip .frame.selected`).removeClass('selected')
// $.timelapseJpeg.frameStrip.find(`.frameStrip .frame[data-filename="${selectedFrame.filename}"]`).addClass('selected')
$.timelapseJpeg.frameStrip.find(`.frameStrip .frame.selected`).removeClass('selected')
$.timelapseJpeg.frameStrip.find(`.frameStrip .frame[data-filename="${selectedFrame.filename}"]`).addClass('selected')
clearTimeout($.timelapseJpeg.playIntervalTimer)
$.timelapseJpeg.playIntervalTimer = setTimeout(function(){
++selectedFrameNumber
@ -116,6 +121,9 @@ $(document).ready(function(e){
// $.timelapseJpeg.e.on('mouseout','.frame',function(e){
//
// })
$.timelapseJpeg.e.on('shown.bs.modal', function (e) {
$.timelapseJpeg.datepicker.change()
})
$.timelapseJpeg.e.on('hidden.bs.modal', function (e) {
$.timelapseJpeg.destroy()
})

View File

@ -1,11 +1,11 @@
<%
if(!window.libURL)window.libURL = originalURL
%>
<link rel="icon" href="<%-window.libURL%>libs/img/icon/favicon.ico" type="image/x-icon" />
<link rel="shortcut icon" href="<%-window.libURL%>libs/img/icon/favicon.ico" type="image/x-icon" />
<link rel="icon" href="<%-window.libURL%><%-config.webFavicon%>" type="image/x-icon" />
<link rel="shortcut icon" href="<%-window.libURL%><%-config.webFavicon%>" type="image/x-icon" />
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="Shinobi">
<meta name="apple-mobile-web-app-title" content="<%-config.webPageTitle%>">
<link rel="apple-touch-icon" href="<%-window.libURL%>libs/img/icon/apple-touch-icon.png" />
<link rel="apple-touch-icon" sizes="57x57" href="<%-window.libURL%>libs/img/icon/apple-touch-icon-57x57.png" />
<link rel="apple-touch-icon" sizes="72x72" href="<%-window.libURL%>libs/img/icon/apple-touch-icon-72x72.png" />

View File

@ -55,7 +55,7 @@
var attributes = []
var styles = []
var sectionClass = []
var headerTitle = userSettings.headerTitle || lang[userSettings.name]
var headerTitle = userSettings.headerTitle || lang[userSettings.name] || userSettings.name
if(userSettings.hidden === true){
styles.push('display:none')
}
@ -71,7 +71,13 @@
if(userSettings.id){
attributes.push(`id="${userSettings.id}"`)
}else{
var userSettingsId = userSettings.name.replace(/[^a-zA-Z ]/g, '').replace(/[^a-zA-Z ]/g, '').replace(/ /g, '')
var userSettingsId
if(userSettings.name){
userSettingsId = userSettings.name.replace(/[^a-zA-Z ]/g, '').replace(/[^a-zA-Z ]/g, '').replace(/ /g, '')
}else{
userSettingsId = "NO NAME"
}
userSettings.id = userSettingsId
attributes.push(`id="${userSettingsId}"`)
}
if(userSettings.color){

View File

@ -6,7 +6,7 @@
<div class="row">
<div class="col-md-6 col-md-offset-3">
<div class="panel panel-login">
<% if(config.showLoginCardHeader){
<% if(config.showLoginCardHeader === true){
var logoImageLink
if(config.logoLocation76x76.indexOf('//') === -1){
logoImageLink = window.libURL + config.logoLocation76x76
@ -45,36 +45,52 @@
<div class="form-group f_i_input f_i_ldap" style="display:none">
<input name="key" id="key" tabindex="2" class="monospace form-control" placeholder="Group Key">
</div>
<div class="form-group">
<div class="row">
<div class="col-md-12 monospace" style="width:calc(100% - 48px)">
<select class="form-control" name="function" selector="f_i">
<% switch(screen){
case'super': %>
<option value="super"><%- lang.Superuser %></option>
<% break;
case'admin': %>
<option value="admin"><%- lang.Admin %></option>
<% break;
default: %>
<option value="dash" selected><%- lang.Dashboard %></option>
<% if(config.productType==='Pro'){ %>
<option value="ldap"><%- lang.LDAP %></option>
<% } %>
<option value="streamer"><%- lang.Streamer %></option>
<option value="cam"><%- lang.Dashcam %> (<%- lang.Streamer %> v2)</option>
<% break;
} %>
</select>
</div>
<div class="text-right" title="<%- lang['Remember Me'] %>" style="display:inline-block">
<label class="mdl-switch mdl-js-switch mdl-js-ripple-effect" for="remember_me">
<input type="checkbox" id="remember_me" value="1" name="remember" class="mdl-switch__input">
<span class="monospace mdl-switch__label"></span>
</label>
</div>
</div>
</div>
<% if(config.showLoginSelector === true){ %>
<div class="form-group">
<div class="row">
<div class="col-md-12 monospace" style="width:calc(100% - 48px)">
<select class="form-control" name="function" selector="f_i">
<% switch(screen){
case'super': %>
<option value="super"><%- lang.Superuser %></option>
<% break;
case'admin': %>
<option value="admin"><%- lang.Admin %></option>
<% break;
default: %>
<option value="dash" selected><%- lang.Dashboard %></option>
<% if(config.productType==='Pro'){ %>
<option value="ldap"><%- lang.LDAP %></option>
<% } %>
<option value="streamer"><%- lang.Streamer %></option>
<option value="cam"><%- lang.Dashcam %> (<%- lang.Streamer %> v2)</option>
<% break;
} %>
</select>
</div>
<div class="text-right" title="<%- lang['Remember Me'] %>" style="display:inline-block">
<label class="mdl-switch mdl-js-switch mdl-js-ripple-effect" for="remember_me">
<input type="checkbox" id="remember_me" value="1" name="remember" class="mdl-switch__input">
<span class="monospace mdl-switch__label"></span>
</label>
</div>
</div>
</div>
<% }else{ %>
<select class="form-control" name="function" style="display:none">
<% switch(screen){
case'super': %>
<option value="super"><%- lang.Superuser %></option>
<% break;
case'admin': %>
<option value="admin"><%- lang.Admin %></option>
<% break;
default: %>
<option value="dash" selected><%- lang.Dashboard %></option>
<% break;
} %>
</select>
<% } %>
<div class="form-group" style="margin:0">
<button type="submit" name="login-submit" id="login-submit" tabindex="4" class="btn btn-success btn-block"><%- lang.Login %></button>
</div>

View File

@ -15,6 +15,7 @@
<link rel="stylesheet" href="<%-window.libURL%>libs/css/main.dash2.css" />
<link rel="stylesheet" href="<%-window.libURL%>libs/css/pnotify.custom.min.css">
<link rel="stylesheet" href="<%-window.libURL%>libs/css/now-ui-kit.css?v=1.1.0" />
<link rel="stylesheet" href="<%-window.libURL%>libs/css/dash2.basic.css">
<link rel="stylesheet" href="<%-window.libURL%>libs/css/dash2.forms.css">
<link rel="stylesheet" href="<%-window.libURL%>libs/css/super-page.css" />
<script src="<%-window.libURL%>libs/js/jquery.min.js"></script>

82
web/pages/timelapse.ejs Normal file
View File

@ -0,0 +1,82 @@
<%
if(config.ssl&&config.ssl.port&&data.protocol==='https'){
data.port=config.ssl.port
}else{
data.port=config.port
}
data.url = baseUrl
if(data.addon && data.addon.indexOf('relative')>-1){
data.url=''
}
%>
<link rel="stylesheet" href="<%=data.url%>/libs/css/font-awesome.min.css">
<link rel="stylesheet" href="<%=data.url%>/libs/css/bootstrap.min.css">
<link rel="stylesheet" href="<%-data.url%>/libs/css/daterangepicker.css">
<link rel="stylesheet" href="<%-data.url%>/libs/css/dash2.basic.css">
<link rel="stylesheet" href="<%-data.url%>/libs/css/dash2.forms.css">
<link rel="stylesheet" href="<%-data.url%>/libs/css/dash2.darktheme.css">
<div id="timelapsejpeg" class="standalone dark">
<div class="modal-body text-center" style="padding-top:0;padding-bottom:0;">
<div class="row">
<div class="col-md-4">
<div class="fieldHolder text-left">
<div class="form-group">
<label><div><span><%-lang['Monitor']%></span></div>
<div><select class="form-control dark monitors_list"></select></div>
</label>
</div>
<div class="form-group">
<label><div><span><%-lang['Date']%></span></div>
<div><input type="text" id="timelapsejpeg_date" class="form-control" value="" /></div>
</label>
</div>
</div>
<div class="">
</div>
<div class="frameIcons row scroll-style-6"></div>
</div>
<div class="col-md-8 frameStripContainer contained">
<div class="playBackView"><img></div>
<!-- <div class="frames"></div>
<div class="frameStripPreview"></div>
<div class="frameStrip"></div> -->
</div>
</div>
</div>
</div>
<script src="<%=data.url%>/libs/js/jquery.min.js"></script>
<script src="<%=data.url%>/libs/js/jquery-ui.min.js"></script>
<script>$.ccio = {mon:{}}</script>
<script><% include ../libs/js/moment.js %></script>
<script src="<%-data.url%>/libs/js/daterangepicker.js"></script>
<script src="<%-data.url%>/libs/js/dash2.init.js"></script>
<link rel="stylesheet" href="<%-data.url%>/libs/css/dash2.timelapse.jpeg.css">
<script src="<%-data.url%>/libs/js/dash2.timelapse.jpeg.js"></script>
<script>
var data = <%- JSON.stringify(data) %>
var $user = <%- JSON.stringify($user) %>
var lang = <%- JSON.stringify(lang) %>
$user.auth_token = data.auth
$(document).ready(function(){
$.timelapseJpeg.pointer = data.url + '/'
$.timelapseJpeg.fieldHolderCssHeightModifier = 50
$.timelapseJpeg.monitors.find('.monitor').remove()
$.getJSON(data.url+'/'+$user.auth_token+'/monitor/'+$user.ke,function(d){
console.log(d)
$.each(d,function(n,monitor){
$.ccio.mon[monitor.ke + monitor.mid + $user.auth_token] = monitor
})
$.each($.ccio.mon,function(n,v){
$.timelapseJpeg.monitors.append('<option class="monitor" value="'+v.mid+'">'+v.name+'</option>')
})
$.timelapseJpeg.monitors.find('.monitor').prop('selected',false)
// if(monitor.mid !== ''){
// $.timelapseJpeg.monitors.find('.monitor[value="'+e.mid+'"]')
// }
})
})
</script>