直播源 含优质9+9m3u
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
m3u/NoobIPTV.sh

1709 lines
64 KiB

#!/bin/bash
###############################
# 名称: NoobIPTV (IPTV 项目相关脚本集合 @小白神器)
# 作者: YanG-1989
# 项目地址:https://github.com/YanG-1989
# 最新版本:2.1.2
###############################
# 设置路径
SCRIPT_PATH="$HOME/NoobIPTV.sh" # 定义脚本路径
CONFIG_FILE="$HOME/.NoobIPTV" # 配置文件路径
REVERSE_PROXY="docker.zhai.cm" # 设置反向代理地址
# 设置颜色变量
RED="\033[1;31m" # 红
GREEN="\033[1;32m" # 绿
YELLOW="\033[1;33m" # 黄
CYAN="\033[1;36m" # 青
RESET="\033[0m" # 重置
# echo -e "${GREEN}这是绿色粗体文本。${RESET}"
############# 菜单 #############
# 显示 菜单
show_menu() {
echo "-------------------"
echo " 请选择一个项目: "
echo "-------------------"
echo "1) Pixman 项目 "
echo "2) Fourgtv 项目 "
echo "3) Doubebly 项目 "
echo "~~~~~~~~~~~~~~~~~~~"
echo "4) Docker 管理助手 "
echo "~~~~~~~~~~~~~~~~~~~"
echo "5) -- 工具箱 -- "
echo "~~~~~~~~~~~~~~~~~~~"
echo "6) ~~ 脚本信息 ~~ "
echo "-------------------"
echo "0) 退出 "
echo " [ Ctrl+C ] "
echo "-------------------"
}
# Pixman 菜单
show_pixman_menu() {
echo "-------------------"
echo " Pixman 菜单: "
echo "-------------------"
echo "1) 安装 Pixman 项目"
echo "2) 卸载 Pixman 项目"
echo "3) 设置 反向代理 地址"
echo "-------------------"
echo "0) 返回主菜单 "
echo "-------------------"
}
# Fourgtv 菜单
show_fourgtv_menu() {
echo "---------------------"
echo " Fourgtv 菜单: "
echo "---------------------"
echo "1) 安装 Fourgtv 项目 "
echo "2) 卸载 Fourgtv 项目 "
echo "3) 设置 反向代理 地址 "
echo "---------------------"
echo "0) 返回主菜单 "
echo "---------------------"
}
# Doubebly 菜单
show_doubebly_menu() {
echo "---------------------"
echo " Doubebly 菜单: "
echo "---------------------"
echo "1) 安装 Doubebly 项目"
echo "2) 卸载 Doubebly 项目"
echo "3) 设置 反向代理 地址"
echo "---------------------"
echo "0) 返回主菜单 "
echo "---------------------"
}
# Watchtower 菜单
show_watchtower_menu() {
echo "----------------------"
echo " Watchtower 菜单: "
echo "----------------------"
echo "1) 一键更新 Docker 项目"
echo "2) 管理 Docker 项目更新"
echo "3) 一键清理 Docker 垃圾"
echo "4) 一键设置 Docker 日志"
echo "----------------------"
echo "0) 返回主菜单 "
echo "----------------------"
}
# 工具箱 菜单
show_toolbox_menu() {
echo "---------------------"
echo " 工具箱菜单: "
echo "---------------------"
echo "1) [Docker] 1Panel "
echo "2) [Docker] o11 "
echo "3) [Docker] 3X-UI "
echo "4) [Docker] Sub Store"
echo "5) [Docker] LibreTV "
echo "6) [233boy] Sing-box "
echo "7) [Jimmy ] Alice DNS"
echo "---------------------"
echo "0) 返回主菜单 "
echo "---------------------"
}
# 1Panel 菜单
show_1panel_menu() {
echo "-------------------"
echo " 1Panel 菜单: "
echo "-------------------"
echo "1) 安装 1Panel "
echo "2) 卸载 1Panel "
echo "3) 设置 1Panel "
echo "-------------------"
echo "0) 返回上级菜单 "
echo "-------------------"
}
# 3X-UI 菜单
show_3x_ui_menu() {
echo "-------------------"
echo " 3X-UI 菜单: "
echo "-------------------"
echo "1) 安装 3X-UI "
echo "2) 更新 3X-UI "
echo "3) 卸载 3X-UI "
echo "-------------------"
echo "0) 返回上级菜单 "
echo "-------------------"
}
# o11 菜单
show_o11_menu() {
echo "-------------------"
echo " o11 菜单: "
echo "-------------------"
echo "1) 安装 o11 "
echo "2) 卸载 o11 "
echo "-------------------"
echo "0) 返回上级菜单 "
echo "-------------------"
}
# subs 菜单
show_subs_menu() {
echo "-------------------"
echo " Sub Store 菜单: "
echo "-------------------"
echo "1) 安装 Sub Store "
echo "2) 卸载 Sub Store "
echo "-------------------"
echo "0) 返回上级菜单 "
echo "-------------------"
}
# libretv 菜单
show_libretv_menu() {
echo "-------------------"
echo " LibreTV 菜单: "
echo "-------------------"
echo "1) 安装 LibreTV "
echo "2) 卸载 LibreTV "
echo "-------------------"
echo "0) 返回上级菜单 "
echo "-------------------"
}
############# Pixman #############
# 判断 Pixman 容器
judge_Pixman() {
local NETWORK_MODE PORT env_vars
echo "正在安装 Pixman 项目 作者: @Pixman..."
if docker ps -a --format '{{.Names}}' | grep -q "^pixman$"; then
local MODE ENV_VARS
ENV_VARS=$(docker inspect --format '{{range .Config.Env}}{{println .}}{{end}}' pixman)
MYTVSUPER_TOKEN=$(echo "$ENV_VARS" | grep -oP 'MYTVSUPER_TOKEN=\K.*')
HAMI_SESSION_ID=$(echo "$ENV_VARS" | grep -oP 'HAMI_SESSION_ID=\K.*')
HAMI_SERIAL_NO=$(echo "$ENV_VARS" | grep -oP 'HAMI_SERIAL_NO=\K.*')
HAMI_SESSION_IP=$(echo "$ENV_VARS" | grep -oP 'HAMI_SESSION_IP=\K.*')
HTTP_PROXY=$(echo "$ENV_VARS" | grep -oP 'HTTP_PROXY=\K.*')
HTTPS_PROXY=$(echo "$ENV_VARS" | grep -oP 'HTTPS_PROXY=\K.*')
echo -e "${CYAN}检测到已存在的 Pixman 容器,将进行重新安装...${RESET}"
echo -e "当前 ${GREEN}Pixman${RESET} 配置参数:"
[ -n "$MYTVSUPER_TOKEN" ] && echo "MYTVSUPER_TOKEN: $MYTVSUPER_TOKEN" || echo "MYTVSUPER_TOKEN: 未设置"
[ -n "$HAMI_SESSION_ID" ] && echo "HAMI_SESSION_ID: $HAMI_SESSION_ID" || echo "HAMI_SESSION_ID: 未设置"
[ -n "$HAMI_SERIAL_NO" ] && echo "HAMI_SERIAL_NO: $HAMI_SERIAL_NO" || echo "HAMI_SERIAL_NO: 未设置"
[ -n "$HAMI_SESSION_IP" ] && echo "HAMI_SESSION_IP: $HAMI_SESSION_IP" || echo "HAMI_SESSION_IP: 未设置"
[ -n "$HTTP_PROXY" ] && echo "HTTP_PROXY: $HTTP_PROXY" || echo "HTTP_PROXY: 未设置"
[ -n "$HTTPS_PROXY" ] && echo "HTTPS_PROXY: $HTTPS_PROXY" || echo "HTTPS_PROXY: 未设置"
docker rm -f pixman > /dev/null 2>&1
docker rmi -f "$IMAGE_SOURCE" > /dev/null 2>&1
install_Pixman "$MYTVSUPER_TOKEN" "$HAMI_SESSION_ID" "$HAMI_SERIAL_NO" "$HAMI_SESSION_IP" "$HTTP_PROXY" "$HTTPS_PROXY"
else
install_Pixman
fi
}
# 安装 Pixman 容器
install_Pixman() {
local PORT=$(check_and_allocate_port 5000)
local ARCH IMAGE_SOURCE PROXY_IMAGE_SOURCE
local MYTVSUPER_TOKEN="$1"
local HAMI_SESSION_ID="$2"
local HAMI_SERIAL_NO="$3"
local HAMI_SESSION_IP="$4"
local HTTP_PROXY="$5"
local HTTPS_PROXY="$6"
echo -e "${CYAN}开始配置 Pixman 参数...${RESET}"
echo "请选择 Pixman 部署方式(默认: 2):"
echo "1) 使用 host 网络模式 (建议:软路由)"
echo "2) 使用 bridge 网络模式 (建议:VPS)"
read -rp "输入选项 (1 或 2): " option_fourgtv
option_fourgtv=${option_fourgtv:-2}
case "$option_fourgtv" in
1) NETWORK_MODE="host" ;;
2) NETWORK_MODE="bridge" ;;
*)
echo -e "${RED}无效选项,使用默认的 bridge 模式。${RESET}"
NETWORK_MODE="bridge"
;;
esac
if [[ "$NETWORK_MODE" == "bridge" ]]; then
read -p "请输入 Pixman 容器端口 (当前值: $PORT 输入null清空): " input_port
if [ -n "$input_port" ]; then
[ "$input_port" = "null" ] && PORT="" || PORT=$(check_and_allocate_port "$input_port")
fi
else
PORT=""
fi
echo "是否需要设置其他环境变量?[y/n](默认:n)"
read -rp "输入选项: " configure_all_vars
configure_all_vars=${configure_all_vars:-n}
if [[ "$configure_all_vars" =~ ^[Yy]$ ]]; then
local env_vars=("MYTVSUPER_TOKEN" "HAMI_SESSION_ID" "HAMI_SERIAL_NO" "HAMI_SESSION_IP" "HTTP_PROXY" "HTTPS_PROXY")
for var in "${env_vars[@]}"; do
local current_value=$(eval echo \$$var)
read -p "请输入 ${var} (当前值: ${current_value:-未设置}, 输入null清空): " input_value
if [ -n "$input_value" ]; then
[ "$input_value" = "null" ] && eval $var="" || eval $var="$input_value"
fi
done
else
echo -e "${YELLOW}已跳过所有环境变量的设置。${RESET}"
fi
ARCH=$(uname -m)
if [[ "$ARCH" == "armv7"* ]]; then
IMAGE_SOURCE="pixman/pixman-armv7"
PROXY_IMAGE_SOURCE="$REVERSE_PROXY/pixman-armv7"
else
IMAGE_SOURCE="pixman/pixman"
PROXY_IMAGE_SOURCE="$REVERSE_PROXY/pixman/pixman"
fi
pull_image "$IMAGE_SOURCE" "$PROXY_IMAGE_SOURCE"
local docker_command="docker run -d --name pixman --restart always"
if [[ "$NETWORK_MODE" == "host" ]]; then
docker_command+=" --net=host"
else
docker_command+=" --net=bridge -p $PORT:5000"
fi
for var in MYTVSUPER_TOKEN HAMI_SESSION_ID HAMI_SERIAL_NO HAMI_SESSION_IP HTTP_PROXY HTTPS_PROXY; do
local value=$(eval echo \$$var)
[ -n "$value" ] && docker_command+=" -e $var=$value"
done
docker_command+=" $IMAGE_SOURCE"
echo -e "${CYAN}正在启动 Pixman 容器...${RESET}"
eval "$docker_command"
echo -e "${GREEN}Pixman 容器已成功启动!${RESET}"
if check_internet_connection; then
install_watchtower "pixman"
else
echo "---------------------------------------------------------"
fi
live_Pixman "$PORT"
}
# 生成 Pixman 订阅
live_Pixman() {
local public_ip=$(get_public_ip)
local port="$1"
echo "◆ 订阅地址:"
echo "■ 四季線上 4GTV : http://$public_ip:$port/4gtv.m3u (部分失效)"
echo "■ MytvSuper : http://$public_ip:$port/mytvsuper.m3u (需填写会员参数)"
echo "■ Hami Video : http://$public_ip:$port/hami.m3u (需填写会员参数)"
echo "---------------------------------------------------------"
echo "--- Pixman 详细使用说明: https://pixman.io/topics/17 ---"
echo "--- NoobIPTV.sh 脚本日志: https://pixman.io/topics/142 ---"
echo "---------------------------------------------------------"
read -p "按 回车键 返回 主菜单 ..."
}
# 卸载 Pixman 项目
uninstall_Pixman() {
echo "是否确定要卸载 Pixman 项目?[y/n](默认:n)"
read -r -t 10 input
input=${input:-n}
if [[ "$input" =~ ^[Yy]$ ]]; then
echo -e "${CYAN}正在卸载 Pixman 项目...${RESET}"
docker stop pixman > /dev/null 2>&1
docker rm -f pixman > /dev/null 2>&1
docker images --format '{{.Repository}}:{{.Tag}}' | grep 'pixman/pixman' | xargs -r docker rmi > /dev/null 2>&1
uninstall_watchtower "pixman"
echo -e "${RED}Pixman 项目 已成功卸载。${RESET}"
else
echo -e "${GREEN}取消卸载操作。${RESET}"
fi
}
############# Fourgtv #############
# 安装 Fourgtv
install_Fourgtv() {
local public_ip
local ENV_VARS
local public_ip=$(get_public_ip)
local port=$(check_and_allocate_port 8000)
IMAGE_SOURCE="ru2025/fourgtv:latest"
PROXY_IMAGE_SOURCE="$REVERSE_PROXY/ru2025/fourgtv:latest"
echo "正在安装 Fourgtv 项目 作者: @刘墉..."
if docker ps -a --format '{{.Names}}' | grep -q "^fourgtv$"; then
echo -e "${CYAN}检测到已存在的 Fourgtv 容器,将进行重新安装...${RESET}"
ENV_VARS=$(docker inspect --format '{{range .Config.Env}}{{println .}}{{end}}' fourgtv)
NOWSESSIONID=$(echo "$ENV_VARS" | grep -oP 'NOWSESSIONID=\K.*')
NOWUSERAGENT=$(echo "$ENV_VARS" | grep -oP 'NOWUSERAGENT=\K.*')
MYTVSUPER_TOKEN=$(echo "$ENV_VARS" | grep -oP 'MYTVSUPER_TOKEN=\K.*')
echo -e "当前 ${GREEN}Fourgtv${RESET} 配置参数:"
[ -n "$NOWSESSIONID" ] && echo "NOWSESSIONID: $NOWSESSIONID" || echo "NOWSESSIONID: 未设置"
[ -n "$NOWUSERAGENT" ] && echo "NOWUSERAGENT: $NOWUSERAGENT" || echo "NOWUSERAGENT: 未设置"
[ -n "$MYTVSUPER_TOKEN" ] && echo "MYTVSUPER_TOKEN: $MYTVSUPER_TOKEN" || echo "MYTVSUPER_TOKEN: 未设置"
docker stop fourgtv > /dev/null 2>&1
docker rm -f fourgtv > /dev/null 2>&1
docker images --format '{{.Repository}}:{{.Tag}}' | grep 'ru2025/fourgtv:latest' | xargs -r docker rmi > /dev/null 2>&1
fi
pull_image "$IMAGE_SOURCE" "$PROXY_IMAGE_SOURCE"
echo "请输入 Fourgtv 配置参数:"
echo "当前 Fourgtv 使用的端口是 $port,是否需要修改?[y/n](默认:n)"
read -r -t 10 input_port
input_port=${input_port:-n}
if [[ "$input_port" =~ ^[Yy]$ ]]; then
read -rp "请输入新的端口号: " port
fi
echo "是否需要修改其他环境变量?[y/n](默认:n)"
read -r -t 10 input_vars
input_vars=${input_vars:-n}
if [[ "$input_vars" =~ ^[Yy]$ ]]; then
read -rp "请输入 NOWSESSIONID: " NOWSESSIONID
read -rp "请输入 NOWUSERAGENT: " NOWUSERAGENT
read -rp "请输入 MYTVSUPER_TOKEN: " MYTVSUPER_TOKEN
fi
echo "请选择 Fourgtv 部署方式(默认: 2):"
echo "1) 使用 host 网络模式 (建议:软路由)"
echo "2) 使用 bridge 网络模式 (建议:VPS)"
read -rp "输入选项 (1 或 2): " option_fourgtv
option_fourgtv=${option_fourgtv:-2}
case $option_fourgtv in
1|host)
echo "正在使用 host 网络模式安装 Fourgtv..."
docker run -d --restart always --net=host -p $port:8000 --name fourgtv \
${NOWSESSIONID:+-e NOWSESSIONID=$NOWSESSIONID} \
${NOWUSERAGENT:+-e NOWUSERAGENT=$NOWUSERAGENT} \
${MYTVSUPER_TOKEN:+-e MYTVSUPER_TOKEN=$MYTVSUPER_TOKEN} \
$IMAGE_SOURCE
;;
2|bridge)
echo "正在使用 bridge 网络模式安装 Fourgtv..."
docker run -d --restart always --net=bridge -p $port:8000 --name fourgtv \
${NOWSESSIONID:+-e NOWSESSIONID=$NOWSESSIONID} \
${NOWUSERAGENT:+-e NOWUSERAGENT=$NOWUSERAGENT} \
${MYTVSUPER_TOKEN:+-e MYTVSUPER_TOKEN=$MYTVSUPER_TOKEN} \
$IMAGE_SOURCE
;;
esac
echo -e "${GREEN}Fourgtv 安装完成。${RESET}"
if check_internet_connection; then
install_watchtower "fourgtv"
else
echo "---------------------------------------------------------"
fi
live_Fourgtv "$public_ip" "$port"
}
# 生成 Fourgtv 订阅
live_Fourgtv() {
local public_ip="$1"
local port="$2"
echo "◆ 订阅地址:"
echo "■ iTV : http://$public_ip:$port/itv.m3u (需消耗服务器流量)"
echo "■ Beesport : http://$public_ip:$port/beesport.m3u (部分地区可直连)"
echo "■ 4GTV : http://$public_ip:$port/4gtv.m3u (部分节目需要解锁台湾IP)"
echo "■ MytvSuper : http://$public_ip:$port/mytvsuper.m3u(需填写会员参数)"
echo "■ Now : http://$public_ip:$port/now.m3u (收费频道,需填写会员参数、原生IP)"
echo "■ Now : http://$public_ip:$port/now-free.m3u (免费频道,需填写会员参数、原生IP)"
echo "■ YouTube : http://$public_ip:$port/youtube/{房间号} (支持列表 list/{列表号} )"
echo "---------------------------------------------------------"
echo "--- Fourgtv 详细使用说明: https://t.me/livednowgroup ---"
echo "--- NoobIPTV.sh 脚本日志: https://pixman.io/topics/142 ---"
echo "---------------------------------------------------------"
read -p "按 回车键 返回 主菜单 ..."
}
# 卸载 Fourgtv
uninstall_Fourgtv() {
echo "是否确定要卸载 Fourgtv 项目?[y/n](默认:n)"
read -r -t 10 input
input=${input:-n}
if [[ "$input" =~ ^[Yy]$ ]]; then
echo -e "${CYAN}正在卸载 Fourgtv 项目...${RESET}"
docker stop fourgtv > /dev/null 2>&1
docker rm -f fourgtv > /dev/null 2>&1
docker images --format '{{.Repository}}:{{.Tag}}' | grep 'ru2025/fourgtv:latest' | xargs -r docker rmi > /dev/null 2>&1
uninstall_watchtower "fourgtv"
echo -e "${RED}Fourgtv 项目 已成功卸载。${RESET}"
else
echo -e "${GREEN}取消卸载操作。${RESET}"
fi
}
############# Doubebly #############
# 安装 Doubebly
install_Doubebly() {
local public_ip=$(get_public_ip)
local port_ofiii=$(check_and_allocate_port 50002)
echo -e "${YELLOW}==================================================${RESET}"
echo -e "${YELLOW}提示:如果你使用的是软路由,请移步 Telegram 查看安装教程${RESET}"
echo -e "${CYAN}👉 https://t.me/doubebly003${RESET}"
echo -e "${YELLOW}==================================================${RESET}"
read -rp "是否继续安装 Doubebly?[y/n](默认:n) " confirm_install
confirm_install=${confirm_install:-n}
if [[ ! "$confirm_install" =~ ^[Yy]$ ]]; then
echo -e "${RED}安装已取消。${RESET}"
return
fi
echo "请输入订阅使用的 Token(默认: Doubebly):"
read -rp "Token: " my_token
my_token=${my_token:-Doubebly}
echo "请输入 DNS 解锁 IP(例如 Alice 提供的):"
read -rp "DNS IP(默认: 8.8.8.8): " custom_dns
custom_dns=${custom_dns:-8.8.8.8}
if docker ps -a --format '{{.Names}}' | grep -q "^doube-ofiii$"; then
echo -e "${CYAN}检测到已存在的 doube-ofiii 容器,正在重新部署...${RESET}"
docker stop doube-ofiii >/dev/null 2>&1
docker rm doube-ofiii >/dev/null 2>&1
fi
docker pull doubebly/doube-ofiii:1.1.3
docker run -d --name=doube-ofiii \
-p ${port_ofiii}:5000 \
-e MY_OFIII_TOKEN="${my_token}" \
--restart=always \
--dns=${custom_dns} \
doubebly/doube-ofiii:1.1.3
echo -e "${GREEN}doube-ofiii 安装完成。${RESET}"
if check_internet_connection; then
install_watchtower "doube-ofiii"
else
echo "---------------------------------------------------------"
fi
echo "◆ 订阅地址:"
echo "◆ 直播TXT订阅地址: http://${public_ip}:${port_ofiii}/Sub.txt"
echo "◆ 直播M3U订阅地址: http://${public_ip}:${port_ofiii}/Sub.m3u"
echo "◆ 点播M3U订阅地址: http://${public_ip}:${port_ofiii}/Sub.vod.m3u?pids=ofiii75"
echo
echo "📌 加参数方式示例:"
echo "▶ http://${public_ip}:${port_ofiii}/Sub.m3u?token=${my_token}&sd=720&proxy=true"
echo "▶ http://${public_ip}:${port_ofiii}/Sub.vod.m3u?token=${my_token}&sd=720&proxy=true&pids=ofiii75,ofiii76"
echo "---------------------------------------------------------"
echo "--- Doubebly 详细使用说明: https://t.me/doubebly003 ----"
echo "--- NoobIPTV.sh 脚本日志: https://pixman.io/topics/142 ---"
echo "---------------------------------------------------------"
read -p "按 回车键 返回主菜单 ..."
}
# 卸载 Doubebly
uninstall_Doubebly() {
echo "是否卸载 doube-ofiii 容器?"
echo "1) 卸载 doube-ofiii"
echo "2) 取消操作"
read -rp "输入选项 (1 或 2): " option
option=${option:-1}
if [[ "$option" != "1" ]]; then
echo "已取消卸载操作。"
return
fi
if docker ps -a --format '{{.Names}}' | grep -q "^doube-ofiii$"; then
echo -e "${CYAN}正在卸载 doube-ofiii...${RESET}"
docker stop doube-ofiii > /dev/null 2>&1
docker rm -f doube-ofiii > /dev/null 2>&1
docker images --format '{{.Repository}}:{{.Tag}}' | grep 'doubebly/doube-ofiii' | xargs -r docker rmi > /dev/null 2>&1
uninstall_watchtower "doube-ofiii"
echo -e "${RED}doube-ofiii 已成功卸载。${RESET}"
else
echo -e "${YELLOW}未找到 doube-ofiii 容器,跳过卸载操作。${RESET}"
fi
}
############# watchtower #############
#一键 watchtower 更新
update_watchtower() {
echo "===== 目前运行中的容器 ====="
local running_containers=$(docker ps --format "{{.Names}}")
if [ -n "$running_containers" ]; then
echo "可选容器列表:"
local index=1
all_container_map=()
while IFS= read -r container; do
all_container_map[$index]=$container
echo "$index. $container"
((index++))
done <<< "$running_containers"
echo ""
echo "容器总数: $((index-1))"
read -p "请选择要更新的容器编号: " container_choice
if [[ $container_choice -ge 1 && $container_choice -lt $index ]]; then
local selected_container=${all_container_map[$container_choice]}
echo -e "${CYAN}正在检测容器: $selected_container${RESET}"
local watchtower_output
watchtower_output=$(docker run --rm -v /var/run/docker.sock:/var/run/docker.sock containrrr/watchtower "$selected_container" --run-once -c 2>&1)
local failed scanned updated
if [[ $watchtower_output =~ Failed=([0-9]+) ]]; then
failed="${BASH_REMATCH[1]}"
fi
if [[ $watchtower_output =~ Scanned=([0-9]+) ]]; then
scanned="${BASH_REMATCH[1]}"
fi
if [[ $watchtower_output =~ Updated=([0-9]+) ]]; then
updated="${BASH_REMATCH[1]}"
fi
if [[ $failed -eq 1 && $scanned -eq 1 && $updated -eq 0 ]]; then
echo -e "${RED}检测失败。${RESET}"
elif [[ $failed -eq 0 && $scanned -eq 1 && $updated -eq 0 ]]; then
echo -e "${YELLOW}无需更新。${RESET}"
elif [[ $failed -eq 0 && $scanned -eq 1 && $updated -eq 1 ]]; then
echo -e "${GREEN}更新成功!${RESET}"
else
echo "未知的检测结果。"
fi
else
echo "无效的选择。"
fi
else
echo "没有运行中的容器。"
fi
}
# 管理 Watchtower 监控容器
manage_watchtower() {
declare -A all_container_map
declare -A container_map
show_monitored_containers() {
existing_args=$(docker inspect --format '{{.Args}}' watchtower)
monitored_containers=$(echo "$existing_args" | grep -oP '([a-zA-Z0-9\-]+)' | grep -vE "cleanup|c|s|^0$|^5$|\*")
echo "===== Watchtower 当前监控的容器 ====="
if [ -n "$monitored_containers" ]; then
echo "监控的容器列表:"
local index=1
container_map=()
for container in $monitored_containers; do
container_map[$index]=$container
if docker ps --format "{{.Names}}" | grep -q "^$container$"; then
echo -e "$index. $container (运行中)"
else
echo -e "$index. $container (未运行)"
fi
((index++))
done
echo ""
echo "容器总数: $((index-1))"
else
echo "当前没有监控任何容器。"
return 1
fi
}
show_all_containers() {
echo "===== 目前运行中的容器 ====="
local running_containers=$(docker ps --format "{{.Names}}" | grep -v "^watchtower$")
if [ -n "$running_containers" ]; then
echo "可选容器列表:"
local index=1
all_container_map=()
while IFS= read -r container; do
all_container_map[$index]=$container
echo "$index. $container"
((index++))
done <<< "$running_containers"
echo ""
echo "容器总数: $((index-1))"
return 0
else
echo "当前没有运行中的容器。"
return 1
fi
}
echo "Watchtower - 自动更新 Docker 镜像与容器"
echo -e "请选择操作类型:"
echo "1. 添加监控容器"
echo "2. 删除监控容器"
read -rp "请输入选项 [1/2]:" action
case "$action" in
1) # 添加容器
if show_all_containers; then
read -rp "请输入要添加到监控的容器编号:" number
if [[ $number =~ ^[0-9]+$ ]] && [ -n "${all_container_map[$number]}" ]; then
name=${all_container_map[$number]}
install_watchtower "$name"
else
echo "编号无效,请重试。"
fi
fi
;;
2) # 删除容器
if show_monitored_containers; then
read -rp "请输入要删除的监控容器编号:" number
if [[ $number =~ ^[0-9]+$ ]] && [ -n "${container_map[$number]}" ]; then
name=${container_map[$number]}
uninstall_watchtower "$name"
else
echo "编号无效,请重试。"
fi
fi
;;
*)
echo "无效选项,请选择 1 或 2。"
;;
esac
}
# 增加 watchtower 监控
install_watchtower() {
local name="$1"
if [ -z "$name" ]; then
echo -e "${RED}错误: 未指定要监控的容器名称${RESET}"
return 1
fi
local monitored_containers=""
if docker ps -q -f name=watchtower > /dev/null 2>&1; then
existing_args=$(docker inspect --format '{{.Args}}' watchtower)
monitored_containers=$(echo "$existing_args" | grep -oP '([a-zA-Z0-9\-]+)' | grep -vE "cleanup|c|s|^0$|^5$|\*")
if echo "$monitored_containers" | grep -qw "$name"; then
echo "---------------------------------------------------------"
echo -e "${CYAN}■ 服务器将于每天凌晨五点,进行 $name 检测更新。${RESET}"
echo "---------------------------------------------------------"
return 0
fi
monitored_containers="${monitored_containers:+$monitored_containers }$name"
docker stop watchtower > /dev/null 2>&1
docker rm watchtower > /dev/null 2>&1
else
monitored_containers="$name"
fi
echo "正在安装或配置 Watchtower 并监控 $name 镜像更新..."
IMAGE_SOURCE="containrrr/watchtower"
PROXY_IMAGE_SOURCE="${REVERSE_PROXY}/containrrr/watchtower"
if ! docker pull "$IMAGE_SOURCE" > /dev/null 2>&1; then
echo -e "${CYAN}尝试使用代理拉取镜像...${RESET}"
if ! docker pull "$PROXY_IMAGE_SOURCE" > /dev/null 2>&1; then
echo -e "${RED}安装 watchtower 失败,请检查反向代理或网络连接。${RESET}"
return 1
fi
IMAGE_SOURCE="$PROXY_IMAGE_SOURCE"
fi
if ! docker run -d --name watchtower --restart always -e TZ=Asia/Shanghai -v /var/run/docker.sock:/var/run/docker.sock $IMAGE_SOURCE $monitored_containers -c -s "0 0 5 * * *" > /dev/null 2>&1; then
echo -e "${RED}Watchtower 运行失败,请检查日志。${RESET}"
return 1
fi
echo "---------------------------------------------------------"
echo -e "${CYAN}■ 服务器将于每天凌晨五点,进行 $name 检测更新。${RESET}"
echo "---------------------------------------------------------"
return 0
}
# 删除 Watchtower 监控
uninstall_watchtower() {
local name="$1"
if docker ps -q -f name=watchtower > /dev/null 2>&1; then
existing_args=$(docker inspect --format '{{.Args}}' watchtower)
monitored_containers=$(echo "$existing_args" | grep -oP '([a-zA-Z0-9\-]+)' | grep -vE "cleanup|c|s|^0$|^5$|\*")
if echo "$monitored_containers" | grep -qw "$name"; then
# 移除指定容器名称
monitored_containers=$(echo "$monitored_containers" | sed "s/\b$name\b//g" | xargs)
if [ -z "$monitored_containers" ]; then
echo "没有其他监控的容器,正在停止并删除 Watchtower..."
docker stop watchtower > /dev/null 2>&1
docker rm watchtower > /dev/null 2>&1
docker images --format '{{.Repository}}:{{.Tag}}' | grep 'containrrr/watchtower' | xargs -r docker rmi > /dev/null 2>&1
echo "Watchtower 已成功卸载。"
else
docker stop watchtower > /dev/null 2>&1
docker rm watchtower > /dev/null 2>&1
IMAGE_SOURCE="containrrr/watchtower"
PROXY_IMAGE_SOURCE="${REVERSE_PROXY}/containrrr/watchtower"
if ! docker pull "$IMAGE_SOURCE" > /dev/null 2>&1; then
echo -e "${CYAN}尝试使用代理拉取镜像...${RESET}"
if ! docker pull "$PROXY_IMAGE_SOURCE" > /dev/null 2>&1; then
echo -e "${RED}安装 watchtower 失败,请检查反向代理或网络连接。${RESET}"
return 1
fi
IMAGE_SOURCE="$PROXY_IMAGE_SOURCE"
fi
if ! docker run -d --name watchtower --restart always -e TZ=Asia/Shanghai -v /var/run/docker.sock:/var/run/docker.sock $IMAGE_SOURCE $monitored_containers -c -s "0 0 5 * * *" > /dev/null 2>&1; then
echo -e "${RED}Watchtower 运行失败,请检查日志。${RESET}"
return 1
fi
echo -e "${GREEN}$name${RESET} 容器已从监控中删除。"
fi
else
echo "容器 $name 未被 Watchtower 监控。"
fi
else
echo "Watchtower 当前未安装。"
fi
}
############# 3X-UI #############
# 安装 3X-UI
install_3x_ui() {
local public_ip=$(get_public_ip)
echo "请选择部署方式:"
echo "1) 使用 host 网络模式 (添加节点方便)"
echo "2) 使用 bridge 网络模式 (添加节点,需映射端口)"
echo "3) 使用 sh 脚本 直接安装 (推荐)"
read -rp "输入选项 (1-3): " option
case $option in
1)
echo "正在使用 host 网络模式安装 3X-UI 面板..."
docker run -d \
-e XRAY_VMESS_AEAD_FORCED=false \
-v "$PWD/db/:/etc/x-ui/" \
-v "$PWD/cert/:/root/cert/" \
--network=host \
--restart=unless-stopped \
--name 3x-ui \
ghcr.io/mhsanaei/3x-ui:latest
echo -e "${GREEN}3X-UI 安装完成。${RESET}"
echo "访问信息:"
echo "URL: http://$public_ip:2053"
;;
2)
echo "正在使用 bridge 网络模式安装 3X-UI 面板..."
local default_port=17878
read -rp "请输入要映射的端口 (默认: $default_port): " port
port=${port:-$default_port}
if ! [[ "$port" =~ ^[0-9]+$ ]] || [ "$port" -lt 1024 ] || [ "$port" -gt 65535 ]; then
echo "无效端口。请使用 1024 到 65535 之间的数字。"
return 1
fi
local node_port1=$(generate_random_port)
local node_port2=$(generate_random_port)
local node_port3=$(generate_random_port)
docker run -d \
-e XRAY_VMESS_AEAD_FORCED=false \
-p $port:2053 \
-p $node_port1:$node_port1 \
-p $node_port2:$node_port2 \
-v "$PWD/db/:/etc/x-ui/" \
-v "$PWD/cert/:/root/cert/" \
--restart=unless-stopped \
--name 3x-ui \
ghcr.io/mhsanaei/3x-ui:latest
echo -e "${GREEN}3X-UI 安装完成。${RESET}"
echo "访问信息:"
echo "URL: http://$public_ip:$port"
echo "随机生成两个节点端口,后续自行添加。"
echo "节点端口: $node_port1"
echo "节点端口: $node_port2"
echo "节点端口: $node_port3"
;;
3)
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
echo -e "${GREEN}3X-UI 安装完成。${RESET}"
echo "访问信息:"
echo "输入 x-ui 进行修改设置"
echo "URL: http://$public_ip:2053"
;;
*) echo "无效的选项,请输入 0-3。" ;;
esac
echo "------------------"
echo "默认用户名: admin"
echo "默认密码: admin"
echo "------------------"
echo "请立即更改默认密码!"
echo "------------------"
echo "GIthub: https://github.com/MHSanaei/3x-ui"
echo "------------------"
read -p "按 回车键 返回 主菜单 ..."
}
# 更新 3X-UI
update_3x_ui() {
echo "正在更新 3X-UI 面板至最新版本..."
if docker ps -a | grep -q 3x-ui; then
docker stop 3x-ui > /dev/null 2>&1
docker rm 3x-ui > /dev/null 2>&1
install_3x_ui
echo "3X-UI 面板已更新至最新版本。"
else
echo "错误:未找到 3x-ui 容器。请先安装 3X-UI。"
return 1
fi
}
# 卸载 3X-UI
uninstall_3x_ui() {
read -p "您确定要卸载 3X-UI 面板吗?[y/n](默认:n)" confirm
if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then
echo "卸载操作已取消。"
return
fi
docker stop 3x-ui > /dev/null 2>&1
docker rm 3x-ui > /dev/null 2>&1
docker images --format '{{.Repository}}:{{.Tag}}' | grep 'mhsanaei/3x-ui' | xargs -r docker rmi > /dev/null 2>&1
[ -d "$PWD/db" ] && rm -rf "$PWD/db"
echo -e "${GREEN}3X-UI 卸载完成。${RESET}"
}
############# o11 #############
# 安装 o11
install_o11() {
if docker ps -a --format '{{.Names}}' | grep -q 'o11'; then
echo -e "${RED}o11 已经安装,请先卸载再重新安装。${RESET}"
return 1
fi
ARCH=$(uname -m)
if [[ "$ARCH" != "arm"* && "$ARCH" != "aarch64" ]]; then
echo "系统架构: $ARCH,支持安装 o11。"
echo "正在安装 o11 面板..."
local port=$(check_and_allocate_port 1234)
local public_ip=$(get_public_ip)
docker run -d --restart=always -p $port:1234 --name o11 wechatofficial/o11:latest
echo -e "${GREEN}o11 安装完成。${RESET}"
echo "访问信息:"
echo "URL: http://$public_ip:$port"
echo "小白教程: https://pixman.io/topics/118"
echo "请根据 o11 的文档进行配置和管理。"
read -p "按 回车键 返回 主菜单 ..."
else
echo "不支持的系统架构: $ARCH,o11 安装失败..."
return
fi
}
# 卸载 o11
uninstall_o11() {
local public_ip=$(get_public_ip)
read -p "您确定要卸载 o11 面板吗?[y/n](默认:n)" confirm
if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then
echo "卸载操作已取消。"
return
fi
docker stop o11 > /dev/null 2>&1
docker rm o11 > /dev/null 2>&1
docker images --format '{{.Repository}}:{{.Tag}}' | grep 'wechatofficial/o11' | xargs -r docker rmi > /dev/null 2>&1
echo -e "${GREEN}o11 卸载完成。${RESET}"
}
############# 1Panel #############
# 安装 1Panel
install_1panel() {
echo "正在安装 1Panel 面板..."
curl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_start.sh && sudo bash quick_start.sh
echo "GIthub: https://github.com/1Panel-dev/1Panel"
echo -e "${GREEN}1Panel 安装完成。${RESET}"
}
# 设置 1Panel
set_1panel() {
1pctl user-info
1pctl update password
}
# 卸载 1Panel
uninstall_1panel() {
read -p "您确定要卸载 1Panel 吗?[y/n](默认:n)" confirm
if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then
echo "卸载操作已取消。"
return
fi
if command -v 1pctl > /dev/null 2>&1; then
1pctl uninstall
fi
echo -e "${GREEN}1Panel 卸载完成。${RESET}"
}
############# Sub Store #############
# 安装 Sub Store
install_sub_store() {
local public_ip=$(get_public_ip)
if docker ps -a --format '{{.Names}}' | grep -q 'sub-store'; then
echo -e "${RED}Sub Store 已经安装,请先卸载再重新安装。${RESET}"
return 1
fi
echo "Sub Store 节点订阅管理工具,是否决定安装? (y/n)"
read -r confirmation
if [[ ! "$confirmation" =~ ^[Yy]$ ]]; then
echo "安装已取消。"
return 0
fi
echo "开始安装 Sub Store..."
local IMAGE_SOURCE="xream/sub-store"
local PROXY_IMAGE_SOURCE="$REVERSE_PROXY/xream/sub-store"
local frontend_backend_key=$(openssl rand -base64 15 | tr -dc 'a-zA-Z0-9' | head -c 20)
echo "拉取 Sub Store 镜像中..."
if ! docker pull "$IMAGE_SOURCE" > /dev/null 2>&1; then
echo -e "${CYAN}尝试使用代理拉取镜像...${RESET}"
if ! docker pull "$PROXY_IMAGE_SOURCE" > /dev/null 2>&1; then
echo -e "${RED}安装 Sub Store 失败,请检查反向代理或网络连接。${RESET}"
exit 1
fi
IMAGE_SOURCE="$PROXY_IMAGE_SOURCE"
fi
echo "正在启动 Sub Store 容器..."
if ! docker run -d --restart=always -e "SUB_STORE_CRON=50 23 * * *" -e "SUB_STORE_FRONTEND_BACKEND_PATH=/$frontend_backend_key" -p 3001:3001 -v /etc/sub-store:/opt/app/data --name sub-store "$IMAGE_SOURCE"; then
echo "错误: 容器启动失败" >&2
return 1
fi
echo "Sub Store 安装成功!"
echo "访问地址: http://${public_ip}:3001?api=http://${public_ip}:3001/$frontend_backend_key"
}
# 卸载 Sub Store
uninstall_sub_store() {
read -p "是否卸载 Sub Store?[y/n](默认:n)" confirm
if [[ $confirm == "y" || $confirm == "Y" ]]; then
echo "正在卸载 Sub Store..."
docker stop sub-store > /dev/null 2>&1
docker rm -f sub-store > /dev/null 2>&1
docker images --format '{{.Repository}}:{{.Tag}}' | grep 'xream/sub-store' | xargs -r docker rmi > /dev/null 2>&1
echo -e "${RED}Sub Store 卸载完成。${RESET}"
else
echo -e "${GREEN}取消卸载操作。${RESET}"
fi
}
############# LibreTV #############
install_libretv() {
local public_ip=$(get_public_ip)
if docker ps -a --format '{{.Names}}' | grep -q 'libretv'; then
echo -e "${RED}LibreTV 已经安装,请先卸载再重新安装。${RESET}"
return 1
fi
echo "LibreTV 视频搜索引擎,是否决定安装? (y/n)"
read -r confirmation
if [[ ! "$confirmation" =~ ^[Yy]$ ]]; then
echo "安装已取消。"
return 0
fi
echo "请输入访问密码(可留空,默认无密码):"
read -r password
echo "开始安装 LibreTV..."
local IMAGE_SOURCE="bestzwei/libretv:latest"
local PROXY_IMAGE_SOURCE="$REVERSE_PROXY/bestzwei/libretv:latest"
echo "拉取 LibreTV 镜像中..."
if ! docker pull "$IMAGE_SOURCE" > /dev/null 2>&1; then
echo -e "${CYAN}尝试使用代理拉取镜像...${RESET}"
if ! docker pull "$PROXY_IMAGE_SOURCE" > /dev/null 2>&1; then
echo -e "${RED}安装 LibreTV 失败,请检查反向代理或网络连接。${RESET}"
return 1
fi
IMAGE_SOURCE="$PROXY_IMAGE_SOURCE"
fi
echo "正在启动 LibreTV 容器..."
if ! docker run -d --name libretv --restart=always -p 8899:80 -e PASSWORD="${password}" "$IMAGE_SOURCE"; then
echo "错误: 容器启动失败" >&2
return 1
fi
echo "---------------------------------------------------------"
echo "LibreTV 安装成功!"
echo "访问地址: http://${public_ip}:8899"
if [[ -n "$password" ]]; then
echo "登录密码: ${password}"
else
echo "当前无访问密码保护。"
fi
echo "---------------------------------------------------------"
echo " LibreTV 详细使用说明: https://github.com/LibreSpark/LibreTV"
echo "--- NoobIPTV.sh 脚本日志: https://pixman.io/topics/142 ---"
echo "---------------------------------------------------------"
}
uninstall_libretv() {
read -p "是否卸载 LibreTV?[y/n](默认:n)" confirm
if [[ $confirm == "y" || $confirm == "Y" ]]; then
echo "正在卸载 LibreTV..."
docker stop libretv > /dev/null 2>&1
docker rm -f libretv > /dev/null 2>&1
docker images --format '{{.Repository}}:{{.Tag}}' | grep 'bestzwei/libretv' | xargs -r docker rmi > /dev/null 2>&1
echo -e "${RED}LibreTV 卸载完成。${RESET}"
else
echo -e "${GREEN}取消卸载操作。${RESET}"
fi
}
############# sing-box #############
# 一键搭建节点
install_233boy() {
echo "欢迎使用一键搭建节点脚本!"
echo "此脚本将从 233boy 仓库安装 sing-box,请确保您信任此来源。"
read -p "继续安装?(y/n): " confirm
if [[ $confirm == "y" || $confirm == "Y" ]]; then
echo "正在下载并运行安装脚本..."
bash <(wget -qO- https://github.com/233boy/sing-box/raw/main/install.sh)
else
echo "安装已取消。"
fi
}
############# Alice 解锁 #############
# 一键搭建 Alice DNS解锁
install_Jimmy() {
echo "欢迎使用一键 Alice DNS解锁 脚本!"
echo "此脚本将从 Jimmyzxk 仓库安装 Alice 解锁,请确保您信任此来源。"
read -p "继续安装?(y/n): " confirm
if [[ $confirm == "y" || $confirm == "Y" ]]; then
echo "正在下载并运行安装脚本..."
wget https://raw.githubusercontent.com/Jimmyzxk/DNS-Alice-Unlock/refs/heads/main/dns-unlock.sh && bash dns-unlock.sh
echo "详细使用说明: https://www.nodeseek.com/post-202393-1"
else
echo "安装已取消。"
fi
}
############# 辅助函数 #############
# 拉取镜像
pull_image() {
local image=$1
local proxy_image=$2
if ! docker pull "$image" > /dev/null 2>&1; then
echo -e "${CYAN}尝试使用代理拉取镜像...${RESET}"
if ! docker pull "$proxy_image" > /dev/null 2>&1; then
echo -e "${RED}安装失败,请检查反向代理或网络连接。${RESET}"
exit 1
fi
docker tag "$proxy_image" "$image"
docker rmi "$proxy_image"
fi
}
# 检查 访问境外 是否受限
check_internet_connection() {
if curl -s --connect-timeout 5 --max-time 10 --retry 2 google.com > /dev/null 2>&1; then
return 0 # 无受限
else
return 1 # 受限
fi
}
# 获取公网 IP / 失败返回 {路由IP}
get_public_ip() {
# IPv4
ip=$(curl -s --max-time 3 https://ipv4.icanhazip.com | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}')
if [[ -n "$ip" ]]; then
echo "$ip"
return 0
fi
# IPv6
ip=$(curl -s --max-time 3 https://ipv6.icanhazip.com | grep -oE '([0-9a-fA-F:]+:+)+[0-9a-fA-F]+')
if [[ -n "$ip" ]]; then
echo "$ip"
return 0
fi
echo "{路由IP}"
return 1
}
# 检查 IP 归属地
check_if_in_china() {
local ip="$1"
local response
response=$(curl -s --max-time 3 "http://ip-api.com/json/$ip")
if echo "$response" | grep -qiE '"country"[[:space:]]*:[[:space:]]*"?(CN|China)"?|中国'; then
return 0
fi
return 1
}
# 检查 Docker 是否安装
check_docker() {
if ! command -v docker &> /dev/null; then
echo -e "${CYAN}Docker 未安装,正在进行安装...${RESET}"
install_docker
else
echo -e "${GREEN}Docker 已安装。${RESET}"
fi
}
# 选择 Docker 版本
install_docker() {
OS=$(lsb_release -is 2>/dev/null || cat /etc/os-release | grep '^ID=' | cut -d= -f2 | tr -d '"')
ARCH=$(uname -m)
case "$OS" in
Ubuntu)
echo "检测到系统为 Ubuntu,正在安装 Docker..."
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository "deb [arch=$(dpkg --print-architecture)] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
sudo apt-get update
sudo apt-get install -y docker-ce
;;
Debian|Armbian)
echo "检测到系统为 Debian 或 Armbian,正在安装 Docker..."
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl software-properties-common
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add -
sudo add-apt-repository "deb [arch=$(dpkg --print-architecture)] https://download.docker.com/linux/debian $(lsb_release -cs) stable"
sudo apt-get update
sudo apt-get install -y docker-ce
;;
centos|rhel|fedora)
echo "检测到系统为 CentOS,正在安装 Docker..."
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
sudo yum install -y docker-ce docker-ce-cli containerd.io
sudo systemctl start docker
sudo systemctl enable docker
;;
openwrt|lede)
echo "检测到系统为 OpenWRT/LEDE,正在安装 Docker..."
opkg update
opkg install dockerd docker-compose luci-app-dockerman
/etc/init.d/dockerd start
/etc/init.d/dockerd enable
;;
*)
echo "不支持的操作系统: $OS"
exit 1
;;
esac
# 如果不是 OpenWRT/LEDE,则启动和启用 Docker
if [[ "$OS" != "openwrt" && "$OS" != "lede" ]]; then
sudo systemctl start docker
sudo systemctl enable docker
fi
echo -e "${GREEN}Docker 安装完成。${RESET}"
}
# 检查 jq 工具 是否安装
check_and_install_jq() {
if ! command -v jq &> /dev/null; then
echo "jq 工具未安装,正在安装..."
if check_if_in_china; then
INSTALL_CMD="sudo apt-get update && sudo apt-get install -y jq --allow-releaseinfo-change"
elif command -v apt-get &> /dev/null; then
INSTALL_CMD="sudo apt-get update && sudo apt-get install -y jq"
elif command -v yum &> /dev/null; then
INSTALL_CMD="sudo yum install -y jq"
elif command -v apk &> /dev/null; then
INSTALL_CMD="sudo apk add --no-cache jq"
elif command -v opkg &> /dev/null; then # OpenWrt, Entware 环境
INSTALL_CMD="opkg update && opkg install jq"
else
echo "无法识别该系统的包管理器,jq 安装失败。"
return 1 # 无法识别包管理器,安装失败
fi
if ! eval "$INSTALL_CMD"; then
echo "安装 jq 失败,请检查系统配置,将影响 参数 功能。"
return 1 # 安装失败
fi
else
return 0 # jq 已安装
fi
}
# 检查 grep 工具 是否安装
check_and_install_grep() {
if ! command -v grep &> /dev/null; then
echo "grep 工具未安装,正在安装..."
if check_if_in_china; then
INSTALL_CMD="apt-get update && sudo apt-get install -y grep --allow-releaseinfo-change"
elif command -v apt-get &> /dev/null; then
INSTALL_CMD="sudo apt-get update && sudo apt-get install -y grep"
elif command -v yum &> /dev/null; then
INSTALL_CMD="sudo yum install -y grep"
elif command -v apk &> /dev/null; then
INSTALL_CMD="sudo apk add --no-cache grep"
elif command -v opkg &> /dev/null; then # OpenWrt, Entware 环境
INSTALL_CMD="opkg update && opkg install grep"
else
echo "安装 grep 失败,请检查系统配置,将影响 Watchtower 功能。"
return 1 # 安装失败
fi
# 执行安装命令
if ! eval "$INSTALL_CMD"; then
echo "安装 grep 失败,请检查系统配置,将影响 Watchtower 功能。"
return 1 # 安装失败
fi
else
return 0 # grep 已安装
fi
}
# 设置反向代理参数
proxy() {
source "$CONFIG_FILE"
read -p "请输入反向代理地址 (当前值: ${REVERSE_PROXY:-未设置}, 输入null清空): " input_reverse_proxy
if [ -n "$input_reverse_proxy" ]; then
[ "$input_reverse_proxy" = "null" ] && REVERSE_PROXY="" || REVERSE_PROXY="$input_reverse_proxy"
fi
echo "反向代理地址已更新为: ${REVERSE_PROXY:-<空>}"
echo "REVERSE_PROXY=${REVERSE_PROXY:-}" > "$CONFIG_FILE"
}
# 清理 Docker 工具
cleanup_docker() {
echo -e "\n${YELLOW}┌─────────────────── Docker 完全清理 ───────────────────┐${RESET}"
echo -e "${YELLOW}${RESET} 此操作将执行: ${YELLOW}${RESET}"
echo -e "${YELLOW}${RESET} • 删除所有已停止的容器 ${YELLOW}${RESET}"
echo -e "${YELLOW}${RESET} • 删除所有未使用的镜像和构建缓存 ${YELLOW}${RESET}"
echo -e "${YELLOW}${RESET} • 删除所有未使用的卷和网络 ${YELLOW}${RESET}"
echo -e "${YELLOW}${RESET} • 清空所有容器的日志文件 ${YELLOW}${RESET}"
echo -e "${YELLOW}└───────────────────────────────────────────────────────┘${RESET}"
echo -e "\n${RED} 警告:此操作将删除大量数据,且无法恢复!${RESET}"
read -p "$(echo -e "${CYAN}确认执行完全清理? (y/n,默认n): ${RESET}")" confirm
confirm=${confirm:-n}
if [[ "$confirm" != "y" ]]; then
echo -e "\n${YELLOW}清理操作已取消${RESET}"
read -p "$(echo -e "${CYAN}按回车键返回主菜单...${RESET}")"
return
fi
# 统计数据
container_count=0
cleaned_logs=0
total_freed=0
# 第1步:清理容器日志
echo -e "\n${YELLOW}[1/2] 正在清理容器日志...${RESET}"
for container_id in $(docker ps -aq); do
container_count=$((container_count+1))
container_name=$(docker inspect --format '{{.Name}}' $container_id | sed 's/\///')
log_path=$(docker inspect --format='{{.LogPath}}' $container_id)
if [ -f "$log_path" ]; then
log_size=$(du -b "$log_path" | awk '{print $1}')
total_freed=$((total_freed + log_size))
if [ $log_size -ge 1073741824 ]; then
log_size_h=$(echo "scale=2; $log_size/1073741824" | bc)
log_size_h="${log_size_h} GB"
elif [ $log_size -ge 1048576 ]; then
log_size_h=$(echo "scale=2; $log_size/1048576" | bc)
log_size_h="${log_size_h} MB"
elif [ $log_size -ge 1024 ]; then
log_size_h=$(echo "scale=2; $log_size/1024" | bc)
log_size_h="${log_size_h} KB"
else
log_size_h="${log_size} bytes"
fi
echo -e "${GREEN}${RESET} 清理容器 ${CYAN}${container_name}${RESET} 日志 (${log_size_h})"
echo "" > "$log_path"
cleaned_logs=$((cleaned_logs+1))
fi
done
if [ $total_freed -ge 1073741824 ]; then
total_freed_h=$(echo "scale=2; $total_freed/1073741824" | bc)
total_freed_h="${total_freed_h} GB"
elif [ $total_freed -ge 1048576 ]; then
total_freed_h=$(echo "scale=2; $total_freed/1048576" | bc)
total_freed_h="${total_freed_h} MB"
elif [ $total_freed -ge 1024 ]; then
total_freed_h=$(echo "scale=2; $total_freed/1024" | bc)
total_freed_h="${total_freed_h} KB"
else
total_freed_h="${total_freed} bytes"
fi
# 第2步:执行Docker系统清理
echo -e "\n${YELLOW}[2/2] 正在执行Docker系统清理...${RESET}"
docker_prune_output=$(docker system prune -a --volumes -f)
# 总结结果
echo -e "\n${GREEN}══════════════ 清理完成 ══════════════${RESET}"
echo -e "${GREEN}• 检查了 ${container_count} 个容器${RESET}"
echo -e "${GREEN}• 清理了 ${cleaned_logs} 个日志文件 (释放约 ${total_freed_h})${RESET}"
echo -e "${GREEN}• 执行了Docker系统完全清理${RESET}"
echo -e "${GREEN}══════════════════════════════════════${RESET}"
echo
read -p "$(echo -e "${CYAN}按回车键返回主菜单...${RESET}")"
}
# 生成随机端口
generate_random_port() {
local port
while :; do
port=$(shuf -i 10000-65535 -n 1)
if ! ss -tuln | grep -q ":$port "; then
echo "$port"
break
fi
done
}
# 检查端口
check_and_allocate_port() {
local port=$1
if ss -tuln | grep -q ":$port "; then
echo "端口 $port 已被占用,正在分配新的端口..."
port=$(generate_random_port)
fi
echo "$port"
}
# # 检查并更新 SH 脚本
download_NoobIPTV() {
REMOTE_VERSION=$(curl -s "https://yang-1989.eu.org/NoobIPTV_version.txt")
if [ $? -ne 0 ]; then
echo -e "${RED}无法检测版本,请检查网络连接。${RESET}"
return
fi
if [ -f "$SCRIPT_PATH" ]; then
LOCAL_VERSION=$(grep -oP '(?<=^# 最新版本:).*' "$SCRIPT_PATH")
else
LOCAL_VERSION=""
fi
if [ "$REMOTE_VERSION" != "$LOCAL_VERSION" ]; then
echo "${GREEN}正在下载最新版本的 NoobIPTV 脚本...${RESET}"
curl -o "$SCRIPT_PATH" "https://yang-1989.eu.org/NoobIPTV.sh"
chmod +x "$SCRIPT_PATH"
echo -e "${GREEN}最新 $REMOTE_VERSION 版本下载已完成。${RESET}"
fi
}
# 设置快捷键
setup_shortcut() {
local script_path="$HOME/NoobIPTV.sh"
echo "脚本路径: $script_path"
curl -sL https://yang-1989.eu.org/NoobIPTV.sh -o "$script_path"
chmod +x "$script_path"
local shell_rc="$HOME/.bashrc"
echo "配置文件: $shell_rc"
if [ -n "$shell_rc" ] && ! grep -q "alias y='bash $script_path'" "$shell_rc"; then
echo "alias y='bash $script_path'" >> "$shell_rc"
echo -e "${GREEN}已设置快捷键 'y'。${RESET}"
source "$shell_rc" 2>/dev/null || true
echo -e "${GREEN}快捷键已生效!现在可以使用 'y' 命令启动脚本。${RESET}"
fi
}
# 展示广告
show_NoobIPTV() {
echo -e "${CYAN}───────────────────────────────────────────────────────────────────────${RESET}
${RED} ███╗ ██╗ ██████╗ ██████╗ ██████╗ ██╗██████╗ ████████╗██╗ ██╗${RESET}
${RED} ████╗ ██║██╔═══██╗██╔═══██╗██╔══██╗██║██╔══██╗╚══██╔══╝██║ ██║${RESET}
${RED} ██╔██╗ ██║██║ ██║██║ ██║██████╔╝██║██████╔╝ ██║ ██║ ██║${RESET}
${RED} ██║╚██╗██║██║ ██║██║ ██║██╔══██╗██║██╔═══╝ ██║ ╚██╗ ██╔╝${RESET}
${RED} ██║ ╚████║╚██████╔╝╚██████╔╝██████╔╝██║██║ ██║ ╚████╔╝ ${RESET}
${RED} ╚═╝ ╚═══╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚═╝╚═╝ ╚═╝ ╚═══╝ ${RESET}
${GREEN} 欢迎关注我们的 ${YELLOW}Telegram ${GREEN}频道: ${CYAN}@Y_anGGGGGG${RESET}
${CYAN}───────────────────────────────────────────────────────────────────────${RESET}
${YELLOW} IPTV项目小白必备的搭建脚本和便捷工具箱,输入 ${GREEN}y${YELLOW} 快捷启动!${RESET}"
}
# 检查是否是第一次运行
check_first_run() {
local config_dir="$HOME/.config/NoobIPTV"
local first_run_flag="$config_dir/initialized"
if [ ! -d "$config_dir" ]; then
mkdir -p "$config_dir"
fi
if [ ! -f "$first_run_flag" ]; then
echo -e "${CYAN}首次运行,正在进行初始化设置...${RESET}"
[ ! -f "$CONFIG_FILE" ] && echo "REVERSE_PROXY=$REVERSE_PROXY" > "$CONFIG_FILE" # 设置配置文件
setup_shortcut # 设置快捷键
touch "$first_run_flag"
fi
}
# 脚本信息
script_log() {
show_NoobIPTV
echo "------------------------------------------------"
echo "项目名称:NoobIPTV"
echo "项目地址:https://github.com/YanG-1989"
echo "脚本日志: https://pixman.io/topics/142"
echo "作者: YanG-1989"
echo "当前版本号: $(grep -oP '(?<=^# 最新版本:).*' "$SCRIPT_PATH")"
echo "最后更新时间: 2024.5.15"
echo "1) 优化 Docker 管理助手 "
echo "2) 新增 LibreTV 快捷部署"
echo "3) 修复 Fourgtv 项目 作者: @刘墉 "
echo "4) 更新 Doubebly 项目 作者: @沐辰 "
echo "------------------------------------------------"
read -p "按 回车键 返回 主菜单 ..."
}
############# 主程序逻辑 #############
show_NoobIPTV
check_first_run # 检查是否是第一次运行
download_NoobIPTV # 检查并更新 SH 脚本
[ -f "$CONFIG_FILE" ] && source "$CONFIG_FILE" # 加载配置文件中的参数
# 主循环
while true; do
show_menu
read -p "请选择操作: " choice
case "$choice" in
1) # 部署 pixman
while true; do
show_pixman_menu
read -p "请输入选项 (0-3): " pixman_choice
case "$pixman_choice" in
1) check_docker ; judge_Pixman ;;
2) uninstall_Pixman ;;
3) proxy ;;
0) echo "返回主菜单。" ; break ;;
*) echo "无效的选项,请输入 0-3。" ;;
esac
done
;;
2) # 部署 Fourgtv
while true; do
show_fourgtv_menu
read -p "请输入选项 (0-3): " fourgtv_choice
case "$fourgtv_choice" in
1) check_docker ; install_Fourgtv ;;
2) uninstall_Fourgtv ;;
3) proxy ;;
0) echo "返回主菜单。" ; break ;;
*) echo "无效的选项,请输入 0-3。" ;;
esac
done
;;
3) # 部署 Doubebly
while true; do
show_doubebly_menu
read -p "请输入选项 (0-3): " doubebly_choice
case "$doubebly_choice" in
1) check_docker ; install_Doubebly ;;
2) uninstall_Doubebly ;;
3) proxy ;;
0) echo "返回主菜单。" ; break ;;
*) echo "无效的选项,请输入 0-3。" ;;
esac
done
;;
4) # 管理 Docker
while true; do
show_watchtower_menu
read -p "请输入选项 (0-4): " watchtower_choice
case "$watchtower_choice" in
1) # 手动 watchtower
if check_internet_connection; then
update_watchtower
else
echo -e "\n${RED} 网络连接异常,无法执行更新操作${RESET}"
echo -e "${YELLOW}请检查网络连接后再尝试此功能${RESET}"
echo -e "按任意键继续..."
read -n 1
fi
;;
2) # 管理 watchtower
if check_internet_connection; then
manage_watchtower
else
echo -e "\n${RED} 网络连接异常,无法执行管理操作${RESET}"
echo -e "${YELLOW}请检查网络连接后再尝试此功能${RESET}"
echo -e "按任意键继续..."
read -n 1
fi
;;
3) cleanup_docker ;; # 清理 Docker 垃圾
4) # 设置 Docker 全局日志大小
curl -L -s https://yang-1989.eu.org/docker.sh | sudo bash
echo -e "\n配置完成! 按任意键继续..."
read -n 1
;;
0) echo "返回主菜单。" ; break ;;
*) echo "无效的选项,请输入 0-4。" ;;
esac
done
;;
5) # 工具箱
while true; do
show_toolbox_menu
read -p "请输入选项 (0-7): " toolbox_choice
case "$toolbox_choice" in
1) # 1Panel
while true; do
show_1panel_menu
read -p "请输入选项 (0-3): " panel_choice
case "$panel_choice" in
1) install_1panel ;;
2) uninstall_1panel ;;
3) set_1panel ;;
0) echo "返回上级菜单。" ; break ;;
*) echo "无效的选项,请输入 0-3。" ;;
esac
done
;;
2) # o11
while true; do
show_o11_menu
read -p "请输入选项 (0-2): " o_choice
case "$o_choice" in
1) check_docker ; install_o11 ;;
2) uninstall_o11 ;;
0) echo "返回上级菜单。" ; break ;;
*) echo "无效的选项,请输入 0-2。" ;;
esac
done
;;
3) # 3X-UI
while true; do
show_3x_ui_menu
read -p "请输入选项 (0-3): " ui_choice
case "$ui_choice" in
1) check_docker ; install_3x_ui ;;
2) update_3x_ui ;;
3) uninstall_3x_ui ;;
0) echo "返回上级菜单。" ; break ;;
*) echo "无效的选项,请输入 0-3。" ;;
esac
done
;;
4) # Sub Store
while true; do
show_subs_menu
read -p "请输入选项 (0-2): " Sub_choice
case "$Sub_choice" in
1) echo check_docker ; install_sub_store ;;
2) echo uninstall_sub_store ;;
0) echo "返回上级菜单。" ; break ;;
*) echo "无效的选项,请输入 0-2。" ;;
esac
done
;;
5) # LibreTV
while true; do
show_libretv_menu
read -p "请输入选项 (0-2): " LibreTV_choice
case "$LibreTV_choice" in
1) check_docker ; install_libretv ;;
2) echo uninstall_libretv ;;
0) echo "返回上级菜单。" ; break ;;
*) echo "无效的选项,请输入 0-2。" ;;
esac
done
;;
6) install_233boy ;; # sing-box
7) install_Jimmy ;; # Alice DNS
0) echo "返回主菜单。" ; break ;;
*) echo "无效的选项,请输入 0-7。" ;;
esac
done
;;
6) script_log ;;
0) echo "退出脚本。" ; exit 0 ;;
*) echo "无效的选项,请输入 0-6。" ;;
esac
done