NextNVR/deploy_nvr.sh

318 lines
9.7 KiB
Bash
Executable file

#!/bin/bash
# NextNVR — Deploy & Update Script
# Usage: curl -sSLO https://git.lohmar.co.uk/cclohmar/NextNVR/raw/branch/main/deploy_nvr.sh
# chmod +x deploy_nvr.sh && ./deploy_nvr.sh
#
# Supports: Debian, Ubuntu, Fedora, RHEL, Rocky, Alma, Arch, Alpine
# Requires: curl, sudo, systemd (or falls back to nohup for non-systemd)
set -e
GIT_URL="https://git.lohmar.co.uk/cclohmar/NextNVR.git"
DEPLOY_DIR="/opt/nextnvr"
SERVICE_NAME="nextnvr"
GO_VERSION="1.24.5"
GO_ARCH="amd64"
# Detect ARM: use arm64 if running on aarch64.
if [ "$(uname -m)" = "aarch64" ]; then GO_ARCH="arm64"; fi
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m'
log() { echo -e "${GREEN}[NextNVR]${NC} $1"; }
warn() { echo -e "${YELLOW}[NextNVR]${NC} $1"; }
err() { echo -e "${RED}[NextNVR]${NC} $1"; }
info() { echo -e "${CYAN}[NextNVR]${NC} $1"; }
echo ""
echo "============================================"
echo " NextNVR — Portable Deployment"
echo " $(uname -m) / $(uname -s)"
echo "============================================"
echo ""
# ── 0. Stop existing service (clean update) ──
if systemctl is-active --quiet "${SERVICE_NAME}" 2>/dev/null; then
log "Stopping existing NextNVR service..."
sudo systemctl stop "${SERVICE_NAME}"
log "Service stopped."
elif pgrep -f "/opt/nextnvr/nextnvr" > /dev/null 2>&1; then
warn "NextNVR is running outside systemd. Stopping..."
sudo pkill -f "/opt/nextnvr/nextnvr" 2>/dev/null || true
sleep 2
fi
# Clean up any lingering processes.
sudo pkill -f "go2rtc" 2>/dev/null || true
sudo pkill -f "ffmpeg.*rec_" 2>/dev/null || true
# ── Check for sudo ──
if ! command -v sudo &>/dev/null; then
if [ "$(id -u)" -eq 0 ]; then
# Running as root — create a no-op sudo.
sudo() { "$@"; }
else
err "sudo is required. Install it first or run as root."
exit 1
fi
fi
# ── 0. Detect distro & package manager ──
detect_pkg_manager() {
if command -v apt-get &>/dev/null; then
PKG_INSTALL="sudo apt-get update -qq && sudo apt-get install -y -qq"
PKG_SEARCH="apt-cache search"
FFMPEG_PKG="ffmpeg"
GIT_PKG="git"
CURL_PKG="curl"
DISTRO="debian"
elif command -v dnf &>/dev/null; then
PKG_INSTALL="sudo dnf install -y -q"
PKG_SEARCH="dnf search"
FFMPEG_PKG="ffmpeg-free"
GIT_PKG="git"
CURL_PKG="curl"
DISTRO="fedora"
elif command -v yum &>/dev/null; then
PKG_INSTALL="sudo yum install -y -q"
PKG_SEARCH="yum search"
FFMPEG_PKG="ffmpeg"
GIT_PKG="git"
CURL_PKG="curl"
DISTRO="rhel"
elif command -v pacman &>/dev/null; then
PKG_INSTALL="sudo pacman -S --noconfirm --needed"
PKG_SEARCH="pacman -Ss"
FFMPEG_PKG="ffmpeg"
GIT_PKG="git"
CURL_PKG="curl"
DISTRO="arch"
elif command -v apk &>/dev/null; then
PKG_INSTALL="sudo apk add --no-cache"
PKG_SEARCH="apk search"
FFMPEG_PKG="ffmpeg"
GIT_PKG="git"
CURL_PKG="curl"
DISTRO="alpine"
else
err "Could not detect package manager. Supported: apt, dnf, yum, pacman, apk."
exit 1
fi
log "Detected: ${DISTRO} ($(echo ${PKG_INSTALL} | awk '{print $2}'))"
}
detect_pkg_manager
# ── 1. Install system dependencies ──
log "Step 1/6: Installing system dependencies..."
if ! command -v ffmpeg &>/dev/null; then
info "Installing ffmpeg (${FFMPEG_PKG})..."
eval "${PKG_INSTALL} ${FFMPEG_PKG}"
log "ffmpeg installed."
else
log "ffmpeg already installed: $(ffmpeg -version 2>&1 | head -1)"
fi
for pkg in git curl; do
pkgvar="$(echo ${pkg} | tr '[:lower:]' '[:upper:]')_PKG"
if ! command -v ${pkg} &>/dev/null; then
info "Installing ${pkg}..."
eval "${PKG_INSTALL} ${!pkgvar}"
fi
done
# ── 1b. Time synchronization ──
log "Checking time sync..."
# Try timedatectl (systemd). Fall back to checking ntp/chrony.
TIME_SYNC_OK=false
if command -v timedatectl &>/dev/null; then
if timedatectl status 2>/dev/null | grep -q "System clock synchronized: yes"; then
TIME_SYNC_OK=true
else
info "Enabling NTP via timedatectl..."
sudo timedatectl set-ntp true 2>/dev/null || true
TIME_SYNC_OK=true
fi
elif systemctl is-active --quiet chronyd 2>/dev/null; then
TIME_SYNC_OK=true
elif systemctl is-active --quiet ntpd 2>/dev/null; then
TIME_SYNC_OK=true
else
warn "No time sync service detected. Trying to install chrony..."
eval "${PKG_INSTALL} chrony 2>/dev/null" || eval "${PKG_INSTALL} chronyd 2>/dev/null" || true
sudo systemctl enable --now chronyd 2>/dev/null || sudo systemctl enable --now chrony 2>/dev/null || true
TIME_SYNC_OK=true
fi
if $TIME_SYNC_OK; then
log "Time sync: OK"
else
warn "Time sync could not be verified. Timestamps may drift."
fi
log "Local time: $(date)"
TZ=$(timedatectl show --property=Timezone --value 2>/dev/null || cat /etc/timezone 2>/dev/null || echo "unknown")
log "Timezone: ${TZ}"
# ── 2. Install Go (portable — no distro packages) ──
log "Step 2/6: Setting up Go ${GO_VERSION}..."
GO_TAR="go${GO_VERSION}.linux-${GO_ARCH}.tar.gz"
GO_URL="https://go.dev/dl/${GO_TAR}"
GO_HOME="$HOME/.local/go"
if [ -f "${GO_HOME}/bin/go" ]; then
log "Go already installed: $(${GO_HOME}/bin/go version)"
else
info "Downloading Go ${GO_VERSION} for linux/${GO_ARCH}..."
curl -sSL "${GO_URL}" -o /tmp/${GO_TAR}
mkdir -p "${GO_HOME}"
tar -C "$HOME/.local" -xzf /tmp/${GO_TAR}
rm /tmp/${GO_TAR}
log "Go ${GO_VERSION} installed."
fi
export PATH="${GO_HOME}/bin:$PATH"
export GOPATH="$HOME/go"
export GOCACHE="$HOME/.cache/go-build"
# ── 3. Clone or update repository ──
log "Step 3/6: Fetching NextNVR source..."
if [ -d "${DEPLOY_DIR}/.git" ]; then
info "Repository exists, pulling latest..."
git -C "${DEPLOY_DIR}" pull origin main
log "Source updated."
else
info "Cloning repository..."
sudo mkdir -p "${DEPLOY_DIR}"
sudo chown "$(whoami):$(id -gn)" "${DEPLOY_DIR}"
git clone "${GIT_URL}" "${DEPLOY_DIR}"
log "Source cloned."
fi
cd "${DEPLOY_DIR}"
# ── 4. Build ──
log "Step 4/6: Building NextNVR binary..."
go mod tidy
VERSION=$(cat VERSION 2>/dev/null || echo "0.0.0")
BUILD_TIME=$(date -u +%Y-%m-%dT%H:%M:%SZ)
CGO_ENABLED=0 go build \
-ldflags="-s -w -X main.Version=${VERSION} -X main.BuildTime=${BUILD_TIME}" \
-o nextnvr .
chmod +x nextnvr
log "Binary built: nextnvr v${VERSION} ($(du -h nextnvr | cut -f1))"
# ── 5. Configuration & directories ──
log "Step 5/6: Configuration..."
# Repo includes a default config.yaml. Check if storage path needs setting.
CURRENT_PATH=$(grep recordings_path "${DEPLOY_DIR}/config.yaml" 2>/dev/null | awk -F'"' '{print $2}')
if [ -z "${CURRENT_PATH}" ] || [ "${CURRENT_PATH}" = "/home/master/nvr_data" ]; then
echo ""
info "Storage path not set. Where should recordings be stored?"
if [ -e /dev/tty ]; then
read -p " Path [${HOME}/nvr_data]: " STORAGE_PATH </dev/tty
fi
STORAGE_PATH="${STORAGE_PATH:-${HOME}/nvr_data}"
STORAGE_PATH="${STORAGE_PATH%/}"
echo ""
sudo mkdir -p "${STORAGE_PATH}"
sudo chown "$(whoami):$(id -gn)" "${STORAGE_PATH}"
sed -i "s|recordings_path:.*|recordings_path: \"${STORAGE_PATH}\"|" "${DEPLOY_DIR}/config.yaml"
log "Storage: ${STORAGE_PATH}"
else
log "Storage: ${CURRENT_PATH}"
sudo mkdir -p "${CURRENT_PATH}" 2>/dev/null || true
fi
# ── 6. Install service ──
log "Step 6/6: Service installation..."
RUN_USER="$(whoami)"
SERVICE_FILE="/etc/systemd/system/${SERVICE_NAME}.service"
HAS_SYSTEMD=false
if command -v systemctl &>/dev/null; then
HAS_SYSTEMD=true
fi
if $HAS_SYSTEMD; then
sudo tee "${SERVICE_FILE}" > /dev/null <<EOF
[Unit]
Description=NextNVR — IP Camera Recorder
After=network.target
Wants=network.target
[Service]
Type=simple
User=${RUN_USER}
WorkingDirectory=${DEPLOY_DIR}
ExecStart=${DEPLOY_DIR}/nextnvr --config ${DEPLOY_DIR}/config.yaml
Restart=always
RestartSec=10
LimitNOFILE=65536
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable "${SERVICE_NAME}"
sudo systemctl restart "${SERVICE_NAME}"
sleep 2
echo ""
sudo systemctl status "${SERVICE_NAME}" --no-pager --lines=5 2>/dev/null || true
else
warn "systemd not detected. Running NextNVR in background with nohup."
# Kill any existing instance.
pkill -f "nextnvr --config" 2>/dev/null || true
sleep 1
nohup "${DEPLOY_DIR}/nextnvr" --config "${DEPLOY_DIR}/config.yaml" \
> "${DEPLOY_DIR}/nextnvr.log" 2>&1 &
sleep 2
if pgrep -f "nextnvr --config" > /dev/null; then
log "NextNVR started (PID $(pgrep -f 'nextnvr --config' | head -1))."
info "Logs: tail -f ${DEPLOY_DIR}/nextnvr.log"
else
err "NextNVR failed to start. Check: ${DEPLOY_DIR}/nextnvr.log"
fi
fi
# ── Done ──
echo ""
echo "============================================"
echo -e " ${GREEN}NextNVR deployment complete!${NC}"
echo "============================================"
echo ""
info " Web UI: http://$(hostname -I 2>/dev/null | awk '{print $1}' || echo 'localhost'):8080"
info " Config: ${DEPLOY_DIR}/config.yaml"
info " Storage: $(grep recordings_path ${DEPLOY_DIR}/config.yaml 2>/dev/null | awk '{print $2}' | tr -d '"')"
echo ""
if $HAS_SYSTEMD; then
echo " Service:"
echo " sudo systemctl status ${SERVICE_NAME}"
echo " sudo systemctl restart ${SERVICE_NAME}"
echo " sudo journalctl -u ${SERVICE_NAME} -f"
else
echo " Process:"
echo " ps aux | grep nextnvr"
echo " tail -f ${DEPLOY_DIR}/nextnvr.log"
echo " To stop: pkill -f 'nextnvr --config'"
fi
echo ""