mirror of https://github.com/YanG-1989/m3u.git
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.
1431 lines
49 KiB
1431 lines
49 KiB
#!/bin/bash
|
|
###############################
|
|
|
|
# @小白直播搭建脚本
|
|
# 项目地址:https://pixman.io/
|
|
# 最新版本:1.9.6
|
|
|
|
###############################
|
|
|
|
# 设置路径
|
|
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) 设置 反向代理 地址 "
|
|
echo "4) 卸载 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 "5) 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 "-------------------"
|
|
}
|
|
|
|
# subs 菜单
|
|
show_subs_menu() {
|
|
echo "-------------------"
|
|
echo " Sub Store 菜单: "
|
|
echo "-------------------"
|
|
echo "1) 安装 Sub Store "
|
|
echo "2) 卸载 Sub Store "
|
|
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 > /dev/null 2>&1
|
|
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 > /dev/null 2>&1
|
|
docker rmi -f "$IMAGE_SOURCE" > /dev/null 2>&1
|
|
docker pull "$IMAGE_SOURCE" > /dev/null 2>&1
|
|
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 stop pixman > /dev/null 2>&1
|
|
docker rm -f pixman > /dev/null 2>&1
|
|
for image in $(docker images --format '{{.Repository}}:{{.Tag}}' | grep 'pixman/pixman'); do
|
|
docker rmi "$image" > /dev/null 2>&1
|
|
done
|
|
crontab -l | grep -v "$SCRIPT_PATH"
|
|
# rm -f "$SCRIPT_PATH"
|
|
# rm -f "$CONFIG_FILE"
|
|
# sed -i '/alias y=/d' ~/.bashrc
|
|
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() {
|
|
|
|
if docker ps -a --format '{{.Names}}' | grep -q "^allinone$"; then
|
|
echo "检测到已存在的 Allinone 容器,将进行手动更新..."
|
|
docker stop allinone > /dev/null 2>&1
|
|
docker rm allinone > /dev/null 2>&1
|
|
for image in $(docker images --format '{{.Repository}}:{{.Tag}}' | grep 'youshandefeiyang/allinone'); do
|
|
docker rmi "$image" > /dev/null 2>&1
|
|
done
|
|
echo -e "${CYAN}已停止并删除旧的 Allinone 项目。${RESET}"
|
|
fi
|
|
|
|
echo "请选择部署方式(默认: 1):"
|
|
echo "1) 使用 host 网络模式"
|
|
echo "2) 使用 bridge 网络模式"
|
|
|
|
read -rp "输入选项 (1 或 2): " option
|
|
option=${option:-1}
|
|
|
|
local public_ip
|
|
local PORT=35455
|
|
|
|
if check_if_in_china; then
|
|
public_ip="{路由IP}"
|
|
else
|
|
public_ip=$(curl -s ifconfig.me || echo "{公网IP}")
|
|
fi
|
|
|
|
IMAGE_SOURCE="youshandefeiyang/allinone"
|
|
PROXY_IMAGE_SOURCE="$REVERSE_PROXY/youshandefeiyang/allinone"
|
|
|
|
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}安装 Allinone 失败,请检查代理或网络连接。${RESET}"
|
|
exit 1
|
|
fi
|
|
IMAGE_SOURCE="$PROXY_IMAGE_SOURCE"
|
|
fi
|
|
|
|
case $option in
|
|
1)
|
|
echo "正在使用 host 网络模式安装 Allinone..."
|
|
port=$PORT
|
|
docker run -d --restart unless-stopped --net=host --privileged=true --name allinone "$IMAGE_SOURCE"
|
|
echo -e "${GREEN}Allinone 安装完成。${RESET}"
|
|
|
|
install_watchtower "allinone"
|
|
|
|
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 --net=bridge --privileged=true -p "$port:35455" --name allinone "$IMAGE_SOURCE"
|
|
|
|
echo -e "${GREEN}Allinone 安装完成。${RESET}"
|
|
|
|
install_watchtower "allinone"
|
|
|
|
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 "按 回车键 返回 主菜单 ..."
|
|
}
|
|
|
|
# 设置反向代理参数
|
|
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 > /dev/null 2>&1
|
|
docker rm allinone > /dev/null 2>&1
|
|
fi
|
|
if docker ps -a | grep -q av3a-assistant; then
|
|
docker stop av3a-assistant > /dev/null 2>&1
|
|
docker rm av3a-assistant > /dev/null 2>&1
|
|
fi
|
|
if [ -d "/av3a" ]; then
|
|
rm -rf /av3a
|
|
fi
|
|
|
|
for image in $(docker images --format '{{.Repository}}:{{.Tag}}' | grep 'youshandefeiyang/allinone'); do
|
|
docker rmi "$image" > /dev/null 2>&1
|
|
done
|
|
|
|
for image in $(docker images --format '{{.Repository}}:{{.Tag}}' | grep 'av3a-assistant'); do
|
|
docker rmi "$image" > /dev/null 2>&1
|
|
done
|
|
|
|
echo -e "${GREEN}Allinone 及其所有相关文件已完全卸载。${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"
|
|
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 > /dev/null 2>&1
|
|
docker rm allinone > /dev/null 2>&1
|
|
fi
|
|
|
|
if generate_docker_compose "$ARCH" "$INSTALL_PATH"; then
|
|
cd "$INSTALL_PATH"
|
|
if docker-compose up -d; then
|
|
echo "---------------------------------------------------------"
|
|
echo -e "${GREEN}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 <<EOF > "$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 <<EOF > "$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
|
|
}
|
|
|
|
############# watchtower #############
|
|
|
|
# 设置 watchtower 任务
|
|
install_watchtower() {
|
|
local name=$1
|
|
|
|
if [ "$(docker ps -q -f name=watchtower)" ]; then
|
|
existing_args=$(docker inspect --format '{{.Args}}' watchtower)
|
|
monitored_containers=$(echo "$existing_args" | grep -oP '(\w+)' | tr '\n' ' ')
|
|
|
|
if echo "$monitored_containers" | grep -qw "$name"; then
|
|
echo "---------------------------------------------------------"
|
|
echo -e "${CYAN}■ 服务器将于每天凌晨五点,进行检测更新。${RESET}"
|
|
return
|
|
fi
|
|
|
|
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
|
|
fi
|
|
IMAGE_SOURCE="$PROXY_IMAGE_SOURCE"
|
|
fi
|
|
|
|
docker run -d --name watchtower --restart unless-stopped -v /var/run/docker.sock:/var/run/docker.sock "$IMAGE_SOURCE" $monitored_containers -c --schedule "0 5 * * *"
|
|
echo "---------------------------------------------------------"
|
|
echo -e "${CYAN}■ 服务器将于每天凌晨五点,进行检测更新。${RESET}"
|
|
}
|
|
|
|
# 卸载 Watchtower 监控指定容器
|
|
uninstall_watchtower() {
|
|
local name=$1
|
|
|
|
if [ "$(docker ps -q -f name=watchtower)" ]; then
|
|
echo "正在检查 Watchtower 监控的容器..."
|
|
|
|
existing_args=$(docker inspect --format '{{.Args}}' watchtower)
|
|
monitored_containers=$(echo "$existing_args" | grep -oP '(\w+)' | tr '\n' ' ')
|
|
|
|
if echo "$monitored_containers" | grep -qw "$name"; then
|
|
|
|
monitored_containers=$(echo "$monitored_containers" | sed "s/\b$name\b//g")
|
|
|
|
if [ -z "$monitored_containers" ]; then
|
|
echo "没有其他监控的容器,正在停止并删除 Watchtower..."
|
|
docker stop watchtower > /dev/null 2>&1
|
|
docker rm watchtower > /dev/null 2>&1
|
|
|
|
for image in $(docker images --format '{{.Repository}}:{{.Tag}}' | grep 'containrrr/watchtower'); do
|
|
docker rmi "$image" > /dev/null 2>&1
|
|
done
|
|
|
|
echo "Watchtower 已成功卸载。"
|
|
else
|
|
docker stop watchtower > /dev/null 2>&1
|
|
docker rm watchtower > /dev/null 2>&1
|
|
echo "正在更新 Watchtower,仅监控剩余容器..."
|
|
|
|
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
|
|
fi
|
|
IMAGE_SOURCE="$PROXY_IMAGE_SOURCE"
|
|
fi
|
|
|
|
docker run -d --name watchtower --restart unless-stopped -v /var/run/docker.sock:/var/run/docker.sock "$IMAGE_SOURCE" $monitored_containers -c --schedule "0 5 * * *"
|
|
echo "· "$name" 容器已从监控中删除。"
|
|
fi
|
|
else
|
|
echo "容器 $name 未被 Watchtower 监控。"
|
|
fi
|
|
else
|
|
echo "Watchtower 当前未安装。"
|
|
fi
|
|
}
|
|
|
|
############# 3X-UI #############
|
|
|
|
# 安装 3X-UI
|
|
install_3x_ui() {
|
|
echo "请选择部署方式:"
|
|
echo "1) 使用 host 网络模式 (添加节点方便)"
|
|
echo "2) 使用 bridge 网络模式 (添加节点,需映射端口)"
|
|
echo "3) 使用 sh 脚本 直接安装 (推荐)"
|
|
read -rp "输入选项 (1-3): " 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)
|
|
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): " confirm
|
|
if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then
|
|
echo "卸载操作已取消。"
|
|
return
|
|
fi
|
|
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
|
|
fi
|
|
if [ -d "$PWD/db" ]; then
|
|
rm -rf "$PWD/db"
|
|
fi
|
|
for image in $(docker images --format '{{.Repository}}:{{.Tag}}' | grep 'mhsanaei/3x-ui'); do
|
|
docker rmi "$image" > /dev/null 2>&1
|
|
done
|
|
|
|
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
|
|
if docker ps -a | grep -q o11; then
|
|
docker stop o11 > /dev/null 2>&1
|
|
docker rm o11 > /dev/null 2>&1
|
|
fi
|
|
for image in $(docker images --format '{{.Repository}}:{{.Tag}}' | grep 'wechatofficial/o11'); do
|
|
docker rmi "$image" > /dev/null 2>&1
|
|
done
|
|
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
|
|
if command -v 1pctl > /dev/null 2>&1; then
|
|
1pctl uninstall
|
|
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() {
|
|
local sources=(
|
|
"https://myip.ipip.net"
|
|
"https://ipinfo.io/country"
|
|
"http://ip-api.com/json/"
|
|
)
|
|
|
|
for source in "${sources[@]}"; do
|
|
response=$(curl -s "$source")
|
|
if echo "$response" | grep -qiE "中国|China|CN"; then
|
|
return 0
|
|
fi
|
|
done
|
|
|
|
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
|
|
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.24"
|
|
echo "更新内容: 优化 CN 判断,修复 Allinone 部署 BUG。计划 增加 Sub Store 部署,独立 watchtower 设置"
|
|
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-4): " allinone_choice
|
|
case "$allinone_choice" in
|
|
1) check_docker ; install_allinone ;;
|
|
2) check_docker ; install_av3a ;;
|
|
3) proxy_allinone ;;
|
|
4) uninstall_allinone ;;
|
|
0) echo "返回主菜单。" ; break ;;
|
|
*) echo "无效的选项,请输入 0-4。" ;;
|
|
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 |