|
|
#!/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
|
|
|
|