修改部署脚本,media-server和agent独立部署

This commit is contained in:
haotian 2026-02-25 15:23:58 +08:00
parent c343b2bc91
commit 317a8b1097
14 changed files with 273 additions and 79 deletions

View File

@ -94,6 +94,17 @@ func main() {
}
}()
// 自动启动 media-server如果启用
if cfg.Agent.MediaServerProcess.Enable {
time.Sleep(500 * time.Millisecond) // 等待 HTTP 服务启动
log.Info("auto-starting media-server...")
if err := h.StartMediaServer(""); err != nil {
log.Error("auto-start media-server failed: " + err.Error())
} else {
log.Info("media-server auto-started")
}
}
if cfg.Agent.DiscoveryEnable {
wg.Add(1)
go func() {

View File

@ -32,7 +32,7 @@ type Server struct {
agentCfg config.AgentConfig
ms *mediaserver.Client
store *modelstore.Store
proc *procctl.Controller
proc procctl.ProcessController
audit *audit.Recorder
tasks *tasks.Registry
baseDir string
@ -46,6 +46,12 @@ type Server struct {
cpuMu sync.Mutex
lastCPU metrics.CPUStat
lastCPUTS time.Time
handler http.Handler
}
// ServeHTTP 实现 http.Handler 接口
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
s.handler.ServeHTTP(w, r)
}
type InfoResponse struct {
@ -62,10 +68,14 @@ type InfoResponse struct {
UptimeSec int64 `json:"uptime_sec"`
}
func New(agentCfg config.AgentConfig, baseDir string, ms *mediaserver.Client, store *modelstore.Store, deviceID string, agentPort int, mediaPort int, version, gitSHA string) http.Handler {
var pc *procctl.Controller
func New(agentCfg config.AgentConfig, baseDir string, ms *mediaserver.Client, store *modelstore.Store, deviceID string, agentPort int, mediaPort int, version, gitSHA string) *Server {
var pc procctl.ProcessController
if agentCfg.MediaServerProcess.Enable {
// 使用传统的 fork/exec 模式
pc = procctl.New(agentCfg, baseDir)
} else {
// 使用 systemctl 模式管理 Media Server
pc = procctl.NewSystemCtlController("media-server")
}
execPath, _ := os.Executable()
if execPath != "" {
@ -119,7 +129,8 @@ func New(agentCfg config.AgentConfig, baseDir string, ms *mediaserver.Client, st
mux.HandleFunc("/v1/assets", s.handleAssets)
mux.HandleFunc("/v1/tasks/", s.handleTask)
return mux
s.handler = mux
return s
}
func (s *Server) handleInfo(w http.ResponseWriter, r *http.Request) {
@ -436,6 +447,15 @@ type mediaProcReq struct {
Config string `json:"config"`
}
// StartMediaServer 启动 media-server供外部调用
func (s *Server) StartMediaServer(configName string) error {
if s.proc == nil || !s.proc.Enabled() {
return errors.New("media server process management not enabled")
}
_, err := s.proc.Start(configName)
return err
}
func (s *Server) handleMediaStart(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
errorJSON(w, http.StatusMethodNotAllowed, "method not allowed")

View File

@ -23,6 +23,19 @@ var ErrConflict = errors.New("conflict")
var ErrInvalidConfig = errors.New("invalid config")
var ErrConfigNotFound = errors.New("config not found")
// ProcessController 定义进程控制器接口
type ProcessController interface {
Enabled() bool
Status() (Status, error)
Version() (string, error)
Start(configName string) (Status, error)
Stop() (Status, error)
Restart(configName string) (Status, error)
BinaryInfo() (BinaryUpdateResult, error)
UpdateBinary(r io.Reader, contentLength int64, expectedSha256 string) (BinaryUpdateResult, error)
RollbackBinary(backupPath string) (BinaryUpdateResult, error)
}
type Status struct {
Running bool `json:"running"`
Pid int `json:"pid"`

View File

@ -0,0 +1,109 @@
package procctl
import (
"bytes"
"fmt"
"io"
"os/exec"
"strconv"
"strings"
"time"
)
// SystemCtlController 使用 systemctl 管理 Media Server
type SystemCtlController struct {
serviceName string
}
// NewSystemCtlController 创建 systemctl 控制器
func NewSystemCtlController(serviceName string) *SystemCtlController {
return &SystemCtlController{
serviceName: serviceName,
}
}
func (s *SystemCtlController) Enabled() bool { return s != nil }
func (s *SystemCtlController) Status() (Status, error) {
// 检查服务是否运行
cmd := exec.Command("systemctl", "is-active", "--quiet", s.serviceName)
err := cmd.Run()
if err != nil {
// 服务未运行
return Status{Running: false}, nil
}
// 获取 PID
pidCmd := exec.Command("systemctl", "show", "--property=MainPID", "--value", s.serviceName)
out, err := pidCmd.Output()
if err != nil {
return Status{Running: true}, nil // 运行但无法获取 PID
}
pid, _ := strconv.Atoi(strings.TrimSpace(string(out)))
if pid <= 0 {
return Status{Running: true}, nil
}
return Status{
Running: true,
Pid: pid,
ConfigPath: "/opt/rk3588-media-server/etc/media-server.json",
}, nil
}
func (s *SystemCtlController) Version() (string, error) {
// 直接执行 media-server --version
cmd := exec.Command("/opt/rk3588-media-server/bin/media-server", "--version")
out, err := cmd.Output()
if err != nil {
return "", fmt.Errorf("get version failed: %w", err)
}
return strings.TrimSpace(string(out)), nil
}
func (s *SystemCtlController) Start(configName string) (Status, error) {
// 使用 systemctl start
cmd := exec.Command("systemctl", "start", s.serviceName)
var stderr bytes.Buffer
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
return Status{}, fmt.Errorf("start failed: %w, stderr: %s", err, stderr.String())
}
// 等待服务启动
time.Sleep(500 * time.Millisecond)
return s.Status()
}
func (s *SystemCtlController) Stop() (Status, error) {
cmd := exec.Command("systemctl", "stop", s.serviceName)
if err := cmd.Run(); err != nil {
return Status{}, fmt.Errorf("stop failed: %w", err)
}
return Status{Running: false}, nil
}
func (s *SystemCtlController) Restart(configName string) (Status, error) {
cmd := exec.Command("systemctl", "restart", s.serviceName)
if err := cmd.Run(); err != nil {
return Status{}, fmt.Errorf("restart failed: %w", err)
}
time.Sleep(500 * time.Millisecond)
return s.Status()
}
// 二进制更新相关功能在 systemctl 模式下不支持
func (s *SystemCtlController) BinaryInfo() (BinaryUpdateResult, error) {
return BinaryUpdateResult{}, ErrNotSupported
}
func (s *SystemCtlController) UpdateBinary(r io.Reader, contentLength int64, expectedSha256 string) (BinaryUpdateResult, error) {
return BinaryUpdateResult{}, ErrNotSupported
}
func (s *SystemCtlController) RollbackBinary(backupPath string) (BinaryUpdateResult, error) {
return BinaryUpdateResult{}, ErrNotSupported
}

BIN
agent/rk3588-agent_linux_arm64 Normal file → Executable file

Binary file not shown.

View File

@ -159,14 +159,14 @@
"template": "security_pipeline",
"params": {
"name": "cam1",
"url": "rtsp://10.0.0.5:8554/cam",
"url": "rtsp://10.0.0.49:8554/cam",
"src_w": 1280,
"src_h": 720,
"fps": 30,
"gop": 60,
"bitrate_kbps": 2000,
"model_path": "./third_party/rknpu2/examples/rknn_yolov5_demo/model/RK3588/yolov5s-640-640.rknn",
"minio_endpoint": "http://10.0.0.5:9000",
"minio_endpoint": "http://10.0.0.49:9000",
"minio_bucket": "test",
"minio_ak": "minioadmin",
"minio_sk": "minioadmin"

View File

@ -9,7 +9,7 @@
"type": "input_rtsp",
"role": "source",
"enable": true,
"url": "rtsp://10.0.0.5:8554/cam",
"url": "rtsp://10.0.0.49:8554/cam",
"fps": 30,
"width": 1280,
"height": 720,
@ -194,7 +194,7 @@
"quality": 85,
"upload": {
"type": "minio",
"endpoint": "http://10.0.0.5:9000",
"endpoint": "http://10.0.0.49:9000",
"bucket": "vi",
"region": "us-east-1",
"access_key": "admin",
@ -209,7 +209,7 @@
"fps": 30,
"upload": {
"type": "minio",
"endpoint": "http://10.0.0.5:9000",
"endpoint": "http://10.0.0.49:9000",
"bucket": "vi",
"region": "us-east-1",
"access_key": "admin",
@ -257,7 +257,7 @@
"quality": 85,
"upload": {
"type": "minio",
"endpoint": "http://10.0.0.5:9000",
"endpoint": "http://10.0.0.49:9000",
"bucket": "vi",
"region": "us-east-1",
"access_key": "admin",

View File

@ -9,7 +9,7 @@
"type": "input_rtsp",
"role": "source",
"enable": true,
"url": "rtsp://10.0.0.5:8554/cam",
"url": "rtsp://10.0.0.49:8554/cam",
"fps": 30,
"width": 1280,
"height": 720,
@ -130,7 +130,7 @@
"quality": 85,
"upload": {
"type": "minio",
"endpoint": "http://10.0.0.5:9000",
"endpoint": "http://10.0.0.49:9000",
"bucket": "test",
"region": "us-east-1",
"access_key": "your-access-key",
@ -145,7 +145,7 @@
"fps": 30,
"upload": {
"type": "minio",
"endpoint": "http://10.0.0.5:9000",
"endpoint": "http://10.0.0.49:9000",
"bucket": "test",
"region": "us-east-1",
"access_key": "your-access-key",
@ -180,7 +180,7 @@
"type": "input_rtsp",
"role": "source",
"enable": true,
"url": "rtsp://10.0.0.5:8554/cam",
"url": "rtsp://10.0.0.49:8554/cam",
"fps": 30,
"width": 1280,
"height": 720,

View File

@ -171,14 +171,14 @@
"template": "strict_minio_alarm_pipeline",
"params": {
"name": "cam1",
"url": "rtsp://10.0.0.5:8554/cam",
"url": "rtsp://10.0.0.49:8554/cam",
"src_w": 1280,
"src_h": 720,
"fps": 30,
"gop": 60,
"bitrate_kbps": 2000,
"model_path": "./third_party/rknpu2/examples/rknn_yolov5_demo/model/RK3588/yolov5s-640-640.rknn",
"minio_endpoint": "http://10.0.0.5:9000",
"minio_endpoint": "http://10.0.0.49:9000",
"minio_bucket": "test",
"minio_ak": "your-access-key",
"minio_sk": "your-secret-key"
@ -189,14 +189,14 @@
"template": "strict_minio_alarm_pipeline",
"params": {
"name": "cam2",
"url": "rtsp://10.0.0.5:8554/cam",
"url": "rtsp://10.0.0.49:8554/cam",
"src_w": 1280,
"src_h": 720,
"fps": 30,
"gop": 60,
"bitrate_kbps": 2000,
"model_path": "./third_party/rknpu2/examples/rknn_yolov5_demo/model/RK3588/yolov5s-640-640.rknn",
"minio_endpoint": "http://10.0.0.5:9000",
"minio_endpoint": "http://10.0.0.49:9000",
"minio_bucket": "test",
"minio_ak": "your-access-key",
"minio_sk": "your-secret-key"
@ -207,14 +207,14 @@
"template": "strict_minio_alarm_pipeline",
"params": {
"name": "cam3",
"url": "rtsp://10.0.0.5:8554/cam",
"url": "rtsp://10.0.0.49:8554/cam",
"src_w": 1280,
"src_h": 720,
"fps": 30,
"gop": 60,
"bitrate_kbps": 2000,
"model_path": "./third_party/rknpu2/examples/rknn_yolov5_demo/model/RK3588/yolov5s-640-640.rknn",
"minio_endpoint": "http://10.0.0.5:9000",
"minio_endpoint": "http://10.0.0.49:9000",
"minio_bucket": "test",
"minio_ak": "your-access-key",
"minio_sk": "your-secret-key"
@ -225,14 +225,14 @@
"template": "strict_minio_alarm_pipeline",
"params": {
"name": "cam4",
"url": "rtsp://10.0.0.5:8554/cam",
"url": "rtsp://10.0.0.49:8554/cam",
"src_w": 1280,
"src_h": 720,
"fps": 30,
"gop": 60,
"bitrate_kbps": 2000,
"model_path": "./third_party/rknpu2/examples/rknn_yolov5_demo/model/RK3588/yolov5s-640-640.rknn",
"minio_endpoint": "http://10.0.0.5:9000",
"minio_endpoint": "http://10.0.0.49:9000",
"minio_bucket": "test",
"minio_ak": "your-access-key",
"minio_sk": "your-secret-key"
@ -243,14 +243,14 @@
"template": "strict_minio_alarm_pipeline",
"params": {
"name": "cam5",
"url": "rtsp://10.0.0.5:8554/cam",
"url": "rtsp://10.0.0.49:8554/cam",
"src_w": 1280,
"src_h": 720,
"fps": 30,
"gop": 60,
"bitrate_kbps": 2000,
"model_path": "./third_party/rknpu2/examples/rknn_yolov5_demo/model/RK3588/yolov5s-640-640.rknn",
"minio_endpoint": "http://10.0.0.5:9000",
"minio_endpoint": "http://10.0.0.49:9000",
"minio_bucket": "test",
"minio_ak": "your-access-key",
"minio_sk": "your-secret-key"

View File

@ -9,7 +9,7 @@
"type": "input_rtsp",
"role": "source",
"enable": true,
"url": "rtsp://10.0.0.5:8554/cam",
"url": "rtsp://10.0.0.49:8554/cam",
"fps": 30,
"width": 1280,
"height": 720,

View File

@ -9,7 +9,7 @@
"type": "input_rtsp",
"role": "source",
"enable": true,
"url": "rtsp://10.0.0.5:8554/cam",
"url": "rtsp://10.0.0.49:8554/cam",
"fps": 30,
"width": 1280,
"height": 720,

View File

@ -9,7 +9,7 @@
"type": "input_rtsp",
"role": "source",
"enable": true,
"url": "rtsp://10.0.0.5:8554/cam",
"url": "rtsp://10.0.0.49:8554/cam",
"fps": 30,
"width": 1280,
"height": 720,
@ -92,7 +92,7 @@
"quality": 85,
"upload": {
"type": "minio",
"endpoint": "http://10.0.0.5:9000",
"endpoint": "http://10.0.0.49:9000",
"bucket": "test",
"region": "us-east-1",
"access_key": "minioadmin",
@ -107,7 +107,7 @@
"fps": 25,
"upload": {
"type": "minio",
"endpoint": "http://10.0.0.5:9000",
"endpoint": "http://10.0.0.49:9000",
"bucket": "test",
"region": "us-east-1",
"access_key": "minioadmin",

View File

@ -18,4 +18,7 @@ ffplay rtsp://localhost:8554/cam
ffplay rtsp://localhost:8555/live/cam1_face_det
- 在 Windows 上用 VLC 播放处理后的流:
rtsp://3588的IP:8555/live/cam1_face_det
rtsp://3588的IP:8555/live/cam1_face_det
- 编译agent
go build -o rk3588-agent_linux_arm64 ./cmd/rk3588-agent

View File

@ -1,5 +1,6 @@
#!/bin/bash
# RK3588 Media Server + Agent 统一管理脚本
# Media Server 和 Agent 是独立的 systemd 服务
# 用法: sudo ./manage.sh [install|status|uninstall]
set -e
@ -25,7 +26,7 @@ cmd_install() {
echo -e "${BLUE}╚════════════════════════════════════════════════════════════╝${NC}"
echo ""
if [ "$EUID" -ne 0 ]; then
if [ "$(id -u)" -ne 0 ]; then
echo -e "${RED}错误: 请使用 sudo 运行此脚本${NC}"
exit 1
fi
@ -48,24 +49,23 @@ cmd_install() {
# 检查插件
if [ ! -d "$BUILD_DIR/plugins" ] || [ -z "$(ls -A $BUILD_DIR/plugins/*.so 2>/dev/null)" ]; then
echo -e "${YELLOW}警告: 未找到插件目录或插件为空${NC}"
read -p "是否继续? (y/N): " -n 1 -r
echo
[[ ! $REPLY =~ ^[Yy]$ ]] && exit 1
read -p "是否继续? (y/N): " -r
[ "$REPLY" != "y" ] && [ "$REPLY" != "Y" ] && exit 1
fi
# 检查 Agent 编译产物(强制要求)
AGENT_SOURCE=""
if [ -f "$PROJECT_DIR/agent/rk3588-agent_linux_arm64" ]; then
AGENT_SOURCE="$PROJECT_DIR/agent/rk3588-agent_linux_arm64"
elif [ -f "$PROJECT_DIR/agent/cmd/rk3588-agent" ]; then
AGENT_SOURCE="$PROJECT_DIR/agent/cmd/rk3588-agent"
elif [ -f "$PROJECT_DIR/agent/cmd/rk3588-agent/rk3588-agent" ]; then
AGENT_SOURCE="$PROJECT_DIR/agent/cmd/rk3588-agent/rk3588-agent"
fi
if [ -z "$AGENT_SOURCE" ]; then
echo -e "${RED}错误: 未找到编译好的 rk3588-agent${NC}"
echo "请先编译 Agent:"
echo " cd $PROJECT_DIR/agent"
echo " GOOS=linux GOARCH=arm64 go build -o rk3588-agent_linux_arm64 ./cmd"
echo " go build -o rk3588-agent_linux_arm64 ./cmd/rk3588-agent"
exit 1
fi
@ -79,7 +79,8 @@ cmd_install() {
echo " 3) test_cam1_face_det_recog_rtsp_server.json (人脸识别)"
echo " 4) sample_cam1.json (完整功能,需手动修复)"
echo ""
read -p "请输入选项 [1-4默认1]: " choice
printf "请输入选项 [1-4默认1]: "
read choice
choice=${choice:-1}
case $choice in
@ -95,7 +96,12 @@ cmd_install() {
# 安装 Media Server
echo -e "${YELLOW}[1/3] 安装 Media Server...${NC}"
rm -rf "$INSTALL_DIR"
mkdir -p "$INSTALL_DIR"/{bin/plugins,lib,etc,logs,models,web/hls}
mkdir -p "$INSTALL_DIR/bin/plugins"
mkdir -p "$INSTALL_DIR/lib"
mkdir -p "$INSTALL_DIR/etc"
mkdir -p "$INSTALL_DIR/logs"
mkdir -p "$INSTALL_DIR/models"
mkdir -p "$INSTALL_DIR/web/hls"
cp "$BUILD_DIR/media-server" "$INSTALL_DIR/bin/"
chmod +x "$INSTALL_DIR/bin/media-server"
@ -127,6 +133,8 @@ project_dir = os.path.expanduser("~/apps/OrangePi3588Media")
def fix_path(path):
if not path or not isinstance(path, str):
return path
if 'third_party/rknpu2' in path:
return os.path.join(install_dir, 'models', os.path.basename(path))
if path.startswith('./'):
if path.startswith('./web/'):
return os.path.join(install_dir, path[2:])
@ -179,6 +187,7 @@ EOF
cp "$AGENT_SOURCE" "$AGENT_INSTALL_DIR/rk3588-agent"
chmod +x "$AGENT_INSTALL_DIR/rk3588-agent"
# Agent 配置 - 禁用进程管理,改为监控模式
cat > "$AGENT_INSTALL_DIR/agent.config.json" << 'AGENT_CONFIG'
{
"agent": {
@ -193,11 +202,11 @@ EOF
"max_upload_mb": 200,
"config_path": "/opt/rk3588-media-server/etc/media-server.json",
"media_server_process": {
"enable": true,
"exec_path": "/opt/rk3588-media-server/bin/media-server",
"work_dir": "/opt/rk3588-media-server",
"configs_dir": "/opt/rk3588-media-server/etc",
"pid_file": "/var/run/rk3588sys-media-server.pid",
"enable": false,
"exec_path": "",
"work_dir": "",
"configs_dir": "",
"pid_file": "",
"graceful_timeout_ms": 5000
},
"media_server_base_url": "http://127.0.0.1:9000",
@ -213,10 +222,32 @@ AGENT_CONFIG
# 安装服务
echo -e "${YELLOW}[3/3] 安装 Systemd 服务...${NC}"
# Media Server 独立服务
cat > "$SERVICE_DIR/media-server.service" << 'EOF'
[Unit]
Description=RK3588 Media Server
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=/opt/rk3588-media-server
ExecStart=/opt/rk3588-media-server/bin/media-server --config /opt/rk3588-media-server/etc/media-server.json
Restart=always
RestartSec=5
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
EOF
# Agent 独立服务
cat > "$SERVICE_DIR/rk3588-agent.service" << 'EOF'
[Unit]
Description=RK3588 Agent Service
After=network.target
After=network.target media-server.service
Wants=media-server.service
[Service]
Type=simple
@ -233,10 +264,11 @@ WantedBy=multi-user.target
EOF
systemctl daemon-reload
touch /var/run/rk3588sys-media-server.pid
chmod 666 /var/run/rk3588sys-media-server.pid
# 启动 AgentAgent 会自动管理 Media Server
# 启动服务(独立运行)
systemctl enable media-server
systemctl start media-server
systemctl enable rk3588-agent
systemctl start rk3588-agent
@ -248,9 +280,13 @@ EOF
echo -e "${GREEN}║ 安装完成! ║${NC}"
echo -e "${GREEN}╚════════════════════════════════════════════════════════════╝${NC}"
echo ""
echo "架构说明:"
echo " Media Server: 独立 systemd 服务(高可用,崩溃自动重启)"
echo " Agent: 独立 systemd 服务监控状态Web 管理)"
echo ""
echo "管理命令:"
echo " 状态: sudo ./manage.sh status"
echo " 日志: sudo journalctl -u rk3588-agent -f"
echo " 日志: sudo journalctl -u media-server -f"
echo " 界面: http://<设备IP>:9100"
}
@ -271,6 +307,24 @@ cmd_status() {
fi
echo ""
echo -e "${YELLOW}[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|8554|8555)" | head -3 | sed 's/^/ /'
else
echo -e " 状态: ${RED}○ 停止${NC}"
echo " 错误日志:"
journalctl -u media-server --no-pager -n 3 2>/dev/null | sed 's/^/ /'
fi
echo ""
echo -e "${YELLOW}[RK3588 Agent]${NC}"
if systemctl is-active --quiet rk3588-agent 2>/dev/null; then
echo -e " 状态: ${GREEN}● 运行中${NC}"
@ -285,51 +339,36 @@ cmd_status() {
fi
echo ""
echo -e "${YELLOW}[Media Server]${NC}"
# 检查 Media Server 是否由 Agent 启动
AGENT_PID=$(systemctl show --property=MainPID --value rk3588-agent 2>/dev/null)
MS_PID=$(pgrep -f "media-server" | head -1)
if [ -n "$MS_PID" ]; then
echo -e " 状态: ${GREEN}● 运行中${NC}"
echo " PID: $MS_PID"
CPU_MEM=$(ps -p $MS_PID -o %cpu,%mem --no-headers 2>/dev/null || echo "N/A")
echo " CPU/MEM: $CPU_MEM"
echo " 端口:"
ss -tlnp 2>/dev/null | grep -E "(9000|8554|8555)" | head -3 | sed 's/^/ /'
else
echo -e " 状态: ${RED}○ 停止${NC}"
fi
echo ""
echo -e "${YELLOW}[最近日志]${NC}"
journalctl -u rk3588-agent --no-pager -n 5 2>/dev/null | tail -5 || echo " 无日志"
echo -e "${YELLOW}[最近日志 - Media Server]${NC}"
journalctl -u media-server --no-pager -n 5 2>/dev/null | tail -5 || echo " 无日志"
echo ""
echo "常用命令:"
echo " 重启: sudo systemctl restart rk3588-agent"
echo " 日志: sudo journalctl -u rk3588-agent -f"
echo " 配置: sudo nano /opt/rk3588-media-server/etc/media-server.json"
echo " 启动: sudo systemctl start media-server"
echo " 停止: sudo systemctl stop media-server"
echo " 重启: sudo systemctl restart media-server"
echo " 配置: sudo nano /opt/rk3588-media-server/etc/media-server.json"
}
cmd_uninstall() {
echo -e "${YELLOW}========== RK3588 Media Server 卸载 ==========${NC}"
echo ""
if [ "$EUID" -ne 0 ]; then
if [ "$(id -u)" -ne 0 ]; then
echo -e "${RED}错误: 请使用 sudo 运行${NC}"
exit 1
fi
read -p "确定要卸载并停止所有服务? (y/N): " -n 1 -r
echo
[[ ! $REPLY =~ ^[Yy]$ ]] && echo "已取消" && exit 0
read -p "确定要卸载并停止所有服务? (y/N): " -r
[ "$REPLY" != "y" ] && [ "$REPLY" != "Y" ] && echo "已取消" && exit 0
echo "[1/3] 停止服务..."
systemctl stop rk3588-agent 2>/dev/null || true
systemctl disable rk3588-agent 2>/dev/null || true
systemctl stop media-server rk3588-agent 2>/dev/null || true
systemctl disable media-server rk3588-agent 2>/dev/null || true
echo -e "${GREEN}${NC} 服务已停止"
echo "[2/3] 删除服务文件..."
rm -f "$SERVICE_DIR/media-server.service"
rm -f "$SERVICE_DIR/rk3588-agent.service"
systemctl daemon-reload
echo -e "${GREEN}${NC} 服务文件已删除"
@ -341,9 +380,8 @@ cmd_uninstall() {
[ -d "$INSTALL_DIR/models" ] && cp -r "$INSTALL_DIR/models" "$BACKUP_DIR/" 2>/dev/null || true
echo -e "${GREEN}${NC} 配置已备份到: $BACKUP_DIR"
read -p "是否删除安装目录? (y/N): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
read -p "是否删除安装目录? (y/N): " -r
if [ "$REPLY" = "y" ] || [ "$REPLY" = "Y" ]; then
rm -rf "$INSTALL_DIR" "$AGENT_INSTALL_DIR"
echo -e "${GREEN}${NC} 目录已删除"
else