Merge branch 'dev' into action-chain
commit
2bc7c37b72
246
.gitlab-ci.yml
246
.gitlab-ci.yml
|
@ -1,37 +1,56 @@
|
|||
# This file is a template, and might need editing before it works on your project.
|
||||
# To contribute improvements to CI/CD templates, please follow the Development guide at:
|
||||
# https://docs.gitlab.com/ee/development/cicd/templates.html
|
||||
# This specific template is located at:
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Docker.gitlab-ci.yml
|
||||
|
||||
# Build a Docker image with CI/CD and push to the GitLab registry.
|
||||
# Docker-in-Docker documentation: https://docs.gitlab.com/ee/ci/docker/using_docker_build.html
|
||||
#
|
||||
# This template uses one generic job with conditional builds
|
||||
# for the default branch and all other (MR) branches.
|
||||
|
||||
docker-latest-build:
|
||||
# Use the official docker image.
|
||||
image: docker:latest
|
||||
stage: build
|
||||
variables:
|
||||
EXCLUDE_DB: "false"
|
||||
BASE_IMAGE: "node:18-buster-slim"
|
||||
services:
|
||||
- docker:dind
|
||||
before_script:
|
||||
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
|
||||
# Default branch leaves tag empty (= latest tag)
|
||||
# All other branches are tagged with the escaped branch name (commit ref slug)
|
||||
script:
|
||||
- |
|
||||
if [[ "$CI_COMMIT_BRANCH" == "$CI_DEFAULT_BRANCH" ]]; then
|
||||
tag=""
|
||||
echo "Running on default branch '$CI_DEFAULT_BRANCH': tag = 'latest'"
|
||||
else
|
||||
tag=":$CI_COMMIT_REF_SLUG"
|
||||
echo "Running on branch '$CI_COMMIT_BRANCH': tag = $tag"
|
||||
fi
|
||||
- docker build --pull -t "$CI_REGISTRY_IMAGE${tag}" .
|
||||
- docker push "$CI_REGISTRY_IMAGE${tag}"
|
||||
# Run this job in a branch where a Dockerfile exists
|
||||
BRANCH_PREFIX="$(if [ $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH ]; then echo ${CI_COMMIT_REF_SLUG} ; else echo 'latest'; fi)"
|
||||
NO_DB_SUFIX="$(${EXCLUDE_DB} && echo '-no-db' || echo '')"
|
||||
BASE_IMAGE_ARCH="$(echo ${BASE_IMAGE} | cut -d'/' -f1)"
|
||||
BASE_IMAGE_TYPE="$(echo ${BASE_IMAGE} | cut -d'/' -f2)"
|
||||
ARCH_TYPE="$(if [ $BASE_IMAGE_ARCH != $BASE_IMAGE_TYPE ]; then echo '-'${BASE_IMAGE_ARCH}; else echo ''; fi )"
|
||||
|
||||
export IMAGE_NAME="$CI_REGISTRY_IMAGE:${BRANCH_PREFIX}${ARCH_TYPE}${NO_DB_SUFIX}"
|
||||
echo "Running on branch '${CI_COMMIT_BRANCH}', Image: ${IMAGE_NAME}" ;
|
||||
|
||||
- docker build --pull --build-arg EXCLUDE_DB="${EXCLUDE_DB}" --build-arg BASE_IMAGE="${BASE_IMAGE}" -t "${IMAGE_NAME}" . -f "Dockerfile"
|
||||
- docker push "${IMAGE_NAME}"
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH != "master" && $CI_COMMIT_BRANCH != "main" && $CI_COMMIT_BRANCH != "dev"
|
||||
when: never
|
||||
- if: $CI_COMMIT_BRANCH
|
||||
exists:
|
||||
- Dockerfile
|
||||
|
||||
docker-latest-no-db-build:
|
||||
image: docker:latest
|
||||
stage: build
|
||||
variables:
|
||||
EXCLUDE_DB: "true"
|
||||
BASE_IMAGE: "node:18-buster-slim"
|
||||
services:
|
||||
- docker:dind
|
||||
before_script:
|
||||
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
|
||||
script:
|
||||
- |
|
||||
BRANCH_PREFIX="$(if [ $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH ]; then echo ${CI_COMMIT_REF_SLUG} ; else echo 'latest'; fi)"
|
||||
NO_DB_SUFIX="$(${EXCLUDE_DB} && echo '-no-db' || echo '')"
|
||||
BASE_IMAGE_ARCH="$(echo ${BASE_IMAGE} | cut -d'/' -f1)"
|
||||
BASE_IMAGE_TYPE="$(echo ${BASE_IMAGE} | cut -d'/' -f2)"
|
||||
ARCH_TYPE="$(if [ $BASE_IMAGE_ARCH != $BASE_IMAGE_TYPE ]; then echo '-'${BASE_IMAGE_ARCH}; else echo ''; fi )"
|
||||
|
||||
export IMAGE_NAME="$CI_REGISTRY_IMAGE:${BRANCH_PREFIX}${ARCH_TYPE}${NO_DB_SUFIX}"
|
||||
echo "Running on branch '${CI_COMMIT_BRANCH}', Image: ${IMAGE_NAME}" ;
|
||||
|
||||
- docker build --pull --build-arg EXCLUDE_DB="${EXCLUDE_DB}" --build-arg BASE_IMAGE="${BASE_IMAGE}" -t "${IMAGE_NAME}" . -f "Dockerfile"
|
||||
- docker push "${IMAGE_NAME}"
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH != "master" && $CI_COMMIT_BRANCH != "main" && $CI_COMMIT_BRANCH != "dev"
|
||||
when: never
|
||||
|
@ -40,60 +59,181 @@ docker-latest-build:
|
|||
- Dockerfile
|
||||
|
||||
docker-arm32v7-build:
|
||||
# Use the official docker image.
|
||||
image: docker:latest
|
||||
stage: build
|
||||
variables:
|
||||
EXCLUDE_DB: "false"
|
||||
BASE_IMAGE: "arm32v7/node:18-buster-slim"
|
||||
services:
|
||||
- docker:dind
|
||||
before_script:
|
||||
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
|
||||
# Default branch leaves tag empty (= latest tag)
|
||||
# All other branches are tagged with the escaped branch name (commit ref slug)
|
||||
script:
|
||||
- |
|
||||
if [[ "$CI_COMMIT_BRANCH" == "$CI_DEFAULT_BRANCH" ]]; then
|
||||
tag=":arm32v7"
|
||||
echo "Running on default branch '$CI_DEFAULT_BRANCH': tag = 'arm32v7'"
|
||||
else
|
||||
tag=":arm32v7-$CI_COMMIT_REF_SLUG"
|
||||
echo "Running on branch '$CI_COMMIT_BRANCH': tag = $tag"
|
||||
fi
|
||||
- docker build --pull -t "$CI_REGISTRY_IMAGE${tag}" . -f Dockerfile.arm32v7
|
||||
- docker push "$CI_REGISTRY_IMAGE${tag}"
|
||||
# Run this job in a branch where a Dockerfile exists
|
||||
BRANCH_PREFIX="$(if [ $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH ]; then echo ${CI_COMMIT_REF_SLUG} ; else echo 'latest'; fi)"
|
||||
NO_DB_SUFIX="$(${EXCLUDE_DB} && echo '-no-db' || echo '')"
|
||||
BASE_IMAGE_ARCH="$(echo ${BASE_IMAGE} | cut -d'/' -f1)"
|
||||
BASE_IMAGE_TYPE="$(echo ${BASE_IMAGE} | cut -d'/' -f2)"
|
||||
ARCH_TYPE="$(if [ $BASE_IMAGE_ARCH != $BASE_IMAGE_TYPE ]; then echo '-'${BASE_IMAGE_ARCH}; else echo ''; fi )"
|
||||
|
||||
export IMAGE_NAME="$CI_REGISTRY_IMAGE:${BRANCH_PREFIX}${ARCH_TYPE}${NO_DB_SUFIX}"
|
||||
echo "Running on branch '${CI_COMMIT_BRANCH}', Image: ${IMAGE_NAME}" ;
|
||||
|
||||
- docker build --pull --build-arg EXCLUDE_DB="${EXCLUDE_DB}" --build-arg BASE_IMAGE="${BASE_IMAGE}" -t "${IMAGE_NAME}" . -f "Dockerfile"
|
||||
- docker push "${IMAGE_NAME}"
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH != "master" && $CI_COMMIT_BRANCH != "main" && $CI_COMMIT_BRANCH != "dev"
|
||||
when: never
|
||||
- if: $CI_COMMIT_BRANCH
|
||||
exists:
|
||||
- Dockerfile.arm32v7
|
||||
- Dockerfile
|
||||
|
||||
docker-arm32v7-no-db-build:
|
||||
image: docker:latest
|
||||
stage: build
|
||||
variables:
|
||||
EXCLUDE_DB: "true"
|
||||
BASE_IMAGE: "arm32v7/node:18-buster-slim"
|
||||
services:
|
||||
- docker:dind
|
||||
before_script:
|
||||
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
|
||||
script:
|
||||
- |
|
||||
BRANCH_PREFIX="$(if [ $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH ]; then echo ${CI_COMMIT_REF_SLUG} ; else echo 'latest'; fi)"
|
||||
NO_DB_SUFIX="$(${EXCLUDE_DB} && echo '-no-db' || echo '')"
|
||||
BASE_IMAGE_ARCH="$(echo ${BASE_IMAGE} | cut -d'/' -f1)"
|
||||
BASE_IMAGE_TYPE="$(echo ${BASE_IMAGE} | cut -d'/' -f2)"
|
||||
ARCH_TYPE="$(if [ $BASE_IMAGE_ARCH != $BASE_IMAGE_TYPE ]; then echo '-'${BASE_IMAGE_ARCH}; else echo ''; fi )"
|
||||
|
||||
export IMAGE_NAME="$CI_REGISTRY_IMAGE:${BRANCH_PREFIX}${ARCH_TYPE}${NO_DB_SUFIX}"
|
||||
echo "Running on branch '${CI_COMMIT_BRANCH}', Image: ${IMAGE_NAME}" ;
|
||||
|
||||
- docker build --pull --build-arg EXCLUDE_DB="${EXCLUDE_DB}" --build-arg BASE_IMAGE="${BASE_IMAGE}" -t "${IMAGE_NAME}" . -f "Dockerfile"
|
||||
- docker push "${IMAGE_NAME}"
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH != "master" && $CI_COMMIT_BRANCH != "main" && $CI_COMMIT_BRANCH != "dev"
|
||||
when: never
|
||||
- if: $CI_COMMIT_BRANCH
|
||||
exists:
|
||||
- Dockerfile
|
||||
|
||||
docker-arm64v8-build:
|
||||
image: docker:latest
|
||||
stage: build
|
||||
variables:
|
||||
EXCLUDE_DB: "false"
|
||||
BASE_IMAGE: "arm64v8/node:18-buster-slim"
|
||||
services:
|
||||
- docker:dind
|
||||
before_script:
|
||||
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
|
||||
script:
|
||||
- |
|
||||
BRANCH_PREFIX="$(if [ $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH ]; then echo ${CI_COMMIT_REF_SLUG} ; else echo 'latest'; fi)"
|
||||
NO_DB_SUFIX="$(${EXCLUDE_DB} && echo '-no-db' || echo '')"
|
||||
BASE_IMAGE_ARCH="$(echo ${BASE_IMAGE} | cut -d'/' -f1)"
|
||||
BASE_IMAGE_TYPE="$(echo ${BASE_IMAGE} | cut -d'/' -f2)"
|
||||
ARCH_TYPE="$(if [ $BASE_IMAGE_ARCH != $BASE_IMAGE_TYPE ]; then echo '-'${BASE_IMAGE_ARCH}; else echo ''; fi )"
|
||||
|
||||
export IMAGE_NAME="$CI_REGISTRY_IMAGE:${BRANCH_PREFIX}${ARCH_TYPE}${NO_DB_SUFIX}"
|
||||
echo "Running on branch '${CI_COMMIT_BRANCH}', Image: ${IMAGE_NAME}" ;
|
||||
|
||||
- docker build --pull --build-arg EXCLUDE_DB="${EXCLUDE_DB}" --build-arg BASE_IMAGE="${BASE_IMAGE}" -t "${IMAGE_NAME}" . -f "Dockerfile"
|
||||
- docker push "${IMAGE_NAME}"
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH != "master" && $CI_COMMIT_BRANCH != "main" && $CI_COMMIT_BRANCH != "dev"
|
||||
when: never
|
||||
- if: $CI_COMMIT_BRANCH
|
||||
exists:
|
||||
- Dockerfile
|
||||
|
||||
docker-arm64v8-no-db-build:
|
||||
image: docker:latest
|
||||
stage: build
|
||||
variables:
|
||||
EXCLUDE_DB: "true"
|
||||
BASE_IMAGE: "arm64v8/node:18-buster-slim"
|
||||
services:
|
||||
- docker:dind
|
||||
before_script:
|
||||
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
|
||||
script:
|
||||
- |
|
||||
BRANCH_PREFIX="$(if [ $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH ]; then echo ${CI_COMMIT_REF_SLUG} ; else echo 'latest'; fi)"
|
||||
NO_DB_SUFIX="$(${EXCLUDE_DB} && echo '-no-db' || echo '')"
|
||||
BASE_IMAGE_ARCH="$(echo ${BASE_IMAGE} | cut -d'/' -f1)"
|
||||
BASE_IMAGE_TYPE="$(echo ${BASE_IMAGE} | cut -d'/' -f2)"
|
||||
ARCH_TYPE="$(if [ $BASE_IMAGE_ARCH != $BASE_IMAGE_TYPE ]; then echo '-'${BASE_IMAGE_ARCH}; else echo ''; fi )"
|
||||
|
||||
export IMAGE_NAME="$CI_REGISTRY_IMAGE:${BRANCH_PREFIX}${ARCH_TYPE}${NO_DB_SUFIX}"
|
||||
echo "Running on branch '${CI_COMMIT_BRANCH}', Image: ${IMAGE_NAME}" ;
|
||||
|
||||
- docker build --pull --build-arg EXCLUDE_DB="${EXCLUDE_DB}" --build-arg BASE_IMAGE="${BASE_IMAGE}" -t "${IMAGE_NAME}" . -f "Dockerfile"
|
||||
- docker push "${IMAGE_NAME}"
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH != "master" && $CI_COMMIT_BRANCH != "main" && $CI_COMMIT_BRANCH != "dev"
|
||||
when: never
|
||||
- if: $CI_COMMIT_BRANCH
|
||||
exists:
|
||||
- Dockerfile
|
||||
|
||||
docker-nvidia-build:
|
||||
# Use the official docker image.
|
||||
image: docker:latest
|
||||
stage: build
|
||||
variables:
|
||||
EXCLUDE_DB: "false"
|
||||
BASE_IMAGE: "nvidia/cuda:11.7.1-cudnn8-runtime-ubuntu22.04"
|
||||
services:
|
||||
- docker:dind
|
||||
before_script:
|
||||
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
|
||||
# Default branch leaves tag empty (= latest tag)
|
||||
# All other branches are tagged with the escaped branch name (commit ref slug)
|
||||
script:
|
||||
- |
|
||||
if [[ "$CI_COMMIT_BRANCH" == "$CI_DEFAULT_BRANCH" ]]; then
|
||||
tag=":nvidia"
|
||||
echo "Running on default branch '$CI_DEFAULT_BRANCH': tag = 'nvidia'"
|
||||
else
|
||||
tag=":nvidia-$CI_COMMIT_REF_SLUG"
|
||||
echo "Running on branch '$CI_COMMIT_BRANCH': tag = $tag"
|
||||
fi
|
||||
- docker build --pull -t "$CI_REGISTRY_IMAGE${tag}" . -f Dockerfile.nvidia
|
||||
- docker push "$CI_REGISTRY_IMAGE${tag}"
|
||||
# Run this job in a branch where a Dockerfile exists
|
||||
BRANCH_PREFIX="$(if [ $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH ]; then echo ${CI_COMMIT_REF_SLUG} ; else echo 'latest'; fi)"
|
||||
NO_DB_SUFIX="$(${EXCLUDE_DB} && echo '-no-db' || echo '')"
|
||||
BASE_IMAGE_ARCH="$(echo ${BASE_IMAGE} | cut -d'/' -f1)"
|
||||
BASE_IMAGE_TYPE="$(echo ${BASE_IMAGE} | cut -d'/' -f2)"
|
||||
ARCH_TYPE="$(if [ $BASE_IMAGE_ARCH != $BASE_IMAGE_TYPE ]; then echo '-'${BASE_IMAGE_ARCH}; else echo ''; fi )"
|
||||
|
||||
export IMAGE_NAME="$CI_REGISTRY_IMAGE:${BRANCH_PREFIX}${ARCH_TYPE}${NO_DB_SUFIX}"
|
||||
echo "Running on branch '${CI_COMMIT_BRANCH}', Image: ${IMAGE_NAME}" ;
|
||||
|
||||
- docker build --pull --build-arg EXCLUDE_DB="${EXCLUDE_DB}" --build-arg BASE_IMAGE="${BASE_IMAGE}" -t "${IMAGE_NAME}" . -f "Dockerfile"
|
||||
- docker push "${IMAGE_NAME}"
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH != "master" && $CI_COMMIT_BRANCH != "main" && $CI_COMMIT_BRANCH != "dev"
|
||||
when: never
|
||||
- if: $CI_COMMIT_BRANCH
|
||||
exists:
|
||||
- Dockerfile.nvidia
|
||||
- Dockerfile
|
||||
|
||||
docker-nvidia-no-db-build:
|
||||
image: docker:latest
|
||||
stage: build
|
||||
variables:
|
||||
EXCLUDE_DB: "true"
|
||||
BASE_IMAGE: "nvidia/cuda:11.7.1-cudnn8-runtime-ubuntu22.04"
|
||||
services:
|
||||
- docker:dind
|
||||
before_script:
|
||||
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
|
||||
script:
|
||||
- |
|
||||
BRANCH_PREFIX="$(if [ $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH ]; then echo ${CI_COMMIT_REF_SLUG} ; else echo 'latest'; fi)"
|
||||
NO_DB_SUFIX="$(${EXCLUDE_DB} && echo '-no-db' || echo '')"
|
||||
BASE_IMAGE_ARCH="$(echo ${BASE_IMAGE} | cut -d'/' -f1)"
|
||||
BASE_IMAGE_TYPE="$(echo ${BASE_IMAGE} | cut -d'/' -f2)"
|
||||
ARCH_TYPE="$(if [ $BASE_IMAGE_ARCH != $BASE_IMAGE_TYPE ]; then echo '-'${BASE_IMAGE_ARCH}; else echo ''; fi )"
|
||||
|
||||
export IMAGE_NAME="$CI_REGISTRY_IMAGE:${BRANCH_PREFIX}${ARCH_TYPE}${NO_DB_SUFIX}"
|
||||
echo "Running on branch '${CI_COMMIT_BRANCH}', Image: ${IMAGE_NAME}" ;
|
||||
|
||||
- docker build --pull --build-arg EXCLUDE_DB="${EXCLUDE_DB}" --build-arg BASE_IMAGE="${BASE_IMAGE}" -t "${IMAGE_NAME}" . -f "Dockerfile"
|
||||
- docker push "${IMAGE_NAME}"
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH != "master" && $CI_COMMIT_BRANCH != "main" && $CI_COMMIT_BRANCH != "dev"
|
||||
when: never
|
||||
- if: $CI_COMMIT_BRANCH
|
||||
exists:
|
||||
- Dockerfile
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
#!/bin/sh
|
||||
|
||||
apt-get install -y \
|
||||
libfreetype6-dev \
|
||||
libgnutls28-dev \
|
||||
libmp3lame-dev \
|
||||
libass-dev \
|
||||
libogg-dev \
|
||||
libtheora-dev \
|
||||
libvorbis-dev \
|
||||
libvpx-dev \
|
||||
libwebp-dev \
|
||||
libssh2-1-dev \
|
||||
libopus-dev \
|
||||
librtmp-dev \
|
||||
libx264-dev \
|
||||
libx265-dev \
|
||||
x264 \
|
||||
ffmpeg
|
||||
|
||||
ffmpeg -version
|
||||
|
||||
apt-get install git -y
|
|
@ -0,0 +1,19 @@
|
|||
#!/bin/sh
|
||||
$EXCLUDE_DB > /dev/null
|
||||
|
||||
if [ "$?" -ne 0 ] ; then
|
||||
echo "Installing MariaDB"
|
||||
set -ex
|
||||
{ \
|
||||
echo "mariadb-server" mysql-server/root_password password '${DB_ROOT_PASSWORD}'; \
|
||||
echo "mariadb-server" mysql-server/root_password_again password '${DB_ROOT_PASSWORD}'; \
|
||||
} | debconf-set-selections
|
||||
|
||||
mkdir -p /var/lib/mysql
|
||||
apt-get update
|
||||
|
||||
apt-get install -y mariadb-server socat
|
||||
|
||||
find /etc/mysql/ -name '*.cnf' -print0 | xargs -0 grep -lZE '^(bind-address|log)' | xargs -rt -0 sed -Ei 's/^(bind-address|log)/#&/'
|
||||
sed -ie "s/^bind-address\s*=\s*127\.0\.0\.1$/#bind-address = 0.0.0.0/" /etc/mysql/my.cnf
|
||||
fi
|
|
@ -0,0 +1,22 @@
|
|||
#!/bin/sh
|
||||
if [ -x "$(command -v node)" ]; then
|
||||
echo "Node.js detected. Version : $(node -v)"
|
||||
else
|
||||
echo "Installing Node.js 18"
|
||||
mkdir -p /etc/apt/keyrings
|
||||
apt-get install -y ca-certificates gnupg
|
||||
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
|
||||
|
||||
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_18.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list
|
||||
|
||||
apt-get update -y --fix-missing
|
||||
apt-get upgrade -y
|
||||
apt-get install nodejs -y
|
||||
fi
|
||||
|
||||
if [ -x "$(command -v npm)" ]; then
|
||||
echo "NPM detected. Version : $(npm -v)"
|
||||
fi
|
||||
npm install --unsafe-perm
|
||||
npm i pm2@latest -g
|
||||
npm i pg
|
105
Dockerfile
105
Dockerfile
|
@ -1,4 +1,8 @@
|
|||
FROM node:16-buster-slim
|
||||
ARG BASE_IMAGE=node:18-buster-slim
|
||||
FROM ${BASE_IMAGE}
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive \
|
||||
EXCLUDE_DB=false
|
||||
|
||||
ENV DB_USER=majesticflame \
|
||||
DB_PASSWORD='' \
|
||||
|
@ -15,97 +19,56 @@ ENV DB_USER=majesticflame \
|
|||
SSL_ORGANIZATION='Shinobi Systems' \
|
||||
SSL_ORGANIZATION_UNIT='IT Department' \
|
||||
SSL_COMMON_NAME='nvr.ninja' \
|
||||
DB_DISABLE_INCLUDED=false
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
DB_DISABLE_INCLUDED=$EXCLUDE_DB
|
||||
|
||||
RUN mkdir -p /home/Shinobi /config /var/lib/mysql
|
||||
WORKDIR /home/Shinobi
|
||||
COPY . ./
|
||||
|
||||
RUN apt update -y
|
||||
RUN apt install wget curl net-tools -y
|
||||
RUN apt-get update -y
|
||||
RUN apt-get upgrade -y
|
||||
|
||||
# Install MariaDB server... the debian way
|
||||
RUN if [ "$DB_DISABLE_INCLUDED" = "false" ] ; then set -ex; \
|
||||
{ \
|
||||
echo "mariadb-server" mysql-server/root_password password '${DB_ROOT_PASSWORD}'; \
|
||||
echo "mariadb-server" mysql-server/root_password_again password '${DB_ROOT_PASSWORD}'; \
|
||||
} | debconf-set-selections; \
|
||||
apt-get update; \
|
||||
apt-get install -y \
|
||||
"mariadb-server" \
|
||||
socat \
|
||||
; \
|
||||
find /etc/mysql/ -name '*.cnf' -print0 \
|
||||
| xargs -0 grep -lZE '^(bind-address|log)' \
|
||||
| xargs -rt -0 sed -Ei 's/^(bind-address|log)/#&/'; fi
|
||||
|
||||
RUN if [ "$DB_DISABLE_INCLUDED" = "false" ] ; then sed -ie "s/^bind-address\s*=\s*127\.0\.0\.1$/#bind-address = 0.0.0.0/" /etc/mysql/my.cnf; fi
|
||||
|
||||
# Install FFmpeg
|
||||
|
||||
RUN apt update --fix-missing
|
||||
RUN apt install -y software-properties-common \
|
||||
libfreetype6-dev \
|
||||
libgnutls28-dev \
|
||||
libmp3lame-dev \
|
||||
libass-dev \
|
||||
libogg-dev \
|
||||
libtheora-dev \
|
||||
libvorbis-dev \
|
||||
libvpx-dev \
|
||||
libwebp-dev \
|
||||
libssh2-1-dev \
|
||||
libopus-dev \
|
||||
librtmp-dev \
|
||||
libx264-dev \
|
||||
libx265-dev \
|
||||
yasm
|
||||
RUN apt install -y \
|
||||
RUN apt-get install -y \
|
||||
wget \
|
||||
curl \
|
||||
net-tools \
|
||||
software-properties-common \
|
||||
build-essential \
|
||||
git \
|
||||
python3 \
|
||||
sudo \
|
||||
pkg-config \
|
||||
apt-utils \
|
||||
yasm \
|
||||
bzip2 \
|
||||
coreutils \
|
||||
procps \
|
||||
gnutls-bin \
|
||||
nasm \
|
||||
tar \
|
||||
x264
|
||||
make \
|
||||
g++ \
|
||||
gcc \
|
||||
tar
|
||||
|
||||
RUN apt install -y \
|
||||
ffmpeg \
|
||||
git \
|
||||
make \
|
||||
g++ \
|
||||
gcc \
|
||||
pkg-config \
|
||||
python3 \
|
||||
wget \
|
||||
tar \
|
||||
sudo \
|
||||
xz-utils
|
||||
RUN sh /home/Shinobi/Docker/install_ffmpeg.sh
|
||||
RUN sh /home/Shinobi/Docker/install_mariadb.sh
|
||||
RUN sh /home/Shinobi/Docker/install_nodejs.sh
|
||||
|
||||
|
||||
WORKDIR /home/Shinobi
|
||||
COPY . .
|
||||
#RUN rm -rf /home/Shinobi/plugins
|
||||
COPY ./plugins /home/Shinobi/plugins
|
||||
RUN chmod -R 777 /home/Shinobi/plugins
|
||||
RUN chmod 777 /home/Shinobi
|
||||
RUN npm i npm@latest -g && \
|
||||
npm install --unsafe-perm && \
|
||||
npm install pm2 -g
|
||||
COPY ./Docker/pm2.yml ./
|
||||
|
||||
# Copy default configuration files
|
||||
RUN chmod -R 777 /home/Shinobi/plugins
|
||||
RUN chmod -f +x /home/Shinobi/Docker/init.sh
|
||||
|
||||
RUN sed -i -e 's/\r//g' /home/Shinobi/Docker/init.sh
|
||||
# RUN chmod -f +x /home/Shinobi/shinobi
|
||||
|
||||
RUN apt-get update -y --fix-missing
|
||||
RUN apt-get upgrade -y
|
||||
|
||||
VOLUME ["/home/Shinobi/videos"]
|
||||
VOLUME ["/home/Shinobi/libs/customAutoLoad"]
|
||||
VOLUME ["/config"]
|
||||
VOLUME ["/var/lib/mysql"]
|
||||
|
||||
EXPOSE 8080 443 21 25
|
||||
|
||||
ENTRYPOINT ["sh","/home/Shinobi/Docker/init.sh"]
|
||||
ENTRYPOINT ["/home/Shinobi/Docker/init.sh"]
|
||||
|
||||
CMD [ "pm2-docker", "/home/Shinobi/Docker/pm2.yml" ]
|
||||
|
|
|
@ -1,109 +0,0 @@
|
|||
FROM arm32v7/node:16-buster-slim
|
||||
|
||||
ENV DB_USER=majesticflame \
|
||||
DB_PASSWORD='' \
|
||||
DB_HOST='localhost' \
|
||||
DB_DATABASE=ccio \
|
||||
DB_PORT=3306 \
|
||||
DB_TYPE='mysql' \
|
||||
SUBSCRIPTION_ID=sub_XXXXXXXXXXXX \
|
||||
PLUGIN_KEYS='{}' \
|
||||
SSL_ENABLED='false' \
|
||||
SSL_COUNTRY='CA' \
|
||||
SSL_STATE='BC' \
|
||||
SSL_LOCATION='Vancouver' \
|
||||
SSL_ORGANIZATION='Shinobi Systems' \
|
||||
SSL_ORGANIZATION_UNIT='IT Department' \
|
||||
SSL_COMMON_NAME='nvr.ninja' \
|
||||
DB_DISABLE_INCLUDED=false
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
RUN mkdir -p /home/Shinobi /config /var/lib/mysql
|
||||
|
||||
RUN apt update -y
|
||||
RUN apt install wget curl net-tools -y
|
||||
|
||||
# Install MariaDB server... the debian way
|
||||
RUN if [ "$DB_DISABLE_INCLUDED" = "false" ] ; then set -ex; \
|
||||
{ \
|
||||
echo "mariadb-server" mysql-server/root_password password '${DB_ROOT_PASSWORD}'; \
|
||||
echo "mariadb-server" mysql-server/root_password_again password '${DB_ROOT_PASSWORD}'; \
|
||||
} | debconf-set-selections; \
|
||||
apt-get update; \
|
||||
apt-get install -y \
|
||||
"mariadb-server" \
|
||||
socat \
|
||||
; \
|
||||
find /etc/mysql/ -name '*.cnf' -print0 \
|
||||
| xargs -0 grep -lZE '^(bind-address|log)' \
|
||||
| xargs -rt -0 sed -Ei 's/^(bind-address|log)/#&/'; fi
|
||||
|
||||
RUN if [ "$DB_DISABLE_INCLUDED" = "false" ] ; then sed -ie "s/^bind-address\s*=\s*127\.0\.0\.1$/#bind-address = 0.0.0.0/" /etc/mysql/my.cnf; fi
|
||||
|
||||
# Install FFmpeg
|
||||
|
||||
RUN apt install -y software-properties-common \
|
||||
libfreetype6-dev \
|
||||
libgnutls28-dev \
|
||||
libmp3lame-dev \
|
||||
libass-dev \
|
||||
libogg-dev \
|
||||
libtheora-dev \
|
||||
libvorbis-dev \
|
||||
libvpx-dev \
|
||||
libwebp-dev \
|
||||
libssh2-1-dev \
|
||||
libopus-dev \
|
||||
librtmp-dev \
|
||||
libx264-dev \
|
||||
libx265-dev \
|
||||
yasm && \
|
||||
apt install -y \
|
||||
build-essential \
|
||||
bzip2 \
|
||||
coreutils \
|
||||
gnutls-bin \
|
||||
nasm \
|
||||
tar \
|
||||
x264
|
||||
|
||||
RUN apt install -y zip
|
||||
|
||||
RUN apt install -y \
|
||||
ffmpeg \
|
||||
git \
|
||||
make \
|
||||
g++ \
|
||||
gcc \
|
||||
pkg-config \
|
||||
python3 \
|
||||
wget \
|
||||
tar \
|
||||
sudo \
|
||||
xz-utils
|
||||
|
||||
|
||||
WORKDIR /home/Shinobi
|
||||
COPY . .
|
||||
#RUN rm -rf /home/Shinobi/plugins
|
||||
COPY ./plugins /home/Shinobi/plugins
|
||||
RUN chmod -R 777 /home/Shinobi/plugins
|
||||
RUN npm i npm@latest -g && \
|
||||
npm install pm2 -g && \
|
||||
npm install --unsafe-perm
|
||||
COPY ./Docker/pm2.yml ./
|
||||
|
||||
# Copy default configuration files
|
||||
# COPY ./config/conf.json ./config/super.json /home/Shinobi/
|
||||
RUN chmod -f +x /home/Shinobi/Docker/init.sh
|
||||
|
||||
VOLUME ["/home/Shinobi/videos"]
|
||||
VOLUME ["/home/Shinobi/libs/customAutoLoad"]
|
||||
VOLUME ["/config"]
|
||||
VOLUME ["/var/lib/mysql"]
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
ENTRYPOINT ["/home/Shinobi/Docker/init.sh"]
|
||||
|
||||
CMD [ "pm2-docker", "pm2.yml" ]
|
|
@ -1,97 +0,0 @@
|
|||
FROM node:16-buster-slim
|
||||
|
||||
ENV DB_USER=majesticflame \
|
||||
DB_PASSWORD='' \
|
||||
DB_HOST='localhost' \
|
||||
DB_DATABASE=ccio \
|
||||
DB_PORT=3306 \
|
||||
DB_TYPE='mysql' \
|
||||
SUBSCRIPTION_ID=sub_XXXXXXXXXXXX \
|
||||
PLUGIN_KEYS='{}' \
|
||||
SSL_ENABLED='false' \
|
||||
SSL_COUNTRY='CA' \
|
||||
SSL_STATE='BC' \
|
||||
SSL_LOCATION='Vancouver' \
|
||||
SSL_ORGANIZATION='Shinobi Systems' \
|
||||
SSL_ORGANIZATION_UNIT='IT Department' \
|
||||
SSL_COMMON_NAME='nvr.ninja' \
|
||||
DB_DISABLE_INCLUDED=true
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
RUN mkdir -p /home/Shinobi /config /var/lib/mysql
|
||||
|
||||
RUN apt update -y
|
||||
RUN apt install wget curl net-tools -y
|
||||
|
||||
# Install FFmpeg
|
||||
|
||||
RUN apt update --fix-missing
|
||||
RUN apt install -y software-properties-common \
|
||||
libfreetype6-dev \
|
||||
libgnutls28-dev \
|
||||
libmp3lame-dev \
|
||||
libass-dev \
|
||||
libogg-dev \
|
||||
libtheora-dev \
|
||||
libvorbis-dev \
|
||||
libvpx-dev \
|
||||
libwebp-dev \
|
||||
libssh2-1-dev \
|
||||
libopus-dev \
|
||||
librtmp-dev \
|
||||
libx264-dev \
|
||||
libx265-dev \
|
||||
yasm
|
||||
RUN apt install -y \
|
||||
build-essential \
|
||||
bzip2 \
|
||||
coreutils \
|
||||
procps \
|
||||
gnutls-bin \
|
||||
nasm \
|
||||
tar \
|
||||
x264
|
||||
|
||||
RUN apt install -y zip
|
||||
|
||||
RUN apt install -y \
|
||||
ffmpeg \
|
||||
git \
|
||||
make \
|
||||
g++ \
|
||||
gcc \
|
||||
pkg-config \
|
||||
python3 \
|
||||
wget \
|
||||
tar \
|
||||
sudo \
|
||||
xz-utils
|
||||
|
||||
|
||||
WORKDIR /home/Shinobi
|
||||
COPY . .
|
||||
#RUN rm -rf /home/Shinobi/plugins
|
||||
COPY ./plugins /home/Shinobi/plugins
|
||||
RUN chmod -R 777 /home/Shinobi/plugins
|
||||
RUN npm i npm@latest -g && \
|
||||
npm install --unsafe-perm && \
|
||||
npm install pm2 -g
|
||||
COPY ./Docker/pm2.yml ./
|
||||
|
||||
RUN npm i pg --save
|
||||
|
||||
# Copy default configuration files
|
||||
# COPY ./config/conf.json ./config/super.json /home/Shinobi/
|
||||
RUN chmod -f +x /home/Shinobi/Docker/init.sh
|
||||
RUN sed -i -e 's/\r//g' /home/Shinobi/Docker/init.sh
|
||||
# RUN chmod -f +x /home/Shinobi/shinobi
|
||||
|
||||
VOLUME ["/home/Shinobi/videos"]
|
||||
VOLUME ["/home/Shinobi/libs/customAutoLoad"]
|
||||
VOLUME ["/config"]
|
||||
|
||||
EXPOSE 8080 443 21 25
|
||||
|
||||
ENTRYPOINT ["sh","/home/Shinobi/Docker/init.sh"]
|
||||
|
||||
CMD [ "pm2-docker", "/home/Shinobi/Docker/pm2.yml" ]
|
|
@ -1,118 +0,0 @@
|
|||
FROM nvidia/cuda:11.7.1-cudnn8-runtime-ubuntu20.04
|
||||
|
||||
ENV DB_USER=majesticflame \
|
||||
DB_PASSWORD='' \
|
||||
DB_HOST='localhost' \
|
||||
DB_DATABASE=ccio \
|
||||
DB_PORT=3306 \
|
||||
DB_TYPE='mysql' \
|
||||
SUBSCRIPTION_ID=sub_XXXXXXXXXXXX \
|
||||
PLUGIN_KEYS='{}' \
|
||||
SSL_ENABLED='false' \
|
||||
SSL_COUNTRY='CA' \
|
||||
SSL_STATE='BC' \
|
||||
SSL_LOCATION='Vancouver' \
|
||||
SSL_ORGANIZATION='Shinobi Systems' \
|
||||
SSL_ORGANIZATION_UNIT='IT Department' \
|
||||
SSL_COMMON_NAME='nvr.ninja' \
|
||||
DB_DISABLE_INCLUDED=false
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
RUN mkdir -p /home/Shinobi /config /var/lib/mysql
|
||||
|
||||
RUN apt update -y
|
||||
RUN apt install wget curl net-tools -y
|
||||
|
||||
# Additional packages to support NodeJS as the base build is NVidia CUDA
|
||||
RUN curl -sL https://deb.nodesource.com/setup_16.x | bash -
|
||||
RUN apt install nodejs -y
|
||||
|
||||
RUN node -v
|
||||
RUN npm -v
|
||||
|
||||
# Install MariaDB server... the debian way
|
||||
RUN if [ "$DB_DISABLE_INCLUDED" = "false" ] ; then set -ex; \
|
||||
{ \
|
||||
echo "mariadb-server" mysql-server/root_password password '${DB_ROOT_PASSWORD}'; \
|
||||
echo "mariadb-server" mysql-server/root_password_again password '${DB_ROOT_PASSWORD}'; \
|
||||
} | debconf-set-selections; \
|
||||
apt-get update; \
|
||||
apt-get install -y \
|
||||
"mariadb-server" \
|
||||
socat \
|
||||
; \
|
||||
find /etc/mysql/ -name '*.cnf' -print0 \
|
||||
| xargs -0 grep -lZE '^(bind-address|log)' \
|
||||
| xargs -rt -0 sed -Ei 's/^(bind-address|log)/#&/'; fi
|
||||
|
||||
RUN if [ "$DB_DISABLE_INCLUDED" = "false" ] ; then sed -ie "s/^bind-address\s*=\s*127\.0\.0\.1$/#bind-address = 0.0.0.0/" /etc/mysql/my.cnf; fi
|
||||
|
||||
# Install FFmpeg
|
||||
|
||||
RUN apt update --fix-missing
|
||||
RUN apt install -y software-properties-common \
|
||||
libfreetype6-dev \
|
||||
libgnutls28-dev \
|
||||
libmp3lame-dev \
|
||||
libass-dev \
|
||||
libogg-dev \
|
||||
libtheora-dev \
|
||||
libvorbis-dev \
|
||||
libvpx-dev \
|
||||
libwebp-dev \
|
||||
libssh2-1-dev \
|
||||
libopus-dev \
|
||||
librtmp-dev \
|
||||
libx264-dev \
|
||||
libx265-dev \
|
||||
yasm
|
||||
RUN apt install -y \
|
||||
build-essential \
|
||||
bzip2 \
|
||||
coreutils \
|
||||
procps \
|
||||
gnutls-bin \
|
||||
nasm \
|
||||
tar \
|
||||
x264
|
||||
|
||||
RUN apt install -y \
|
||||
ffmpeg \
|
||||
git \
|
||||
make \
|
||||
g++ \
|
||||
gcc \
|
||||
pkg-config \
|
||||
python3 \
|
||||
wget \
|
||||
tar \
|
||||
sudo \
|
||||
xz-utils
|
||||
|
||||
|
||||
WORKDIR /home/Shinobi
|
||||
COPY . .
|
||||
#RUN rm -rf /home/Shinobi/plugins
|
||||
COPY ./plugins /home/Shinobi/plugins
|
||||
RUN chmod -R 777 /home/Shinobi/plugins
|
||||
RUN npm i npm@latest -g && \
|
||||
npm install --unsafe-perm && \
|
||||
npm install pm2 -g
|
||||
COPY ./Docker/pm2.yml ./
|
||||
|
||||
# Copy default configuration files
|
||||
# COPY ./config/conf.json ./config/super.json /home/Shinobi/
|
||||
RUN chmod -f +x /home/Shinobi/Docker/init.sh
|
||||
RUN sed -i -e 's/\r//g' /home/Shinobi/Docker/init.sh
|
||||
# RUN chmod -f +x /home/Shinobi/shinobi
|
||||
|
||||
VOLUME ["/home/Shinobi/videos"]
|
||||
VOLUME ["/home/Shinobi/libs/customAutoLoad"]
|
||||
VOLUME ["/config"]
|
||||
VOLUME ["/var/lib/mysql"]
|
||||
|
||||
EXPOSE 8080 443 21 25
|
||||
|
||||
ENTRYPOINT ["sh","/home/Shinobi/Docker/init.sh"]
|
||||
|
||||
CMD [ "pm2-docker", "/home/Shinobi/Docker/pm2.yml" ]
|
|
@ -1,2 +1,2 @@
|
|||
sudo yum install --nogpgcheck https://rpm.nodesource.com/pub_16.x/nodistro/repo/nodesource-release-nodistro-1.noarch.rpm -y
|
||||
sudo yum install --nogpgcheck https://rpm.nodesource.com/pub_18.x/nodistro/repo/nodesource-release-nodistro-1.noarch.rpm -y
|
||||
sudo yum install --nogpgcheck nodejs -y
|
||||
|
|
|
@ -2,7 +2,7 @@ sudo apt-get update
|
|||
sudo apt-get install -y ca-certificates curl gnupg
|
||||
sudo mkdir -p /etc/apt/keyrings
|
||||
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
|
||||
NODE_MAJOR=16
|
||||
NODE_MAJOR=18
|
||||
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list
|
||||
sudo apt-get update
|
||||
sudo apt-get install nodejs -y
|
||||
|
|
|
@ -2911,7 +2911,7 @@ module.exports = function(s,config,lang){
|
|||
"name": "detail=snap_seconds_inward",
|
||||
"field": lang['Delay for Snapshot'],
|
||||
"description": lang[lang["fieldTextSnapSecondsInward"]],
|
||||
"default": "0"
|
||||
"default": "5"
|
||||
},
|
||||
{
|
||||
hidden: true,
|
||||
|
@ -6405,6 +6405,7 @@ module.exports = function(s,config,lang){
|
|||
"Saved Filters": {
|
||||
"name": lang["Saved Filters"],
|
||||
"color": "green",
|
||||
"blockquote": lang.eventFilterEnableNoticeText,
|
||||
"info": [
|
||||
{
|
||||
"field": lang["Monitor"],
|
||||
|
@ -7618,8 +7619,7 @@ module.exports = function(s,config,lang){
|
|||
},
|
||||
"Zoom In": {
|
||||
"label": lang['Zoom In'],
|
||||
"attr": `monitor="zoomStreamWithMouse"`,
|
||||
"class": "default",
|
||||
"class": "default magnify-glass-live-grid-stream",
|
||||
"icon": "search-plus"
|
||||
},
|
||||
// "Calendar": {
|
||||
|
@ -8835,7 +8835,7 @@ module.exports = function(s,config,lang){
|
|||
<div class="btn-group">
|
||||
<a class="btn btn-sm btn-primary" timeline-action="autoGridSizer" title="${lang.autoResizeGrid}"><i class="fa fa-expand"></i></a>
|
||||
<a class="btn btn-sm btn-primary" timeline-action="playUntilVideoEnd" title="${lang.playUntilVideoEnd}"><i class="fa fa-step-forward"></i></a>
|
||||
<a class="btn btn-sm btn-primary" timeline-action="dontShowDetection" title="${lang['Hide Detection on Stream']}"><i class="fa fa-grav"></i></a>
|
||||
<a class="btn btn-sm btn-primary btn-warning" timeline-action="dontShowDetection" title="${lang['Hide Detection on Stream']}"><i class="fa fa-grav"></i></a>
|
||||
</div>
|
||||
<div class="btn-group">
|
||||
<a class="btn btn-sm btn-success" timeline-action="downloadAll" title="${lang.Download}"><i class="fa fa-download"></i></a>
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
"contactAdmin": "Contact the maintainer of your Shinobi installation.",
|
||||
"accountAdded": "Account Added",
|
||||
"accountAddedText": "Account has been added.",
|
||||
"tooManyMonitorsSelected": "Too Many Monitors Selected",
|
||||
"performanceMayBeAffected": "Performance may be affected. Do you wish to continue?",
|
||||
"monitorDeleted": "Monitor Deleted",
|
||||
"accountCreationError": "Account Creation Error",
|
||||
"accountEditError": "Account Edit Error",
|
||||
|
@ -19,6 +21,7 @@
|
|||
"fieldTextGeolocation": "The map coordinates of this camera in the real world. This will plot a point for your camera on the Monitor Map.",
|
||||
"playUntilVideoEnd": "Play until video end",
|
||||
"Ignitor": "Ignitor",
|
||||
"Monitor Saved": "Monitor Saved",
|
||||
"Unmute": "Unmute",
|
||||
"byUser": "by user",
|
||||
"Chains": "Chains",
|
||||
|
@ -763,7 +766,7 @@
|
|||
"hlsOptionsInvalid": "HLS Options are Invalid",
|
||||
"HLS Video Encoder": "Video Encoder",
|
||||
"HLS Audio Encoder": "Audio Encoder",
|
||||
"HLS Segment Length": "Segment Length <small>in Seconds</small>",
|
||||
"HLS Segment Length": "Segment Length",
|
||||
"HLS Preset": "Preset Template",
|
||||
"in seconds": "in seconds",
|
||||
"HLS List Size": "List Size",
|
||||
|
@ -1444,11 +1447,12 @@
|
|||
"Daily Events": "Daily Events",
|
||||
"Send Notification": "Send Notification",
|
||||
"Send to": "Send to",
|
||||
"setMaxStorageAmountText": "You should set your Max Storage Amount in your Account Settings located on the left. Find the option under the Profile section. Default is 10 GB.",
|
||||
"setMaxStorageAmountText": "Please configure the Max Storage Amount in your Account Settings/Profile located on the left. Set at least 20 GB to disable this alert (Default 10 GB).",
|
||||
"Save Events": "Save Events",
|
||||
"Original Choice": "Original Choice",
|
||||
"Legacy Webhook": "Legacy Webhook",
|
||||
"eventFilterActionText": "These are the actions that occur from the filter conditions that have succeeded. \"Original Choice\" refers to the option you had chosen in your Monitor's Settings.",
|
||||
"eventFilterEnableNoticeText": "This is an advnaced feature. Ensure you have enabled \"Event Filters\" in your Detector Settings section. If you cannot see this option toggle Simple to Advanced in the bottom right corner of the Monitor Settings.",
|
||||
"Telegram": "Telegram",
|
||||
"Before": "Before",
|
||||
"After": "After",
|
||||
|
@ -1903,5 +1907,6 @@
|
|||
"HowToConnectDes2": "If you would like to get access to a private (dedicated) P2P server please create an account at the <a href='https: //licenses.shinobi.video/login' target='_blank'>Shinobi<b>Shop</b></a> and contact us via the Live Chat widget",
|
||||
"User": "User",
|
||||
"Current Version": "Current Version",
|
||||
"Default is Global value": "Default is Global value"
|
||||
"Default is Global value": "Default is Global value",
|
||||
"rejectUnauth": "Ignore server certificate"
|
||||
}
|
||||
|
|
|
@ -228,6 +228,18 @@ module.exports = (processCwd,config) => {
|
|||
|
||||
return s;
|
||||
}
|
||||
function setDefaultIfUndefined(config, key, defaultValue) {
|
||||
const mustDoDefault = !config.userHasSubscribed;
|
||||
if (Array.isArray(defaultValue)) {
|
||||
if (config[key] === undefined || mustDoDefault) {
|
||||
config[key] = [...defaultValue]; // Spread operator to clone the array
|
||||
}
|
||||
} else {
|
||||
if (config[key] === undefined || mustDoDefault) {
|
||||
config[key] = defaultValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
parseJSON: parseJSON,
|
||||
stringJSON: stringJSON,
|
||||
|
@ -248,5 +260,6 @@ module.exports = (processCwd,config) => {
|
|||
asyncSetTimeout: asyncSetTimeout,
|
||||
copyFile: copyFile,
|
||||
hmsToSeconds,
|
||||
setDefaultIfUndefined,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,46 +1,31 @@
|
|||
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.showLoginCardHeader === undefined){config.showLoginCardHeader=true}
|
||||
if(config.webFavicon === undefined){config.webFavicon = 'libs/img/icon/favicon.ico'}
|
||||
if(!config.logoLocationAppleTouchIcon)config.logoLocationAppleTouchIcon = 'libs/img/icon/apple-touch-icon.png';
|
||||
if(!config.logoLocation57x57)config.logoLocation57x57 = 'libs/img/icon/apple-touch-icon-57x57.png';
|
||||
if(!config.logoLocation72x72)config.logoLocation72x72 = 'libs/img/icon/apple-touch-icon-72x72.png';
|
||||
if(!config.logoLocation76x76)config.logoLocation76x76 = 'libs/img/icon/apple-touch-icon-76x76.png';
|
||||
if(!config.logoLocation114x114)config.logoLocation114x114 = 'libs/img/icon/apple-touch-icon-114x114.png';
|
||||
if(!config.logoLocation120x120)config.logoLocation120x120 = 'libs/img/icon/apple-touch-icon-120x120.png';
|
||||
if(!config.logoLocation144x144)config.logoLocation144x144 = 'libs/img/icon/apple-touch-icon-144x144.png';
|
||||
if(!config.logoLocation152x152)config.logoLocation152x152 = 'libs/img/icon/apple-touch-icon-152x152.png';
|
||||
if(!config.logoLocation196x196)config.logoLocation196x196 = 'libs/img/icon/favicon-196x196.png';
|
||||
if(config.logoLocation76x76Link === undefined){config.logoLocation76x76Link='https://shinobi.video'}
|
||||
if(config.logoLocation76x76Style === undefined){config.logoLocation76x76Style='border-radius:50%'}
|
||||
if(config.loginScreenBackground === undefined){config.loginScreenBackground='assets/img/splash.avif'}
|
||||
if(config.showLoginSelector === undefined){config.showLoginSelector=true}
|
||||
if(config.defaultTheme === undefined)config.defaultTheme = 'Ice-v3';
|
||||
if(config.socialLinks === undefined){
|
||||
config.socialLinks = [
|
||||
{
|
||||
icon: 'home',
|
||||
href: 'https://shinobi.video',
|
||||
title: 'Homepage'
|
||||
},
|
||||
{
|
||||
icon: 'facebook',
|
||||
href: 'https://www.facebook.com/ShinobiCCTV',
|
||||
title: 'Facebook'
|
||||
},
|
||||
{
|
||||
icon: 'twitter',
|
||||
href: 'https://twitter.com/ShinobiCCTV',
|
||||
title: 'Twitter'
|
||||
},
|
||||
{
|
||||
icon: 'youtube',
|
||||
href: 'https://www.youtube.com/channel/UCbgbBLTK-koTyjOmOxA9msQ',
|
||||
title: 'YouTube'
|
||||
}
|
||||
]
|
||||
}
|
||||
const processCwd = process.cwd();
|
||||
const { setDefaultIfUndefined } = require('./basic/utils.js')(processCwd,config);
|
||||
setDefaultIfUndefined(config, 'showPoweredByShinobi', true);
|
||||
setDefaultIfUndefined(config, 'poweredByShinobi', 'Powered by Shinobi.Systems');
|
||||
setDefaultIfUndefined(config, 'showLoginCardHeader', true);
|
||||
setDefaultIfUndefined(config, 'webFavicon', 'libs/img/icon/favicon.ico');
|
||||
setDefaultIfUndefined(config, 'logoLocationAppleTouchIcon', 'libs/img/icon/apple-touch-icon.png');
|
||||
setDefaultIfUndefined(config, 'logoLocation57x57', 'libs/img/icon/apple-touch-icon-57x57.png');
|
||||
setDefaultIfUndefined(config, 'logoLocation72x72', 'libs/img/icon/apple-touch-icon-72x72.png');
|
||||
setDefaultIfUndefined(config, 'logoLocation76x76', 'libs/img/icon/apple-touch-icon-76x76.png');
|
||||
setDefaultIfUndefined(config, 'logoLocation114x114', 'libs/img/icon/apple-touch-icon-114x114.png');
|
||||
setDefaultIfUndefined(config, 'logoLocation120x120', 'libs/img/icon/apple-touch-icon-120x120.png');
|
||||
setDefaultIfUndefined(config, 'logoLocation144x144', 'libs/img/icon/apple-touch-icon-144x144.png');
|
||||
setDefaultIfUndefined(config, 'logoLocation152x152', 'libs/img/icon/apple-touch-icon-152x152.png');
|
||||
setDefaultIfUndefined(config, 'logoLocation196x196', 'libs/img/icon/favicon-196x196.png');
|
||||
setDefaultIfUndefined(config, 'logoLocation76x76Link', 'https://shinobi.video');
|
||||
setDefaultIfUndefined(config, 'logoLocation76x76Style', 'border-radius:50%');
|
||||
setDefaultIfUndefined(config, 'loginScreenBackground', 'assets/img/splash.avif');
|
||||
setDefaultIfUndefined(config, 'showLoginSelector', true);
|
||||
setDefaultIfUndefined(config, 'defaultTheme', 'Ice-v3');
|
||||
setDefaultIfUndefined(config, 'socialLinks', [
|
||||
{ icon: 'home', href: 'https://shinobi.video', title: 'Homepage' },
|
||||
{ icon: 'facebook', href: 'https://www.facebook.com/ShinobiCCTV', title: 'Facebook' },
|
||||
{ icon: 'twitter', href: 'https://twitter.com/ShinobiCCTV', title: 'Twitter' },
|
||||
{ icon: 'youtube', href: 'https://www.youtube.com/channel/UCbgbBLTK-koTyjOmOxA9msQ', title: 'YouTube' }
|
||||
]);
|
||||
|
||||
s.getConfigWithBranding = function(domain){
|
||||
var configCopy = Object.assign({},config)
|
||||
if(config.brandingConfig && config.brandingConfig[domain]){
|
||||
|
|
|
@ -137,7 +137,7 @@ if(rawMonitorConfig.details.detector === '1' && rawMonitorConfig.details.detecto
|
|||
try{
|
||||
const attachPamDetector = require(config.monitorDetectorDaemonPath ? config.monitorDetectorDaemonPath : __dirname + '/detector.js')(jsonData,(detectorObject) => {
|
||||
dataPort.send(JSON.stringify(detectorObject))
|
||||
})
|
||||
},dataPort)
|
||||
attachPamDetector(cameraProcess)
|
||||
}catch(err){
|
||||
writeToStderr(err.stack)
|
||||
|
|
|
@ -67,7 +67,7 @@ module.exports = function(s,config,lang,app,io){
|
|||
})
|
||||
}else{
|
||||
s.debugLog('Child Node Force Disconnected!',new Date(),ipAddress)
|
||||
client.destroy()
|
||||
client.disconnect()
|
||||
}
|
||||
}
|
||||
client.on('message',onAuthenticate)
|
||||
|
|
|
@ -20,7 +20,7 @@ module.exports = function(s,config,lang,app,io){
|
|||
break;
|
||||
case'kill':
|
||||
s.initiateMonitorObject(d.d);
|
||||
cameraDestroy(d.d)
|
||||
await cameraDestroy(d.d);
|
||||
break;
|
||||
case'sync':
|
||||
s.initiateMonitorObject(d.sync);
|
||||
|
@ -41,7 +41,7 @@ module.exports = function(s,config,lang,app,io){
|
|||
break;
|
||||
case'cameraStart'://start or record camera
|
||||
try{
|
||||
await s.camera(d.mode,d.d)
|
||||
await s.camera(d.d.mode,d.d)
|
||||
let activeMonitor = s.group[d.d.ke].activeMonitors[d.d.mid]
|
||||
// activeMonitor.masterSaysToStop = false
|
||||
clearTimeout(activeMonitor.recordingChecker);
|
||||
|
|
|
@ -5,6 +5,7 @@ module.exports = function(s,config,lang,app,io){
|
|||
} = require('./events/utils.js')(s,config,lang)
|
||||
s.dataPortTokens = {}
|
||||
const theWebSocket = createWebSocketServer()
|
||||
s.dataPortServer = theWebSocket;
|
||||
function setClientKillTimerIfNotAuthenticatedInTime(client){
|
||||
client.killTimer = setTimeout(function(){
|
||||
client.terminate()
|
||||
|
@ -39,6 +40,8 @@ module.exports = function(s,config,lang,app,io){
|
|||
s.debugLog(data.data)
|
||||
break;
|
||||
default:
|
||||
console.log(`No Data Port Handler!`)
|
||||
console.log(`here's what we got :`)
|
||||
console.log(data)
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,16 @@ module.exports = function(s,config){
|
|||
isMySQL,
|
||||
} = require('./utils.js')(s,config)
|
||||
s.preQueries = async function(){
|
||||
await createTable('Logs',[
|
||||
isMySQL ? {name: 'utf8', type: 'charset'} : null,
|
||||
isMySQL ? {name: 'utf8_general_ci', type: 'collate'} : null,
|
||||
{name: 'ke', length: 50, type: 'string'},
|
||||
{name: 'mid', length: 100, type: 'string'},
|
||||
{name: 'info', type: 'text'},
|
||||
{name: 'time', type: 'timestamp', defaultTo: currentTimestamp()},
|
||||
// KEY `logs_index` (`ke`,`mid`,`time`)
|
||||
{name: ['ke', 'mid', 'time'], type: 'index', length: 'logs_index'},
|
||||
]);
|
||||
await createTable('Users',[
|
||||
isMySQL ? {name: 'utf8', type: 'charset'} : null,
|
||||
isMySQL ? {name: 'utf8_general_ci', type: 'collate'} : null,
|
||||
|
@ -130,16 +140,6 @@ module.exports = function(s,config){
|
|||
{name: 'size', type: 'bigint'},
|
||||
{name: 'details', type: 'text'},
|
||||
]);
|
||||
await createTable('Logs',[
|
||||
isMySQL ? {name: 'utf8', type: 'charset'} : null,
|
||||
isMySQL ? {name: 'utf8_general_ci', type: 'collate'} : null,
|
||||
{name: 'ke', length: 50, type: 'string'},
|
||||
{name: 'mid', length: 100, type: 'string'},
|
||||
{name: 'info', type: 'text'},
|
||||
{name: 'time', type: 'timestamp', defaultTo: currentTimestamp()},
|
||||
// KEY `logs_index` (`ke`,`mid`,`time`)
|
||||
{name: ['ke', 'mid', 'time'], type: 'index', length: 'logs_index'},
|
||||
]);
|
||||
await createTable('Monitors',[
|
||||
isMySQL ? {name: 'utf8', type: 'charset'} : null,
|
||||
isMySQL ? {name: 'utf8_general_ci', type: 'collate'} : null,
|
||||
|
|
|
@ -11,6 +11,18 @@ module.exports = function(s,config,lang,app,io){
|
|||
let text = buff.toString('ascii');
|
||||
return text
|
||||
}
|
||||
async function copyFileAsync(filePath, snapPath) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const readStream = fs.createReadStream(filePath);
|
||||
const writeStream = fs.createWriteStream(snapPath);
|
||||
|
||||
readStream.on('error', reject);
|
||||
writeStream.on('error', reject);
|
||||
writeStream.on('finish', resolve);
|
||||
|
||||
readStream.pipe(writeStream);
|
||||
});
|
||||
}
|
||||
const {
|
||||
triggerEvent,
|
||||
} = require('./events/utils.js')(s,config,lang)
|
||||
|
@ -39,8 +51,8 @@ module.exports = function(s,config,lang,app,io){
|
|||
var filename = getFileNameFromPath(filePath)
|
||||
if(search(filename,'.jpg') || search(filename,'.jpeg')){
|
||||
var snapPath = s.dir.streams + ke + '/' + mid + '/s.jpg'
|
||||
fs.rm(snapPath,function(err){
|
||||
fs.createReadStream(filePath).pipe(fs.createWriteStream(snapPath))
|
||||
fs.rm(snapPath,async function(err){
|
||||
await copyFileAsync(filePath, snapPath)
|
||||
triggerEvent({
|
||||
id: mid,
|
||||
ke: ke,
|
||||
|
|
|
@ -1,7 +1,15 @@
|
|||
const movingThings = require('shinobi-node-moving-things-tracker').Tracker
|
||||
module.exports = (s,config,lang) => {
|
||||
module.exports = (config) => {
|
||||
const objectTrackers = {}
|
||||
const objectTrackerTimeouts = {}
|
||||
const peopleTags = new Set(config.peopleTags || ["person", "Person", "Man", "Woman", "Boy", "Girl"]);
|
||||
const trackedMatrices = {}
|
||||
function shiftSet(set) {
|
||||
for (const res of set) {
|
||||
set.delete(res);
|
||||
return res
|
||||
}
|
||||
}
|
||||
function resetObjectTracker(trackerId,matrices){
|
||||
const Tracker = movingThings.newTracker();
|
||||
objectTrackers[trackerId] = {
|
||||
|
@ -146,6 +154,243 @@ module.exports = (s,config,lang) => {
|
|||
}
|
||||
return movedMatrices
|
||||
}
|
||||
function isMatrixNearAnother(firstMatrix, secondMatrix, imgWidth, imgHeight) {
|
||||
// Calculate the distance between two rectangles
|
||||
function rectDistance(rect1, rect2) {
|
||||
const xDist = Math.max(rect1.x - (rect2.x + rect2.width), rect2.x - (rect1.x + rect1.width), 0);
|
||||
const yDist = Math.max(rect1.y - (rect2.y + rect2.height), rect2.y - (rect1.y + rect1.height), 0);
|
||||
return Math.sqrt(xDist * xDist + yDist * yDist);
|
||||
}
|
||||
|
||||
// Calculate the overlap area
|
||||
function overlapArea(rect1, rect2) {
|
||||
const xOverlap = Math.max(0, Math.min(rect1.x + rect1.width, rect2.x + rect2.width) - Math.max(rect1.x, rect2.x));
|
||||
const yOverlap = Math.max(0, Math.min(rect1.y + rect1.height, rect2.y + rect2.height) - Math.max(rect1.y, rect2.y));
|
||||
return xOverlap * yOverlap;
|
||||
}
|
||||
|
||||
const pxDistance = rectDistance(firstMatrix, secondMatrix);
|
||||
const overlapAreaValue = overlapArea(firstMatrix, secondMatrix);
|
||||
const totalArea = firstMatrix.width * firstMatrix.height + secondMatrix.width * secondMatrix.height - overlapAreaValue;
|
||||
const overlapPercent = totalArea > 0 ? (overlapAreaValue / totalArea) * 100 : 0;
|
||||
const distancePercent = Math.sqrt(Math.pow(pxDistance / imgWidth, 2) + Math.pow(pxDistance / imgHeight, 2)) * 100;
|
||||
const isOverlap = overlapAreaValue > 0;
|
||||
const nearThreshold = 50;
|
||||
const isNear = pxDistance < nearThreshold;
|
||||
|
||||
return { pxDistance, overlapPercent, distancePercent, isOverlap, isNear };
|
||||
}
|
||||
function combinePeopleAndMiscObjects(matrices, imgWidth, imgHeight) {
|
||||
const peopleMatrices = [];
|
||||
const otherMatrices = [];
|
||||
|
||||
// Separate the matrices into two arrays
|
||||
matrices.forEach(matrix => {
|
||||
if (peopleTags.has(matrix.tag)) {
|
||||
peopleMatrices.push({...matrix, nearBy: []});
|
||||
} else {
|
||||
otherMatrices.push({...matrix, associatedPeople: [], color: 'green'}); // Initialize associatedPeople array
|
||||
}
|
||||
});
|
||||
|
||||
// Compare each people matrix with each other matrix
|
||||
peopleMatrices.forEach(personMatrix => {
|
||||
otherMatrices.forEach(otherMatrix => {
|
||||
const comparisonResult = isMatrixNearAnother(personMatrix, otherMatrix, imgWidth, imgHeight);
|
||||
// console.error(`comparisonResult (${comparisonResult.overlapPercent}%) : ${otherMatrix.tag} (${otherMatrix.id}) is on ${personMatrix.tag} (${personMatrix.id}) about ${comparisonResult.overlapPercent}%`,comparisonResult)
|
||||
if (comparisonResult.overlapPercent > 35) {
|
||||
// Attach the person's ID to the otherMatrix
|
||||
|
||||
// Combine comparison result with the properties of the other matrix
|
||||
personMatrix.nearBy.push({
|
||||
...otherMatrix,
|
||||
...comparisonResult,
|
||||
});
|
||||
otherMatrix.associatedPeople.push(personMatrix.id);
|
||||
otherMatrix.color = 'yellow'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return [...peopleMatrices, ...otherMatrices];
|
||||
}
|
||||
function addToTrackedHistory(theEvent){
|
||||
const groupKey = theEvent.ke
|
||||
const monitorId = theEvent.id
|
||||
const matrices = theEvent.details.matrices
|
||||
matrices.forEach((matrix) => {
|
||||
const trackerId = `${groupKey}${monitorId}${matrix.id}${matrix.tag}`;
|
||||
if(!trackedMatrices[trackerId])trackedMatrices[trackerId] = new Set();
|
||||
trackedMatrices[trackerId].add(matrix);
|
||||
if (trackedMatrices[trackerId].length > 30) {
|
||||
shiftSet(trackedMatrices[trackerId]);
|
||||
}
|
||||
});
|
||||
}
|
||||
function filterOutLessSeenNearBy(theEvent){
|
||||
const groupKey = theEvent.ke;
|
||||
const monitorId = theEvent.id;
|
||||
const matrices = theEvent.details.matrices;
|
||||
matrices.forEach(matrix => {
|
||||
if(!matrix.nearBy)matrix.nearBy = [];
|
||||
const trackerId = `${groupKey}${monitorId}${matrix.id}${matrix.tag}`;
|
||||
const trackedSet = trackedMatrices[trackerId];
|
||||
if (trackedSet && trackedSet.size > 0) {
|
||||
const frequencyMap = new Map();
|
||||
trackedSet.forEach(trackedMatrix => {
|
||||
trackedMatrix.nearBy.forEach(nearByMatrix => {
|
||||
const key = JSON.stringify(nearByMatrix); // Assuming 'nearByMatrix' is an object
|
||||
frequencyMap.set(key, (frequencyMap.get(key) || 0) + 1);
|
||||
});
|
||||
});
|
||||
matrix.nearBy = matrix.nearBy.filter(nearByMatrix => {
|
||||
const key = JSON.stringify(nearByMatrix);
|
||||
return frequencyMap.get(key) / trackedSet.size >= 0.8;
|
||||
});
|
||||
}
|
||||
});
|
||||
return theEvent;
|
||||
}
|
||||
function separateMatricesByTag(matrices) {
|
||||
const groupedByTag = matrices.reduce((acc, matrix) => {
|
||||
if (!acc[matrix.tag]) {
|
||||
acc[matrix.tag] = [];
|
||||
}
|
||||
acc[matrix.tag].push(matrix);
|
||||
return acc;
|
||||
}, {});
|
||||
return Object.values(groupedByTag);
|
||||
}
|
||||
function trackMatrices(theEvent){
|
||||
const groupKey = theEvent.ke;
|
||||
const monitorId = theEvent.id;
|
||||
const eventDetails = theEvent.details;
|
||||
const trackedObjects = []
|
||||
separateMatricesByTag(eventDetails.matrices).forEach((matrices) => {
|
||||
if(!matrices[0])return;
|
||||
const matrixTag = matrices[0].tag
|
||||
const trackerId = `${groupKey}${monitorId}${matrixTag}`;
|
||||
trackObjectWithTimeout(trackerId,matrices);
|
||||
trackedObjects.push(...getTracked(trackerId));
|
||||
setLastTracked(trackerId, trackedObjects);
|
||||
});
|
||||
return trackedObjects;
|
||||
}
|
||||
function markMatricesWithRedFlagTags(theEvent, redFlags) {
|
||||
const groupKey = theEvent.ke;
|
||||
const monitorId = theEvent.id;
|
||||
const matrices = theEvent.details.matrices;
|
||||
|
||||
matrices.forEach((matrix) => {
|
||||
const trackerId = `${groupKey}${monitorId}${matrix.id}${matrix.tag}`;
|
||||
const trackedMatrixSet = trackedMatrices[trackerId];
|
||||
|
||||
if (trackedMatrixSet) {
|
||||
let redFlagCount = 0; // Counter for matrices with red flag tags
|
||||
|
||||
trackedMatrixSet.forEach((trackedMatrix) => {
|
||||
// Check if any nearBy matrix has a tag that matches the red flags
|
||||
if (trackedMatrix.nearBy && trackedMatrix.nearBy.some(nearByMatrix => redFlags.includes(nearByMatrix.tag))) {
|
||||
redFlagCount++; // Increment counter for each match
|
||||
}
|
||||
});
|
||||
|
||||
// Calculate if the red flag count is at least 30% of the trackedMatrixSet
|
||||
if (redFlagCount / trackedMatrixSet.size >= 0.3) {
|
||||
matrix.suspect = true; // Mark the matrix as suspect
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
function setMissingRecentlyMatrices(theEvent, redFlags) {
|
||||
const groupKey = theEvent.ke;
|
||||
const monitorId = theEvent.id;
|
||||
let eventMatrices = theEvent.details.matrices.map(matrix => {
|
||||
return { ...matrix, missingRecently: [] }
|
||||
});
|
||||
let nearByFrequencies = {};
|
||||
|
||||
// Calculate frequencies of nearBy tags across all trackedMatrixSets
|
||||
eventMatrices.forEach((matrix) => {
|
||||
if(!matrix.suspect)return;
|
||||
const trackerId = `${groupKey}${monitorId}${matrix.id}${matrix.tag}`;
|
||||
const trackedMatrixSet = trackedMatrices[trackerId];
|
||||
|
||||
if (trackedMatrixSet) {
|
||||
trackedMatrixSet.forEach((trackedMatrix) => {
|
||||
if (trackedMatrix.nearBy) {
|
||||
trackedMatrix.nearBy.forEach((nearByMatrix) => {
|
||||
nearByFrequencies[nearByMatrix.tag] = (nearByFrequencies[nearByMatrix.tag] || 0) + 1;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Determine which nearBy items are seen in at least 30% of trackedMatrixSet
|
||||
let frequentItems = Object.keys(nearByFrequencies).filter(tag => nearByFrequencies[tag] / Object.keys(trackedMatrices).length >= 0.3);
|
||||
|
||||
// Update eventMatrices with missingRecently data
|
||||
eventMatrices = eventMatrices.map(matrix => {
|
||||
if(!matrix.suspect)return matrix;
|
||||
let missingTags = frequentItems.filter(item => !!item && !(matrix.nearBy && matrix.nearBy.some(nearByMatrix => nearByMatrix.tag === item)));
|
||||
if (missingTags.length > 0) {
|
||||
matrix.missingRecently = missingTags;
|
||||
}
|
||||
return matrix;
|
||||
});
|
||||
|
||||
return eventMatrices;
|
||||
}
|
||||
|
||||
function checkMissingItemsNearRedFlagContainers(theEvent, redFlagContainers) {
|
||||
const groupKey = theEvent.ke;
|
||||
const monitorId = theEvent.id;
|
||||
let eventMatrices = theEvent.details.matrices;
|
||||
|
||||
eventMatrices.forEach(matrix => {
|
||||
if(!matrix.suspect)return;
|
||||
// Initialize an array to store red flag containers that are near missing items, if not already present
|
||||
if (!matrix.nearRedFlagContainers) {
|
||||
matrix.nearRedFlagContainers = [];
|
||||
}
|
||||
|
||||
// Proceed if the matrix has missingRecently items
|
||||
if (matrix.missingRecently && matrix.missingRecently.length > 0) {
|
||||
const trackerId = `${groupKey}${monitorId}${matrix.id}${matrix.tag}`;
|
||||
const trackedMatrixSet = trackedMatrices[trackerId];
|
||||
|
||||
if (trackedMatrixSet) {
|
||||
trackedMatrixSet.forEach(trackedMatrix => {
|
||||
// Check if this trackedMatrix is a redFlagContainer
|
||||
if (redFlagContainers.includes(trackedMatrix.tag)) {
|
||||
// Now, check each missingRecently item
|
||||
matrix.missingRecently.forEach(missingItemTag => {
|
||||
console.log(`missingItemTag`,missingItemTag)
|
||||
// Find the original matrix for the missingItemTag to perform proximity check
|
||||
const missingItemMatrix = Array.from(trackedMatrixSet).find(m => m.tag === missingItemTag);
|
||||
if (missingItemMatrix) {
|
||||
// Check if missingItemMatrix is near the redFlagContainer
|
||||
const isNear = isMatrixNearAnother(missingItemMatrix, trackedMatrix, theEvent.imgWidth, theEvent.imgHeight);
|
||||
if (isNear) {
|
||||
// If near, add redFlagContainer information to the matrix
|
||||
matrix.nearRedFlagContainers.push({
|
||||
tag: trackedMatrix.tag,
|
||||
id: trackedMatrix.id,
|
||||
missingItemTag: missingItemTag
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return eventMatrices;
|
||||
}
|
||||
|
||||
return {
|
||||
trackObjectWithTimeout,
|
||||
resetObjectTracker,
|
||||
|
@ -153,5 +398,15 @@ module.exports = (s,config,lang) => {
|
|||
getTracked,
|
||||
setLastTracked,
|
||||
getAllMatricesThatMoved,
|
||||
isMatrixNearAnother,
|
||||
combinePeopleAndMiscObjects,
|
||||
filterOutLessSeenNearBy,
|
||||
separateMatricesByTag,
|
||||
addToTrackedHistory,
|
||||
trackMatrices,
|
||||
markMatricesWithRedFlagTags,
|
||||
setMissingRecentlyMatrices,
|
||||
checkMissingItemsNearRedFlagContainers,
|
||||
peopleTags,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ module.exports = (s,config,lang) => {
|
|||
setLastTracked,
|
||||
trackObjectWithTimeout,
|
||||
getAllMatricesThatMoved,
|
||||
} = require('./tracking.js')(s,config,lang)
|
||||
} = require('./tracking.js')(config)
|
||||
const {
|
||||
isEven,
|
||||
fetchTimeout,
|
||||
|
@ -448,8 +448,8 @@ module.exports = (s,config,lang) => {
|
|||
createEventBasedRecording(d,moment(eventTime).subtract(secondBefore,'seconds').format('YYYY-MM-DDTHH-mm-ss'))
|
||||
}
|
||||
d.currentTime = eventTime
|
||||
d.currentTimestamp = s.timeObject(d.currentTime).format()
|
||||
d.screenshotName = eventDetails.reason + '_'+(monitorConfig.name.replace(/[^\w\s]/gi,''))+'_'+d.id+'_'+d.ke+'_'+s.formattedTime()
|
||||
d.currentTimestamp = s.timeObject(eventTime).format()
|
||||
d.screenshotName = eventDetails.reason + '_'+(monitorConfig.name.replace(/[^\w\s]/gi,''))+'_'+d.id+'_'+d.ke+'_'+s.formattedTime(eventTime)
|
||||
d.screenshotBuffer = null
|
||||
|
||||
if(filter.webhook && monitorDetails.detector_webhook === '1' && !s.group[d.ke].activeMonitors[d.id].detector_webhook){
|
||||
|
|
|
@ -617,7 +617,8 @@ module.exports = (s,config,lang) => {
|
|||
const sendFramesGlobally = (e.details.detector_send_frames === '1')
|
||||
const objectDetectorOutputIsEnabled = (e.details.detector_use_detect_object === '1')
|
||||
const builtInMotionDetectorIsEnabled = (e.details.detector_pam === '1')
|
||||
const sendFramesToObjectDetector = (e.details.detector_send_frames_object !== '0' && e.details.detector_use_detect_object === '1')
|
||||
const objectDetectorSendFrames = e.details.detector_send_frames_object !== '0' || e.details.detector_pam !== '1'
|
||||
const sendFramesToObjectDetector = (objectDetectorSendFrames && e.details.detector_use_detect_object === '1')
|
||||
const baseWidth = e.details.detector_scale_x ? e.details.detector_scale_x : '640'
|
||||
const baseHeight = e.details.detector_scale_y ? e.details.detector_scale_y : '480'
|
||||
const baseDimensionsFlag = `-s ${baseWidth}x${baseHeight}`
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
buildOptions = function(field,possiblities){
|
||||
if(!field)console.error('field',field)
|
||||
var fieldElement = ''
|
||||
possiblities.forEach(function(option){
|
||||
if(option.optgroup){
|
||||
fieldElement += '<optgroup label="' + option.name + '">'
|
||||
fieldElement += buildOptions(field,option.optgroup)
|
||||
fieldElement += '</optgroup>'
|
||||
}else{
|
||||
var selected = ''
|
||||
if(option.value === field.default){
|
||||
selected = 'selected'
|
||||
}
|
||||
fieldElement += '<option value="' + option.value + '" ' + selected + '>' + option.name + '</option>'
|
||||
}
|
||||
})
|
||||
return fieldElement
|
||||
}
|
||||
module.exports = {
|
||||
buildOptions: buildOptions
|
||||
}
|
|
@ -158,7 +158,7 @@ module.exports = function(s,config,lang){
|
|||
var outputOptions = []
|
||||
var streamDir = s.dir.streams + monitor.ke + '/' + monitor.mid + '/'
|
||||
var url = options.url
|
||||
var secondsInward = options.secondsInward || '0'
|
||||
var secondsInward = options.secondsInward || '5'
|
||||
if(secondsInward.length === 1 && !isNaN(secondsInward))secondsInward = '0' + secondsInward;
|
||||
var dynamicTimeout = (secondsInward * 1000) + 5000;
|
||||
if(options.flags)outputOptions.push(options.flags)
|
||||
|
@ -196,7 +196,7 @@ module.exports = function(s,config,lang){
|
|||
var iconImageFile = streamDir + 'icon.jpg'
|
||||
const snapRawFilters = monitor.details.cust_snap_raw
|
||||
if(snapRawFilters)outputOptions.push(snapRawFilters);
|
||||
var ffmpegCmd = splitForFFMPEG(`-y -loglevel warning ${isDetectorStream ? '-live_start_index 2' : ''} -re ${inputOptions.join(' ')} -i "${url}" ${outputOptions.join(' ')} -f image2 -an -frames:v 1 "${temporaryImageFile}"`)
|
||||
var ffmpegCmd = splitForFFMPEG(`-y -loglevel warning ${isDetectorStream ? '-live_start_index 2' : ''} -re ${inputOptions.join(' ')} -timeout 4000000 -i "${url}" ${outputOptions.join(' ')} -f image2 -an -frames:v 1 "${temporaryImageFile}"`)
|
||||
try{
|
||||
await fs.promises.mkdir(streamDir, {recursive: true}, (err) => {s.debugLog(err)})
|
||||
}catch(err){
|
||||
|
@ -666,8 +666,8 @@ module.exports = function(s,config,lang){
|
|||
e.functionMode = selectedMode
|
||||
if(!e.mode){e.mode = selectedMode}
|
||||
s.checkDetails(e)
|
||||
checkObjectsInMonitorDetails(e)
|
||||
s.initiateMonitorObject({ke:e.ke,mid:monitorId})
|
||||
checkObjectsInMonitorDetails(e)
|
||||
switch(e.functionMode){
|
||||
case'watch_on':
|
||||
monitorAddViewer(e,cn)
|
||||
|
@ -688,7 +688,7 @@ module.exports = function(s,config,lang){
|
|||
await monitorStart(e)
|
||||
break;
|
||||
default:
|
||||
console.log(selectedMode)
|
||||
console.log('No s.camera execute : ',selectedMode)
|
||||
break;
|
||||
}
|
||||
if(typeof cn === 'function'){cn()}
|
||||
|
|
|
@ -8,6 +8,7 @@ const spawn = require('child_process').spawn;
|
|||
const connectionTester = require('connection-tester')
|
||||
const SoundDetection = require('shinobi-sound-detection')
|
||||
const streamViewerCountTimeouts = {}
|
||||
const { createQueueAwaited } = require('../common.js')
|
||||
module.exports = (s,config,lang) => {
|
||||
const {
|
||||
applyPartialToConfiguration,
|
||||
|
@ -330,17 +331,31 @@ module.exports = (s,config,lang) => {
|
|||
console.log(data.toString())
|
||||
})
|
||||
}
|
||||
if(logLevel !== 'quiet'){
|
||||
subStreamProcess.stderr.on('data',(data) => {
|
||||
|
||||
subStreamProcess.stderr.on('data',(data) => {
|
||||
const string = data.toString();
|
||||
if(string.includes('No such')){
|
||||
processKill(subStreamProcess);
|
||||
return;
|
||||
}
|
||||
if(logLevel !== 'quiet'){
|
||||
s.userLog({
|
||||
ke: groupKey,
|
||||
mid: monitorId,
|
||||
},{
|
||||
type: lang["Substream Process"],
|
||||
msg: data.toString()
|
||||
msg: string
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
subStreamProcess.stdio[5].on('data',(data) => {
|
||||
resetStreamCheck({
|
||||
ke: groupKey,
|
||||
mid: monitorId,
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
subStreamProcess.on('close',(data) => {
|
||||
if(!activeMonitor.allowDestroySubstream){
|
||||
subStreamProcess.stderr.on('data',(data) => {
|
||||
|
@ -1431,6 +1446,9 @@ module.exports = (s,config,lang) => {
|
|||
const typeIsLocal = e.type === 'local'
|
||||
const monitorConfig = theGroup.rawMonitorConfigurations[monitorId]
|
||||
const doPingTest = e.type !== 'socket' && e.type !== 'dashcam' && e.protocol !== 'udp' && e.type !== 'local' && e.details.skip_ping !== '1';
|
||||
if(!theGroup.startMonitorInQueue){
|
||||
theGroup.startMonitorInQueue = createQueueAwaited(0.5, 1)
|
||||
}
|
||||
const startMonitorInQueue = theGroup.startMonitorInQueue
|
||||
if(!activeMonitor.isStarted)return;
|
||||
// e = monitor object
|
||||
|
|
|
@ -13,6 +13,10 @@ module.exports = function(s,config,lang,getSnapshot){
|
|||
//discord bot
|
||||
if(config.discordBot === true){
|
||||
try{
|
||||
const messageFooter = {
|
||||
icon_url: config.iconURL,
|
||||
text: config.notifyFooterText || "Shinobi Systems"
|
||||
};
|
||||
const sendMessage = function(data,files,groupKey){
|
||||
if(!data)data = {};
|
||||
var bot = s.group[groupKey].discordBot
|
||||
|
@ -26,10 +30,7 @@ module.exports = function(s,config,lang,getSnapshot){
|
|||
description: "",
|
||||
fields: [],
|
||||
timestamp: new Date(),
|
||||
footer: {
|
||||
icon_url: config.iconURL,
|
||||
text: "Shinobi Systems"
|
||||
}
|
||||
footer: messageFooter
|
||||
},data)
|
||||
const discordChannel = bot.channels.cache.get(s.group[groupKey].init.discordbot_channel)
|
||||
if(discordChannel && discordChannel.send){
|
||||
|
@ -39,8 +40,7 @@ module.exports = function(s,config,lang,getSnapshot){
|
|||
}).catch(err => {
|
||||
if(err){
|
||||
s.userLog({ke:groupKey,mid:'$USER'},{type:lang.DiscordErrorText,msg:err})
|
||||
s.group[groupKey].discordBot = null
|
||||
s.loadGroupApps({ke:groupKey})
|
||||
restartDiscordBot(groupKey, 1)
|
||||
}
|
||||
})
|
||||
}else{
|
||||
|
@ -50,7 +50,7 @@ module.exports = function(s,config,lang,getSnapshot){
|
|||
},{
|
||||
type: lang.DiscordErrorText,
|
||||
msg: 'Check the Channel ID'
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
const onEventTriggerBeforeFilterForDiscord = function(d,filter){
|
||||
|
@ -85,10 +85,7 @@ module.exports = function(s,config,lang,getSnapshot){
|
|||
description: notifyText+' '+d.currentTimestamp,
|
||||
fields: [],
|
||||
timestamp: d.currentTime,
|
||||
footer: {
|
||||
icon_url: config.iconURL,
|
||||
text: "Shinobi Systems"
|
||||
}
|
||||
footer: messageFooter
|
||||
},[
|
||||
{
|
||||
attachment: d.screenshotBuffer,
|
||||
|
@ -121,10 +118,7 @@ module.exports = function(s,config,lang,getSnapshot){
|
|||
description: notifyText,
|
||||
fields: [],
|
||||
timestamp: d.currentTime,
|
||||
footer: {
|
||||
icon_url: config.iconURL,
|
||||
text: "Shinobi Systems"
|
||||
}
|
||||
footer: messageFooter
|
||||
},[
|
||||
{
|
||||
attachment: videoPath,
|
||||
|
@ -147,13 +141,19 @@ module.exports = function(s,config,lang,getSnapshot){
|
|||
description: '**'+s.factorAuth[r.ke][r.uid].key+'** '+r.lang.FactorAuthText1,
|
||||
fields: [],
|
||||
timestamp: new Date(),
|
||||
footer: {
|
||||
icon_url: config.iconURL,
|
||||
text: "Shinobi Systems"
|
||||
}
|
||||
footer: messageFooter
|
||||
},[],r.ke)
|
||||
}
|
||||
}
|
||||
let restartAttemptLockTimer = null
|
||||
const restartDiscordBot = function(groupKey, timer = 60000 * 5){
|
||||
s.debugLog(`Discord Bot Restarting : ${groupKey}`)
|
||||
s.group[groupKey].discordBot = null;
|
||||
clearTimeout(restartAttemptLockTimer);
|
||||
restartAttemptLockTimer = setTimeout(function(){
|
||||
s.loadGroupApps({ke: groupKey});
|
||||
}, timer);
|
||||
}
|
||||
const loadDiscordBotForUser = function(user){
|
||||
const userDetails = s.parseJSON(user.details);
|
||||
//discordbot
|
||||
|
@ -162,19 +162,35 @@ module.exports = function(s,config,lang,getSnapshot){
|
|||
userDetails.discordbot === '1' &&
|
||||
userDetails.discordbot_token !== ''
|
||||
){
|
||||
const groupKey = user.ke;
|
||||
const theGroup = s.group[groupKey];
|
||||
s.debugLog(`!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!`)
|
||||
s.debugLog(`Discord Connecting ${userDetails.discordbot_token}`)
|
||||
s.group[user.ke].discordBot = new Discord.Client()
|
||||
s.group[user.ke].discordBot.on('ready', () => {
|
||||
const discordBot = new Discord.Client();
|
||||
discordBot.on('ready', () => {
|
||||
const botTag = discordBot.user.tag;
|
||||
s.debugLog(`Discord Bot Ready : ${groupKey} : ${botTag}`)
|
||||
s.userLog({
|
||||
ke: user.ke,
|
||||
ke: groupKey,
|
||||
mid: '$USER'
|
||||
},{
|
||||
type: lang.DiscordLoggedIn,
|
||||
msg: s.group[user.ke].discordBot.user.tag
|
||||
msg: botTag
|
||||
})
|
||||
})
|
||||
s.group[user.ke].discordBot.login(userDetails.discordbot_token)
|
||||
});
|
||||
discordBot.on('error', (error) => {
|
||||
s.debugLog(`Discord Error : ${groupKey} : ${error}`)
|
||||
s.userLog({
|
||||
ke: groupKey,
|
||||
mid: '$USER'
|
||||
},{
|
||||
type: lang.DiscordErrorText,
|
||||
msg: error
|
||||
});
|
||||
restartDiscordBot(groupKey)
|
||||
});
|
||||
discordBot.login(userDetails.discordbot_token);
|
||||
theGroup.discordBot = discordBot;
|
||||
}
|
||||
}
|
||||
const unloadDiscordBotForUser = function(user){
|
||||
|
@ -200,10 +216,7 @@ module.exports = function(s,config,lang,getSnapshot){
|
|||
description: html,
|
||||
fields: [],
|
||||
timestamp: currentTime,
|
||||
footer: {
|
||||
icon_url: config.iconURL,
|
||||
text: "Shinobi Systems"
|
||||
}
|
||||
footer: messageFooter
|
||||
},[],e.ke)
|
||||
}
|
||||
}
|
||||
|
@ -222,10 +235,7 @@ module.exports = function(s,config,lang,getSnapshot){
|
|||
description: description,
|
||||
fields: [],
|
||||
timestamp: currentTime,
|
||||
footer: {
|
||||
icon_url: config.iconURL,
|
||||
text: "Shinobi Systems"
|
||||
}
|
||||
footer: messageFooter
|
||||
},[],monitorConfig.ke)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,11 +65,15 @@ module.exports = function (s, config, lang, getSnapshot) {
|
|||
){
|
||||
const optionsPass = userDetails.emailClient_pass || ''
|
||||
const optionsSecure = userDetails.emailClient_secure === '1' ? true : false
|
||||
const optionsUnauth = userDetails.emailClient_unauth === '1' ? false : true
|
||||
const optionsPort = isNaN(userDetails.emailClient_port) ? (optionsSecure ? 465 : 587) : parseInt(userDetails.emailClient_port)
|
||||
const clientOptions = {
|
||||
host: optionsHost,
|
||||
port: optionsPort,
|
||||
secure: optionsSecure,
|
||||
tls: {
|
||||
rejectUnauthorized: optionsUnauth,
|
||||
},
|
||||
auth: {
|
||||
user: optionsUser,
|
||||
pass: optionsPass
|
||||
|
@ -354,6 +358,25 @@ module.exports = function (s, config, lang, getSnapshot) {
|
|||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
hidden: true,
|
||||
name: 'detail=emailClient_unauth',
|
||||
'form-group-class': 'u_emailClient_input u_emailClient_1',
|
||||
field: lang.rejectUnauth,
|
||||
default: '0',
|
||||
example: '',
|
||||
fieldType: 'select',
|
||||
possible: [
|
||||
{
|
||||
name: lang.No,
|
||||
value: '0',
|
||||
},
|
||||
{
|
||||
name: lang.Yes,
|
||||
value: '1',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
hidden: true,
|
||||
field: lang.Email,
|
||||
|
|
|
@ -31,12 +31,13 @@ module.exports = function(s,config,lang,getSnapshot){
|
|||
};
|
||||
client.sendMessage(roomId, content);
|
||||
}).catch(err => {
|
||||
console.error(err)
|
||||
s.userLog({
|
||||
ke: groupKey,
|
||||
mid: '$USER'
|
||||
},{
|
||||
type: 'matrix.org Error',
|
||||
msg: err
|
||||
msg: err.toString()
|
||||
});
|
||||
})
|
||||
}
|
||||
|
|
|
@ -140,9 +140,12 @@ module.exports = function(s,config,lang,getSnapshot){
|
|||
videoPath = siftedVideoFileFromRam.filePath
|
||||
videoName = siftedVideoFileFromRam.filename
|
||||
}
|
||||
if(videoPath){
|
||||
const thumbFile = getStreamDirectory(d) + 'thumb.jpg';
|
||||
var thumbFile = undefined;
|
||||
if(d.screenshotBuffer){
|
||||
thumbFile = getStreamDirectory(d) + 'thumb.jpg';
|
||||
fs.writeFileSync(thumbFile, d.screenshotBuffer)
|
||||
}
|
||||
if(videoPath){
|
||||
sendMessage({
|
||||
title: notifyText,
|
||||
},[
|
||||
|
|
|
@ -240,7 +240,29 @@ module.exports = function(s,config,lang,getSnapshot){
|
|||
"placeholder": "http://your-webhook-point/onEvent/{{INNER_EVENT_TITLE}}?info={{INNER_EVENT_INFO}}",
|
||||
"field": lang["Webhook URL"],
|
||||
"form-group-class":"u_global_webhook_input u_global_webhook_1",
|
||||
}
|
||||
},
|
||||
{
|
||||
hidden: true,
|
||||
"name": "detail=global_webhook_method",
|
||||
"field": lang['Call Method'],
|
||||
"default": "GET",
|
||||
"form-group-class":"u_global_webhook_input u_global_webhook_1",
|
||||
"fieldType": "select",
|
||||
"possible": [
|
||||
{
|
||||
"name": `GET (${lang.Default})`,
|
||||
"value": "GET"
|
||||
},
|
||||
{
|
||||
"name": "PUT",
|
||||
"value": "PUT"
|
||||
},
|
||||
{
|
||||
"name": "POST",
|
||||
"value": "POST"
|
||||
}
|
||||
]
|
||||
},
|
||||
]
|
||||
}
|
||||
s.definitions["Event Filters"].blocks["Action for Selected"].info.push({
|
||||
|
|
|
@ -47,7 +47,9 @@ module.exports = function(s,config,lang){
|
|||
}
|
||||
var backblazeErr = function(err){
|
||||
// console.log(err)
|
||||
s.userLog({mid:'$USER',ke:e.ke},{type:lang['Backblaze Error'],msg:err.stack || err.data || err})
|
||||
const msg = err.stack || err.data || err;
|
||||
delete(msg.data)
|
||||
s.userLog({mid:'$USER',ke:e.ke},{type:lang['Backblaze Error'],msg: msg})
|
||||
}
|
||||
async function createB2Connection(){
|
||||
const b2 = new B2({
|
||||
|
@ -107,6 +109,7 @@ module.exports = function(s,config,lang){
|
|||
const theGroup = s.group[e.ke]
|
||||
if(theGroup.bb_b2 && theGroup.init.use_bb_b2 !== '0' && theGroup.init.bb_b2_save === '1'){
|
||||
function backblazeErr(err){
|
||||
delete(err.data)
|
||||
s.userLog({mid:'$USER',ke:e.ke},{type:lang['Backblaze Error'],msg:err})
|
||||
s.debugLog(err)
|
||||
}
|
||||
|
|
|
@ -391,7 +391,7 @@ module.exports = (s,config,lang) => {
|
|||
where: [
|
||||
['status','!=','0'],
|
||||
['ke','=',groupKey],
|
||||
['details','LIKE',`%"type":"${storageType}"%`],
|
||||
['type','=',storageType],
|
||||
],
|
||||
orderBy: ['time','asc'],
|
||||
limit: 2
|
||||
|
@ -413,7 +413,7 @@ module.exports = (s,config,lang) => {
|
|||
amount : -(video.size/1048576),
|
||||
storageType : storageType
|
||||
})
|
||||
s.deleteVideoFromCloudExtensionsRunner(groupKey,storageType,video)
|
||||
s.deleteVideoFromCloudExtensionsRunner({ke: groupKey},storageType,video)
|
||||
})
|
||||
const whereGroupLength = whereGroup.length
|
||||
if(whereGroupLength > 0){
|
||||
|
@ -464,7 +464,7 @@ module.exports = (s,config,lang) => {
|
|||
amount : -(frame.size/1048576),
|
||||
storageType : storageType
|
||||
})
|
||||
// s.deleteVideoFromCloudExtensionsRunner(groupKey,storageType,frame)
|
||||
// s.deleteVideoFromCloudExtensionsRunner({ke: groupKey},storageType,frame)
|
||||
})
|
||||
const whereGroupLength = whereGroup.length
|
||||
if(whereGroupLength > 0){
|
||||
|
|
|
@ -12,6 +12,7 @@ var cors = require('cors')
|
|||
var proxy = httpProxy.createProxyServer({})
|
||||
var ejs = require('ejs');
|
||||
var fileupload = require("express-fileupload");
|
||||
var fieldBuild = require('./fieldBuild');
|
||||
module.exports = function(s,config,lang,app,io){
|
||||
const {
|
||||
ptzControl,
|
||||
|
@ -44,6 +45,7 @@ module.exports = function(s,config,lang,app,io){
|
|||
passables.originalURL = s.getOriginalUrl(req)
|
||||
passables.baseUrl = req.protocol+'://'+req.hostname
|
||||
passables.config = s.getConfigWithBranding(req.hostname)
|
||||
passables.fieldBuild = fieldBuild
|
||||
res.render(paths,passables,callback)
|
||||
}
|
||||
//child node proxy check
|
||||
|
@ -1284,12 +1286,12 @@ module.exports = function(s,config,lang,app,io){
|
|||
]
|
||||
})
|
||||
s.group[r.ke].rawMonitorConfigurations[r.mid]=r;
|
||||
s.tx({f:'monitor_edit',mid:r.mid,ke:r.ke,mon:r},'GRP_'+r.ke);
|
||||
s.tx({f:'monitor_edit',mid:r.mid,ke:r.ke,mon:r},'STR_'+r.ke);
|
||||
await s.camera('stop',s.cleanMonitorObject(r));
|
||||
if(req.params.f!=='stop'){
|
||||
s.camera(req.params.f,s.cleanMonitorObject(r));
|
||||
await s.camera(req.params.f,s.cleanMonitorObject(r));
|
||||
}
|
||||
s.tx({f:'monitor_edit',mid:r.mid,ke:r.ke,mon:r},'GRP_'+r.ke);
|
||||
s.tx({f:'monitor_edit',mid:r.mid,ke:r.ke,mon:r},'STR_'+r.ke);
|
||||
response.msg = user.lang['Monitor mode changed']+' : '+req.params.f;
|
||||
}else{
|
||||
response.msg = user.lang['Reset Timer'];
|
||||
|
@ -1331,7 +1333,7 @@ module.exports = function(s,config,lang,app,io){
|
|||
r.fps=s.group[r.ke].activeMonitors[r.mid].currentState.fps;
|
||||
await s.camera('stop',s.cleanMonitorObject(r));
|
||||
if(s.group[r.ke].activeMonitors[r.mid].currentState.mode!=='stop'){
|
||||
s.camera(s.group[r.ke].activeMonitors[r.mid].currentState.mode,s.cleanMonitorObject(r));
|
||||
await s.camera(s.group[r.ke].activeMonitors[r.mid].currentState.mode,s.cleanMonitorObject(r));
|
||||
}
|
||||
s.group[r.ke].rawMonitorConfigurations[r.mid]=r;
|
||||
s.tx({f:'monitor_edit',mid:r.mid,ke:r.ke,mon:r},'GRP_'+r.ke);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -24,7 +24,7 @@
|
|||
"cws": "^2.0.0",
|
||||
"digest-fetch": "^1.2.1",
|
||||
"discord.js": "^12.2.0",
|
||||
"ejs": "^2.7.4",
|
||||
"ejs": "^3.1.9",
|
||||
"express": "^4.17.1",
|
||||
"express-fileupload": "^1.4.0",
|
||||
"form-data": "^4.0.0",
|
||||
|
@ -53,11 +53,11 @@
|
|||
"pixel-change": "^1.1.0",
|
||||
"pushover-notifications": "^1.2.2",
|
||||
"sat": "^0.7.1",
|
||||
"shinobi-node-moving-things-tracker": "^0.9.1",
|
||||
"shinobi-node-moving-things-tracker": "^0.9.3",
|
||||
"shinobi-onvif": "0.1.9",
|
||||
"shinobi-sound-detection": "^0.1.13",
|
||||
"shinobi-zwave": "^1.0.11",
|
||||
"smtp-server": "^3.9.0",
|
||||
"smtp-server": "^3.13.0",
|
||||
"socket.io": "^4.4.1",
|
||||
"socket.io-client": "^4.5.3",
|
||||
"tree-kill": "1.2.2",
|
||||
|
|
|
@ -7,6 +7,12 @@ if(!importFilePath){
|
|||
return console.error(`Example Use : node ./createMonitorsJsonFromTxt.js MONITOR_BASE.json PLAIN_LIST.txt`)
|
||||
}
|
||||
const monitorBase = require(monitorBasePath)
|
||||
function generateId(x){
|
||||
if(!x){x=10};var t = "";var p = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
for( var i=0; i < x; i++ )
|
||||
t += p.charAt(Math.floor(Math.random() * p.length));
|
||||
return t;
|
||||
}
|
||||
function getUrlProtocol(urlString){
|
||||
let modifiedUrlString = `${urlString}`.split('://')
|
||||
const originalProtocol = `${modifiedUrlString[0]}`
|
||||
|
@ -37,6 +43,8 @@ function makeConfig(streamUrl){
|
|||
// streamUrl = 'rtsp://1.1.1.1:554/'
|
||||
const copyOfBaseConfig = Object.assign({},monitorBase)
|
||||
const urlParts = getUrlParts(streamUrl)
|
||||
copyOfBaseConfig.mid = generateId()
|
||||
copyOfBaseConfig.name = urlParts.hostname
|
||||
copyOfBaseConfig.host = urlParts.hostname
|
||||
copyOfBaseConfig.port = urlParts.port
|
||||
copyOfBaseConfig.path = urlParts.pathname
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
#!/bin/bash
|
||||
DIR=$(dirname $0)
|
||||
PLUGIN_DIR="$DIR/../plugins/$1"
|
||||
SPECIFIED_KEY="$3"
|
||||
if [ -d "$PLUGIN_DIR" ]; then
|
||||
INSTALLER_SCRIPT="$PLUGIN_DIR/INSTALL.sh"
|
||||
if [ "$SPECIFIED_KEY" == "" ]; then
|
||||
echo "Generating Random Plugin Key"
|
||||
SPECIFIED_KEY=$(head -c 64 < /dev/urandom | sha256sum | awk '{print substr($1,1,60)}')
|
||||
else
|
||||
echo "Using specified Plugin Key"
|
||||
fi
|
||||
sudo node $DIR/modifyConfigurationForPlugin.js $1 key=
|
||||
if [ "$2" == "true" ]; then
|
||||
cd $INSTALLER_SCRIPT
|
||||
if [ -f "$INSTALLER_SCRIPT" ]; then
|
||||
sudo sh $INSTALLER_SCRIPT
|
||||
else
|
||||
sudo npm install
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo "Plugin not found : $PLUGIN_DIR"
|
||||
fi
|
|
@ -7,9 +7,9 @@ if(!process.argv[2]||!process.argv[3]||!process.argv[4]){
|
|||
console.log('# node translateLanguageFile.js en_CA en ar')
|
||||
return
|
||||
}
|
||||
let translate;
|
||||
let translate = {};
|
||||
try{
|
||||
translate = require('@vitalets/google-translate-api')
|
||||
translate = (require('@vitalets/google-translate-api')).translate
|
||||
}catch(err){
|
||||
console.log(`You are missing a module to use this tool. Run "npm install @vitalets/google-translate-api" to install the required module.`)
|
||||
return
|
||||
|
@ -108,13 +108,26 @@ function runTranslation(termKey,numberInLine){
|
|||
})
|
||||
}
|
||||
async function runTranslatorOnSourceTerms(){
|
||||
await makeFolderForOutput()
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
let termKey = list[i]
|
||||
await runTranslation(termKey,i)
|
||||
await writeLanguageFile(newList)
|
||||
try{
|
||||
await makeFolderForOutput()
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
let termKey = list[i]
|
||||
await runTranslation(termKey,i)
|
||||
await writeLanguageFile(newList)
|
||||
}
|
||||
await moveNewLanguageFile()
|
||||
console.log('Building Language File Complete!')
|
||||
}catch(err){
|
||||
console.log(err)
|
||||
console.log('!!!!!!!!!!!!!!!----------------------')
|
||||
console.log('!!!!!!!!!!!!!!!----------------------')
|
||||
console.log('!!!!!!!!!!!!!!!----------------------')
|
||||
console.log('!!!!!!!!!!!!!!!----------------------')
|
||||
console.log('!!!!!!!!!!!!!!!----------------------')
|
||||
console.log('!!!!!!!!!!!!!!!----------------------')
|
||||
console.log('!!!!!!!!!!!!!!!----------------------')
|
||||
console.log(translate)
|
||||
}
|
||||
await moveNewLanguageFile()
|
||||
console.log('Building Language File Complete!')
|
||||
|
||||
}
|
||||
runTranslatorOnSourceTerms()
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
const fs = require('fs').promises;
|
||||
const { spawn } = require('child_process');
|
||||
const path = require('path');
|
||||
|
||||
const languagesDir = path.join(__dirname, '../languages/');
|
||||
|
||||
async function translateFiles() {
|
||||
try {
|
||||
const files = await fs.readdir(languagesDir);
|
||||
for (const file of files) {
|
||||
if (path.extname(file) === '.json' && file !== 'en_CA.json') {
|
||||
const languageCode = path.basename(file, '.json');
|
||||
await translateLanguageFile('en_CA', 'en', languageCode);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error reading language files:', err);
|
||||
}
|
||||
}
|
||||
|
||||
function translateLanguageFile(sourceLang, targetLang, languageCode) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const command = 'node';
|
||||
const args = [`${__dirname}/translateLanguageFile.js`, sourceLang, targetLang, languageCode];
|
||||
const process = spawn(command, args);
|
||||
|
||||
process.stdout.on('data', (data) => {
|
||||
console.log(`stdout: ${data}`);
|
||||
});
|
||||
|
||||
process.stderr.on('data', (data) => {
|
||||
console.error(`stderr: ${data}`);
|
||||
});
|
||||
|
||||
process.on('close', (code) => {
|
||||
if (code === 0) {
|
||||
console.log(`Successfully translated ${languageCode}`);
|
||||
resolve();
|
||||
} else {
|
||||
console.error(`Translation process for ${languageCode} exited with code ${code}`);
|
||||
reject(new Error(`Process exited with code ${code}`));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
translateFiles();
|
|
@ -189,7 +189,19 @@ img.circle-img,div.circle-img{border-radius:50%;height:50px;width:50px}
|
|||
.stream-objects .stream-detected-object{position:absolute;top:0;left:0;border:3px dotted red;background:transparent;border-radius:5px}
|
||||
.stream-objects .stream-detected-point{position:absolute;top:0;left:0;border:3px solid yellow;background:transparent;border-radius:5px}
|
||||
.stream-objects .point{position:absolute;top:0;left:0;border:3px solid red;border-radius:50%}
|
||||
|
||||
.stream-objects .matrix-info {
|
||||
margin-top: 1rem;
|
||||
background: rgba(0,0,0,0.3);
|
||||
color: #fff;
|
||||
width: 100%;
|
||||
padding: 1rem;
|
||||
font-family: monospace;
|
||||
z-index: 99;
|
||||
position: relative;
|
||||
}
|
||||
.stream-objects .matrix-info.yellow{
|
||||
color: yellow;
|
||||
}
|
||||
.monitor_item .gps-map {
|
||||
position: absolute;
|
||||
width: 190px;
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
.stream-block{
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.zoomGlass {
|
||||
overflow: hidden;
|
||||
transition: none;
|
||||
width: 350px; height: 350px;
|
||||
position: absolute;
|
||||
border-radius: 15px;
|
||||
border: 3px solid #ddd;
|
||||
z-index:9998;
|
||||
}
|
||||
.zoomGlass iframe,
|
||||
.zoomGlass video,
|
||||
.zoomGlass canvas{
|
||||
padding: 0!important;
|
||||
max-width: none!important;
|
||||
position: absolute;
|
||||
transition: none;
|
||||
}
|
||||
.zoomHoverShade{
|
||||
position:absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width:100%;
|
||||
height:100%;
|
||||
z-index:9999;
|
||||
}
|
|
@ -141,6 +141,7 @@ body {
|
|||
box-shadow: 0 0 5px #1e2b37;
|
||||
}
|
||||
|
||||
.dot-grey {background:#777}
|
||||
.dot-red {background:#d9534f}
|
||||
.dot-purple {background:#3f51b5}
|
||||
.dot-blue {background:#375182}
|
||||
|
@ -148,7 +149,6 @@ body {
|
|||
.dot-green {background:#449d44}
|
||||
.dot-forestgreen {background:#408693}
|
||||
.dot-orange {background:#c49a68}
|
||||
.dot-grey {background:#777}
|
||||
|
||||
.slidemenu .nav-link:hover {
|
||||
color: #9abadd!important;
|
||||
|
|
|
@ -10,6 +10,7 @@ $(document).ready(function(){
|
|||
if(!x.class){x.class='btn-success'}
|
||||
if(!x.title){x.title='Save changes'}
|
||||
$.confirm.footer.find('.confirmaction'+place).click(function(){
|
||||
delete($.confirm.onCancel);
|
||||
$.confirm.e.modal('hide')
|
||||
callback();
|
||||
})
|
||||
|
@ -23,20 +24,24 @@ $(document).ready(function(){
|
|||
}
|
||||
}
|
||||
$.confirm.create = function(options){
|
||||
if(options.title && options.body){
|
||||
$.confirm.e.modal('show')
|
||||
$.confirm.title.text(options.title)
|
||||
$.confirm.body.css('word-wrap','initial')
|
||||
if(options.breakWord){
|
||||
$.confirm.body.css('word-wrap','break-word')
|
||||
}
|
||||
$.confirm.body.html(options.body)
|
||||
}else{
|
||||
alert('No Title, Language file Update?')
|
||||
options.title = options.title || 'No Title'
|
||||
options.body = options.body || 'No Text'
|
||||
$.confirm.e.modal('show')
|
||||
$.confirm.title.text(options.title)
|
||||
$.confirm.body.css('word-wrap','initial')
|
||||
if(options.breakWord){
|
||||
$.confirm.body.css('word-wrap','break-word')
|
||||
}
|
||||
if(options.clickOptions && options.clickCallback || options.clickOptions instanceof Array)$.confirm.click(options.clickOptions,options.clickCallback)
|
||||
$.confirm.body.html(options.body)
|
||||
if(options.clickOptions && options.clickCallback || options.clickOptions instanceof Array){
|
||||
$.confirm.click(options.clickOptions, options.clickCallback);
|
||||
}
|
||||
$.confirm.onCancel = options.onCancel;
|
||||
}
|
||||
$.confirm.e.on('hidden.bs.modal', function () {
|
||||
if($.confirm.onCancel){
|
||||
$.confirm.onCancel()
|
||||
}
|
||||
$.confirm.body.empty()
|
||||
$.confirm.footer.find('.confirmaction').remove()
|
||||
})
|
||||
|
|
|
@ -235,8 +235,11 @@ function getLocation(d){
|
|||
}
|
||||
return url
|
||||
}
|
||||
function getApiHost(path,isAdmin){
|
||||
return getLocation() + (window.adminApiPrefix && isAdmin ? `${window.adminApiPrefix}` : '');
|
||||
}
|
||||
function getApiPrefix(path,isAdmin){
|
||||
var mainPart = getLocation() + (window.adminApiPrefix && isAdmin ? `${window.adminApiPrefix}` : '') + $user.auth_token
|
||||
var mainPart = getApiHost(path,isAdmin) + $user.auth_token
|
||||
return path ? mainPart + '/' + path + '/' + $user.ke : mainPart
|
||||
}
|
||||
|
||||
|
@ -728,7 +731,12 @@ function getAllSectionsFromDefinition(definitionsBase){
|
|||
function buildSubMenuItems(listOfItems){
|
||||
var html = ''
|
||||
$.each(listOfItems,function(n,item){
|
||||
if(item)html += `<li><a class="${definitions.Theme.isDark ? 'text-white' : 'text-dark'} text-decoration-none ${item.class || ''}" ${item.attributes || ''}><span class="${item.hasParent ? 'ml-3' : ''} dot dot-${item.color || 'blue'} mr-2"></span>${item.label}</a></li>`
|
||||
if(!item)return;
|
||||
if(item.divider){
|
||||
html += `<li><hr class="dropdown-divider"/></li>`
|
||||
}else{
|
||||
html += `<li><a class="${definitions.Theme.isDark ? 'text-white' : 'text-dark'} text-decoration-none ${item.class || ''}" ${item.attributes || ''}><span class="${item.hasParent ? 'ml-3' : ''} dot dot-${item.color || 'blue'} mr-2"></span>${item.label}</a></li>`
|
||||
}
|
||||
})
|
||||
return html
|
||||
}
|
||||
|
|
|
@ -22,6 +22,9 @@ function onBuildStreamElement(callback){
|
|||
onBuildStreamElementExtensions.push(callback)
|
||||
}
|
||||
//
|
||||
function debugLog(...args){
|
||||
console.log(...args)
|
||||
}
|
||||
function buildStreamElementHtml(streamType){
|
||||
var html = ''
|
||||
if(window.jpegModeOn === true){
|
||||
|
@ -500,6 +503,26 @@ function requestMonitorInit(){
|
|||
id: monitorId
|
||||
});
|
||||
}
|
||||
function toggleSubStream(monitorId,callback){
|
||||
var monitor = loadedMonitors[monitorId]
|
||||
var substreamConfig = monitor.details.substream
|
||||
var isSubStreamConfigured = !!substreamConfig.output;
|
||||
if(!isSubStreamConfigured){
|
||||
new PNotify({
|
||||
type: 'warning',
|
||||
title: lang['Invalid Settings'],
|
||||
text: lang.SubstreamNotConfigured,
|
||||
});
|
||||
return;
|
||||
}
|
||||
if(monitor.subStreamToggleLock)return false;
|
||||
monitor.subStreamToggleLock = true
|
||||
$.getJSON(getApiPrefix() + '/toggleSubstream/'+$user.ke+'/'+monitorId,function(d){
|
||||
monitor.subStreamToggleLock = false
|
||||
debugLog(d)
|
||||
if(callback)callback()
|
||||
})
|
||||
}
|
||||
$(document).ready(function(e){
|
||||
$('body')
|
||||
.on('dblclick','.stream-block',function(){
|
||||
|
|
|
@ -264,9 +264,9 @@ function updateLiveGridElementHeightWidth(monitorId){
|
|||
var streamElement = liveGridElement.streamElement
|
||||
liveGridElement.width = streamElement.width()
|
||||
liveGridElement.height = streamElement.height()
|
||||
console.log(liveGridElement.width,liveGridElement.height)
|
||||
console.log('update drawArea',monitorId,liveGridElement.width,liveGridElement.height)
|
||||
}
|
||||
function updateAllLiveGridElementsHeightWidth(monitorId){
|
||||
function updateAllLiveGridElementsHeightWidth(){
|
||||
$.each(liveGridElements,function(monitorId){
|
||||
updateLiveGridElementHeightWidth(monitorId)
|
||||
})
|
||||
|
@ -676,12 +676,12 @@ function loadPreviouslyOpenedLiveGridBlocks(){
|
|||
})
|
||||
setTimeout(function(){
|
||||
sortListMonitors()
|
||||
if(dashboardOptions().switches.jpegMode === 1){
|
||||
mainSocket.f({
|
||||
f: 'monitor',
|
||||
ff: 'jpeg_on'
|
||||
})
|
||||
}
|
||||
// if(dashboardOptions().switches.jpegMode === 1){
|
||||
// mainSocket.f({
|
||||
// f: 'monitor',
|
||||
// ff: 'jpeg_on'
|
||||
// })
|
||||
// }
|
||||
},1000)
|
||||
drawMonitorGroupList()
|
||||
})
|
||||
|
@ -1004,6 +1004,7 @@ $(document).ready(function(e){
|
|||
$('body')
|
||||
.resize(function(){
|
||||
resetAllLiveGridDimensionsInMemory()
|
||||
updateAllLiveGridElementsHeightWidth()
|
||||
})
|
||||
.on('click','.launch-live-grid-monitor',function(){
|
||||
var monitorId = $(this).parents('[data-mid]').attr('data-mid')
|
||||
|
@ -1034,7 +1035,8 @@ $(document).ready(function(e){
|
|||
f: 'monitor',
|
||||
ff: 'watch_on',
|
||||
id: monitorId
|
||||
})
|
||||
});
|
||||
updateLiveGridElementHeightWidth(monitorId)
|
||||
})
|
||||
.on('click','.close-live-grid-monitor',function(){
|
||||
var monitorId = $(this).parents('[data-mid]').attr('data-mid')
|
||||
|
@ -1140,6 +1142,46 @@ $(document).ready(function(e){
|
|||
]
|
||||
});
|
||||
})
|
||||
.on('click','.magnify-glass-live-grid-stream',function(){
|
||||
const monitorId = $(this).parents('[data-mid]').attr('data-mid')
|
||||
const streamWindow = $('.monitor_item[data-mid="'+monitorId+'"]')
|
||||
const monitor = loadedMonitors[monitorId]
|
||||
if(monitor.magnifyStreamEnabled){
|
||||
monitor.magnifyStreamEnabled = false
|
||||
clearTimeout(monitor.magnifyMouseActionTimeout)
|
||||
var zoomHoverShade = createMagnifyStreamMask({
|
||||
p: streamWindow,
|
||||
})
|
||||
zoomHoverShade
|
||||
.off('mousemove', monitor.magnifyMouseAction)
|
||||
.off('touchmove', monitor.magnifyMouseAction);
|
||||
streamWindow
|
||||
.find('.zoomGlass,.zoomHoverShade').remove()
|
||||
}else{
|
||||
streamWindow.find('.mdl-overlay-menu-backdrop').addClass('hidden')
|
||||
monitor.magnifyStreamEnabled = true
|
||||
var zoomHoverShade = createMagnifyStreamMask({
|
||||
p: streamWindow,
|
||||
})
|
||||
monitor.magnifyMouseAction = function(e){
|
||||
clearTimeout(monitor.magnifyMouseActionTimeout)
|
||||
monitor.magnifyMouseActionTimeout = setTimeout(function(){
|
||||
magnifyStream({
|
||||
p: streamWindow,
|
||||
zoomAmount: 1,
|
||||
auto: false,
|
||||
animate: false,
|
||||
pageX: e.pageX,
|
||||
pageY: e.pageY,
|
||||
attribute: '.magnify-glass-live-grid-stream'
|
||||
})
|
||||
},50)
|
||||
}
|
||||
zoomHoverShade
|
||||
.on('mousemove', monitor.magnifyMouseAction)
|
||||
.on('touchmove', monitor.magnifyMouseAction)
|
||||
}
|
||||
})
|
||||
$('.open-all-monitors').click(function(){
|
||||
openAllLiveGridPlayers()
|
||||
})
|
||||
|
@ -1155,11 +1197,15 @@ $(document).ready(function(e){
|
|||
},700)
|
||||
})
|
||||
.on('resizestop', function(){
|
||||
resetAllLiveGridDimensionsInMemory()
|
||||
setTimeout(() => {
|
||||
resetAllLiveGridDimensionsInMemory()
|
||||
},2000)
|
||||
saveLiveGridBlockPositions()
|
||||
updateAllLiveGridElementsHeightWidth()
|
||||
});
|
||||
addOnTabReopen('liveGrid', function () {
|
||||
pauseAllLiveGridPlayers(true)
|
||||
updateAllLiveGridElementsHeightWidth()
|
||||
})
|
||||
addOnTabAway('liveGrid', function () {
|
||||
pauseAllLiveGridPlayers(false)
|
||||
|
@ -1228,20 +1274,20 @@ $(document).ready(function(e){
|
|||
}
|
||||
showHideSubstreamActiveIcon(monitorId,!!subStreamChannel)
|
||||
break;
|
||||
case'mode_jpeg_off':
|
||||
window.jpegModeOn = false
|
||||
$.each(loadedMonitors,function(n,v){
|
||||
stopJpegStream(v.mid)
|
||||
resetMonitorCanvas(v.mid)
|
||||
initiateLiveGridPlayer(v)
|
||||
})
|
||||
$('body').removeClass('jpegMode')
|
||||
break;
|
||||
case'mode_jpeg_on':
|
||||
window.jpegModeOn = true
|
||||
startAllJpegStreams()
|
||||
$('body').addClass('jpegMode')
|
||||
break;
|
||||
// case'mode_jpeg_off':
|
||||
// window.jpegModeOn = false
|
||||
// $.each(loadedMonitors,function(n,v){
|
||||
// stopJpegStream(v.mid)
|
||||
// resetMonitorCanvas(v.mid)
|
||||
// initiateLiveGridPlayer(v)
|
||||
// })
|
||||
// $('body').removeClass('jpegMode')
|
||||
// break;
|
||||
// case'mode_jpeg_on':
|
||||
// window.jpegModeOn = true
|
||||
// startAllJpegStreams()
|
||||
// $('body').addClass('jpegMode')
|
||||
// break;
|
||||
case'detector_trigger':
|
||||
var monitorId = d.id
|
||||
var liveGridElement = liveGridElements[monitorId]
|
||||
|
|
|
@ -1150,8 +1150,8 @@ editorForm.find('[name="type"]').change(function(e){
|
|||
setCosmeticMonitorInfo(newMonitorData)
|
||||
drawMonitorGroupList()
|
||||
if(!d.silenceNote){
|
||||
new PNotify({
|
||||
title: 'Monitor Saved',
|
||||
redAlertNotify({
|
||||
title: lang['Monitor Saved'],
|
||||
text: '<b>'+newMonitorData.name+'</b> <small>'+newMonitorData.mid+'</small> has been saved.',
|
||||
type: 'success'
|
||||
})
|
||||
|
|
|
@ -182,7 +182,12 @@ $(document).ready(function(){
|
|||
onWebSocketEvent(function (d){
|
||||
switch(d.f){
|
||||
case'monitor_edit':
|
||||
loadMonitorsFromMemory()
|
||||
clearTimeout(window.renewListTimeout)
|
||||
window.renewListTimeout = setTimeout(() => {
|
||||
if(tabTree.name === 'monitorsList'){
|
||||
loadMonitorsFromMemory()
|
||||
}
|
||||
},5000)
|
||||
break;
|
||||
}
|
||||
})
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
var monitorGroupSelections = $('#monitor-group-selections')
|
||||
var onGetSnapshotByStreamExtensions = []
|
||||
var redAlertNotices = {};
|
||||
function onGetSnapshotByStream(callback){
|
||||
onGetSnapshotByStreamExtensions.push(callback)
|
||||
}
|
||||
|
@ -645,6 +646,61 @@ function launchImportMonitorWindow(callback){
|
|||
reader.readAsText(f);
|
||||
});
|
||||
}
|
||||
function readAlertNotice(title, text, type) {
|
||||
var redAlertNotice = redAlertNotices[title];
|
||||
if (redAlertNotice) {
|
||||
redAlertNotice.update({
|
||||
title: title,
|
||||
text: text,
|
||||
type: type,
|
||||
hide: false,
|
||||
delay: 30000
|
||||
});
|
||||
} else {
|
||||
redAlertNotices[title] = new PNotify({
|
||||
title: title,
|
||||
text: text,
|
||||
type: type,
|
||||
hide: false,
|
||||
delay: 30000
|
||||
});
|
||||
redAlertNotices[title].on('close', function() {
|
||||
redAlertNotices[title] = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
function redAlertNotify(options) {
|
||||
var redAlertNotice = redAlertNotices[title];
|
||||
var notifyOptions = {
|
||||
title: options.title,
|
||||
text: options.text,
|
||||
type: options.type,
|
||||
hide: options.hide === undefined ? false : options.hide,
|
||||
delay: options.delay || 30000
|
||||
};
|
||||
if (redAlertNotice) {
|
||||
redAlertNotice.update(notifyOptions);
|
||||
} else {
|
||||
redAlertNotices[title] = new PNotify(notifyOptions);
|
||||
redAlertNotices[title].on('close', function() {
|
||||
redAlertNotices[title] = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
function buildPosePoints(bodyParts, x, y){
|
||||
let theArray = []
|
||||
for(const point of bodyParts){
|
||||
theArray.push({
|
||||
tag: point.name,
|
||||
x: x + point.x - 5, // Assuming a 10x10 rectangle for the wrist
|
||||
y: y + point.y - 5,
|
||||
width: 10,
|
||||
height: 10,
|
||||
confidence: point.score,
|
||||
})
|
||||
}
|
||||
return theArray;
|
||||
}
|
||||
function drawMatrices(event,options){
|
||||
var theContainer = options.theContainer
|
||||
var height = options.height
|
||||
|
@ -654,11 +710,54 @@ function drawMatrices(event,options){
|
|||
var objectTagGroup = event.details.reason === 'motion' ? 'motion' : event.details.name
|
||||
theContainer.find(`.stream-detected-object[name="${objectTagGroup}"]`).remove()
|
||||
var html = ''
|
||||
$.each(event.details.matrices,function(n,matrix){
|
||||
html += `<div class="stream-detected-object" name="${objectTagGroup}" style="height:${heightRatio * matrix.height}px;width:${widthRatio * matrix.width}px;top:${heightRatio * matrix.y}px;left:${widthRatio * matrix.x}px;">`
|
||||
let moreMatrices = []
|
||||
var monitorId = event.id;
|
||||
function processMatrix(n,matrix){
|
||||
html += `<div class="stream-detected-object" name="${objectTagGroup}" style="height:${heightRatio * matrix.height}px;width:${widthRatio * matrix.width}px;top:${heightRatio * matrix.y}px;left:${widthRatio * matrix.x}px;border-color: ${matrix.color};">`
|
||||
if(matrix.tag)html += `<span class="tag">${matrix.tag}${!isNaN(matrix.id) ? ` <small class="label label-default">${matrix.id}</small>`: ''}</span>`
|
||||
if(matrix.notice)html += `<div class="matrix-info" style="color:yellow">${matrix.notice}</div>`;
|
||||
if(matrix.missingNear && matrix.missingNear.length > 0){
|
||||
html += `<div class="matrix-info yellow"><small>Missing Near</small><br>${matrix.missingRecently.map(item => `${item.tag} (${item.id}) by ${item.missedNear.tag} (${item.missedNear.id})`).join(', ')}</div>`;
|
||||
}
|
||||
if(matrix.missingRecentlyNearHands && matrix.missingRecentlyNearHands.length > 0){
|
||||
html += `<div class="matrix-info yellow"><small>Missing Recently</small><br>${matrix.missingRecentlyNearHands.map(item => `${item.tag} (${item.id})`).join(', ')}</div>`;
|
||||
}
|
||||
if(matrix.pose){
|
||||
var pose = matrix.pose;
|
||||
html += `<div class="matrix-info text-left">`;
|
||||
if(pose.isPersonFallen)html += `<div><small>Stance</small><br>${pose.isPersonFallen}</div>`;
|
||||
if(pose.isPersonReaching){
|
||||
html += `<div><small>Left Hand</small><br>${pose.isPersonReaching.left.pose}</div>`;
|
||||
html += `<div><small>Right Hand</small><br>${pose.isPersonReaching.right.pose}</div>`;
|
||||
}
|
||||
// if(pose.isPersonTouchingWaistOrHips)html += `<div>Waist or Hips : ${pose.isPersonTouchingWaistOrHips}</div>`;
|
||||
html += `</div>`;
|
||||
// console.log(matrix.poseInference)
|
||||
}
|
||||
if(matrix.poseInference)moreMatrices.push(...buildPosePoints(matrix.poseInference.keypoints,matrix.x,matrix.y))
|
||||
if(matrix.nearHands){
|
||||
var leftHand = matrix.nearHands.leftWrist;
|
||||
var rightHand = matrix.nearHands.rightWrist;
|
||||
html += `<div class="matrix-info text-left">`
|
||||
html += `<div><small>Left Interact</small><br>${leftHand.matrices.map(item => `${item.tag} (${item.id})`).join(', ')}</div>`;
|
||||
html += `<div><small>Right Interact</small><br>${rightHand.matrices.map(item => `${item.tag} (${item.id})`).join(', ')}</div>`;
|
||||
html += `</div>`
|
||||
}
|
||||
if(matrix.nearBy){
|
||||
html += `<div class="matrix-info">`
|
||||
matrix.nearBy.forEach((nearMatrix) => {
|
||||
html += `<div class="mb-1">${nearMatrix.tag} <small class="label label-default">${nearMatrix.id}</small> (${nearMatrix.overlapPercent}%)</div>`
|
||||
});
|
||||
html += `</div>`
|
||||
}
|
||||
if(matrix.redAlert){
|
||||
var monitor = loadedMonitors[monitorId]
|
||||
readAlertNotice(`${monitor.name}`,`${matrix.tag} (${matrix.id})<br>${matrix.notice}`,'danger');
|
||||
}
|
||||
html += '</div>'
|
||||
})
|
||||
}
|
||||
$.each(event.details.matrices, processMatrix);
|
||||
$.each(moreMatrices, processMatrix);
|
||||
theContainer.append(html)
|
||||
}
|
||||
function setMonitorCountOnUI(){
|
||||
|
@ -757,6 +856,21 @@ function buildDefaultMonitorMenuItems(){
|
|||
<li><a class="dropdown-item cursor-pointer" set-mode="start">${lang['Watch-Only']}</a></li>
|
||||
<li><a class="dropdown-item cursor-pointer" set-mode="record">${lang.Record}</a></li>`
|
||||
}
|
||||
function createMagnifyStreamMask(options){
|
||||
if(!options.p && !options.parent){
|
||||
var el = $(this),
|
||||
parent = el.parents('[mid]')
|
||||
}else{
|
||||
parent = options.p || options.parent
|
||||
}
|
||||
var zoomHoverShade = parent.find('.zoomHoverShade')
|
||||
if(zoomHoverShade.length === 0){
|
||||
const html = `<div class="zoomHoverShade magnify-glass-live-grid-stream"></div>`
|
||||
parent.append(html)
|
||||
zoomHoverShade = parent.find('.zoomHoverShade')
|
||||
}
|
||||
return zoomHoverShade
|
||||
}
|
||||
function magnifyStream(options){
|
||||
if(!options.p && !options.parent){
|
||||
var el = $(this),
|
||||
|
@ -794,15 +908,10 @@ function magnifyStream(options){
|
|||
magnifiedElement = 'video'
|
||||
}
|
||||
if(!options.mon && !options.monitor){
|
||||
var groupKey = parent.attr('ke')//group key
|
||||
var monitorId = parent.attr('mid')//monitor id
|
||||
var sessionKey = parent.attr('auth')//authkey
|
||||
var monitor = $.ccio.mon[groupKey + monitorId + sessionKey]//monitor configuration
|
||||
var monitorId = parent.attr('data-mid')//monitor id
|
||||
var monitor = loadedMonitors[monitorId]
|
||||
}else{
|
||||
var monitor = options.mon || options.monitor
|
||||
var groupKey = monitor.ke//group key
|
||||
var monitorId = monitor.mid//monitor id
|
||||
var sessionKey = monitor.auth//authkey
|
||||
}
|
||||
if(options.zoomAmount)zoomAmount = 3
|
||||
if(!zoomAmount)zoomAmount = 3
|
||||
|
@ -833,14 +942,14 @@ function magnifyStream(options){
|
|||
zoomGlass = parent.find(".zoomGlass")
|
||||
var zoomGlassShell = function(contents){return `<div ${options.attribute} class="zoomGlass">${contents}</div>`}
|
||||
if(!options.videoUrl){
|
||||
$.ccio.snapshot(monitor,function(url,buffer,w,h){
|
||||
getSnapshot(monitor,function(url,buffer,w,h){
|
||||
parent.attr('realWidth',w)
|
||||
parent.attr('realHeight',h)
|
||||
if(zoomGlass.length === 0){
|
||||
if(options.useCanvas === true){
|
||||
parent.append(zoomGlassShell('<canvas class="blenderCanvas"></canvas>'))
|
||||
}else{
|
||||
parent.append(zoomGlassShell('<iframe src="'+getApiPrefix('embed')+'/'+monitorId+'/fullscreen|jquery|relative"/><div class="hoverShade"></div>'))
|
||||
parent.append(zoomGlassShell('<iframe src="'+getApiPrefix('embed')+'/'+monitorId+'/fullscreen|jquery|relative"/>'))
|
||||
}
|
||||
zoomGlass = parent.find(".zoomGlass")
|
||||
}
|
||||
|
|
|
@ -75,7 +75,7 @@ $(document).ready(function(){
|
|||
function onRecentVideosFieldChange(){
|
||||
var theSelected = `${monitorList.val()}`
|
||||
loadVideos({
|
||||
limit: 10,
|
||||
limit: 0,
|
||||
monitorId: theSelected || undefined,
|
||||
},function(){
|
||||
liveStamp()
|
||||
|
@ -88,7 +88,7 @@ $(document).ready(function(){
|
|||
drawMonitorListToSelector(monitorList.find('optgroup'))
|
||||
monitorList.val(theSelected)
|
||||
loadVideos({
|
||||
limit: 20,
|
||||
limit: 0,
|
||||
monitorId: theSelected || undefined,
|
||||
},function(){
|
||||
liveStamp()
|
||||
|
@ -97,7 +97,7 @@ $(document).ready(function(){
|
|||
onDashboardReady(function(){
|
||||
drawMonitorListToSelector(monitorList.find('optgroup'))
|
||||
loadVideos({
|
||||
limit: 20,
|
||||
limit: 0,
|
||||
},function(){
|
||||
liveStamp()
|
||||
})
|
||||
|
|
|
@ -311,6 +311,13 @@ $(document).ready(function(e){
|
|||
function downloadTimelapseFrame(frame){
|
||||
downloadFile(frame.href,frame.filename)
|
||||
}
|
||||
function buildFileBinUrl(data){
|
||||
return apiBaseUrl + '/fileBin/' + data.ke + '/' + data.mid + '/' + data.name
|
||||
}
|
||||
function downloadTimelapseVideo(data){
|
||||
var downloadUrl = buildFileBinUrl(data)
|
||||
downloadFile(downloadUrl,data.name)
|
||||
}
|
||||
function onTimelapseVideoBuildComplete(data){
|
||||
var saveBuiltVideo = dashboardOptions().switches.timelapseSaveBuiltVideo
|
||||
if(saveBuiltVideo === 1){
|
||||
|
|
|
@ -30,7 +30,7 @@ $(document).ready(function(){
|
|||
var timeStripItemColors = {}
|
||||
var timeStripAutoGridSizer = false
|
||||
var timeStripListOfQueries = []
|
||||
var timeStripSelectedMonitors = []
|
||||
var timeStripSelectedMonitors = dashboardOptions().timeStripSelectedMonitors || []
|
||||
var timeStripAutoScrollTimeout = null;
|
||||
var timeStripAutoScrollPositionStart = null;
|
||||
var timeStripAutoScrollPositionEnd = null;
|
||||
|
@ -42,12 +42,14 @@ $(document).ready(function(){
|
|||
var loadedVideoElsOnCanvasNextVideoTimeout = {}
|
||||
var loadedVideoEndingTimeouts = {}
|
||||
var playUntilVideoEnd = false
|
||||
var dontShowDetectionOnTimeline = false
|
||||
var dontShowDetectionOnTimeline = true
|
||||
var isPlaying = false
|
||||
var earliestStart = null
|
||||
var latestEnd = null
|
||||
var timeChanging = false
|
||||
var dateRangeChanging = false
|
||||
var lastDateChecked = new Date(0)
|
||||
var monitorSelectionElements = []
|
||||
function setLoadingMask(turnOn){
|
||||
if(turnOn){
|
||||
if(theWindow.find('.loading-mask').length === 0){
|
||||
|
@ -127,25 +129,30 @@ $(document).ready(function(){
|
|||
return videos;
|
||||
}
|
||||
async function getVideosByTimeStripRange(addOrOverWrite){
|
||||
// timeStripSelectedMonitors = selected monitors
|
||||
var currentVideosLength = parseInt(loadedVideosOnTimeStrip.length)
|
||||
var stripDate = getTimestripDate()
|
||||
var startDate = stripDate.start
|
||||
var endDate = stripDate.end
|
||||
var gaps = findGapsInSearchRanges(timeStripListOfQueries, [startDate,endDate])
|
||||
// console.error([startDate,endDate])
|
||||
// console.log('range : ',JSON.stringify([startDate,endDate]))
|
||||
// console.log('timeRanges : ',JSON.stringify(timeStripListOfQueries))
|
||||
// console.log('gaps : ',JSON.stringify(gaps))
|
||||
if(gaps.length > 0){
|
||||
setLoadingMask(true)
|
||||
timeStripListOfQueries.push(...gaps)
|
||||
var videos = await getVideosInGaps(gaps,timeStripSelectedMonitors)
|
||||
videos = addVideoBeforeAndAfter(videos)
|
||||
loadedVideosOnTimeStrip.push(...videos)
|
||||
if(currentVideosLength !== loadedVideosOnTimeStrip.length)addTimelineItems(loadedVideosOnTimeStrip);
|
||||
setLoadingMask(false)
|
||||
var dateNow = new Date()
|
||||
var isOverCacheTime = dateNow.getTime() - lastDateChecked.getTime() >= 20 * 60 * 1000;
|
||||
if(isOverCacheTime){
|
||||
timeStripListOfQueries = []
|
||||
loadedVideosOnTimeStrip = []
|
||||
}
|
||||
if(timeStripSelectedMonitors.length > 0){
|
||||
var gaps = findGapsInSearchRanges(timeStripListOfQueries, [startDate,endDate])
|
||||
if(gaps.length > 0){
|
||||
setLoadingMask(true)
|
||||
timeStripListOfQueries.push(...gaps)
|
||||
var videos = await getVideosInGaps(gaps,timeStripSelectedMonitors)
|
||||
videos = addVideoBeforeAndAfter(videos)
|
||||
loadedVideosOnTimeStrip.push(...videos)
|
||||
if(currentVideosLength !== loadedVideosOnTimeStrip.length)addTimelineItems(loadedVideosOnTimeStrip);
|
||||
setLoadingMask(false)
|
||||
}
|
||||
lastDateChecked = new Date();
|
||||
}
|
||||
lastDateChecked = new Date();
|
||||
return loadedVideosOnTimeStrip
|
||||
}
|
||||
function selectVideosForCanvas(time, videos){
|
||||
|
@ -236,10 +243,15 @@ $(document).ready(function(){
|
|||
}
|
||||
async function resetTimeline(clickTime){
|
||||
await getAndDrawVideosToTimeline(clickTime,true)
|
||||
setTickDate(clickTime)
|
||||
setTimeLabel(clickTime)
|
||||
setTimeOfCanvasVideos(clickTime)
|
||||
setHollowClickQueue()
|
||||
if(timeStripSelectedMonitors.length > 0){
|
||||
setTickDate(clickTime)
|
||||
setTimeLabel(clickTime)
|
||||
setTimeOfCanvasVideos(clickTime)
|
||||
setHollowClickQueue()
|
||||
}else{
|
||||
setViewForNoMonitorsSelected()
|
||||
}
|
||||
setSideMenuMonitorVisualSelection()
|
||||
}
|
||||
function timeStripActionWithPausePlay(restartPlaySpeed){
|
||||
return new Promise((resolve,reject) => {
|
||||
|
@ -769,11 +781,12 @@ $(document).ready(function(){
|
|||
}
|
||||
function drawFoundCamerasSubMenu(){
|
||||
var tags = getListOfTagsFromMonitors()
|
||||
var monitorsOrdered = Object.values(loadedMonitors).sort((a, b) => a.name.localeCompare(b.name));
|
||||
var allFound = [
|
||||
{
|
||||
attributes: `timeline-menu-action="selectMonitorGroup" tag=""`,
|
||||
class: `cursor-pointer`,
|
||||
color: 'green',
|
||||
color: 'forestgreen',
|
||||
label: lang['All Monitors'],
|
||||
}
|
||||
]
|
||||
|
@ -792,9 +805,33 @@ $(document).ready(function(){
|
|||
color: ' d-none',
|
||||
label: `<small class="mt-1">${lang.addTagText}</small>`,
|
||||
})
|
||||
}else if(allFound.length !== 0){
|
||||
allFound.push({
|
||||
divider: true
|
||||
})
|
||||
}
|
||||
$.each(monitorsOrdered,function(monitorKey,monitor){
|
||||
var monitorId = monitor.mid
|
||||
var label = monitor.name
|
||||
allFound.push({
|
||||
attributes: `timeline-menu-action="selectMonitor" data-mid="${monitorId}"`,
|
||||
class: `cursor-pointer timeline-selectMonitor`,
|
||||
color: 'grey',
|
||||
label,
|
||||
})
|
||||
})
|
||||
var html = buildSubMenuItems(allFound)
|
||||
sideMenuList.html(html)
|
||||
monitorSelectionElements = sideMenuList.find('.timeline-selectMonitor')
|
||||
}
|
||||
async function setSideMenuMonitorVisualSelection(){
|
||||
var getForAllMonitors = timeStripSelectedMonitors.length === 0;
|
||||
monitorSelectionElements.find('.dot').removeClass('dot-green')
|
||||
if(!getForAllMonitors){
|
||||
timeStripSelectedMonitors.forEach((monitorId) => {
|
||||
sideMenuList.find(`[data-mid="${monitorId}"] .dot`).addClass('dot-green')
|
||||
})
|
||||
}
|
||||
}
|
||||
function setColorReferences(){
|
||||
$.each(loadedMonitors,function(monitorId,monitor){
|
||||
|
@ -853,20 +890,71 @@ $(document).ready(function(){
|
|||
if(!video)return console.log('No More!')
|
||||
await jumpToVideo(video)
|
||||
}
|
||||
function onSelectedMonitorChange(){
|
||||
dashboardOptions('timeStripSelectedMonitors', timeStripSelectedMonitors)
|
||||
}
|
||||
function setViewForNoMonitorsSelected(){
|
||||
destroyTimeline();
|
||||
timeStripVideoCanvas.html(`<h3 class="my-3 text-center text-white vertical-center flex-column"><i class="fa fa-hand-pointer-o fa-3x m-3"></i><div>${lang['No Monitors Selected']}</div></h3>`)
|
||||
}
|
||||
function isAllMonitorsSelected(percent = 100){
|
||||
var divisor = percent / 100;
|
||||
var allMonitorIds = Object.values(loadedMonitors).map(item => item.mid);
|
||||
return timeStripSelectedMonitors.length >= (allMonitorIds.length * divisor);
|
||||
}
|
||||
function deselectAllMonitors(){
|
||||
timeStripSelectedMonitors = [];
|
||||
onSelectedMonitorChange()
|
||||
refreshTimeline()
|
||||
}
|
||||
function refreshTimelineOnAgree(){
|
||||
var askToLoad = isAllMonitorsSelected(50)
|
||||
if(askToLoad){
|
||||
$.confirm.create({
|
||||
title: lang.tooManyMonitorsSelected,
|
||||
body: lang.performanceMayBeAffected,
|
||||
clickOptions: {
|
||||
title: lang.getVideos,
|
||||
class: 'btn-success'
|
||||
},
|
||||
clickCallback: function(){
|
||||
refreshTimeline()
|
||||
},
|
||||
onCancel: function(){
|
||||
deselectAllMonitors()
|
||||
}
|
||||
})
|
||||
}else{
|
||||
refreshTimeline()
|
||||
}
|
||||
}
|
||||
sideMenuList.on('click','[timeline-menu-action]',function(){
|
||||
var el = $(this)
|
||||
var type = el.attr('timeline-menu-action')
|
||||
switch(type){
|
||||
case'selectMonitor':
|
||||
var monitorId = el.attr('data-mid')
|
||||
var alreadySelected = timeStripSelectedMonitors.indexOf(monitorId) > -1;
|
||||
if(alreadySelected){
|
||||
timeStripSelectedMonitors = timeStripSelectedMonitors.filter(fillId => monitorId !== fillId)
|
||||
}else{
|
||||
timeStripSelectedMonitors.push(monitorId)
|
||||
}
|
||||
onSelectedMonitorChange()
|
||||
refreshTimeline()
|
||||
break;
|
||||
case'selectMonitorGroup':
|
||||
var tag = el.attr('tag')
|
||||
if(!tag){
|
||||
timeStripSelectedMonitors = []
|
||||
timeStripSelectedMonitors = Object.values(loadedMonitors).map(item => item.mid)
|
||||
refreshTimelineOnAgree()
|
||||
}else{
|
||||
var tags = getListOfTagsFromMonitors()
|
||||
var monitorIds = tags[tag]
|
||||
timeStripSelectedMonitors = [...monitorIds];
|
||||
onSelectedMonitorChange()
|
||||
refreshTimeline()
|
||||
}
|
||||
refreshTimeline()
|
||||
break;
|
||||
}
|
||||
})
|
||||
|
@ -931,8 +1019,8 @@ $(document).ready(function(){
|
|||
})
|
||||
addOnTabOpen('timeline', async function () {
|
||||
setColorReferences()
|
||||
refreshTimeline()
|
||||
drawFoundCamerasSubMenu()
|
||||
refreshTimelineOnAgree()
|
||||
})
|
||||
addOnTabReopen('timeline', function () {
|
||||
drawFoundCamerasSubMenu()
|
||||
|
|
|
@ -606,7 +606,7 @@ async function unarchiveVideos(videos){
|
|||
}
|
||||
function buildDefaultVideoMenuItems(file,options){
|
||||
var isLocalVideo = !file.videoSet || file.videoSet === 'videos'
|
||||
var href = file.href + `${!isLocalVideo ? `?type=${file.type}` : ''}`
|
||||
var href = getApiHost() + file.href + `${!isLocalVideo ? `?type=${file.type}` : ''}`
|
||||
options = options ? options : {play: true}
|
||||
return `
|
||||
<li><a class="dropdown-item" href="${href}" download>${lang.Download}</a></li>
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -14,7 +14,7 @@
|
|||
<meta http-equiv="content-type" content="text/html;charset=UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="apple-mobile-web-app-title" content="Shinobi">
|
||||
<% include header-favicon.ejs %>
|
||||
<%- include('header-favicon'); %>
|
||||
<meta name="application-name" content="Shinobi"/>
|
||||
<meta name="msapplication-TileColor" content="#333333" />
|
||||
<meta name="msapplication-TileImage" content="libs/img/icon/mstile-144x144.png" />
|
||||
|
@ -35,6 +35,7 @@
|
|||
<link rel="stylesheet" href="<%-window.libURL%>assets/vendor/vis.min.css">
|
||||
<link rel="stylesheet" href="<%-window.libURL%>assets/css/clock.css">
|
||||
<link rel="stylesheet" href="<%-window.libURL%>assets/css/bs5.liveGrid.css">
|
||||
<link rel="stylesheet" href="<%-window.libURL%>assets/css/bs5.zoomGlass.css">
|
||||
<link rel="stylesheet" href="<%-window.libURL%>assets/css/bs5.regionEditor.css">
|
||||
<link rel="stylesheet" href="<%-window.libURL%>assets/css/bs5.onvifScanner.css">
|
||||
<link rel="stylesheet" href="<%-window.libURL%>assets/css/bs5.timelapseViewer.css">
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<body class="bg-darker">
|
||||
<% include home/menuFirst.ejs %>
|
||||
<%- include('home/menuFirst'); %>
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<% include home/menuSide.ejs %>
|
||||
<%- include('home/menuSide'); %>
|
||||
<main id="pageTabContainer" class="col-md-9 ms-sm-auto col-lg-10 px-md-4">
|
||||
<% (config.webBlocksPreloaded).forEach(function(block){ %>
|
||||
<%- include(__dirname + '/web/pages/blocks/' +block + '.ejs') %>
|
||||
|
|
|
@ -8,18 +8,13 @@
|
|||
</div>
|
||||
<%
|
||||
const details = $user.details
|
||||
var drawBlock
|
||||
var buildOptions
|
||||
%>
|
||||
<%
|
||||
include fieldBuilders.ejs
|
||||
%>
|
||||
<% Object.keys(define['Account Settings'].blocks).forEach(function(blockKey){
|
||||
var accountSettings = define['Account Settings'].blocks[blockKey]
|
||||
drawBlock(accountSettings)
|
||||
}) %>
|
||||
<%
|
||||
include stickySubmitBar.ejs
|
||||
%>
|
||||
<% Object.keys(define['Account Settings'].blocks).forEach(function(blockKey) { -%>
|
||||
<%- include('drawBlock', {
|
||||
theBlock: define['Account Settings'].blocks[blockKey],
|
||||
details: details
|
||||
}) %>
|
||||
<% }) -%>
|
||||
<%- include('stickySubmitBar'); %>
|
||||
</form>
|
||||
</main>
|
||||
|
|
|
@ -1,15 +1,9 @@
|
|||
<main class="container page-tab pt-3" id="tab-apiKeys">
|
||||
<div class="dark row" id="apis">
|
||||
<%
|
||||
var drawBlock
|
||||
var buildOptions
|
||||
%>
|
||||
<%
|
||||
include fieldBuilders.ejs
|
||||
%>
|
||||
<% Object.keys(define['API Keys'].blocks).forEach(function(blockKey){
|
||||
var accountSettings = define['API Keys'].blocks[blockKey]
|
||||
drawBlock(accountSettings)
|
||||
}) %>
|
||||
<% Object.keys(define['API Keys'].blocks).forEach(function(blockKey) { -%>
|
||||
<%- include('drawBlock', {
|
||||
theBlock: define['API Keys'].blocks[blockKey]
|
||||
}) %>
|
||||
<% }) -%>
|
||||
</div>
|
||||
</main>
|
||||
|
|
|
@ -1,16 +1,10 @@
|
|||
<main class="page-tab pt-3" id="tab-calendarView">
|
||||
<div class="row <%- define.Theme.isDark ? `dark` : '' %>">
|
||||
<%
|
||||
var drawBlock
|
||||
var buildOptions
|
||||
%>
|
||||
<%
|
||||
include fieldBuilders.ejs
|
||||
%>
|
||||
<% Object.keys(define['Calendar'].blocks).forEach(function(blockKey){
|
||||
var theBlock = define['Calendar'].blocks[blockKey]
|
||||
drawBlock(theBlock)
|
||||
}) %>
|
||||
<% Object.keys(define['Calendar'].blocks).forEach(function(blockKey) { -%>
|
||||
<%- include('drawBlock', {
|
||||
theBlock: define['Calendar'].blocks[blockKey]
|
||||
}) %>
|
||||
<% }) -%>
|
||||
</div>
|
||||
<link rel="stylesheet" href="<%-window.libURL%>assets/vendor/fullcalendar.min.css">
|
||||
<script src="<%-window.libURL%>assets/vendor/js/fullcalendar.min.js"></script>
|
||||
|
|
|
@ -1,15 +1,9 @@
|
|||
<main class="container page-tab pt-3" id="tab-cameraProbe">
|
||||
<form class="dark row">
|
||||
<%
|
||||
var drawBlock
|
||||
var buildOptions
|
||||
%>
|
||||
<%
|
||||
include fieldBuilders.ejs
|
||||
%>
|
||||
<% Object.keys(define['Camera Probe'].blocks).forEach(function(blockKey){
|
||||
var accountSettings = define['Camera Probe'].blocks[blockKey]
|
||||
drawBlock(accountSettings)
|
||||
}) %>
|
||||
<% Object.keys(define['Camera Probe'].blocks).forEach(function(blockKey) { -%>
|
||||
<%- include('drawBlock', {
|
||||
theBlock: define['Camera Probe'].blocks[blockKey]
|
||||
}) %>
|
||||
<% }) -%>
|
||||
</form>
|
||||
</main>
|
||||
|
|
|
@ -1,16 +1,10 @@
|
|||
<main class="page-tab pt-3 dark" id="tab-configFinder">
|
||||
<div class="row">
|
||||
<%
|
||||
var drawBlock
|
||||
var buildOptions
|
||||
%>
|
||||
<%
|
||||
include fieldBuilders.ejs
|
||||
%>
|
||||
<% Object.keys(define['Montior Configuration Finder'].blocks).forEach(function(blockKey){
|
||||
var accountSettings = define['Montior Configuration Finder'].blocks[blockKey]
|
||||
drawBlock(accountSettings)
|
||||
}) %>
|
||||
<% Object.keys(define['Montior Configuration Finder'].blocks).forEach(function(blockKey) { -%>
|
||||
<%- include('drawBlock', {
|
||||
theBlock: define['Montior Configuration Finder'].blocks[blockKey]
|
||||
}) %>
|
||||
<% }) -%>
|
||||
</div>
|
||||
<% window.additionalJsScripts.push('assets/js/bs5.configFinder.js') %>
|
||||
</main>
|
||||
|
|
|
@ -1,69 +1,51 @@
|
|||
<%
|
||||
buildOptions = function(field,possiblities){
|
||||
if(!field)console.error('field',field)
|
||||
var fieldElement = ''
|
||||
possiblities.forEach(function(option){
|
||||
if(option.optgroup){
|
||||
fieldElement += '<optgroup label="' + option.name + '">'
|
||||
fieldElement += buildOptions(field,option.optgroup)
|
||||
fieldElement += '</optgroup>'
|
||||
}else{
|
||||
var selected = ''
|
||||
if(option.value === field.default){
|
||||
selected = 'selected'
|
||||
}
|
||||
fieldElement += '<option value="' + option.value + '" ' + selected + '>' + option.name + '</option>'
|
||||
}
|
||||
})
|
||||
return fieldElement
|
||||
}
|
||||
drawBlock = function(monitorSettings){
|
||||
if(monitorSettings.evaluation && !eval(monitorSettings.evaluation)){
|
||||
drawBlock = function(theBlock){
|
||||
if(theBlock.evaluation && !eval(theBlock.evaluation)){
|
||||
return
|
||||
}
|
||||
var attributes = []
|
||||
var styles = []
|
||||
var sectionClass = []
|
||||
var headerTitle = monitorSettings.headerTitle || lang[monitorSettings.name] || monitorSettings.name
|
||||
if(monitorSettings.hidden === true){
|
||||
var headerTitle = theBlock.headerTitle || lang[theBlock.name] || theBlock.name
|
||||
if(theBlock.hidden === true){
|
||||
styles.push('display:none')
|
||||
}
|
||||
if(monitorSettings.style){
|
||||
styles.push(monitorSettings.style)
|
||||
if(theBlock.style){
|
||||
styles.push(theBlock.style)
|
||||
}
|
||||
if(monitorSettings.isSection === true){
|
||||
if(theBlock.isSection === true){
|
||||
attributes.push('section')
|
||||
}
|
||||
if(monitorSettings.attribute){
|
||||
attributes.push(monitorSettings.attribute)
|
||||
if(theBlock.attribute){
|
||||
attributes.push(theBlock.attribute)
|
||||
}
|
||||
if(!monitorSettings.noId && !monitorSettings.id && monitorSettings.name){
|
||||
var userSettingsId = monitorSettings.name.replace(/[^a-zA-Z ]/g, '').replace(/[^a-zA-Z ]/g, '').replace(/ /g, '')
|
||||
monitorSettings.id = userSettingsId
|
||||
if(!theBlock.noId && !theBlock.id && theBlock.name){
|
||||
var userSettingsId = theBlock.name.replace(/[^a-zA-Z ]/g, '').replace(/[^a-zA-Z ]/g, '').replace(/ /g, '')
|
||||
theBlock.id = userSettingsId
|
||||
}
|
||||
if(monitorSettings.id)attributes.push(`id="${monitorSettings.id}"`);
|
||||
if(monitorSettings.color){
|
||||
sectionClass.push(monitorSettings.color)
|
||||
if(theBlock.id)attributes.push(`id="${theBlock.id}"`);
|
||||
if(theBlock.color){
|
||||
sectionClass.push(theBlock.color)
|
||||
}
|
||||
if(monitorSettings['section-class']){
|
||||
sectionClass.push(monitorSettings['section-class'])
|
||||
if(theBlock['section-class']){
|
||||
sectionClass.push(theBlock['section-class'])
|
||||
}
|
||||
if(monitorSettings.isAdvanced){ %>
|
||||
if(theBlock.isAdvanced){ %>
|
||||
<div class="h_us_input h_us_advanced" style="display:none">
|
||||
<% }
|
||||
if(monitorSettings['section-pre-pre-class']){ %>
|
||||
<div class="<%- monitorSettings['section-pre-pre-class'] %>">
|
||||
if(theBlock['section-pre-pre-class']){ %>
|
||||
<div class="<%- theBlock['section-pre-pre-class'] %>">
|
||||
<% }
|
||||
if(monitorSettings['section-pre-class']){ %>
|
||||
<div class="<%- monitorSettings['section-pre-class'] %>">
|
||||
if(theBlock['section-pre-class']){ %>
|
||||
<div class="<%- theBlock['section-pre-class'] %>">
|
||||
<% }
|
||||
%>
|
||||
<<%- monitorSettings.isForm ? 'form' : 'div' %> <%- attributes.join(' ') %> style="<%- styles.join(';') %>" class="<%- !monitorSettings.noDefaultSectionClasses ? `card form-group-group p-3 ${define.Theme.isDark ? 'bg-dark' : 'bg-light'} mb-3 shadow` : '' %> <%- sectionClass.join(' ') %>">
|
||||
<% if(!monitorSettings['noHeader']){ %>
|
||||
<h4 class="form-section-header <%- monitorSettings.headerButtons || monitorSettings.headerTitle && ( monitorSettings.headerTitle.indexOf('<a ') > -1 || monitorSettings.headerTitle.indexOf('<button ') > -1 ) ? 'no-toggle-header' : '' %> cursor-pointer mb-3 pb-3 <%- define.Theme.isDark ? 'text-white' : '' %> border-bottom-dotted border-color-<%- monitorSettings.color || 'dark' %> <%- monitorSettings.headerClass %>"><%- headerTitle %>
|
||||
<% if(monitorSettings.headerButtons){ %>
|
||||
<<%- theBlock.isForm ? 'form' : 'div' %> <%- attributes.join(' ') %> style="<%- styles.join(';') %>" class="<%- !theBlock.noDefaultSectionClasses ? `card form-group-group p-3 ${define.Theme.isDark ? 'bg-dark' : 'bg-light'} mb-3 shadow` : '' %> <%- sectionClass.join(' ') %>">
|
||||
<% if(!theBlock['noHeader']){ %>
|
||||
<h4 class="form-section-header <%- theBlock.headerButtons || theBlock.headerTitle && ( theBlock.headerTitle.indexOf('<a ') > -1 || theBlock.headerTitle.indexOf('<button ') > -1 ) ? 'no-toggle-header' : '' %> cursor-pointer mb-3 pb-3 <%- define.Theme.isDark ? 'text-white' : '' %> border-bottom-dotted border-color-<%- theBlock.color || 'dark' %> <%- theBlock.headerClass %>"><%- headerTitle %>
|
||||
<% if(theBlock.headerButtons){ %>
|
||||
<div class="pull-right">
|
||||
<% monitorSettings.headerButtons.forEach(function(button){ %>
|
||||
<% theBlock.headerButtons.forEach(function(button){ %>
|
||||
<a class="btn btn-success btn-xs <%- button.class %>">
|
||||
<% if(button.icon){ %><i class="fa fa-<%- button.icon %>"></i><% } %>
|
||||
<% if(button.text){ %><%- button.text %><% } %>
|
||||
|
@ -73,9 +55,9 @@ drawBlock = function(monitorSettings){
|
|||
<% } %>
|
||||
</h4>
|
||||
<% } %>
|
||||
<div class="box-wrapper <%- monitorSettings['box-wrapper-class'] || '' %>" style="<%- monitorSettings['box-wrapper-style'] || '' %>">
|
||||
<% if(monitorSettings['input-mapping']){ %>
|
||||
<div class="form-group-group btn-default card shadow mb-2" style="display:none" input-mapping="<%- monitorSettings['input-mapping'] %>">
|
||||
<div class="box-wrapper <%- theBlock['box-wrapper-class'] || '' %>" style="<%- theBlock['box-wrapper-style'] || '' %>">
|
||||
<% if(theBlock['input-mapping']){ %>
|
||||
<div class="form-group-group btn-default card shadow mb-2" style="display:none" input-mapping="<%- theBlock['input-mapping'] %>">
|
||||
<h5 class="card-body d-flex flex-row m-0">
|
||||
<div class="flex-grow-1">
|
||||
<%-lang['Input Feeds Selected']%>
|
||||
|
@ -87,17 +69,17 @@ drawBlock = function(monitorSettings){
|
|||
<div class="card-footer pb-0 choices"></div>
|
||||
</div>
|
||||
<% } %>
|
||||
<% if(monitorSettings.blockquote){ %>
|
||||
<blockquote class="<%- monitorSettings.blockquoteClass || '' %>">
|
||||
<%- monitorSettings.blockquote %>
|
||||
<% if(theBlock.blockquote){ %>
|
||||
<blockquote class="<%- theBlock.blockquoteClass || '' %>">
|
||||
<%- theBlock.blockquote %>
|
||||
</blockquote>
|
||||
<% } %>
|
||||
<% if(monitorSettings.blocks){
|
||||
monitorSettings.blocks.forEach(function(settingsBlock){
|
||||
drawBlock(settingsBlock)
|
||||
<% if(theBlock.blocks){
|
||||
theBlock.blocks.forEach(function(theBlock){
|
||||
drawBlock(theBlock)
|
||||
})
|
||||
}
|
||||
if(monitorSettings.info){
|
||||
if(theBlock.info){
|
||||
function drawInfoItem(field){
|
||||
let evaluation = `${field.evaluation || ''}`
|
||||
if(field.ejs){
|
||||
|
@ -257,7 +239,7 @@ drawBlock = function(monitorSettings){
|
|||
break;
|
||||
case'select':
|
||||
fieldElement = `<select class="form-control ${fieldClass.join(' ')}" ${attributes.join(' ')}>`
|
||||
fieldElement += buildOptions(field,possiblities)
|
||||
fieldElement += fieldBuild.buildOptions(field,possiblities)
|
||||
fieldElement += '</select>'
|
||||
break;
|
||||
}
|
||||
|
@ -319,20 +301,21 @@ drawBlock = function(monitorSettings){
|
|||
<% };
|
||||
};
|
||||
}
|
||||
monitorSettings.info.forEach(drawInfoItem)
|
||||
theBlock.info.forEach(drawInfoItem)
|
||||
}
|
||||
%>
|
||||
</div>
|
||||
</<%- monitorSettings.isForm ? 'form' : 'div' %>>
|
||||
</<%- theBlock.isForm ? 'form' : 'div' %>>
|
||||
<%
|
||||
if(monitorSettings['section-pre-class']){ %>
|
||||
if(theBlock['section-pre-class']){ %>
|
||||
</div>
|
||||
<% }
|
||||
if(monitorSettings['section-pre-pre-class']){ %>
|
||||
if(theBlock['section-pre-pre-class']){ %>
|
||||
</div>
|
||||
<% }
|
||||
if(monitorSettings.isAdvanced){ %>
|
||||
if(theBlock.isAdvanced){ %>
|
||||
</div>
|
||||
<% }
|
||||
}
|
||||
drawBlock(theBlock)
|
||||
%>
|
|
@ -1,20 +1,12 @@
|
|||
<main class="container page-tab pt-3" id="tab-eventFilters">
|
||||
<form class="dark row">
|
||||
<%
|
||||
var drawBlock
|
||||
var buildOptions
|
||||
%>
|
||||
<%
|
||||
include fieldBuilders.ejs
|
||||
%>
|
||||
<% Object.keys(define['Event Filters'].blocks).forEach(function(blockKey){
|
||||
var accountSettings = define['Event Filters'].blocks[blockKey]
|
||||
drawBlock(accountSettings)
|
||||
}) %>
|
||||
<% Object.keys(define['Event Filters'].blocks).forEach(function(blockKey) { -%>
|
||||
<%- include('drawBlock', {
|
||||
theBlock: define['Event Filters'].blocks[blockKey]
|
||||
}) %>
|
||||
<% }) -%>
|
||||
<div class="col-md-12">
|
||||
<%
|
||||
include stickySubmitBar.ejs
|
||||
%>
|
||||
<%- include('stickySubmitBar'); %>
|
||||
</div>
|
||||
</form>
|
||||
</main>
|
||||
|
|
|
@ -1,16 +1,10 @@
|
|||
<main class="page-tab pt-3" id="tab-eventListWithPics">
|
||||
<form class="dark row">
|
||||
<%
|
||||
var drawBlock
|
||||
var buildOptions
|
||||
%>
|
||||
<%
|
||||
include fieldBuilders.ejs
|
||||
%>
|
||||
<% Object.keys(define['Events'].blocks).forEach(function(blockKey){
|
||||
var theBlocks = define['Events'].blocks[blockKey]
|
||||
drawBlock(theBlocks)
|
||||
}) %>
|
||||
<% Object.keys(define['Events'].blocks).forEach(function(blockKey) { -%>
|
||||
<%- include('drawBlock', {
|
||||
theBlock: define['Events'].blocks[blockKey]
|
||||
}) %>
|
||||
<% }) -%>
|
||||
</form>
|
||||
</main>
|
||||
<script src="<%-window.libURL%>assets/js/bs5.eventListWithPics.js"></script>
|
||||
|
|
|
@ -1,16 +1,10 @@
|
|||
<main class="page-tab pt-3" id="tab-fileBinView">
|
||||
<div class="row <%- define.Theme.isDark ? `dark` : '' %>">
|
||||
<%
|
||||
var drawBlock
|
||||
var buildOptions
|
||||
%>
|
||||
<%
|
||||
include fieldBuilders.ejs
|
||||
%>
|
||||
<% Object.keys(define['FileBin'].blocks).forEach(function(blockKey){
|
||||
var theBlock = define['FileBin'].blocks[blockKey]
|
||||
drawBlock(theBlock)
|
||||
}) %>
|
||||
<% Object.keys(define['FileBin'].blocks).forEach(function(blockKey) { -%>
|
||||
<%- include('drawBlock', {
|
||||
theBlock: define['FileBin'].blocks[blockKey]
|
||||
}) %>
|
||||
<% }) -%>
|
||||
</div>
|
||||
<div style="margin-bottom: 50px;"></div>
|
||||
<h4 class="sticky-bar p-3 d-flex flex-row">
|
||||
|
|
|
@ -1,16 +1,10 @@
|
|||
<main class="page-tab pt-3" id="tab-helpWindow">
|
||||
<div class="row <%- define.Theme.isDark ? `dark` : '' %>">
|
||||
<%
|
||||
var drawBlock
|
||||
var buildOptions
|
||||
%>
|
||||
<%
|
||||
include fieldBuilders.ejs
|
||||
%>
|
||||
<% Object.keys(define['Help Window'].blocks).forEach(function(blockKey){
|
||||
var theBlock = define['Help Window'].blocks[blockKey]
|
||||
drawBlock(theBlock)
|
||||
}) %>
|
||||
<% Object.keys(define['Help Window'].blocks).forEach(function(blockKey) { -%>
|
||||
<%- include('drawBlock', {
|
||||
theBlock: define['Help Window'].blocks[blockKey]
|
||||
}) %>
|
||||
<% }) -%>
|
||||
</div>
|
||||
<style>
|
||||
.blockquoteInHelp:before,.blockquoteInHelp:after{
|
||||
|
|
|
@ -1,15 +1,9 @@
|
|||
<main class="page-tab" id="tab-initial">
|
||||
<div class="row">
|
||||
<%
|
||||
var drawBlock
|
||||
var buildOptions
|
||||
%>
|
||||
<%
|
||||
include fieldBuilders.ejs
|
||||
%>
|
||||
<% Object.keys(define['Home'].blocks).forEach(function(blockKey){
|
||||
const theBlock = define['Home'].blocks[blockKey];
|
||||
drawBlock(theBlock)
|
||||
}) %>
|
||||
<% Object.keys(define['Home'].blocks).forEach(function(blockKey) { -%>
|
||||
<%- include('drawBlock', {
|
||||
theBlock: define['Home'].blocks[blockKey]
|
||||
}) %>
|
||||
<% }) -%>
|
||||
</div>
|
||||
</main>
|
||||
|
|
|
@ -1,16 +1,10 @@
|
|||
<main class="container page-tab pt-3" id="tab-logViewer">
|
||||
<form class="dark row">
|
||||
<%
|
||||
var drawBlock
|
||||
var buildOptions
|
||||
%>
|
||||
<%
|
||||
include fieldBuilders.ejs
|
||||
%>
|
||||
<% Object.keys(define['Log Viewer'].blocks).forEach(function(blockKey){
|
||||
var accountSettings = define['Log Viewer'].blocks[blockKey]
|
||||
drawBlock(accountSettings)
|
||||
}) %>
|
||||
<% Object.keys(define['Log Viewer'].blocks).forEach(function(blockKey) { -%>
|
||||
<%- include('drawBlock', {
|
||||
theBlock: define['Log Viewer'].blocks[blockKey]
|
||||
}) %>
|
||||
<% }) -%>
|
||||
</form>
|
||||
<script src="<%-window.libURL%>assets/js/bs5.logViewer.js"></script>
|
||||
</main>
|
||||
|
|
|
@ -25,14 +25,8 @@
|
|||
</div>
|
||||
<% } %>
|
||||
</div>
|
||||
<%
|
||||
var drawBlock
|
||||
var buildOptions
|
||||
%>
|
||||
<%
|
||||
include fieldBuilders.ejs
|
||||
%>
|
||||
<% drawBlock(define.SideMenu.blocks.SideMenuBeforeList) %>
|
||||
|
||||
<%- include('drawBlock', {theBlock: define.SideMenu.blocks.SideMenuBeforeList}); %>
|
||||
<div tab-specific-content="liveGrid" style="display:none">
|
||||
<div class="form-group px-3">
|
||||
<div class="dropdown">
|
||||
|
@ -57,7 +51,7 @@
|
|||
<i class="fa fa-<%- item.icon %>"></i> <%- item.label %>
|
||||
</a>
|
||||
<% if(item.addUl){ %>
|
||||
<ul class="btn-default rounded btn-toggle-nav list-unstyled fw-normal ml-3 mt-2 px-2 pb-2 pt-1 <%- item.ulClass ? item.ulClass : '' %>">
|
||||
<ul class="btn-default rounded btn-toggle-nav list-unstyled fw-normal ml-3 mt-2 px-2 pb-2 pt-1 <%- item.ulClass ? item.ulClass : '' %>" style="<%- item.ulStyle ? item.ulStyle : '' %>">
|
||||
<% if(item.ulItems){
|
||||
item.ulItems.forEach((listItem) => { %>
|
||||
<li><a class="<%- define.Theme.isDark ? 'text-white' : 'text-dark' %> <%- listItem.class ? listItem.class : '' %>" <%- listItem.attributes ? listItem.attributes : '' %>><span class="dot dot-<%- listItem.color ? listItem.color : 'blue' %> shadow mr-2"></span><%- listItem.label ? listItem.label : 'Need Label' %></a></li>
|
||||
|
@ -72,7 +66,7 @@
|
|||
</div>
|
||||
</ul>
|
||||
</div>
|
||||
<% drawBlock(define.SideMenu.blocks.SideMenuAfterList) %>
|
||||
<%- include('drawBlock', {theBlock: define.SideMenu.blocks.SideMenuAfterList}); %>
|
||||
<% if(showMonitors){ %>
|
||||
<div>
|
||||
<div class="form-group px-3">
|
||||
|
|
|
@ -1,19 +1,12 @@
|
|||
<main class="page-tab pt-3" id="tab-monitorMap">
|
||||
<form class="dark row">
|
||||
<%
|
||||
var drawBlock
|
||||
var buildOptions
|
||||
%>
|
||||
<%
|
||||
include fieldBuilders.ejs
|
||||
%>
|
||||
<%
|
||||
var pageName = 'Monitor Map';
|
||||
Object.keys(define[pageName].blocks).forEach(function(blockKey){
|
||||
var pageLayout = define[pageName].blocks[blockKey]
|
||||
drawBlock(pageLayout)
|
||||
})
|
||||
%>
|
||||
var pageName = 'Monitor Map';
|
||||
Object.keys(define[pageName].blocks).forEach(function(blockKey) { -%>
|
||||
<%- include('drawBlock', {
|
||||
theBlock: define[pageName].blocks[blockKey]
|
||||
}) %>
|
||||
<% }) -%>
|
||||
|
||||
</form>
|
||||
<link rel="stylesheet" href="<%-window.libURL%>assets/css/bs5.monitorMap.css" />
|
||||
|
|
|
@ -1,17 +1,11 @@
|
|||
<main class="container page-tab pt-3" id="tab-monitorSettings">
|
||||
<form>
|
||||
<div class="dark">
|
||||
<%
|
||||
var drawBlock
|
||||
var buildOptions
|
||||
%>
|
||||
<%
|
||||
include fieldBuilders.ejs
|
||||
%>
|
||||
<% Object.keys(define['Monitor Settings'].blocks).forEach(function(blockKey){
|
||||
var accountSettings = define['Monitor Settings'].blocks[blockKey]
|
||||
drawBlock(accountSettings)
|
||||
}) %>
|
||||
<% Object.keys(define['Monitor Settings'].blocks).forEach(function(blockKey) { -%>
|
||||
<%- include('drawBlock', {
|
||||
theBlock: define['Monitor Settings'].blocks[blockKey]
|
||||
}) %>
|
||||
<% }) -%>
|
||||
<div style="margin-bottom: 50px;"></div>
|
||||
</div>
|
||||
<div class="sticky-bar p-3 d-flex flex-row">
|
||||
|
@ -46,13 +40,6 @@
|
|||
</div>
|
||||
</form>
|
||||
</main>
|
||||
<%
|
||||
var drawBlock
|
||||
var buildOptions
|
||||
%>
|
||||
<%
|
||||
include fieldBuilders.ejs
|
||||
%>
|
||||
|
||||
<script>
|
||||
var monitorSettingsAdditionalInputMapFieldHtml = `<% Object.keys(define["Monitor Settings Additional Input Map"].blocks).forEach(function(blockKey){
|
||||
|
|
|
@ -1,20 +1,12 @@
|
|||
<main class="container page-tab pt-3" id="tab-monitorStates">
|
||||
<form class="dark row">
|
||||
<%
|
||||
var drawBlock
|
||||
var buildOptions
|
||||
%>
|
||||
<%
|
||||
include fieldBuilders.ejs
|
||||
%>
|
||||
<% Object.keys(define['Monitor States'].blocks).forEach(function(blockKey){
|
||||
var theBlock = define['Monitor States'].blocks[blockKey]
|
||||
drawBlock(theBlock)
|
||||
}) %>
|
||||
<% Object.keys(define['Monitor States'].blocks).forEach(function(blockKey) { -%>
|
||||
<%- include('drawBlock', {
|
||||
theBlock: define['Monitor States'].blocks[blockKey]
|
||||
}) %>
|
||||
<% }) -%>
|
||||
<div class="col-md-12">
|
||||
<%
|
||||
include stickySubmitBar.ejs
|
||||
%>
|
||||
<%- include('stickySubmitBar'); %>
|
||||
</div>
|
||||
</form>
|
||||
</main>
|
||||
|
|
|
@ -1,20 +1,12 @@
|
|||
<main class="container page-tab pt-3" id="tab-onvifDeviceManager">
|
||||
<form class="dark row">
|
||||
<%
|
||||
var drawBlock
|
||||
var buildOptions
|
||||
%>
|
||||
<%
|
||||
include fieldBuilders.ejs
|
||||
%>
|
||||
<% Object.keys(define['ONVIF Device Manager'].blocks).forEach(function(blockKey){
|
||||
var accountSettings = define['ONVIF Device Manager'].blocks[blockKey]
|
||||
drawBlock(accountSettings)
|
||||
}) %>
|
||||
<% Object.keys(define['ONVIF Device Manager'].blocks).forEach(function(blockKey) { -%>
|
||||
<%- include('drawBlock', {
|
||||
theBlock: define['ONVIF Device Manager'].blocks[blockKey]
|
||||
}) %>
|
||||
<% }) -%>
|
||||
<div class="col-md-12">
|
||||
<%
|
||||
include stickySubmitBar.ejs
|
||||
%>
|
||||
<%- include('stickySubmitBar'); %>
|
||||
</div>
|
||||
</form>
|
||||
</main>
|
||||
|
|
|
@ -1,15 +1,9 @@
|
|||
<main class="page-tab pt-3" id="tab-onvifScanner">
|
||||
<form class="dark row">
|
||||
<%
|
||||
var drawBlock
|
||||
var buildOptions
|
||||
%>
|
||||
<%
|
||||
include fieldBuilders.ejs
|
||||
%>
|
||||
<% Object.keys(define['ONVIF Scanner'].blocks).forEach(function(blockKey){
|
||||
var accountSettings = define['ONVIF Scanner'].blocks[blockKey]
|
||||
drawBlock(accountSettings)
|
||||
}) %>
|
||||
<% Object.keys(define['ONVIF Scanner'].blocks).forEach(function(blockKey) { -%>
|
||||
<%- include('drawBlock', {
|
||||
theBlock: define['ONVIF Scanner'].blocks[blockKey]
|
||||
}) %>
|
||||
<% }) -%>
|
||||
</form>
|
||||
</main>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<option value="1" selected><%- `${lang.Last} 1 ${lang.hour}` %></option>
|
||||
<option value="3"><%- `${lang.Last} 3 ${lang.hours}` %></option>
|
||||
<option value="6"><%- `${lang.Last} 6 ${lang.hours}` %></option>
|
||||
<option value="9"><%- `${lang.Last} 6 ${lang.hours}` %></option>
|
||||
<option value="9"><%- `${lang.Last} 9 ${lang.hours}` %></option>
|
||||
<option value="12"><%- `${lang.Last} 12 ${lang.hours}` %></option>
|
||||
<option value="24"><%- `${lang.Last} 24 ${lang.hours}` %></option>
|
||||
<option value="72"><%- `${lang.Last} 3 ${lang.Days}` %></option>
|
||||
|
|
|
@ -1,20 +1,12 @@
|
|||
<main class="container page-tab pt-3" id="tab-regionEditor">
|
||||
<form class="dark row">
|
||||
<%
|
||||
var drawBlock
|
||||
var buildOptions
|
||||
%>
|
||||
<%
|
||||
include fieldBuilders.ejs
|
||||
%>
|
||||
<% Object.keys(define['Region Editor'].blocks).forEach(function(blockKey){
|
||||
var accountSettings = define['Region Editor'].blocks[blockKey]
|
||||
drawBlock(accountSettings)
|
||||
}) %>
|
||||
<% Object.keys(define['Region Editor'].blocks).forEach(function(blockKey) { -%>
|
||||
<%- include('drawBlock', {
|
||||
theBlock: define['Region Editor'].blocks[blockKey]
|
||||
}) %>
|
||||
<% }) -%>
|
||||
<div class="col-md-12">
|
||||
<%
|
||||
include stickySubmitBar.ejs
|
||||
%>
|
||||
<%- include('stickySubmitBar'); %>
|
||||
</div>
|
||||
</form>
|
||||
</main>
|
||||
|
|
|
@ -1,20 +1,12 @@
|
|||
<main class="container page-tab pt-3" id="tab-schedules">
|
||||
<form class="dark row">
|
||||
<%
|
||||
var drawBlock
|
||||
var buildOptions
|
||||
%>
|
||||
<%
|
||||
include fieldBuilders.ejs
|
||||
%>
|
||||
<% Object.keys(define['Schedules'].blocks).forEach(function(blockKey){
|
||||
var theBlock = define['Schedules'].blocks[blockKey]
|
||||
drawBlock(theBlock)
|
||||
}) %>
|
||||
<% Object.keys(define['Schedules'].blocks).forEach(function(blockKey) { -%>
|
||||
<%- include('drawBlock', {
|
||||
theBlock: define['Schedules'].blocks[blockKey]
|
||||
}) %>
|
||||
<% }) -%>
|
||||
<div class="col-md-12">
|
||||
<%
|
||||
include stickySubmitBar.ejs
|
||||
%>
|
||||
<%- include('stickySubmitBar'); %>
|
||||
</div>
|
||||
</form>
|
||||
</main>
|
||||
|
|
|
@ -1,16 +1,10 @@
|
|||
<main class="page-tab pt-3" id="tab-studio">
|
||||
<div class="row <%- define.Theme.isDark ? `dark` : '' %>" id="studio">
|
||||
<%
|
||||
var drawBlock
|
||||
var buildOptions
|
||||
%>
|
||||
<%
|
||||
include fieldBuilders.ejs
|
||||
%>
|
||||
<% Object.keys(define['Studio'].blocks).forEach(function(blockKey){
|
||||
var theBlock = define['Studio'].blocks[blockKey]
|
||||
drawBlock(theBlock)
|
||||
}) %>
|
||||
<% Object.keys(define['Studio'].blocks).forEach(function(blockKey) { -%>
|
||||
<%- include('drawBlock', {
|
||||
theBlock: define['Studio'].blocks[blockKey]
|
||||
}) %>
|
||||
<% }) -%>
|
||||
</div>
|
||||
</main>
|
||||
<link rel="stylesheet" href="<%-window.libURL%>assets/css/bs5.studio.css">
|
||||
|
|
|
@ -1,16 +1,10 @@
|
|||
<main class="container page-tab dark pt-3" id="tab-subAccountManager">
|
||||
<div id="subAccountManager" class="row">
|
||||
<%
|
||||
const details = $user.details
|
||||
var drawBlock
|
||||
var buildOptions
|
||||
%>
|
||||
<%
|
||||
include fieldBuilders.ejs
|
||||
%>
|
||||
<% Object.keys(define['Sub-Account Manager'].blocks).forEach(function(blockKey){
|
||||
var accountSettings = define['Sub-Account Manager'].blocks[blockKey]
|
||||
drawBlock(accountSettings)
|
||||
}) %>
|
||||
<% Object.keys(define['Sub-Account Manager'].blocks).forEach(function(blockKey) { -%>
|
||||
<%- include('drawBlock', {
|
||||
theBlock: define['Sub-Account Manager'].blocks[blockKey],
|
||||
details: $user.details
|
||||
}) %>
|
||||
<% }) -%>
|
||||
</div>
|
||||
</main>
|
||||
|
|
|
@ -1,16 +1,10 @@
|
|||
<main class="page-tab pt-3" id="tab-timelapseViewer">
|
||||
<div class="dark row">
|
||||
<%
|
||||
var drawBlock
|
||||
var buildOptions
|
||||
%>
|
||||
<%
|
||||
include fieldBuilders.ejs
|
||||
%>
|
||||
<% Object.keys(define['Timelapse'].blocks).forEach(function(blockKey){
|
||||
var accountSettings = define['Timelapse'].blocks[blockKey]
|
||||
drawBlock(accountSettings)
|
||||
}) %>
|
||||
<% Object.keys(define['Timelapse'].blocks).forEach(function(blockKey) { -%>
|
||||
<%- include('drawBlock', {
|
||||
theBlock: define['Timelapse'].blocks[blockKey]
|
||||
}) %>
|
||||
<% }) -%>
|
||||
</div>
|
||||
<div style="margin-bottom: 50px;"></div>
|
||||
<h4 class="sticky-bar p-3 d-flex flex-row">
|
||||
|
|
|
@ -1,19 +1,12 @@
|
|||
<main class="page-tab" id="tab-timeline" style="position:relative;margin-right:-1.5rem;margin-left:-1.5rem;">
|
||||
<div class="dark">
|
||||
<%
|
||||
var drawBlock
|
||||
var buildOptions
|
||||
%>
|
||||
<%
|
||||
include fieldBuilders.ejs
|
||||
%>
|
||||
<%
|
||||
var pageName = 'Timeline';
|
||||
Object.keys(define[pageName].blocks).forEach(function(blockKey){
|
||||
var pageLayout = define[pageName].blocks[blockKey]
|
||||
drawBlock(pageLayout)
|
||||
})
|
||||
%>
|
||||
var pageName = 'Timeline';
|
||||
Object.keys(define[pageName].blocks).forEach(function(blockKey) { -%>
|
||||
<%- include('drawBlock', {
|
||||
theBlock: define[pageName].blocks[blockKey]
|
||||
}) %>
|
||||
<% }) -%>
|
||||
|
||||
</div>
|
||||
<link rel="stylesheet" href="<%-window.libURL%>assets/css/bs5.timeline.css" />
|
||||
|
|
|
@ -1,17 +1,11 @@
|
|||
<main class="page-tab pt-3" id="tab-videosTableView">
|
||||
<link rel="stylesheet" href="<%-window.libURL%>assets/css/bs5.videosTable.css">
|
||||
<div class="row <%- define.Theme.isDark ? `dark` : '' %>">
|
||||
<%
|
||||
var drawBlock
|
||||
var buildOptions
|
||||
%>
|
||||
<%
|
||||
include fieldBuilders.ejs
|
||||
%>
|
||||
<% Object.keys(define['Videos Table'].blocks).forEach(function(blockKey){
|
||||
var theBlock = define['Videos Table'].blocks[blockKey]
|
||||
drawBlock(theBlock)
|
||||
}) %>
|
||||
<% Object.keys(define['Videos Table'].blocks).forEach(function(blockKey) { -%>
|
||||
<%- include('drawBlock', {
|
||||
theBlock: define['Videos Table'].blocks[blockKey]
|
||||
}) %>
|
||||
<% }) -%>
|
||||
</div>
|
||||
<div style="margin-bottom: 50px;"></div>
|
||||
<h4 class="sticky-bar p-3 d-flex flex-row">
|
||||
|
|
|
@ -17,9 +17,7 @@
|
|||
var buildOptions
|
||||
%>
|
||||
<form class="dark">
|
||||
<%
|
||||
include fieldBuilders.ejs
|
||||
%>
|
||||
<%- include('home/fieldBuilders'); %>
|
||||
<%
|
||||
drawBlock(define['LDAP'].blocks.LDAP)
|
||||
%>
|
||||
|
|
|
@ -3,17 +3,11 @@
|
|||
<div class="modal-dialog" role="document">
|
||||
<form class="modal-content" style="background:transparent">
|
||||
<div>
|
||||
<%
|
||||
var drawBlock
|
||||
var buildOptions
|
||||
%>
|
||||
<%
|
||||
include home/fieldBuilders.ejs
|
||||
%>
|
||||
<% Object.keys(define['Admin Account Settings'].blocks).forEach(function(blockKey){
|
||||
var accountSettings = define['Admin Account Settings'].blocks[blockKey]
|
||||
drawBlock(accountSettings)
|
||||
}) %>
|
||||
<% Object.keys(define['Admin Account Settings'].blocks).forEach(function(blockKey) { -%>
|
||||
<%- include('home/drawBlock', {
|
||||
theBlock: define['Admin Account Settings'].blocks[blockKey]
|
||||
}) %>
|
||||
<% }) -%>
|
||||
</div>
|
||||
<input style="display:none" type="checkbox" id="edit"/>
|
||||
<input type="hidden" name="details" value="{}">
|
||||
|
|
|
@ -1,14 +1,9 @@
|
|||
<form>
|
||||
<%
|
||||
var drawBlock
|
||||
var buildOptions
|
||||
%>
|
||||
<%
|
||||
include home/fieldBuilders.ejs
|
||||
%>
|
||||
<% Object.keys(define['Super User Preferences'].blocks).forEach(function(blockKey){
|
||||
var accountSettings = define['Super User Preferences'].blocks[blockKey]
|
||||
drawBlock(accountSettings)
|
||||
}) %>
|
||||
<% Object.keys(define['Super User Preferences'].blocks).forEach(function(blockKey) { -%>
|
||||
<%- include('home/drawBlock', {
|
||||
theBlock: define['Super User Preferences'].blocks[blockKey]
|
||||
}) %>
|
||||
<% }) -%>
|
||||
|
||||
</form>
|
||||
<script src="<%-window.libURL%>assets/js/super.preferences.js" type="text/javascript"></script>
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-5">
|
||||
<% include systemInfoBlock.ejs %>
|
||||
<%- include('systemInfoBlock'); %>
|
||||
</div>
|
||||
<div id="logs" class="col-md-7">
|
||||
<div class="form-group-group red" style="height:400px;overflow:auto">
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<% var pageTitle = lang.Shinobi %>
|
||||
<% include blocks/header %>
|
||||
<%- include('blocks/header', {pageTitle: pageTitle}); %>
|
||||
<script>var $user=<%- JSON.stringify($user) %>;</script>
|
||||
<link rel="stylesheet" href="<%-window.libURL%>assets/vendor/bootstrap5/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="<%-window.libURL%>assets/css/bootstrap5-theme.css">
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
<% var pageTitle = lang.Shinobi + ' - ' + lang["2-Factor Authentication"] %>
|
||||
<head>
|
||||
<!-- Powered by Shinobi, http://shinobi.video -->
|
||||
<% include blocks/header-title.ejs %>
|
||||
<%- include('blocks/header-title', {pageTitle: pageTitle}); %>
|
||||
<%
|
||||
if(config.baseURL)window.libURL = config.baseURL;
|
||||
if(!window.libURL)window.libURL = originalURL;
|
||||
%>
|
||||
<% include blocks/header-meta.ejs %>
|
||||
<%- include('blocks/header-meta'); %>
|
||||
<meta http-equiv="content-type" content="text/html;charset=UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<link rel="stylesheet" href="<%-window.libURL%>assets/vendor/bootstrap5/css/bootstrap.min.css">
|
||||
|
@ -16,7 +16,7 @@
|
|||
<link rel="stylesheet" href="<%-window.libURL%>assets/css/gradients.css">
|
||||
<link rel="stylesheet" href="<%-window.libURL%>assets/css/bs5.darktheme.css">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
|
||||
<% include blocks/header-favicon.ejs %>
|
||||
<%- include('blocks/header-favicon'); %>
|
||||
<script src="<%-window.libURL%>assets/vendor/js/jquery.min.js"></script>
|
||||
<script src="<%-window.libURL%>assets/vendor/js/jquery-ui.min.js"></script>
|
||||
<script src="<%-window.libURL%>assets/vendor/js/jquery.serialize.js"></script>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<% var pageTitle = lang.Shinobi %>
|
||||
|
||||
<% include blocks/header.ejs %>
|
||||
<% include blocks/header-loggedIn.ejs %>
|
||||
<%- include('blocks/header', {pageTitle: pageTitle}); %>
|
||||
<%- include('blocks/header-loggedIn'); %>
|
||||
<!-- Page -->
|
||||
<% include blocks/home.ejs %>
|
||||
<%- include('blocks/home'); %>
|
||||
<!-- Footer -->
|
||||
<% include blocks/footer.ejs %>
|
||||
<%- include('blocks/footer'); %>
|
||||
|
|
|
@ -2,12 +2,12 @@
|
|||
<html lang="en">
|
||||
<head>
|
||||
<!-- Powered by Shinobi, http://shinobi.video -->
|
||||
<% include blocks/header-title.ejs %>
|
||||
<%- include('blocks/header-title'); %>
|
||||
<%
|
||||
if(config.baseURL)window.libURL = config.baseURL;
|
||||
if(!window.libURL)window.libURL = originalURL;
|
||||
%>
|
||||
<% include blocks/header-meta.ejs %>
|
||||
<%- include('blocks/header-meta'); %>
|
||||
<meta http-equiv="content-type" content="text/html;charset=UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<link rel="stylesheet" href="<%-window.libURL%>assets/vendor/bootstrap5/css/bootstrap.min.css">
|
||||
|
@ -17,7 +17,7 @@
|
|||
<link rel="stylesheet" href="<%-window.libURL%>assets/css/gradients.css">
|
||||
<link rel="stylesheet" href="<%-window.libURL%>assets/css/bs5.darktheme.css">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
|
||||
<% include blocks/header-favicon.ejs %>
|
||||
<%- include('blocks/header-favicon'); %>
|
||||
<script src="<%-window.libURL%>assets/vendor/js/jquery.min.js"></script>
|
||||
<script src="<%-window.libURL%>assets/vendor/js/jquery-ui.min.js"></script>
|
||||
<script src="<%-window.libURL%>assets/vendor/js/jquery.serialize.js"></script>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<% var pageTitle = lang.Shinobi %>
|
||||
<% include blocks/header %>
|
||||
<%- include('blocks/header', {pageTitle: pageTitle}); %>
|
||||
<script>var $user=<%- JSON.stringify($user) %>;</script>
|
||||
<link rel="stylesheet" href="<%-window.libURL%>assets/vendor/bootstrap5/css/bootstrap.min.css">
|
||||
<div id="main" class="container">
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
if(config.baseURL)window.libURL = config.baseURL;
|
||||
if(!window.libURL)window.libURL = originalURL;
|
||||
%>
|
||||
<% include blocks/header-meta.ejs %>
|
||||
<% include blocks/header-favicon.ejs %>
|
||||
<%- include('blocks/header-meta'); %>
|
||||
<%- include('blocks/header-favicon'); %>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||
<meta content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0, shrink-to-fit=no' name='viewport' />
|
||||
<!-- Fonts and icons -->
|
||||
|
@ -96,8 +96,8 @@
|
|||
<div class="tab-pane active" id="accounts" role="tabpanel">
|
||||
<div class="row">
|
||||
<div class="col-md-5 text-left">
|
||||
<% include blocks/heyActivate.ejs %>
|
||||
<% include blocks/systemInfoBlock.ejs %>
|
||||
<%- include('blocks/heyActivate'); %>
|
||||
<%- include('blocks/systemInfoBlock'); %>
|
||||
</div>
|
||||
<div class="col-md-7">
|
||||
<div class="mb-4"><a href="#" class="add btn btn-block btn-default"><i class="fa fa-plus"></i> <%- lang.Add %></a></div>
|
||||
|
@ -108,22 +108,22 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="tab-pane text-left" id="config" role="tabpanel">
|
||||
<% include blocks/superConfigEditor.ejs %>
|
||||
<%- include('blocks/superConfigEditor'); %>
|
||||
</div>
|
||||
<div class="tab-pane text-left" id="system" role="tabpanel">
|
||||
<% include blocks/superSystemTab.ejs %>
|
||||
<%- include('blocks/superSystemTab'); %>
|
||||
</div>
|
||||
<div class="tab-pane text-left" id="customAutoLoad" role="tabpanel">
|
||||
<% include blocks/superCustomAutoLoadManager.ejs %>
|
||||
<%- include('blocks/superCustomAutoLoadManager'); %>
|
||||
</div>
|
||||
<div class="tab-pane text-left" id="changeSuperPreferences" role="tabpanel">
|
||||
<% include blocks/superPreferences.ejs %>
|
||||
<%- include('blocks/superPreferences'); %>
|
||||
</div>
|
||||
<div class="tab-pane text-left" id="easyRemoteAccess" role="tabpanel">
|
||||
<% include blocks/easyRemoteAccess.ejs %>
|
||||
<%- include('blocks/easyRemoteAccess'); %>
|
||||
</div>
|
||||
<div class="tab-pane text-left" id="superPluginManager" role="tabpanel">
|
||||
<% include blocks/superPluginManager.ejs %>
|
||||
<%- include('blocks/superPluginManager'); %>
|
||||
</div>
|
||||
<% customAutoLoad.superPageBlocks.forEach(function(block){ %>
|
||||
<%- include(block) %>
|
||||
|
@ -146,7 +146,7 @@
|
|||
</div>
|
||||
<div id="temp" style="display:none"></div>
|
||||
</body>
|
||||
<% include blocks/confirm.ejs %>
|
||||
<%- include('blocks/confirm'); %>
|
||||
<script src="<%-window.libURL%>assets/vendor/js/pnotify.custom.min.js" type="text/javascript"></script>
|
||||
<script src="<%-window.libURL%>assets/vendor/moment-with-locales.js"></script>
|
||||
<script src="<%-window.libURL%>assets/vendor/js/livestamp.min.js" type="text/javascript"></script>
|
||||
|
@ -340,7 +340,7 @@ $('body')
|
|||
location.href = location.href.split('/super')[0];
|
||||
})
|
||||
</script>
|
||||
<% include blocks/mainpermissions.ejs %>
|
||||
<%- include('blocks/mainpermissions'); %>
|
||||
<% customAutoLoad.superLibsJs.forEach(function(lib){ %>
|
||||
<script src="<%-window.libURL%>libs/js/<%-lib%>"></script>
|
||||
<% }) %>
|
||||
|
|
Loading…
Reference in New Issue