随机
Enter 搜索 ↑↓ 切换 Esc 清空

compose_install_nastools

脚本

Docker Compose 部署 NASTools 媒体管理套件

compose_install_nastools

Docker Compose 部署 NASTools 媒体管理套件

一键脚本

bash <(curl -sL gitee.com/meimolihan/linux-command_sh/raw/master/compose_install_nastools.sh) 3000 /vol1/1000/compose/nastools
传参方式 命令示例 说明
不传参(交互式) 脚本.sh 正常进入交互式流程
先目录,后端口 脚本.sh /vol1/1000/compose/nastools 3000 同时传入目录和端口
先端口,后目录 脚本.sh 8080 /vol1/1000/compose/nastools 同时传入端口和目录
只传目录 脚本.sh /vol1/1000/compose/nastools 仅传入目录参数
只传端口 脚本.sh 3000 仅传入端口参数

项目简介

NASTools 是一个完整的媒体管理解决方案,集成了多个工具来简化和自动化媒体文件的下载、整理、字幕获取和元数据刮削。包含 ChineseSubFinder、Jackett、NASTools 等核心组件,提供一站式的媒体管理体验。

默认登录信息

服务 用户名 密码
NASTools admin password
ChineseSubFinder admin admin

效果预览

执行脚本效果预览

WEB效果预览

WEB效果预览

WEB效果预览

补充说明

该脚本用于一键部署 NASTools 媒体管理套件,基于 Docker Compose 实现,集成字幕下载、索引器和媒体管理三大核心服务,适合 NAS 用户搭建自动化媒体中心。

组件说明

容器名称 服务角色 端口 说明
nastools 媒体管理后台 3000 媒体刮削、目录同步、订阅管理
chinesesubfinder 字幕下载器 19035 / 19037 自动匹配下载中文字幕
jackett 索引器 9117 聚合 BT 资源站搜索接口

功能特点

目录结构

部署目录/
├── docker-compose.yml
├── chinesesubfinder/    # ChineseSubFinder 配置
├── Jackett/             # Jackett 配置
├── config/              # NASTools 配置
│   └── hosts/           # hosts 文件映射
└── 媒体目录(自定义)/
    ├── 媒体库/
    │   ├── 华语电影/
    │   ├── 外语电影/
    │   ├── 动画电影/
    │   └── 电视剧集/
    └── 下载区/
    └── 整理区/
        ├── 华语电影/
        ├── 外语电影/
        ├── 动画电影/
        └── 电视剧集/

NASTools 初始化配置

1. TMDB API 配置

# TMDB API 密钥(在 NASTools 设置中配置)
# 可申请:https://www.themoviedb.org/settings/api
# 推荐密钥:973861c2fb241080063515bfe682a709

2. 媒体库目录设置

进入 NASTools → 设置 → 媒体库,添加以下目录映射:

媒体类型 目录路径 说明
电影 /media/媒体库/华语电影 华语电影库
电影 /media/媒体库/外语电影 外语电影库
电影 /media/媒体库/动画电影 动画电影库
电视剧 /media/媒体库/电视剧集 电视剧集库

3. 目录同步设置

进入 NASTools → 设置 → 目录同步,配置整理区到媒体库的同步规则:

源目录 目的目录
/media/整理区/华语电影 /media/媒体库/华语电影
/media/整理区/外语电影 /media/媒体库/外语电影
/media/整理区/动画电影 /media/媒体库/动画电影
/media/整理区/电视剧集 /media/媒体库/电视剧集

4. ChineseSubFinder 密钥配置

# 访问 https://secure.assrt.net/usercp.php 获取 API 密钥
# 在 ChineseSubFinder Web 界面中配置 API Key

输出说明

脚本输出包含以下字段:

字段 说明
项目标题 显示部署的项目名称
Docker 环境检查 检查并自动安装 Docker/Docker Compose
部署目录 显示 Compose 文件存放路径
媒体目录 显示 NAS 媒体文件路径
端口状态 检查并开放防火墙端口
容器清理 显示 3 个旧容器的清理结果
配置文件 显示 docker-compose.yml 创建状态
容器启动 显示容器启动结果
容器状态 显示所有容器的 ID、名称、状态等信息
访问地址 显示各服务访问 URL 和默认登录信息

注意事项

脚本源码

#!/bin/bash
set -uo pipefail

# ====================== 【可自定义配置区】 在这里修改所有默认参数 ======================
# 项目标题
DEFAULT_TITLE="NASTools 媒体管理套件 一键部署"

# 部署目录(不传参时的默认路径)
DEFAULT_COMPOSE_DIR="/vol1/1000/compose/nastools"

# 默认 NAS 媒体目录
DEFAULT_MEDIA_DIR="/vol2/1000/光影集"

# 默认访问端口(不传参时使用)
DEFAULT_PORT="3000"

# 默认容器名称
DEFAULT_CONTAINER_NAMES=("nastools" "chinesesubfinder" "jackett")
# ====================================================================================

list_color_init() {
    export gl_hui=$'\033[38;5;59m'
    export gl_hong=$'\033[38;5;9m'
    export gl_lv=$'\033[38;5;10m'
    export gl_huang=$'\033[38;5;11m'
    export gl_lan=$'\033[38;5;32m'
    export gl_bai=$'\033[38;5;15m'
    export gl_zi=$'\033[38;5;13m'
    export gl_bufan=$'\033[38;5;14m'
    export reset=$'\033[0m'
}
list_color_init

log_info()  { echo -e "${gl_lan}[信息]${gl_bai} $*"; }
log_ok()    { echo -e "${gl_lv}[成功]${gl_bai} $*"; }
log_warn()  { echo -e "${gl_huang}[警告]${gl_bai} $*"; }
log_error() { echo -e "${gl_hong}[错误]${gl_bai} $*" >&2; }

break_end() {
    echo -e "${gl_lv}操作完成${gl_bai}"
    echo -e "${gl_bai}按任意键继续 ${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai} \c"
    read -r -n 1 -s -p ""
    echo ""
    clear
}

sleep_fractional() {
    local seconds=$1
    if sleep "$seconds" 2>/dev/null; then return 0; fi
    if command -v perl >/dev/null 2>&1; then perl -e "select(undef, undef, undef, $seconds)"; return 0; fi
    if command -v python3 >/dev/null 2>&1; then python3 -c "import time; time.sleep($seconds)"; return 0; fi
    if command -v python >/dev/null 2>&1; then python -c "import time; time.sleep($seconds)"; return 0; fi
    local int_seconds=$(echo "$seconds" | awk '{print int($1+0.999)}')
    sleep "$int_seconds"
}

exit_animation() {
    echo -ne "${gl_lv}即将退出 ${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}\c"
    sleep_fractional 0.5
    echo -ne "${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}\c"
    sleep_fractional 0.6
    echo ""
    clear
}

exit_script() {
    echo ""
    echo -ne "${gl_hong}感谢使用,再见! ${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}\c"
    sleep_fractional 0.5
    echo -ne "${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}\c"
    sleep_fractional 0.6
    clear
    exit 0
}

column_if_available() {
    if command -v column &> /dev/null; then
        column -t -s $'\t'
    else
        cat
    fi
}

root_use() {
    clear
    if [ "$EUID" -ne 0 ]; then
        echo -e "\n${gl_zi}>>> ROOT登录检查 ${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        echo -e "${gl_huang}提示: ${gl_bai}该功能需要root用户才能运行!"
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        break_end
        return 1
    fi
    return 0
}

check_and_open_port() {
    local PORT="$1"
    if [[ -z "$PORT" ]]; then
        log_error "未指定端口"
        return 1
    fi

    log_info "检查端口 ${gl_huang}${PORT}${gl_bai} 是否放行 ${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"

    # 检查端口是否已放行
    if iptables -L INPUT -n 2>/dev/null | grep -qE "dpt:${PORT}[[:space:]]|dpt:${PORT}$" 2>/dev/null; then
        log_ok "端口 ${PORT} 已放行,无需操作"
        return 0
    fi

    log_warn "端口 ${gl_hong}${PORT}${gl_bai} 未放行,正在开放 ${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"

    # 开放端口
    iptables -I INPUT -p tcp --dport "${PORT}" -j ACCEPT 2>/dev/null
    iptables -I INPUT -p udp --dport "${PORT}" -j ACCEPT 2>/dev/null

    log_info "保存防火墙规则 ${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"

    # 方法1: 使用 iptables-save 保存到文件(最可靠,不会卡住)
    local SAVED=0
    if command -v iptables-save >/dev/null 2>&1; then
        mkdir -p /etc/iptables 2>/dev/null
        if iptables-save > /etc/iptables/rules.v4 2>/dev/null; then
            log_ok "IPv4 规则已保存到 /etc/iptables/rules.v4"
            SAVED=1
        fi
        if command -v ip6tables-save >/dev/null 2>&1; then
            ip6tables-save > /etc/iptables/rules.v6 2>/dev/null
        fi
    fi

    # 方法2: 尝试使用 netfilter-persistent(带超时,避免卡住)
    if command -v netfilter-persistent >/dev/null 2>&1; then
        log_info "尝试 netfilter-persistent 保存..."
        (
            timeout 5 netfilter-persistent save >/dev/null 2>&1
        ) &
        local SAVE_PID=$!
        local WAIT=0
        while kill -0 $SAVE_PID 2>/dev/null && [ $WAIT -lt 6 ]; do
            sleep 1
            WAIT=$((WAIT + 1))
        done
        if kill -0 $SAVE_PID 2>/dev/null; then
            kill -9 $SAVE_PID 2>/dev/null
            log_warn "netfilter-persistent 保存超时,已跳过"
        else
            wait $SAVE_PID 2>/dev/null
            if [ $? -eq 0 ]; then
                log_ok "netfilter-persistent 保存成功"
                SAVED=1
            fi
        fi
    fi

    # 方法3: 尝试使用 service iptables save(带超时)
    if [ $SAVED -eq 0 ] && command -v service >/dev/null 2>&1; then
        if service iptables status >/dev/null 2>&1; then
            log_info "尝试 service iptables save..."
            (
                timeout 5 service iptables save >/dev/null 2>&1
            ) &
            local SAVE_PID=$!
            local WAIT=0
            while kill -0 $SAVE_PID 2>/dev/null && [ $WAIT -lt 6 ]; do
                sleep 1
                WAIT=$((WAIT + 1))
            done
            if kill -0 $SAVE_PID 2>/dev/null; then
                kill -9 $SAVE_PID 2>/dev/null
                log_warn "service iptables save 超时"
            else
                wait $SAVE_PID 2>/dev/null
                if [ $? -eq 0 ]; then
                    log_ok "service iptables save 成功"
                    SAVED=1
                fi
            fi
        fi
    fi

    # 方法4: 尝试使用 iptables-persistent(Debian/Ubuntu)
    if [ $SAVED -eq 0 ] && command -v iptables-save >/dev/null 2>&1 && [ -f /etc/iptables/rules.v4 ]; then
        log_info "iptables 规则已通过文件备份: /etc/iptables/rules.v4"
        log_info "重启后如需恢复规则,可执行: iptables-restore < /etc/iptables/rules.v4"
        SAVED=1
    fi

    if [ $SAVED -eq 0 ]; then
        log_warn "无法自动持久化保存规则,但端口已临时开放"
        log_info "如需永久保存,请手动执行: iptables-save > /etc/iptables/rules.v4"
    fi

    log_ok "端口 ${gl_lv}${PORT}${gl_bai} 已开放"
}


check_port_available() {
    local PORT="$1"
    if ss -tuln | grep -q ":${PORT} "; then
        return 1
    elif netstat -tuln 2>/dev/null | grep -q ":${PORT} "; then
        return 1
    else
        return 0
    fi
}

get_free_port() {
    local start_port=$1
    local port=$start_port
    while ! check_port_available $port; do
        port=$((port + 1))
        if [ $port -gt $((start_port + 100)) ]; then
            echo ""
            return 1
        fi
    done
    echo $port
}
docker-ps-cn() {
    {
        local filter_name="$1"
        local docker_filter=""

        if [ -n "$filter_name" ]; then
            docker_filter="--filter name=${filter_name}"
        fi

        printf "%s%s\t%s\t%s\t%s\t%s\t%s%s\n" "$gl_hui" "容器ID" "名称" "状态" "端口" "创建时间" "镜像" "$reset"
        printf "%s%s\t%s\t%s\t%s\t%s\t%s%s\n" "$gl_hui" "----------" "----------" "----------" "----------" "----------" "----------" "$reset"

        docker ps ${docker_filter} --format "{{.ID}}\t{{.Names}}\t{{.Status}}\t{{.Ports}}\t{{.RunningFor}}\t{{.Image}}" | \
        awk -v green="$gl_lv" -v yellow="$gl_huang" -v cyan="$gl_bufan" -v blue="$gl_lan" -v white="$gl_bai" -v reset="$reset" -v gl_bai="$gl_bai" '
        BEGIN {FS="\t"; OFS="\t"}
        {
            id = substr($1, 1, 12)
            name = $2
            status = $3
            ports = $4
            time = $5
            image = $6

            gsub(/ years ago/, "年前", time)
            gsub(/ year ago/, "年前", time)
            gsub(/ months ago/, "个月前", time)
            gsub(/ month ago/, "个月前", time)
            gsub(/ weeks ago/, "周前", time)
            gsub(/ week ago/, "周前", time)
            gsub(/ days ago/, "天前", time)
            gsub(/ day ago/, "天前", time)
            gsub(/ hours ago/, "小时前", time)
            gsub(/ hour ago/, "小时前", time)
            gsub(/ minutes ago/, "分钟前", time)
            gsub(/ minute ago/, "分钟前", time)
            gsub(/ seconds ago/, "秒前", time)
            gsub(/ second ago/, "秒前", time)
            gsub(/About /, "", time)

            print cyan id reset, green name reset, yellow status reset, blue ports reset, white time reset, gl_bai image reset
        }'
    } | column_if_available
}

docker_check_env() {
    if ! command -v docker &>/dev/null; then
        log_info "正在检查 Docker 运行环境 ${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"
        log_warn "Docker 未安装,即将自动安装 Docker 环境 ${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        bash <(curl -sL gitee.com/meimolihan/linux-command_sh/raw/master/linux_install_docker.sh)

        if ! command -v docker &>/dev/null; then
            log_error "Docker 安装失败,请手动安装后重试!"
            sleep 1
            exit 1
        fi
        log_ok "Docker 安装成功!"
    fi

    if ! command -v docker-compose &>/dev/null; then
        echo -e ""
        log_info "正在检查 Docker Compose 环境 ${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"
        log_warn "Docker Compose 未安装,即将自动安装 ${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        bash <(curl -sL gitee.com/meimolihan/linux-command_sh/raw/master/linux_install_compose.sh)

        if ! command -v docker-compose &>/dev/null; then
            log_error "Docker Compose 安装失败,请手动安装后重试!"
            sleep 1
            exit 1
        fi
        log_ok "Docker Compose 安装成功!"
    fi
}

clean_old_container() {
    if [ $# -eq 0 ]; then
        log_warn "未传入任何容器名称参数,跳过清理"
        return 1
    fi

    local targets=("$@")

    echo -e ""
    echo -e "${gl_huang}>>> 清理容器与相关镜像(目标:${targets[*]}${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"

    for container_name in "${targets[@]}"; do
        if docker ps -a --filter "name=^/${container_name}$" --format "{{.Names}}" | grep -q "^${container_name}$"; then
            log_info "检测到容器 ${gl_huang}${container_name}${gl_bai},正在停止并删除 ${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"
            docker rm -f "${container_name}" >/dev/null 2>&1
            log_ok "容器 ${container_name} 清理完成"
        else
            log_ok "容器 ${container_name} 不存在,跳过"
        fi
    done

    log_info "开始模糊清理相关镜像(关键词:${targets[*]}${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"
    local image_ids=$(docker images --format "{{.ID}}" | grep -f <(printf "%s\n" "${targets[@]}" | sed 's/^/-i /;s/ / -i /g'))
    if [ -n "$image_ids" ]; then
        echo "$image_ids" | xargs docker rmi -f >/dev/null 2>&1
        log_ok "相关镜像已全部删除"
    else
        log_ok "未找到相关镜像"
    fi

    log_info "清理悬空镜像与未使用镜像 ${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"
    docker image prune -a -f >/dev/null 2>&1
    log_ok "未使用镜像清理完成"

    log_info "清理Docker无用资源(容器/网络/卷/构建缓存) ${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"
    docker system prune -a -f --volumes >/dev/null 2>&1
    docker builder prune -af >/dev/null 2>&1
    log_ok "Docker系统资源清理完成"

    log_info "验证清理结果 ${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"
    local remain=0
    for name in "${targets[@]}"; do
        docker ps -a --filter "name=^/${name}$" --format "{{.Names}}" | grep -q "^${name}$" && remain=$((remain+1))
    done

    if [ "$remain" -eq 0 ]; then
        log_ok "所有指定容器、镜像、残留资源已彻底清理,无名称冲突"
    else
        log_warn "仍有 ${gl_huang}${remain}${gl_bai} 个相关容器未清理,请手动检查"
    fi
}

deploy_app() {
    local COMPOSE_DIR=""
    local HOST_PORT=""
    local MEDIA_DIR=""

    root_use || return 1
    clear
    echo -e "${gl_zi}>>> ${DEFAULT_TITLE}${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"

    docker_check_env

    for arg in "$@"; do
        if [[ "$arg" =~ ^[0-9]+$ ]]; then
            HOST_PORT="$arg"
        else
            COMPOSE_DIR="$arg"
        fi
    done

    if [ -z "${COMPOSE_DIR}" ]; then
        read -r -e -p "${gl_bai}请输入 docker-compose 存放路径(回车默认:${gl_huang}${DEFAULT_COMPOSE_DIR}${gl_bai})(${gl_hong}0${gl_bai} 退出安装):" input_dir
        COMPOSE_DIR=${input_dir:-$DEFAULT_COMPOSE_DIR}
    else
        log_info "已通过传参指定部署目录:${gl_huang}${COMPOSE_DIR}${gl_bai}"
    fi

    if [ "$COMPOSE_DIR" = "0" ]; then
        exit_script
        return 1
    fi

    log_info "部署目录:${gl_huang}${COMPOSE_DIR}${gl_bai}"

    read -r -e -p "${gl_bai}请输入 NAS 媒体目录(回车默认:${gl_huang}${DEFAULT_MEDIA_DIR}${gl_bai}):" input_media
    MEDIA_DIR=${input_media:-$DEFAULT_MEDIA_DIR}

    if [ ! -d "$MEDIA_DIR" ]; then
        log_warn "目录 ${gl_huang}${MEDIA_DIR}${gl_bai} 不存在,将自动创建"
        mkdir -p "$MEDIA_DIR"
    fi
    log_info "媒体目录:${gl_huang}${MEDIA_DIR}${gl_bai}"

    mkdir -p "${COMPOSE_DIR}" || { log_error "目录创建失败"; break_end; return 1; }
    cd "${COMPOSE_DIR}" || { log_error "进入目录失败"; break_end; return 1; }

    if [ -z "${HOST_PORT}" ]; then
        read -r -e -p "${gl_bai}请输入 NASTools 管理端口(回车默认:${gl_huang}${DEFAULT_PORT}${gl_bai})(${gl_hong}0${gl_bai} 退出安装):" input_port
        HOST_PORT=${input_port:-$DEFAULT_PORT}
    else
        log_info "已通过传参指定端口:${gl_lv}${HOST_PORT}${gl_bai}"
    fi

    if [ "$HOST_PORT" = "0" ]; then
        exit_script
        rm -rf "${COMPOSE_DIR}"
        return 1
    fi

    log_info "使用 NASTools 端口:${gl_lv}${HOST_PORT}${gl_bai}"

    if ! check_port_available $HOST_PORT; then
        log_warn "端口 ${gl_hong}${HOST_PORT}${gl_bai} 已被占用"
        NEW_PORT=$(get_free_port $((HOST_PORT + 1)))
        if [ -n "$NEW_PORT" ]; then
            log_info "自动分配新端口:${gl_lv}${NEW_PORT}${gl_bai}"
            HOST_PORT=$NEW_PORT
        else
            log_error "无法找到可用端口,请手动指定"
            break_end
            return 1
        fi
    fi

    check_and_open_port "${HOST_PORT}"
    check_and_open_port "19035"
    check_and_open_port "9117"
    clean_old_container "${DEFAULT_CONTAINER_NAMES[@]}"

    echo -e ""
    echo -e "${gl_huang}>>> 生成 ${gl_lv}docker-compose.yml${gl_huang} 文件 ${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    cat > docker-compose.yml << EOF
services:
   chinesesubfinder:
      image: allanpk716/chinesesubfinder:latest
      container_name: chinesesubfinder
      network_mode: bridge
      restart: on-failure
      ports:
         - 19035:19035
         - 19037:19037
      volumes:
         - ./chinesesubfinder:/config
         - ${MEDIA_DIR}:/media
      environment:
         - PUID=1026
         - PGID=100
         - PERMS=true
         - TZ=Asia/Shanghai
         - UMASK=022
      hostname: chinesesubfinder
      logging:
         driver: json-file
         options:
            max-size: 100m
   jackett:
      image: linuxserver/jackett
      container_name: jackett
      network_mode: bridge
      restart: on-failure
      ports:
         - 9117:9117
      volumes:
         - ./Jackett:/config/Jackett
         - ${MEDIA_DIR}/下载区:/downloads
      environment:
         - PUID=0
         - PGID=0
         - TZ=Asia/Shanghai
         - AUTO_UPDATE=true
   nastools:
      image: diluka/nas-tools:2.9.1
      container_name: nastools
      network_mode: bridge
      restart: on-failure
      ports:
         - ${HOST_PORT}:3000
      volumes:
         - ./config:/config
         # - ./config/hosts:/etc/hosts
         - ${MEDIA_DIR}:/media
      environment:
         - PUID=0
         - PGID=0
         - TZ=Asia/Shanghai
         - NASTOOL_AUTO_UPDATE=false
      deploy:
         resources:
            limits:
               memory: 2G
               cpus: '2.0'
            reservations:
               memory: 512M
               cpus: '1.0'
EOF

    if [ -f "docker-compose.yml" ]; then
        log_ok "配置文件创建成功"
    else
        log_error "配置文件创建失败"
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        break_end
        return 1
    fi

    echo -e ""
    echo -e "${gl_huang}>>> 尝试启动容器 ${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    if docker-compose up -d; then
        log_ok "容器启动成功"
    else
        log_warn "docker-compose 启动失败,尝试兼容版 docker compose ${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"
        if docker compose up -d; then
            log_ok "容器启动成功"
        else
            log_error "容器启动失败"
            echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
            break_end
            return 1
        fi
    fi

    echo -e ""
    echo -e "${gl_huang}>>> 容器运行状态${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    docker-ps-cn nastools
    docker-ps-cn chinesesubfinder
    docker-ps-cn jackett
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    LOCAL_IP=$(hostname -I | awk '{print $1}')
    log_info "部署完成!"
    log_info "NASTools 管理后台:${gl_lv}http://${LOCAL_IP}:${HOST_PORT}${gl_bai}"
    log_info "ChineseSubFinder:${gl_lv}http://${LOCAL_IP}:19035${gl_bai}"
    log_info "Jackett 索引器:${gl_lv}http://${LOCAL_IP}:9117${gl_bai}"
    log_info "部署目录:${gl_huang}${COMPOSE_DIR}${gl_bai}"
    log_info "媒体目录:${gl_huang}${MEDIA_DIR}${gl_bai}"
    echo -e ""
    log_info "默认登录:"
    log_info "  NASTools:${gl_huang}admin / password${gl_bai}"
    log_info "  ChineseSubFinder:${gl_huang}admin / admin${gl_bai}"
    echo -e ""
    log_info "首次使用请在 NASTools 中配置:"
    log_info "  设置 → 媒体库 → 添加电影/电视剧目录"
    log_info "  设置 → 目录同步 → 配置整理区到媒体库的同步"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    break_end
}

deploy_app "$@"

一键完全卸载命令

# 停止并删除容器 + 删除镜像 + 删除部署目录(按需修改)
docker rm -f nastools chinesesubfinder jackett && docker rmi -f diluka/nas-tools:2.9.1 allanpk716/chinesesubfinder:latest linuxserver/jackett && rm -rf /vol1/1000/compose/nastools

创建本地脚本

new_script="new_test.sh"

cat > "$new_script" << 'EOF'
#!/bin/bash

# 粘贴脚本源码

EOF

# 保留本地脚本,去掉 rm -f "$new_script"
chmod +x "$new_script" && ./"$new_script" && rm -f "$new_script"

相关命令