From 6ce74cfc077c684274d1e04c782e7e98ada94407 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A7=E8=91=B1?= <109458285+YanG-1989@users.noreply.github.com> Date: Tue, 22 Oct 2024 17:48:12 +0800 Subject: [PATCH] Add files via upload --- Pixman.sh | 1387 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1387 insertions(+) create mode 100644 Pixman.sh diff --git a/Pixman.sh b/Pixman.sh new file mode 100644 index 0000000..d48a7db --- /dev/null +++ b/Pixman.sh @@ -0,0 +1,1387 @@ +#!/bin/bash +############################### + +# @小白直播搭建脚本 +# 项目地址:https://pixman.io/ +# 最新版本:1.9.5 + +############################### + +# 设置路径 +SCRIPT_PATH="$HOME/pixman.sh" # 定义脚本路径 +CONFIG_FILE="$HOME/.pixman" # 配置文件路径 + +# 设置默认环境变量 +REVERSE_PROXY="dockerpull.com" # 设置反向代理地址 +CRON_SCHEDULE="0 12 * * *" # 默认定时任务时间 +PORT="52055" # 默认端口 +MYTVSUPER_TOKEN="" # myTV 参数 +HAMI_SESSION_ID="" # Hami 参数 +HAMI_SERIAL_NO="" # Hami 参数 +HAMI_SESSION_IP="" # Hami 参数 +HTTP_PROXY="" # 设置代理 +HTTPS_PROXY="" # 设置代理 + + +# 设置颜色变量 +RED="\033[1;31m" # 红 +GREEN="\033[1;32m" # 绿 +YELLOW="\033[1;33m" # 黄 +CYAN="\033[1;36m" # 青 +RESET="\033[0m" # 重置 + +# echo -e "${CYAN}这是绿色粗体文本。${RESET}" + +############# 菜单 ############# + +# 显示 菜单 +show_menu() { + echo "-------------------" + echo " @小白直播搭建工具 " + echo " 输入 y 快捷启动 " + echo "-------------------" + echo " 请选择一个项目: " + echo "-------------------" + echo "1) Pixman 项目 " + echo "2) Allinone 项目 " + echo "~~~~~~~~~~~~~~~~~~~" + echo "3) -- 工具箱 -- " + echo "~~~~~~~~~~~~~~~~~~~" + echo "4) ~~ 脚本信息 ~~ " + echo "-------------------" + echo "0) 退出 " + echo " [ Ctrl+C ] " + echo "-------------------" + +} + +# Pixman 菜单 +show_pixman_menu() { + echo "-------------------" + echo " Pixman 菜单: " + echo "-------------------" + echo "1) 安装 Pixman 项目" + echo "2) 修改 Pixman 参数" + echo "3) 生成 Pixman 订阅" + echo "4) 转换 myTV 订阅" + echo "5) 卸载 Pixman 项目" + echo "-------------------" + echo "0) 返回主菜单 " + echo "-------------------" +} + +# allinone 菜单 +show_allinone_menu() { + echo "---------------------" + echo " Allinone 菜单: " + echo "---------------------" + echo "1) 安装 Allinone 项目" + echo "2) 安装 av3a 助手" + echo "3) 设置 IPTV 启停" + echo "4) 设置 反向代理 地址 " + echo "5) 卸载 Allinone 项目" + echo "---------------------" + echo "0) 返回主菜单 " + echo "---------------------" +} + +# 工具箱 菜单 +show_toolbox_menu() { + echo "-------------------" + echo " 工具箱菜单: " + echo "-------------------" + echo "1) 1Panle 面板 " + echo "2) [Docker] o11 " + echo "3) [Docker] 3X-UI " + echo "~~~~~~~~~~~~~~~~~~~" + echo "4) Docker 一键清理 " + 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 "-------------------" +} + +############# Pixman ############# + +# 加载 Pixman 参数 +load_parameters() { + if [ -e "$CONFIG_FILE" ]; then + source "$CONFIG_FILE" + else + if docker ps -a --format '{{.Names}}' | grep -q "^pixman$"; then + check_and_install_jq + extract_container_parameters + source "$CONFIG_FILE" + else + return 1 + fi + fi +} + +# 提取 Pixman 参数 +extract_container_parameters() { + container_info=$(docker inspect "pixman") + + PORT=$(echo "$container_info" | jq -r '.[0].HostConfig.PortBindings."5000/tcp"[0].HostPort // empty') + + if [ -z "$PORT" ]; then + PORT=5000 + fi + + MYTVSUPER_TOKEN=$(echo "$container_info" | jq -r '.[0].Config.Env[] | select(startswith("MYTVSUPER_TOKEN="))' | cut -d= -f2) + HAMI_SESSION_ID=$(echo "$container_info" | jq -r '.[0].Config.Env[] | select(startswith("HAMI_SESSION_ID="))' | cut -d= -f2) + HAMI_SERIAL_NO=$(echo "$container_info" | jq -r '.[0].Config.Env[] | select(startswith("HAMI_SERIAL_NO="))' | cut -d= -f2) + HAMI_SESSION_IP=$(echo "$container_info" | jq -r '.[0].Config.Env[] | select(startswith("HAMI_SESSION_IP="))' | cut -d= -f2) + HTTP_PROXY=$(echo "$container_info" | jq -r '.[0].Config.Env[] | select(startswith("HTTP_PROXY="))' | cut -d= -f2) + HTTPS_PROXY=$(echo "$container_info" | jq -r '.[0].Config.Env[] | select(startswith("HTTPS_PROXY="))' | cut -d= -f2) + + echo "PORT=$PORT" > "$CONFIG_FILE" + echo "MYTVSUPER_TOKEN=$MYTVSUPER_TOKEN" >> "$CONFIG_FILE" + echo "HAMI_SESSION_ID=$HAMI_SESSION_ID" >> "$CONFIG_FILE" + echo "HAMI_SERIAL_NO=$HAMI_SERIAL_NO" >> "$CONFIG_FILE" + echo "HAMI_SESSION_IP=$HAMI_SESSION_IP" >> "$CONFIG_FILE" + echo "HTTP_PROXY=$HTTP_PROXY" >> "$CONFIG_FILE" + echo "HTTPS_PROXY=$HTTPS_PROXY" >> "$CONFIG_FILE" +} + +# 保存 Pixman 参数 +save_parameters() { + { + echo "REVERSE_PROXY=$REVERSE_PROXY" + echo "SCRIPT_PATH=$SCRIPT_PATH" + [ -n "$PORT" ] && echo "PORT=$PORT" + [ -n "$CRON_SCHEDULE" ] && echo "CRON_SCHEDULE=$CRON_SCHEDULE" + [ -n "$MYTVSUPER_TOKEN" ] && echo "MYTVSUPER_TOKEN=$MYTVSUPER_TOKEN" + [ -n "$HAMI_SESSION_ID" ] && echo "HAMI_SESSION_ID=$HAMI_SESSION_ID" + [ -n "$HAMI_SERIAL_NO" ] && echo "HAMI_SERIAL_NO=$HAMI_SERIAL_NO" + [ -n "$HAMI_SESSION_IP" ] && echo "HAMI_SESSION_IP=$HAMI_SESSION_IP" + [ -n "$HTTP_PROXY" ] && echo "HTTP_PROXY=$HTTP_PROXY" + [ -n "$HTTPS_PROXY" ] && echo "HTTPS_PROXY=$HTTPS_PROXY" + } > "$CONFIG_FILE" +} + +# 设置 Pixman 参数 +set_parameters() { + local original_port="$PORT" + local original_token="$MYTVSUPER_TOKEN" + local original_session_id="$HAMI_SESSION_ID" + local original_serial_no="$HAMI_SERIAL_NO" + local original_session_ip="$HAMI_SESSION_IP" + local original_http_proxy="$HTTP_PROXY" + local original_https_proxy="$HTTPS_PROXY" + + read -p "请输入反向代理地址 (回车跳过保持当前值: $REVERSE_PROXY): " input_reverse_proxy + [ -n "$input_reverse_proxy" ] && REVERSE_PROXY="$input_reverse_proxy" + + read -p "请确认脚本路径 (回车跳过保持当前值: $SCRIPT_PATH): " input_path + [ -n "$input_path" ] && SCRIPT_PATH="$input_path" + + read -p "请输入定时任务时间 (cron格式,回车跳过保持当前值: $CRON_SCHEDULE): " input_cron + [ -n "$input_cron" ] && CRON_SCHEDULE="$input_cron" + + read -p "请输入端口 (回车跳过保持当前值: $PORT): " input_port + [ -n "$input_port" ] && PORT="$input_port" + + read -p "请输入 MYTVSUPER_TOKEN (回车跳过保持当前值: $MYTVSUPER_TOKEN): " input_token + [ -n "$input_token" ] && MYTVSUPER_TOKEN="$input_token" + + read -p "请输入 HAMI_SESSION_ID (回车跳过保持当前值: $HAMI_SESSION_ID): " input_id + [ -n "$input_id" ] && HAMI_SESSION_ID="$input_id" + + read -p "请输入 HAMI_SERIAL_NO (回车跳过保持当前值: $HAMI_SERIAL_NO): " input_serial + [ -n "$input_serial" ] && HAMI_SERIAL_NO="$input_serial" + + read -p "请输入 HAMI_SESSION_IP (回车跳过保持当前值: $HAMI_SESSION_IP): " input_ip + [ -n "$input_ip" ] && HAMI_SESSION_IP="$input_ip" + + read -p "请输入 HTTP_PROXY (回车跳过保持当前值: $HTTP_PROXY): " input_http_proxy + [ -n "$input_http_proxy" ] && HTTP_PROXY="$input_http_proxy" + + read -p "请输入 HTTPS_PROXY (回车跳过保持当前值: $HTTPS_PROXY): " input_https_proxy + [ -n "$input_https_proxy" ] && HTTPS_PROXY="$input_https_proxy" + + save_parameters + + if [[ "$PORT" != "$original_port" || \ + "$MYTVSUPER_TOKEN" != "$original_token" || \ + "$HAMI_SESSION_ID" != "$original_session_id" || \ + "$HAMI_SERIAL_NO" != "$original_serial_no" || \ + "$HAMI_SESSION_IP" != "$original_session_ip" || \ + "$HTTP_PROXY" != "$original_http_proxy" || \ + "$HTTPS_PROXY" != "$original_https_proxy" ]]; then + echo -e "${CYAN}检测到参数变化,正在卸载旧的 Pixman 容器...${RESET}" + docker rm -f pixman + check_update + else + echo -e "${CYAN}参数未发生变化,无需重启 Pixman 容器${RESET}" + return 0 + fi +} + +# 设置 Pixman 自动更新 +set_cron_job() { + (crontab -l 2>/dev/null | grep -v "$SCRIPT_PATH --auto"; echo "$CRON_SCHEDULE $SCRIPT_PATH --auto") | crontab - +} + +# 判断 Pixman 容器 +check_update() { + echo -e "${CYAN}检查更新...${RESET}" + + IMAGE_SOURCE="pixman/pixman:latest" + PROXY_IMAGE_SOURCE="$REVERSE_PROXY/pixman/pixman:latest" + + if docker ps -a --format '{{.Names}}' | grep -q "^pixman$"; then + current_image_version=$(docker inspect --format='{{index .Config.Labels "org.opencontainers.image.version"}}' pixman) + + MODE=$(docker inspect --format='{{.HostConfig.NetworkMode}}' pixman) + + 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}安装 Pixman 失败,请检查代理或网络连接。${RESET}" + exit 1 + fi + IMAGE_SOURCE="$PROXY_IMAGE_SOURCE" + fi + + latest_image_version=$(docker inspect --format='{{index .Config.Labels "org.opencontainers.image.version"}}' "$IMAGE_SOURCE") + + if [ "$current_image_version" != "$latest_image_version" ]; then + echo -e "${GREEN}发现新版本 ($latest_image_version),正在更新...${RESET}" + docker rm -f pixman + docker rmi -f "$IMAGE_SOURCE" + docker pull "$IMAGE_SOURCE" + start_container "$IMAGE_SOURCE" "$MODE" + else + echo -e "${GREEN}当前版本 ($current_image_version),无需更新...${RESET}" + fi + else + if ! docker images --format '{{.Repository}}:{{.Tag}}' | grep -q "^pixman/pixman:latest$"; then + if ! docker pull "$IMAGE_SOURCE" > /dev/null 2>&1; then + echo "尝试使用代理..." + if ! docker pull "$PROXY_IMAGE_SOURCE" > /dev/null 2>&1; then + echo "使用代理拉取失败,请检查代理或网络连接。" + return 1 + fi + latest_image_version=$(docker inspect --format='{{index .Config.Labels "org.opencontainers.image.version"}}' "$IMAGE_SOURCE") + echo -e "${GREEN}目前版本 ($latest_image_version),正在安装...${RESET}" + IMAGE_SOURCE="$PROXY_IMAGE_SOURCE" + fi + fi + start_container "$IMAGE_SOURCE" + fi +} + +# 部署 Pixman 容器 +start_container() { + local image_source="$1" + local mode="$2" + local port="${PORT:-52055}" + + echo -e "${CYAN}启动 Pixman 容器...${RESET}" + + if [ "$mode" != "bridge" ] && [ "$mode" != "host" ]; then + echo "请选择 Docker 模式:" + echo "1. Bridge 模式 (默认)" + echo "2. Host 模式" + + read -p "输入选择 [1/2]: " user_choice + mode="bridge" + [[ "$user_choice" == "2" ]] && mode="host" + fi + + if [[ "$mode" == "host" ]]; then + echo "目前使用 host 模式,默认端口: 5000。" + docker_command="docker run -d --name pixman --restart always --net=host" + else + echo "目前使用 bridge 模式,默认端口: $port 。" + docker_command="docker run -d --name pixman --restart always -p $port:5000" + fi + + [ -n "$MYTVSUPER_TOKEN" ] && docker_command+=" -e MYTVSUPER_TOKEN=$MYTVSUPER_TOKEN" + [ -n "$HAMI_SESSION_ID" ] && docker_command+=" -e HAMI_SESSION_ID=$HAMI_SESSION_ID" + [ -n "$HAMI_SERIAL_NO" ] && docker_command+=" -e HAMI_SERIAL_NO=$HAMI_SERIAL_NO" + [ -n "$HAMI_SESSION_IP" ] && docker_command+=" -e HAMI_SESSION_IP=$HAMI_SESSION_IP" + [ -n "$HTTP_PROXY" ] && docker_command+=" -e HTTP_PROXY=$HTTP_PROXY" + [ -n "$HTTPS_PROXY" ] && docker_command+=" -e HTTPS_PROXY=$HTTPS_PROXY" + + docker_command+=" $image_source" + eval "$docker_command" + + echo -e "${GREEN}Pixman 容器已启动。${RESET}" +} + +# 卸载 Pixman 项目 +uninstall_pixman() { + echo "是否确定要卸载 Pixman 项目?[y/n]" + read -r -t 10 input + input=${input:-n} + + if [[ "$input" =~ ^[Yy]$ ]]; then + echo -e "${CYAN}正在卸载 Pixman 项目...${RESET}" + docker rm -f pixman || { echo "卸载容器失败"; return 1; } + docker rmi -f pixman/pixman:latest || { echo "删除镜像失败"; return 1; } + crontab -l | grep -v "$SCRIPT_PATH" | crontab - || { echo "删除定时任务失败"; return 1; } + # rm -f "$SCRIPT_PATH" || { echo "删除脚本文件失败"; return 1; } + # rm -f "$CONFIG_FILE" || { echo "删除配置文件失败"; return 1; } + # sed -i '/alias y=/d' ~/.bashrc || { echo "删除快捷指令失败"; return 1; } + echo -e "${RED}Pixman 项目已成功卸载。${RESET}" + else + echo -e "${GREEN}取消卸载操作。${RESET}" + fi +} + +# 生成 Pixman 订阅 +live_pixman() { + local public_ip + local port + local container_id + container_id=$(docker ps -aq -f name=pixman 2>/dev/null) + + if [ -z "$container_id" ]; then + echo -e "${RED}错误: Pixman 容器不存在。${RESET}" + return 1 + fi + + MODE=$(docker inspect --format='{{.HostConfig.NetworkMode}}' pixman) + + if [[ "$MODE" == "host" ]]; then + port=5000 + else + port=$(docker inspect -f '{{ (index (index .HostConfig.PortBindings "5000/tcp") 0).HostPort }}' pixman 2>/dev/null) + fi + + if check_if_in_china; then + public_ip="{路由IP}" + else + public_ip=$(curl -s ifconfig.me || echo "{公网IP}") + fi + + parse_cron_schedule "$CRON_SCHEDULE" + echo "■ 央视频 YSP : http://${public_ip}:${port}/ysp.m3u" + echo "■ 江苏移动魔百盒 TPTV : http://${public_ip}:${port}/tptv.m3u 或 http://${public_ip}:${port}/tptv_proxy.m3u" + echo "■ 中国移动 iTV : http://${public_ip}:${port}/itv.m3u 或 http://${public_ip}:${port}/itv_proxy.m3u" + + if check_internet_connection; then + echo "■ YouTube 代理 : http://${public_ip}:${port}/youtube/{VIDEO_ID} (房间号)" + echo "■ 四季線上 4GTV : http://${public_ip}:${port}/4gtv.m3u" + + if [ -n "$MYTVSUPER_TOKEN" ]; then + echo "■ MytvSuper : http://${public_ip}:${port}/mytvsuper.m3u" + fi + + if [ -n "$HAMI_SESSION_ID" ] && [ -n "$HAMI_SERIAL_NO" ] && [ -n "$HAMI_SESSION_IP" ]; then + echo "■ Hami Video : http://${public_ip}:${port}/hami.m3u" + fi + fi + echo "■ Beesport : http://${public_ip}:${port}/beesport.m3u" + echo "■ TheTV : http://${public_ip}:${port}/thetv.m3u" + echo "■ DLHD : http://${public_ip}:${port}/dlhd.m3u" + echo "---------------------------------------------------------" + echo "--- Pixman 详细使用说明: https://pixman.io/topics/17 ---" + echo "--- Pixman.sh 脚本日志: https://pixman.io/topics/142 ---" + echo "---------------------------------------------------------" + + read -p "按 回车键 返回 主菜单 ..." +} + +# 生成 myTV 订阅 +Convert_pixman() { + local public_ip + local port + local container_id + container_id=$(docker ps -aq -f name=pixman 2>/dev/null) + + if [ -z "$container_id" ]; then + echo -e "${RED}错误: Pixman 容器不存在。${RESET}" + return 1 + fi + + if [ -n "$MYTVSUPER_TOKEN" ]; then + if ping -c 1 google.com > /dev/null 2>&1; then + MODE=$(docker inspect --format='{{.HostConfig.NetworkMode}}' pixman) + + if [[ "$MODE" == "host" ]]; then + port=5000 + else + port=$(docker inspect -f '{{ (index (index .HostConfig.PortBindings "5000/tcp") 0).HostPort }}' pixman 2>/dev/null) + fi + + if check_if_in_china; then + public_ip="{路由IP}" + else + public_ip=$(curl -s ifconfig.me || echo "{公网IP}") + fi + + echo "生成订阅中..." + docker exec pixman sh -c 'flask mytvsuper_tivimate' + echo "---------------------------------------------------------" + echo "■ MytvSuper-tivimate : http://${public_ip}:${port}/mytvsuper-tivimate.m3u" + + (crontab -l; echo "0 */12 * * * /usr/bin/docker exec pixman sh -c 'flask mytvsuper_tivimate'") | crontab - + + echo "■ 定时任务已设置,每 12 小时自动更新 M3U。" + else + echo -e "${RED}请查看网络环境,目前已禁用 myTV 服务。${RESET}" + return 1 + fi + else + echo -e "${CYAN}MYTVSUPER_TOKEN 参数不能为空,无法生成订阅。${RESET}" + return 1 + fi + + echo "---------------------------------------------------------" + echo "--- Pixman 详细使用说明: https://pixman.io/topics/17 ---" + echo "--- Pixman.sh 脚本日志: https://pixman.io/topics/142 ---" + echo "---------------------------------------------------------" + + read -p "按 回车键 返回 主菜单 ..." +} + +############# Allinone ############# + +# 安装 Allinone +install_allinone() { + echo "请选择部署方式(默认: 1):" + echo "1) 使用 host 网络模式" + echo "2) 使用 bridge 网络模式" + + read -rp "输入选项 (1 或 2): " option + option=${option:-1} + + local public_ip + local PORT=35455 + local reverse_proxy_option="" + + if check_if_in_china; then + public_ip="{路由IP}" # 获取本地 IP + reverse_proxy_option="-e REVERSE_PROXY=$REVERSE_PROXY" + else + public_ip=$(curl -s ifconfig.me || echo "{公网IP}") + reverse_proxy_option="-tv=false" + echo -e "${RED}境外已禁止开启 IPTV 服务。${RESET}" + fi + + case $option in + 1) + echo "正在使用 host 网络模式安装 Allinone..." + docker run -d \ + --restart unless-stopped \ + --net=host \ + --privileged=true \ + $reverse_proxy_option \ + --name allinone \ + youshandefeiyang/allinone + + echo -e "${GREEN}Allinone 安装完成。${RESET}" + + install_watchtower + + echo "---------------------------------------------------------" + echo "■ 订阅地址:" + if check_if_in_china; then + echo "■ TV 集合 : http://$public_ip:$PORT/tv.m3u" + echo "■ TPTV : http://$public_ip:$PORT/tptv.m3u" + fi + + ;; + + 2) + echo "正在使用 bridge 网络模式安装 Allinone..." + + read -rp "请输入要映射的端口 (默认: $PORT): " port + port=${port:-$PORT} + + if ! [[ "$port" =~ ^[0-9]+$ ]] || [ "$port" -lt 1024 ] || [ "$port" -gt 65535 ]; then + echo "无效端口。请使用 1024 到 65535 之间的数字。" + return 1 + fi + + docker run -d \ + --restart unless-stopped \ + -p $port:35455 \ + --privileged=true \ + $reverse_proxy_option \ + --name allinone \ + youshandefeiyang/allinone + + echo -e "${GREEN}Allinone 安装完成。${RESET}" + + install_watchtower # 定时任务 + + echo "---------------------------------------------------------" + echo "■ 订阅地址:" + if check_if_in_china; then + echo "■ TV 集合 : http://$public_ip:$port/tv.m3u" + echo "■ TPTV : http://$public_ip:$port/tptv.m3u" + fi + + ;; + + *) + echo -e "${CYAN}无效选项,请选择 1 或 2。${RESET}" + return 1 + ;; + esac + + live_allinone "$public_ip" "$port" +} + +# 生成 Allinone 订阅 +live_allinone() { + local public_ip="$1" + local PORT="$2" + + echo "■ YY轮播 : http://${public_ip}:${PORT}/yylunbo.m3u" + echo "■ BiliBili生活 : http://${public_ip}:${PORT}/bililive.m3u" + echo "■ 虎牙一起看 : http://${public_ip}:${PORT}/huyayqk.m3u" + echo "■ 斗鱼一起看 : http://${public_ip}:${PORT}/douyuyqk.m3u" + echo "---------------------------------------------------------" + echo "■ 代理地址:" + echo "■ BiliBili 代理 : http://${public_ip}:${PORT}/bilibili/{VIDEO_ID}" + echo "■ 虎牙 代理 : http://${public_ip}:${PORT}/huya/{VIDEO_ID}" + echo "■ 斗鱼 代理 : http://${public_ip}:${PORT}/douyu/{VIDEO_ID}" + echo "■ YY 代理 : http://${public_ip}:${PORT}/yy/{VIDEO_ID}" + echo "■ 抖音 代理 : http://${public_ip}:${PORT}/douyin/{VIDEO_ID}" + echo "■ YouTube 代理 : http://${public_ip}:${PORT}/youtube/{VIDEO_ID}" + echo "---------------------------------------------------------" + echo "--- allinone 详细使用说明: https://yycx.eu.org ---" + echo "--- Pixman.sh 脚本日志: https://pixman.io/topics/142 ---" + echo "---------------------------------------------------------" + + read -p "按 回车键 返回 主菜单 ..." +} + +# 设置 IPTV 启停 +set_allinone() { + if docker ps -q -f name=allinone; then + + if ! check_if_in_china; then + echo "境外已禁用 IPTV 服务,无法进行开启。" + return + fi + + current_args=$(docker inspect --format '{{.Args}}' allinone) + tv_setting=$(echo "$current_args" | grep -oP '(-tv=\w+)') + is_host_mode=$(echo "$current_args" | grep -oP '(^--net=host)') + + if [[ "$tv_setting" == "-tv=false" ]]; then + read -p "当前 TV 直播服务已关闭,是否要开启?(y/n): " tv_option + if [[ "$tv_option" == "y" || "$tv_option" == "Y" ]]; then + echo "正在开启 TV 直播服务..." + docker stop allinone + docker rm allinone + if [[ $is_host_mode ]]; then + docker run -d --restart unless-stopped --net=host --privileged=true --name allinone youshandefeiyang/allinone + else + port=$(echo "$current_args" | grep -oP '(-p\s+\d+:\d+)' | awk -F':' '{print $1}' | tr -d '-p ') + docker run -d --restart unless-stopped -p $port:35455 --privileged=true --name allinone youshandefeiyang/allinone + fi + echo "Allinone 已开启 TV 直播服务。" + else + echo "保持 TV 直播服务关闭。" + fi + else + read -p "当前 TV 直播服务已开启,是否要关闭?(y/n): " tv_option + if [[ "$tv_option" == "y" || "$tv_option" == "Y" ]]; then + echo "正在关闭 TV 直播服务..." + docker stop allinone + docker rm allinone + if [[ $is_host_mode ]]; then + docker run -d --restart unless-stopped --net=host --privileged=true --name allinone -tv=false youshandefeiyang/allinone + else + port=$(echo "$current_args" | grep -oP '(-p\s+\d+:\d+)' | awk -F':' '{print $1}' | tr -d '-p ') + docker run -d --restart unless-stopped -p $port:35455 --privileged=true --name allinone -tv=false youshandefeiyang/allinone + fi + echo "Allinone 已关闭 TV 直播服务。" + else + echo "保持 TV 直播服务开启。" + fi + fi + else + echo "当前 Allinone 未安装,请先安装 Allinone 项目。" + fi +} + +# 设置反向代理参数 +proxy_allinone() { + read -p "请输入反向代理地址 (回车跳过保持当前值: $REVERSE_PROXY): " input_reverse_proxy + [ -n "$input_reverse_proxy" ] && REVERSE_PROXY="$input_reverse_proxy" + + echo "反向代理地址已更新为: $REVERSE_PROXY" + + save_parameters +} + +# 卸载 Allinone +uninstall_allinone() { + read -p "您确定要卸载 Allinone 及删除所有相关文件吗?(y/n): " confirm + if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then + echo "卸载操作已取消。" + return + fi + if docker ps -a | grep -q allinone; then + docker stop allinone + docker rm allinone + fi + if docker ps -a | grep -q av3a-assistant; then + docker stop av3a-assistant + docker rm av3a-assistant + fi + if [ -d "/av3a" ]; then + rm -rf /av3a + fi + if docker ps -a | grep -q watchtower; then + docker stop watchtower + docker rm watchtower + fi + echo -e "${GREEN}Allinone 及其所有相关文件已完全卸载。${RESET}" +} + +# 设置 Allinone 更新 +install_watchtower() { + echo "正在安装或配置 Watchtower 并监控 Allinone 镜像更新..." + + if check_if_in_china; then + reverse_proxy_option="-e REVERSE_PROXY=$REVERSE_PROXY" + else + reverse_proxy_option="" + fi + + if [ "$(docker ps -a -q -f name=watchtower)" ]; then + echo "Watchtower 已经存在,更新定时任务..." + docker stop watchtower + docker rm watchtower + fi + + docker run -d \ + --name watchtower \ + --restart unless-stopped \ + -v /var/run/docker.sock:/var/run/docker.sock \ + $reverse_proxy_option \ + containrrr/watchtower allinone -c --schedule "0 0 2 * * *" + echo "---------------------------------------------------------" + echo -e "${CYAN}■ 服务器将于每天凌晨两点,进行检测更新。${RESET}" +} + +# 检查 Docker Compose 是否安装 +install_Docker_Compose() { + if ! command -v docker-compose &> /dev/null; then + echo "Docker Compose 未安装,正在尝试安装..." + sudo curl -L "https://ghproxy.cc/https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose + sudo chmod +x /usr/local/bin/docker-compose + + if ! command -v docker-compose &> /dev/null; then + echo "Docker Compose 安装失败,请手动安装。" + echo "参考资料:https://blog.csdn.net/Jimu2018/article/details/138325666" + read -p "按 回车键 返回 主菜单 ..." + exit 1 + fi + + echo -e "${GREEN}Docker Compose 安装完成。${RESET}" + else + echo -e "${GREEN}Docker Compose 已安装。${RESET}" + fi +} + +# 安装 av3a +install_av3a() { + if ! check_if_in_china; then + echo -e "${RED}境外已禁止开启 av3a 服务。{RESET}" + return + fi + + local public_ip="{路由IP}" + local PORT=35455 + + echo "若安装 av3a 助手,将固定端口,并删除 Allinone 部署,且 Docker 空间建议预留 3G 以上。" + read -p "是否继续安装 (y/n,默认 n): " CONFIRM_INSTALL + CONFIRM_INSTALL=${CONFIRM_INSTALL:-n} + if [[ "$CONFIRM_INSTALL" != "y" ]]; then + echo "安装已被终止。" + return + fi + + if docker ps -a --format '{{.Names}}' | grep -q "av3a-assistant"; then + echo "av3a-assistant 容器已安装,跳过安装步骤。" + return + fi + + install_Docker_Compose + + ARCH=$(uname -m) + if [[ "$ARCH" == "x86_64" || "$ARCH" == "amd64" ]]; then + echo "系统架构: amd64/x86_64" + INSTALL_PATH="/av3a" + generate_docker_compose "amd64" "$INSTALL_PATH" + elif [[ "$ARCH" == "aarch64" || "$ARCH" == "arm64" ]]; then + echo "系统架构: arm64/aarch64" + INSTALL_PATH="/av3a" + generate_docker_compose "arm64" "$INSTALL_PATH" + else + echo "不支持的系统架构: $ARCH,av3a 安装失败..." + return + fi + + if docker ps -a --format '{{.Names}}' | grep -q '^allinone$'; then + echo "检测到已存在的 allinone 容器,正在停止并删除..." + docker stop allinone + docker rm allinone + fi + + if generate_docker_compose "$ARCH" "$INSTALL_PATH"; then + cd "$INSTALL_PATH" + if docker-compose up -d; then + echo "---------------------------------------------------------" + echo -e "${CYAN}Allinone 和 av3a-assistant 均已启动${RESET}" + echo "---------------------------------------------------------" + echo "■ 订阅地址:" + echo "■ TV 集合 : http://$public_ip:35442/tv.m3u (av3a)" + echo "■ TV 集合 : http://$public_ip:35455/tv.m3u (原版)" + echo "■ TPTV : http://$public_ip:35455/tptv.m3u" + live_allinone "$public_ip" "$PORT" + else + echo "启动 Docker 容器失败。" + exit 1 + fi + else + echo "生成 Docker Compose 文件失败,后续操作将被终止。" + exit 1 + fi +} + +# 生成 Docker Compose 文件 +generate_docker_compose() { + local arch=$1 + local install_path=$2 + + mkdir -p "$install_path" || { echo "无法创建目录 $install_path"; return 1; } + + if [[ "$arch" == "x86_64" || "$arch" == "amd64" ]]; then + cat < "$install_path/docker-compose.yml" +services: + av3a-assistant: + image: ${REVERSE_PROXY}/youshandefeiyang/av3a-assistant:amd64 + container_name: av3a-assistant + privileged: true + restart: unless-stopped + ports: + - "35442:35442" + networks: + - my-network + + allinone: + image: ${REVERSE_PROXY}/youshandefeiyang/allinone + container_name: allinone + privileged: true + restart: unless-stopped + ports: + - "35455:35455" + networks: + - my-network + +networks: + my-network: + driver: bridge +EOF + elif [[ "$arch" == "aarch64" || "$arch" == "arm64" ]]; then + cat < "$install_path/docker-compose.yml" +services: + av3a-assistant: + image: ${REVERSE_PROXY}/youshandefeiyang/av3a-assistant:arm64 + container_name: av3a-assistant + privileged: true + restart: unless-stopped + ports: + - "35442:35442" + networks: + - my-network + + allinone: + image: ${REVERSE_PROXY}/youshandefeiyang/allinone + container_name: allinone + privileged: true + restart: unless-stopped + ports: + - "35455:35455" + networks: + - my-network + +networks: + my-network: + driver: bridge +EOF + else + echo "不支持的系统架构: $arch" + return 1 + fi +} + +############# 3X-UI ############# + +# 安装 3X-UI +install_3x_ui() { + echo "请选择部署方式:" + echo "1) 使用 host 网络模式 (添加节点方便)" + echo "2) 使用 bridge 网络模式 (添加节点,需映射端口)" + read -rp "输入选项 (1 或 2): " option + + if check_if_in_china; then + local public_ip="{路由IP}" + else + local public_ip=$(curl -s ifconfig.me || echo "{公网IP}") + fi + + 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) + + 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 "无效选项,请选择 1 或 2。" + ;; + 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 + docker rm 3x-ui + 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): " confirm + if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then + echo "卸载操作已取消。" + return + fi + + echo "正在卸载 3X-UI 面板..." + if docker ps -a | grep -q 3x-ui; then + docker stop 3x-ui + docker rm 3x-ui + else + echo "警告:未找到 3x-ui 容器。" + fi + + if [ -d "$PWD/db" ]; then + rm -rf "$PWD/db" + echo "配置文件已删除。" + fi + + echo -e "${GREEN}3X-UI 卸载完成。${RESET}" +} + +############# o11 ############# + +# 安装 o11 +install_o11() { + echo "正在安装 o11 面板..." + local port=$(generate_random_port) + + if check_if_in_china; then + local public_ip="{路由IP}" + else + local public_ip=$(curl -s ifconfig.me || echo "{公网IP}") + fi + + 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 "按 回车键 返回 主菜单 ..." +} + +# 卸载 o11 +uninstall_o11() { + read -p "您确定要卸载 o11 面板吗?(y/n): " confirm + if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then + echo "卸载操作已取消。" + return + fi + + echo "正在卸载 o11 面板..." + if docker ps -a | grep -q o11; then + docker stop o11 + docker rm o11 + else + echo "警告:未找到 o11 容器。" + fi + 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): " confirm + if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then + echo "卸载操作已取消。" + return + fi + + echo "正在卸载 1Panel..." + if command -v 1pctl > /dev/null 2>&1; then + 1pctl uninstall + else + echo "警告:未找到 1Panel 安装。" + fi + echo -e "${GREEN}1Panel 卸载完成。${RESET}" +} + +############# 辅助函数 ############# + +# 检查 网络 是否支持外网 +check_internet_connection() { + if curl -s --max-time 8 google.com > /dev/null; then + return 0 # 能连接外网 + else + return 1 # 不能连接外网 + fi +} + +# 检查 IP 归属地 +check_if_in_china() { + country=$(curl -s https://ipinfo.io/country) + if [ "$country" = "CN" ]; then + return 0 # 在中国 + else + return 1 # 不在中国 + fi +} + +# 检查 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 + if check_if_in_china; then + sudo apt-get update && sudo apt-get install -y jq --allow-releaseinfo-change + elif command -v apt-get &> /dev/null; then + sudo apt-get update && sudo apt-get install -y jq + elif command -v yum &> /dev/null; then + sudo yum install -y jq + elif command -v apk &> /dev/null; then + sudo apk add --no-cache jq + elif command -v opkg &> /dev/null; then # OpenWrt, Entware 环境 + opkg update && opkg install jq + else + return 1 # jq 安装失败 + fi + else + return 0 # jq 已安装 + fi +} + +# 清理 Docker 工具 +cleanup_docker() { + echo -e "\n🚨 警告:此操作将删除所有已停止的容器、未使用的镜像和卷。" + read -p "你确认要继续吗?(y/n,默认n): " confirm + confirm=${confirm:-n} + + if [[ "$confirm" != "y" ]]; then + echo -e "清理已取消。\n" + return + fi + + docker system prune -a --volumes -f + + echo -e "🎉 清理完成。" + read -p "按 回车键 返回 主菜单 ..." +} + +# 转换 Cron 表达式 +parse_cron_schedule() { + local schedule="$1" + local minute=$(echo "$schedule" | cut -d' ' -f1) + local hour=$(echo "$schedule" | cut -d' ' -f2) + local hour_list=() + local minute_desc="" + + if [[ "$minute" == "0" ]]; then + minute_desc="整点" + else + minute_desc=" ${minute} 分" + fi + + if [[ "$hour" == "*" ]]; then + hour_list+=("每小时") + elif [[ "$hour" == */* ]]; then + local interval=$(echo "$hour" | cut -d'/' -f2) + hour_list+=("每 ${interval} 小时") + else + IFS=',' read -r -a hours <<< "$hour" + for h in "${hours[@]}"; do + if [[ "$h" =~ ^[0-9]+$ ]]; then + hour_list+=("每天 ${h} 点") + fi + done + fi + + if [[ ${#hour_list[@]} -gt 0 ]]; then + echo "---------------------------------------------------------" + echo -e "${CYAN}■ 服务器将于${hour_list[*]}的${minute_desc},进行检测更新。${RESET}" + echo "---------------------------------------------------------" + fi +} + +# 生成随机端口 +generate_random_port() { + local port + while :; do + port=$(shuf -i 20000-65535 -n 1) + ss -tuln | grep -q :$port || { echo "$port"; break; } + done +} + +# 更新 SH 脚本 +download_pixman() { + REMOTE_VERSION=$(curl -s "https://yang-1989.eu.org/pixman_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 "正在下载最新版本的 Pixman 脚本..." + curl -o "$SCRIPT_PATH" "https://yang-1989.eu.org/pixman.sh" + chmod +x "$SCRIPT_PATH" + echo -e "${GREEN}最新 $REMOTE_VERSION 版本下载已完成。${RESET}" + # echo "设置 'y' 为快捷启动命令..." + if [ ! -f ~/.bashrc ]; then + touch ~/.bashrc + fi + if ! grep -q "alias y=" ~/.bashrc; then + echo "alias y='bash \"$SCRIPT_PATH\" --from-y'" >> ~/.bashrc + source ~/.bashrc + fi + fi +} + +# 脚本信息 +script_log() { + echo "------------------------------------------------" + echo "Pixman 懒人脚本" + echo "项目地址: https://pixman.io/" + echo "脚本日志: https://pixman.io/topics/142" + echo "作者: YanG" + echo "当前版本号: $(grep -oP '(?<=^# 最新版本:).*' "$SCRIPT_PATH")" + echo "最后更新时间: 2024.10.21" + echo "更新内容: 修复BUG,部署 Pixman 将不再自动判断,改为手动模式。" + echo "------------------------------------------------" + read -p "按 回车键 返回 主菜单 ..." +} + +############# 主程序逻辑 ############# + +load_parameters # 加载配置参数 +download_pixman # 检查脚本更新 + +# 检查是否启动定时任务 +if [ "$1" == "--auto" ]; then + echo "定时任务进行中..." + check_update + exit 0 +fi + +# 主循环 +while true; do + show_menu + read -p "请选择操作: " choice + case "$choice" in + 1) # 显示 pixman 菜单 + while true; do + show_pixman_menu + read -p "请输入选项 (0-5): " pixman_choice + case "$pixman_choice" in + 1) + check_docker + check_update + set_cron_job + live_pixman + ;; + 2) + set_parameters + set_cron_job + live_pixman + ;; + 3) + live_pixman + ;; + 4) + Convert_pixman + ;; + 5) + uninstall_pixman + ;; + 0) + echo "返回主菜单。" + break + ;; + *) + echo "无效的选项,请输入 0-5。" + ;; + esac + done + ;; + 2) # 显示 allinone 菜单 + while true; do + show_allinone_menu + read -p "请输入选项 (0-5): " allinone_choice + case "$allinone_choice" in + 1) check_docker ; install_allinone ;; + 2) check_docker ; install_av3a ;; + 3) set_allinone ;; + 4) proxy_allinone ;; + 5) uninstall_allinone ;; + 0) echo "返回主菜单。" ; break ;; + *) echo "无效的选项,请输入 0-5。" ;; + esac + done + ;; + 3) # 工具箱 + while true; do + show_toolbox_menu + read -p "请输入选项 (0-4): " 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) cleanup_docker ;; + 0) echo "返回主菜单。" ; break ;; + *) echo "无效的选项,请输入 0-3。" ;; + esac + done + ;; + 4) script_log ;; + 0) echo "退出脚本。" ; exit 0 ;; + *) echo "无效的选项,请输入 0-4。" ;; + esac +done \ No newline at end of file