#!/bin/bash # RK3588 Media Server + Agent 完善部署脚本 # 部署到 /opt,包含完整的运行时目录、权限、日志轮转等 # # 用法: sudo ./deploy.sh [install|upgrade|status|logs|clean-hls|uninstall] # 首次安装 Agent 时传入后台管理统一主钥匙;后续部署默认沿用设备保存的主钥匙: # sudo AGENT_TOKEN= ./deploy.sh install set -e # 颜色定义 RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' CYAN='\033[0;36m' NC='\033[0m' # 目录定义 SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" BUILD_DIR="$PROJECT_DIR/build" # 安装目录 INSTALL_DIR="/opt/rk3588-media-server" AGENT_INSTALL_DIR="/opt/rk3588-agent" SERVICE_DIR="/etc/systemd/system" LOGROTATE_DIR="/etc/logrotate.d" # 运行时数据目录(持久化数据) RUNTIME_DIR="/var/lib/rk3588-media-server" HLS_DIR="$RUNTIME_DIR/hls" LOGS_DIR="$RUNTIME_DIR/logs" ALARMS_DIR="$RUNTIME_DIR/alarms" CLIPS_DIR="$RUNTIME_DIR/clips" # 默认使用模板/profile/overlay 渲染部署配置。可通过环境变量覆盖: # DEPLOY_CONFIG_TEMPLATE=configs/templates/workshop_face_shoe_alarm.json # DEPLOY_CONFIG_PROFILE=configs/profiles/local_3588_test.json # DEPLOY_CONFIG_OVERLAYS="configs/overlays/face_debug.json configs/overlays/production_quiet.json" # DEPLOY_CONFIG_ID=local_3588_face_debug # DEPLOY_CONFIG_VERSION=20260419.001 DEPLOY_CONFIG_TEMPLATE="${DEPLOY_CONFIG_TEMPLATE:-configs/templates/workshop_face_shoe_alarm.json}" DEPLOY_CONFIG_PROFILE="${DEPLOY_CONFIG_PROFILE:-configs/profiles/local_3588_test.json}" DEPLOY_CONFIG_OVERLAYS="${DEPLOY_CONFIG_OVERLAYS:-configs/overlays/face_debug.json}" DEPLOY_CONFIG_ID="${DEPLOY_CONFIG_ID:-local_3588_face_debug}" DEPLOY_CONFIG_VERSION="${DEPLOY_CONFIG_VERSION:-$(date +%Y%m%d.%H%M%S)}" # 后台管理系统访问所有设备 agent 的统一主钥匙。传入 AGENT_TOKEN 表示写入/轮换主钥匙; # 不传时沿用设备上保存的主钥匙。禁止设备本地随机生成,避免后台和设备 token 漂移。 # 首次安装示例: # sudo AGENT_TOKEN=4fe2d69fda23d0d5d04a1486d4920e68 ./scripts/deploy.sh install # 后续升级示例: # sudo ./scripts/deploy.sh upgrade AGENT_TOKEN="${AGENT_TOKEN:-}" AGENT_TOKEN_FILE="$AGENT_INSTALL_DIR/agent.token" resolve_agent_token() { if [ -z "$AGENT_TOKEN" ]; then if [ -f "$AGENT_TOKEN_FILE" ]; then AGENT_TOKEN="$(tr -d '\r\n[:space:]' < "$AGENT_TOKEN_FILE")" echo -e "${GREEN}✓${NC} 使用设备已保存的后台管理统一主钥匙" else echo -e "${RED}错误: 未提供 AGENT_TOKEN,且未找到已保存的主钥匙 $AGENT_TOKEN_FILE${NC}" echo "首次安装必须使用:sudo AGENT_TOKEN= ./scripts/deploy.sh install" echo "部署脚本不会本地生成 token。" exit 1 fi else echo -e "${GREEN}✓${NC} 使用 AGENT_TOKEN 写入/轮换后台管理统一主钥匙" fi if ! printf '%s' "$AGENT_TOKEN" | grep -Eq '^[A-Za-z0-9._:-]+$'; then echo -e "${RED}错误: AGENT_TOKEN 包含不支持的字符${NC}" echo "请使用字母、数字、点、下划线、冒号或短横线组成的 token。" exit 1 fi } # 创建必要的运行时目录 create_runtime_dirs() { echo -e "${CYAN}[创建运行时目录]${NC}" # 主运行时目录 mkdir -p "$RUNTIME_DIR" mkdir -p "$HLS_DIR" mkdir -p "$LOGS_DIR" mkdir -p "$ALARMS_DIR" mkdir -p "$CLIPS_DIR" # 创建 HLS 符号链接,使 HTTP server 能访问 HLS 文件 # HLS 输出到 /var/lib/...,但 HTTP server 从 web_root/hls/ 提供文件 if [ ! -L "$INSTALL_DIR/web/hls" ]; then rm -rf "$INSTALL_DIR/web/hls" ln -s "$HLS_DIR" "$INSTALL_DIR/web/hls" echo -e "${GREEN}✓${NC} 创建 HLS 符号链接: web/hls -> $HLS_DIR" fi # 设置权限(日志目录使用 orangepi 用户,方便查看和管理) chown -R root:root "$RUNTIME_DIR" # HLS 输出目录 - root 所有,但 orangepi 可读取 chmod 755 "$HLS_DIR" # 日志目录 - orangepi 用户完全控制 chown orangepi:orangepi "$LOGS_DIR" chmod 755 "$LOGS_DIR" # 告警目录 - orangepi 用户完全控制 chown orangepi:orangepi "$ALARMS_DIR" chmod 755 "$ALARMS_DIR" # 视频片段目录 - orangepi 用户完全控制 chown orangepi:orangepi "$CLIPS_DIR" chmod 755 "$CLIPS_DIR" echo -e "${GREEN}✓${NC} 运行时目录创建完成:" echo " HLS输出: $HLS_DIR (root:orangepi, 755)" echo " 日志: $LOGS_DIR (orangepi:orangepi, 755)" echo " 告警图片: $ALARMS_DIR (orangepi:orangepi, 755)" echo " 告警视频: $CLIPS_DIR (orangepi:orangepi, 755)" } # 安装日志轮转配置 install_logrotate() { echo -e "${CYAN}[配置日志轮转]${NC}" # Media Server 日志轮转 cat > "$LOGROTATE_DIR/rk3588-media-server" << 'EOF' /var/lib/rk3588-media-server/logs/*.log { daily rotate 7 compress delaycompress missingok notifempty create 644 orangepi orangepi sharedscripts postrotate /bin/kill -HUP $(cat /var/run/syslogd.pid 2>/dev/null) 2>/dev/null || true endscript } EOF # HLS 分片清理(保留最近7天) cat > "$LOGROTATE_DIR/rk3588-hls" << 'EOF' /var/lib/rk3588-media-server/hls/*/index*.ts { daily rotate 1 maxage 7 missingok notifempty nocompress } /var/lib/rk3588-media-server/hls/*/index*.m3u8 { daily rotate 1 maxage 7 missingok notifempty nocompress } EOF chmod 644 "$LOGROTATE_DIR/rk3588-media-server" chmod 644 "$LOGROTATE_DIR/rk3588-hls" echo -e "${GREEN}✓${NC} 日志轮转配置完成" } # 安装 HLS 清理脚本 install_hls_cleanup() { echo -e "${CYAN}[安装 HLS 清理脚本]${NC}" cat > "$INSTALL_DIR/bin/cleanup-hls.sh" << 'EOF' #!/bin/bash # HLS 分片清理脚本 - 保留最近 N 天的分片 KEEP_DAYS=${1:-3} HLS_DIR="/var/lib/rk3588-media-server/hls" if [ ! -d "$HLS_DIR" ]; then echo "HLS 目录不存在: $HLS_DIR" exit 1 fi # 清理旧的分片文件 find "$HLS_DIR" -name "*.ts" -type f -mtime +$KEEP_DAYS -delete 2>/dev/null # 清理空的摄像头目录 find "$HLS_DIR" -type d -empty -delete 2>/dev/null echo "$(date): HLS 清理完成 (保留最近 $KEEP_DAYS 天)" EOF chmod +x "$INSTALL_DIR/bin/cleanup-hls.sh" # 添加到 crontab(每天凌晨3点执行) CRON_FILE="/etc/cron.d/rk3588-media-server" cat > "$CRON_FILE" << 'EOF' # RK3588 Media Server 定时任务 SHELL=/bin/bash PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin # 每天凌晨3点清理HLS旧分片(保留3天) 0 3 * * * root /opt/rk3588-media-server/bin/cleanup-hls.sh 3 >> /var/lib/rk3588-media-server/logs/cleanup.log 2>&1 # 每周日凌晨4点清理告警视频(保留30天) 0 4 * * 0 root find /var/lib/rk3588-media-server/clips -name "*.mp4" -type f -mtime +30 -delete 2>/dev/null # 每周日凌晨4点清理告警图片(保留90天) 30 4 * * 0 root find /var/lib/rk3588-media-server/alarms -name "*.jpg" -type f -mtime +90 -delete 2>/dev/null EOF chmod 644 "$CRON_FILE" echo -e "${GREEN}✓${NC} HLS 清理脚本安装完成" echo " 清理策略:" echo " - HLS分片: 保留3天" echo " - 告警视频: 保留30天" echo " - 告警图片: 保留90天" } # 安装 Media Server cmd_install_media_server() { echo -e "${BLUE}[1/5] 安装 Media Server...${NC}" # 检查编译产物 if [ ! -f "$BUILD_DIR/media-server" ]; then echo -e "${RED}错误: 未找到编译好的 media-server${NC}" echo "请先编译: cd $PROJECT_DIR && cmake --build build" exit 1 fi # 创建目录结构 mkdir -p "$INSTALL_DIR/bin/plugins" mkdir -p "$INSTALL_DIR/lib" mkdir -p "$INSTALL_DIR/etc" mkdir -p "$INSTALL_DIR/web" mkdir -p "$INSTALL_DIR/models" # 复制二进制文件 cp "$BUILD_DIR/media-server" "$INSTALL_DIR/bin/" chmod +x "$INSTALL_DIR/bin/media-server" # 复制运维脚本 mkdir -p "$INSTALL_DIR/scripts" if [ -f "$PROJECT_DIR/scripts/ops.sh" ]; then cp "$PROJECT_DIR/scripts/ops.sh" "$INSTALL_DIR/scripts/" chmod +x "$INSTALL_DIR/scripts/ops.sh" echo -e "${GREEN}✓${NC} 已复制运维脚本: ops.sh" fi if [ -f "$PROJECT_DIR/scripts/monitor_hw.sh" ]; then cp "$PROJECT_DIR/scripts/monitor_hw.sh" "$INSTALL_DIR/scripts/" chmod +x "$INSTALL_DIR/scripts/monitor_hw.sh" echo -e "${GREEN}✓${NC} 已复制运维脚本: monitor_hw.sh" fi # 复制插件 if [ -d "$BUILD_DIR/plugins" ]; then cp "$BUILD_DIR"/plugins/*.so "$INSTALL_DIR/bin/plugins/" 2>/dev/null || true PLUGIN_COUNT=$(ls "$INSTALL_DIR/bin/plugins/"/*.so 2>/dev/null | wc -l) echo -e "${GREEN}✓${NC} 已复制插件 ($PLUGIN_COUNT 个)" fi # 复制依赖库 if ls "$BUILD_DIR"/*.so 1> /dev/null 2>&1; then cp "$BUILD_DIR"/*.so "$INSTALL_DIR/lib/" 2>/dev/null || true fi # 复制项目 lib 目录中的 RKNN 等系统库 if [ -d "$PROJECT_DIR/lib" ]; then for lib_file in "$PROJECT_DIR"/lib/*.so*; do [ -f "$lib_file" ] || continue cp "$lib_file" "$INSTALL_DIR/lib/" echo -e "${GREEN}✓${NC} 已复制 $(basename "$lib_file")" done fi # 复制共享状态库(rk_shared_state) if [ -f "$BUILD_DIR/librk_shared_state.so" ]; then cp "$BUILD_DIR/librk_shared_state.so" "$INSTALL_DIR/lib/" echo -e "${GREEN}✓${NC} 已复制 librk_shared_state.so" fi # 复制 web 静态文件 if [ -d "$PROJECT_DIR/web" ]; then cp -r "$PROJECT_DIR/web"/* "$INSTALL_DIR/web/" 2>/dev/null || true WEB_FILES=$(find "$INSTALL_DIR/web" -type f | wc -l) echo -e "${GREEN}✓${NC} 已复制 web 静态文件 ($WEB_FILES 个文件)" fi # 复制模型文件(包括 .rknn 模型和 .db 人脸库) if [ -d "$PROJECT_DIR/models" ]; then cp -r "$PROJECT_DIR/models"/* "$INSTALL_DIR/models/" 2>/dev/null || true MODEL_COUNT=$(find "$INSTALL_DIR/models" -type f \( -name "*.rknn" -o -name "*.db" \) | wc -l) echo -e "${GREEN}✓${NC} 已复制模型文件 ($MODEL_COUNT 个模型/人脸库)" fi # 选择并复制配置文件 select_config # 修复配置文件路径 fix_config_paths echo -e "${GREEN}✓${NC} Media Server 安装完成" } # 渲染模板化部署配置 render_template_config() { local template_path="$PROJECT_DIR/$DEPLOY_CONFIG_TEMPLATE" local profile_path="$PROJECT_DIR/$DEPLOY_CONFIG_PROFILE" local render_tool="$PROJECT_DIR/tools/render_config.py" local rendered_at local out_file local overlay_args=() local overlay_path if [ ! -f "$render_tool" ] || [ ! -f "$template_path" ] || [ ! -f "$profile_path" ]; then return 1 fi rendered_at="$(date -Iseconds)" out_file="/tmp/rk3588-media-server-${DEPLOY_CONFIG_ID}-${DEPLOY_CONFIG_VERSION}.json" for overlay in $DEPLOY_CONFIG_OVERLAYS; do overlay_path="$PROJECT_DIR/$overlay" if [ ! -f "$overlay_path" ]; then echo -e "${YELLOW}警告: overlay 不存在,跳过: $overlay${NC}" continue fi overlay_args+=(--overlay "$overlay_path") done echo -e "${CYAN}[渲染模板配置]${NC}" echo " template: $DEPLOY_CONFIG_TEMPLATE" echo " profile: $DEPLOY_CONFIG_PROFILE" echo " overlays: ${DEPLOY_CONFIG_OVERLAYS:-}" echo " config_id: $DEPLOY_CONFIG_ID" echo " config_version: $DEPLOY_CONFIG_VERSION" python3 "$render_tool" \ --template "$template_path" \ --profile "$profile_path" \ "${overlay_args[@]}" \ --config-id "$DEPLOY_CONFIG_ID" \ --config-version "$DEPLOY_CONFIG_VERSION" \ --rendered-at "$rendered_at" \ --out "$out_file" cp "$out_file" "$INSTALL_DIR/etc/media-server.json" echo -e "${GREEN}✓${NC} 已渲染部署配置: $out_file" return 0 } # 选择配置文件 select_config() { echo -e "${CYAN}[选择配置文件]${NC}" if render_template_config; then return fi echo -e "${YELLOW}模板渲染不可用,回退到旧配置文件选择流程${NC}" # 查找所有可用的配置文件 CONFIGS=() for f in "$PROJECT_DIR/configs"/*.json; do if [ -f "$f" ]; then CONFIGS+=("$(basename "$f")") fi done echo "可用的配置文件:" for i in "${!CONFIGS[@]}"; do printf " %2d) %s\n" $((i+1)) "${CONFIGS[$i]}" done echo "" printf "请选择 [1-${#CONFIGS[@]},默认1]: " read choice choice=${choice:-1} if ! [[ "$choice" =~ ^[0-9]+$ ]] || [ "$choice" -lt 1 ] || [ "$choice" -gt "${#CONFIGS[@]}" ]; then choice=1 fi SELECTED_CONFIG="$PROJECT_DIR/configs/${CONFIGS[$((choice-1))]}" cp "$SELECTED_CONFIG" "$INSTALL_DIR/etc/media-server.json" echo -e "${GREEN}✓${NC} 使用配置: ${CONFIGS[$((choice-1))]}" } # 修复配置文件中的路径 fix_config_paths() { echo -e "${CYAN}[修复配置路径]${NC}" python3 << 'EOF' import json import os import re config_file = "/opt/rk3588-media-server/etc/media-server.json" install_dir = "/opt/rk3588-media-server" runtime_dir = "/var/lib/rk3588-media-server" project_dir = os.path.expanduser("~/apps/OrangePi3588Media") def fix_path(path): """修复配置文件中的路径""" if not path or not isinstance(path, str): return path # 模型路径 -> 安装目录的 models if 'third_party/rknpu2' in path or '.rknn' in path: return os.path.join(install_dir, 'models', os.path.basename(path)) # HLS 输出路径 -> 运行时目录的 hls if 'web/hls' in path or path.startswith('./web/hls'): return os.path.join(runtime_dir, 'hls', os.path.basename(os.path.dirname(path)), 'index.m3u8') # web 根目录 -> 安装目录的 web if path.startswith('./web/'): return os.path.join(install_dir, path[2:]) # models 目录 -> 安装目录的 models if path.startswith('./models/'): return os.path.join(install_dir, path[2:]) # 其他相对路径 -> 尝试解析 if path.startswith('./'): abs_path = os.path.join(project_dir, path) if os.path.exists(abs_path): return abs_path return os.path.join(install_dir, path[2:]) return path def process_node(node): """处理节点配置""" # 修复模型路径 if 'model_path' in node: node['model_path'] = fix_path(node['model_path']) # 修复图库路径 if 'gallery' in node and isinstance(node['gallery'], dict) and 'path' in node['gallery']: node['gallery']['path'] = fix_path(node['gallery']['path']) # 修复输出路径(HLS、报警图片等) if 'outputs' in node and isinstance(node['outputs'], list): for output in node['outputs']: if 'path' in output: output['path'] = fix_path(output['path']) # 修复 MinIO 端点(如果是本地) if 'minio_endpoint' in node: endpoint = node['minio_endpoint'] if '127.0.0.1' in endpoint or 'localhost' in endpoint: # 保持本地端点不变 pass return node try: with open(config_file, 'r', encoding='utf-8') as f: config = json.load(f) # 处理 graphs 格式 if 'graphs' in config: for graph in config['graphs']: if 'nodes' in graph: for node in graph['nodes']: process_node(node) # 处理 templates + instances 格式 if 'templates' in config and 'instances' in config: for template_name, template in config['templates'].items(): if 'nodes' in template: for node in template['nodes']: process_node(node) # 添加全局配置(如果不存在) if 'global' not in config: config['global'] = {} # 确保 web_root 指向正确位置 config['global']['web_root'] = os.path.join(install_dir, 'web') config['global']['metrics_port'] = config['global'].get('metrics_port', 9000) with open(config_file, 'w', encoding='utf-8') as f: json.dump(config, f, indent=2, ensure_ascii=False) print(f"配置路径修复完成") except Exception as e: print(f"配置处理出错: {e}") import traceback traceback.print_exc() EOF } # 安装 Agent cmd_install_agent() { echo -e "${BLUE}[2/5] 安装 Agent...${NC}" resolve_agent_token # 查找 Agent 编译产物(优先使用预编译的 arm64 二进制) AGENT_SOURCE="" if [ -f "$PROJECT_DIR/agent/rk3588-agent_linux_arm64" ]; then AGENT_SOURCE="$PROJECT_DIR/agent/rk3588-agent_linux_arm64" echo -e "${GREEN}✓${NC} 找到预编译 Agent: rk3588-agent_linux_arm64" elif [ -f "$PROJECT_DIR/agent/cmd/rk3588-agent/rk3588-agent" ]; then AGENT_SOURCE="$PROJECT_DIR/agent/cmd/rk3588-agent/rk3588-agent" echo -e "${GREEN}✓${NC} 找到源码编译 Agent: rk3588-agent" fi if [ -z "$AGENT_SOURCE" ]; then echo -e "${YELLOW}警告: 未找到编译好的 Agent,跳过安装${NC}" return fi mkdir -p "$AGENT_INSTALL_DIR" mkdir -p "/var/lib/rk3588-agent" cp "$AGENT_SOURCE" "$AGENT_INSTALL_DIR/rk3588-agent" chmod +x "$AGENT_INSTALL_DIR/rk3588-agent" printf '%s\n' "$AGENT_TOKEN" > "$AGENT_TOKEN_FILE" chmod 600 "$AGENT_TOKEN_FILE" # Agent 配置 cat > "$AGENT_INSTALL_DIR/agent.config.json" << EOF { "agent": { "listen": "0.0.0.0:9100", "token": "$AGENT_TOKEN", "require_token_for_read": false, "discovery_enable": true, "discovery_port": 35688, "device_name": "rk3588_$(hostname)", "device_id_path": "/var/lib/rk3588-agent/device_id", "models_dir": "$INSTALL_DIR/models", "max_upload_mb": 200, "config_path": "$INSTALL_DIR/etc/media-server.json", "media_server_process": { "enable": false, "configs_dir": "$INSTALL_DIR/etc" }, "media_server_base_url": "http://127.0.0.1:9000", "media_server_timeout_ms": 3000, "media_server_retry": { "max_attempts": 3, "backoff_ms": [200, 500] } } } EOF chown -R orangepi:orangepi "/var/lib/rk3588-agent" echo -e "${GREEN}✓${NC} Agent 安装完成,已写入后台管理统一主钥匙" } # 安装 systemd 服务 cmd_install_services() { echo -e "${BLUE}[3/5] 安装 Systemd 服务...${NC}" # Media Server 服务 cat > "$SERVICE_DIR/media-server.service" << EOF [Unit] Description=RK3588 Media Server After=network.target Wants=network.target [Service] Type=simple User=root Group=root WorkingDirectory=$INSTALL_DIR Environment="LD_LIBRARY_PATH=$INSTALL_DIR/lib:/usr/local/lib" Environment="RKNPU_LOG_LEVEL=2" ExecStartPre=/bin/bash -c 'mkdir -p $HLS_DIR $LOGS_DIR $ALARMS_DIR $CLIPS_DIR && chown orangepi:orangepi $LOGS_DIR $ALARMS_DIR $CLIPS_DIR && chmod 755 $LOGS_DIR $ALARMS_DIR $CLIPS_DIR' ExecStart=$INSTALL_DIR/bin/media-server --config $INSTALL_DIR/etc/media-server.json Restart=always RestartSec=5 StartLimitInterval=60s StartLimitBurst=3 StandardOutput=journal StandardError=journal SyslogIdentifier=media-server # 资源限制 LimitNOFILE=65536 LimitNPROC=4096 # 优雅关闭 TimeoutStopSec=30 KillSignal=SIGTERM [Install] WantedBy=multi-user.target EOF # Agent 服务 cat > "$SERVICE_DIR/rk3588-agent.service" << EOF [Unit] Description=RK3588 Agent Service After=network.target media-server.service Wants=media-server.service [Service] Type=simple User=root Group=root WorkingDirectory=$AGENT_INSTALL_DIR ExecStart=$AGENT_INSTALL_DIR/rk3588-agent --config $AGENT_INSTALL_DIR/agent.config.json Restart=always RestartSec=5 StandardOutput=journal StandardError=journal SyslogIdentifier=rk3588-agent [Install] WantedBy=multi-user.target EOF systemctl daemon-reload echo -e "${GREEN}✓${NC} Systemd 服务安装完成" } # 主安装流程 cmd_install() { echo -e "${BLUE}╔════════════════════════════════════════════════════════════╗${NC}" echo -e "${BLUE}║ RK3588 Media Server 完善部署脚本 ║${NC}" echo -e "${BLUE}╚════════════════════════════════════════════════════════════╝${NC}" echo "" # 检查 root 权限 if [ "$(id -u)" -ne 0 ]; then echo -e "${RED}错误: 请使用 sudo 运行此脚本${NC}" exit 1 fi echo "项目目录: $PROJECT_DIR" echo "安装目录: $INSTALL_DIR" echo "运行时目录: $RUNTIME_DIR" echo "" # 执行安装步骤 cmd_install_media_server cmd_install_agent create_runtime_dirs cmd_install_services install_logrotate install_hls_cleanup # 启动服务 echo -e "${BLUE}[启动服务]${NC}" systemctl enable media-server systemctl start media-server systemctl enable rk3588-agent systemctl start rk3588-agent echo "" echo -e "${GREEN}╔════════════════════════════════════════════════════════════╗${NC}" echo -e "${GREEN}║ 部署完成! ║${NC}" echo -e "${GREEN}╚════════════════════════════════════════════════════════════╝${NC}" echo "" echo "目录结构:" echo " 程序: $INSTALL_DIR/" echo " 数据: $RUNTIME_DIR/" echo " 配置: $INSTALL_DIR/etc/media-server.json" echo "" echo "服务管理:" echo " 状态: sudo systemctl status media-server" echo " 日志: sudo journalctl -u media-server -f" echo " 界面: http://$(hostname -I | awk '{print $1}'):9100" echo "" echo "HLS播放:" echo " http://$(hostname -I | awk '{print $1}'):9000/hls_player.html" } # 升级 cmd_upgrade() { echo -e "${YELLOW}========== 升级 Media Server ==========${NC}" if [ "$(id -u)" -ne 0 ]; then echo -e "${RED}错误: 请使用 sudo 运行${NC}" exit 1 fi # 备份配置 BACKUP_DIR="/root/rk3588-backup-$(date +%Y%m%d%H%M%S)" mkdir -p "$BACKUP_DIR" [ -f "$INSTALL_DIR/etc/media-server.json" ] && cp "$INSTALL_DIR/etc/media-server.json" "$BACKUP_DIR/" echo "配置已备份到: $BACKUP_DIR" # 停止服务 echo "停止服务..." systemctl stop media-server rk3588-agent # 重新安装 cmd_install echo -e "${GREEN}升级完成${NC}" } # 查看状态 cmd_status() { echo -e "${BLUE}========== RK3588 Media Server 状态 ==========${NC}" echo "" # 系统信息 echo -e "${CYAN}[系统信息]${NC}" echo " 时间: $(date '+%Y-%m-%d %H:%M:%S')" echo " 运行时间: $(uptime -p 2>/dev/null || uptime | awk -F',' '{print $1}')" echo "" # NPU echo -e "${CYAN}[NPU 负载]${NC}" if [ -f /proc/rknpu/load ]; then cat /proc/rknpu/load | sed 's/^/ /' else echo " NPU 信息不可用" fi echo "" # Media Server echo -e "${CYAN}[Media Server]${NC}" if systemctl is-active --quiet media-server 2>/dev/null; then echo -e " 状态: ${GREEN}● 运行中${NC}" PID=$(systemctl show --property=MainPID --value media-server 2>/dev/null) echo " PID: $PID" if [ -n "$PID" ] && [ "$PID" != "0" ]; then CPU_MEM=$(ps -p $PID -o %cpu,%mem --no-headers 2>/dev/null || echo "N/A") echo " CPU/MEM: $CPU_MEM" fi echo " 端口:" ss -tlnp 2>/dev/null | grep -E "(9000|8555)" | head -3 | sed 's/^/ /' else echo -e " 状态: ${RED}○ 停止${NC}" fi echo "" # HLS 输出 echo -e "${CYAN}[HLS 输出]${NC}" if [ -d "$HLS_DIR" ]; then CHANNELS=$(find "$HLS_DIR" -maxdepth 1 -type d | wc -l) CHANNELS=$((CHANNELS - 1)) echo " 通道数: $CHANNELS" if [ $CHANNELS -gt 0 ]; then echo " 分片统计:" for d in "$HLS_DIR"/*/; do [ -d "$d" ] || continue name=$(basename "$d") count=$(find "$d" -name "*.ts" | wc -l) size=$(du -sh "$d" 2>/dev/null | cut -f1) echo " $name: ${count}个分片, ${size}" done fi else echo " HLS 目录不存在" fi echo "" # Agent echo -e "${CYAN}[RK3588 Agent]${NC}" if systemctl is-active --quiet rk3588-agent 2>/dev/null; then echo -e " 状态: ${GREEN}● 运行中${NC}" echo " 管理界面: http://$(hostname -I | awk '{print $1}'):9100" else echo -e " 状态: ${RED}○ 停止${NC}" fi echo "" # 存储使用 echo -e "${CYAN}[存储使用]${NC}" echo " HLS 输出: $(du -sh "$HLS_DIR" 2>/dev/null | cut -f1)" echo " 日志: $(du -sh "$LOGS_DIR" 2>/dev/null | cut -f1)" echo " 告警图片: $(du -sh "$ALARMS_DIR" 2>/dev/null | cut -f1)" echo " 告警视频: $(du -sh "$CLIPS_DIR" 2>/dev/null | cut -f1)" echo "" echo "常用命令:" echo " 查看日志: sudo journalctl -u media-server -f" echo " HLS清理: sudo $INSTALL_DIR/bin/cleanup-hls.sh" echo " 实时监控: watch -n 1 cat /proc/rknpu/load" } # 查看日志 cmd_logs() { SERVICE="${2:-media-server}" echo -e "${BLUE}查看 $SERVICE 日志 (按 Ctrl+C 退出)...${NC}" journalctl -u "$SERVICE" -f } # 清理 HLS cmd_clean_hls() { echo -e "${YELLOW}清理 HLS 旧分片...${NC}" KEEP_DAYS="${2:-3}" if [ -f "$INSTALL_DIR/bin/cleanup-hls.sh" ]; then "$INSTALL_DIR/bin/cleanup-hls.sh" "$KEEP_DAYS" else echo "清理脚本不存在" exit 1 fi } # 卸载 cmd_uninstall() { echo -e "${YELLOW}========== 卸载 RK3588 Media Server ==========${NC}" if [ "$(id -u)" -ne 0 ]; then echo -e "${RED}错误: 请使用 sudo 运行${NC}" exit 1 fi read -p "确定要卸载? (y/N): " -r [ "$REPLY" != "y" ] && [ "$REPLY" != "Y" ] && echo "已取消" && exit 0 echo "[1/4] 停止服务..." systemctl stop media-server rk3588-agent 2>/dev/null || true systemctl disable media-server rk3588-agent 2>/dev/null || true echo "[2/4] 删除服务文件..." rm -f "$SERVICE_DIR/media-server.service" rm -f "$SERVICE_DIR/rk3588-agent.service" rm -f "$LOGROTATE_DIR/rk3588-media-server" rm -f "$LOGROTATE_DIR/rk3588-hls" rm -f "/etc/cron.d/rk3588-media-server" systemctl daemon-reload echo "[3/4] 备份数据..." BACKUP_DIR="/root/rk3588-backup-$(date +%Y%m%d%H%M%S)" mkdir -p "$BACKUP_DIR" [ -f "$INSTALL_DIR/etc/media-server.json" ] && cp "$INSTALL_DIR/etc/media-server.json" "$BACKUP_DIR/" [ -d "$HLS_DIR" ] && cp -r "$HLS_DIR" "$BACKUP_DIR/" 2>/dev/null || true echo "数据已备份到: $BACKUP_DIR" echo "[4/4] 删除文件..." read -p "是否删除所有数据 (包括 HLS/日志/告警)? (y/N): " -r if [ "$REPLY" = "y" ] || [ "$REPLY" = "Y" ]; then rm -rf "$INSTALL_DIR" "$AGENT_INSTALL_DIR" "$RUNTIME_DIR" echo "所有数据已删除" else rm -rf "$INSTALL_DIR" "$AGENT_INSTALL_DIR" echo "程序已删除,数据保留在: $RUNTIME_DIR" fi echo -e "${GREEN}卸载完成${NC}" } # 主入口 case "${1:-install}" in install) cmd_install ;; upgrade) cmd_upgrade ;; status) cmd_status ;; logs) cmd_logs "$@" ;; clean-hls) cmd_clean_hls "$@" ;; uninstall) cmd_uninstall ;; *) echo "RK3588 Media Server 部署脚本" echo "" echo "用法: sudo ./deploy.sh [命令]" echo "" echo "命令:" echo " install 安装/部署服务 (默认)" echo " upgrade 升级服务 (保留配置和数据)" echo " status 查看运行状态" echo " logs 查看日志" echo " clean-hls 清理 HLS 旧分片" echo " uninstall 卸载服务" echo "" echo "示例:" echo " sudo AGENT_TOKEN= ./deploy.sh install" echo " sudo ./deploy.sh upgrade # 沿用设备保存的后台管理统一主钥匙" echo " sudo ./deploy.sh status" echo " sudo ./deploy.sh clean-hls 7 # 保留最近7天" exit 1 ;; esac