Merge branch 'dev' into action-chain

action-chain
Moe 2024-04-30 10:56:18 -07:00
commit 2bc7c37b72
94 changed files with 4333 additions and 3469 deletions

View File

@ -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

23
Docker/install_ffmpeg.sh Normal file
View File

@ -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

19
Docker/install_mariadb.sh Normal file
View File

@ -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

22
Docker/install_nodejs.sh Normal file
View File

@ -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

View File

@ -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" ]

View File

@ -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" ]

View File

@ -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" ]

View File

@ -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" ]

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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"
}

View File

@ -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,
}
}

View File

@ -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]){

View File

@ -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)

View File

@ -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)

View File

@ -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);

View File

@ -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;
}

View File

@ -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,

View File

@ -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,

View File

@ -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,
}
}

View File

@ -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){

View File

@ -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}`

21
libs/fieldBuild.js Normal file
View File

@ -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
}

View File

@ -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()}

View File

@ -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

View File

@ -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)
}
}

View File

@ -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,

View File

@ -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()
});
})
}

View File

@ -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,
},[

View File

@ -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({

View File

@ -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)
}

View File

@ -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){

View File

@ -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);

5197
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -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",

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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();

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -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()
})

View File

@ -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
}

View File

@ -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(){

View File

@ -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]

View File

@ -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'
})

View File

@ -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;
}
})

View File

@ -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")
}

View File

@ -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()
})

View File

@ -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){

View File

@ -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()

View File

@ -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

View File

@ -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">

View File

@ -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') %>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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)
%>

View File

@ -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>

View File

@ -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>

View File

@ -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">

View File

@ -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{

View File

@ -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>

View File

@ -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>

View File

@ -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> &nbsp; <%- 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">

View File

@ -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" />

View File

@ -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){

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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">

View File

@ -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>

View File

@ -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">

View File

@ -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" />

View File

@ -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">

View File

@ -17,9 +17,7 @@
var buildOptions
%>
<form class="dark">
<%
include fieldBuilders.ejs
%>
<%- include('home/fieldBuilders'); %>
<%
drawBlock(define['LDAP'].blocks.LDAP)
%>

View File

@ -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="{}">

View File

@ -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>

View File

@ -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">

View File

@ -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">

View File

@ -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>

View File

@ -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'); %>

View File

@ -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>

View File

@ -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">

View File

@ -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>
<% }) %>