From 1ddaa07ffbb0a6c6e6c3fc4c60e09858bc614cbe Mon Sep 17 00:00:00 2001 From: wnlen <62139570+wnlen@users.noreply.github.com> Date: Fri, 16 Jan 2026 15:57:57 +0800 Subject: [PATCH] Reapply "clash_profile_conversion.sh resolve_subconverter.sh" This reverts commit 9dcc0a85e5107067908ee67d0fb26d843e1e5dc5. --- scripts/clash_profile_conversion.sh | 119 +++++++++++++++------- scripts/resolve_subconverter.sh | 152 ++++++++++++---------------- 2 files changed, 146 insertions(+), 125 deletions(-) diff --git a/scripts/clash_profile_conversion.sh b/scripts/clash_profile_conversion.sh index ce8dbb4..60faf4e 100644 --- a/scripts/clash_profile_conversion.sh +++ b/scripts/clash_profile_conversion.sh @@ -1,42 +1,85 @@ -#!/bin/bash +#!/usr/bin/env bash +set -euo pipefail -# 加载clash配置文件内容 -raw_content=$(cat ${Server_Dir}/temp/clash.yaml) +# 作用: +# - 将订阅内容转换成 Clash Meta / Mihomo 可用的完整 YAML 配置 +# - 默认使用 subconverter HTTP /sub 接口(最稳) +# - 失败则跳过,不影响主流程 +# +# 输入/输出约定: +# - IN_FILE:原订阅(默认 temp/clash.yaml) +# - OUT_FILE:转换后的配置(默认 temp/clash_config.yaml) +# +# 设计原则: +# - 绝不 exit 1(失败只 warn 并 exit 0) +# - 已是完整 Clash 配置则直接 copy +# - 没有 CLASH_URL(原始订阅 URL)则不转换(subconverter 最稳是 url=...) -# 判断订阅内容是否符合clash配置文件标准 -#if echo "$raw_content" | jq 'has("proxies") and has("proxy-groups") and has("rules")' 2>/dev/null; then -if echo "$raw_content" | awk '/^proxies:/{p=1} /^proxy-groups:/{g=1} /^rules:/{r=1} p&&g&&r{exit} END{if(p&&g&&r) exit 0; else exit 1}'; then - echo "订阅内容符合clash标准" - echo "$raw_content" > ${Server_Dir}/temp/clash_config.yaml -else - # 判断订阅内容是否为base64编码 - if echo "$raw_content" | base64 -d &>/dev/null; then - # 订阅内容为base64编码,进行解码 - decoded_content=$(echo "$raw_content" | base64 -d) +Server_Dir="${Server_Dir:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}" +Temp_Dir="${Temp_Dir:-$Server_Dir/temp}" - # 判断解码后的内容是否符合clash配置文件标准 - #if echo "$decoded_content" | jq 'has("proxies") and has("proxy-groups") and has("rules")' 2>/dev/null; then - if echo "$decoded_content" | awk '/^proxies:/{p=1} /^proxy-groups:/{g=1} /^rules:/{r=1} p&&g&&r{exit} END{if(p&&g&&r) exit 0; else exit 1}'; then - echo "解码后的内容符合clash标准" - echo "$decoded_content" > ${Server_Dir}/temp/clash_config.yaml - else - echo "解码后的内容不符合clash标准,尝试将其转换为标准格式" - if [ -z "$SUBCONVERTER_BIN" ]; then - echo "subconverter 未配置,无法执行转换" - exit 1 - fi - "${SUBCONVERTER_BIN}" -g &>> ${Server_Dir}/logs/subconverter.log - converted_file=${Server_Dir}/temp/clash_config.yaml - # 判断转换后的内容是否符合clash配置文件标准 - if awk '/^proxies:/{p=1} /^proxy-groups:/{g=1} /^rules:/{r=1} p&&g&&r{exit} END{if(p&&g&&r) exit 0; else exit 1}' $converted_file; then - echo "配置文件已成功转换成clash标准格式" - else - echo "配置文件转换标准格式失败" - exit 1 - fi - fi - else - echo "订阅内容不符合clash标准,无法转换为配置文件" - exit 1 - fi +mkdir -p "$Temp_Dir" + +IN_FILE="${IN_FILE:-$Temp_Dir/clash.yaml}" +OUT_FILE="${OUT_FILE:-$Temp_Dir/clash_config.yaml}" + +# “更先进”的默认:Clash Meta / Mihomo +SUB_TARGET="${SUB_TARGET:-clashmeta}" # 推荐 clashmeta(兼容面最广) +SUB_UDP="${SUB_UDP:-true}" +SUB_EMOJI="${SUB_EMOJI:-true}" +SUB_SORT="${SUB_SORT:-true}" + +# 订阅原始 URL(你 .env 里 export CLASH_URL=...) +SUB_URL="${CLASH_URL:-}" + +# 0) 输入不存在就跳过 +if [ ! -s "$IN_FILE" ]; then + echo "[WARN] no input file: $IN_FILE" + exit 0 fi + +# 1) 如果看起来已经是完整 Clash 配置,就直接用,不转换 +# (包含 proxies / proxy-providers / rules / port 等任一关键词即可认为是完整配置) +if grep -qE '^(proxies:|proxy-providers:|mixed-port:|port:|rules:|dns:)' "$IN_FILE"; then + cp -f "$IN_FILE" "$OUT_FILE" + echo "[OK] input already looks like a Clash config -> $OUT_FILE" + exit 0 +fi + +# 2) subconverter 不可用就跳过 +if [ "${SUBCONVERTER_READY:-false}" != "true" ] || [ -z "${SUBCONVERTER_URL:-}" ]; then + echo "[WARN] subconverter not ready, skip conversion" + exit 0 +fi + +# 3) 没有原始 URL 就不转(subconverter 最稳是 url=... 拉取) +if [ -z "${SUB_URL:-}" ]; then + echo "[WARN] CLASH_URL empty, cannot convert via /sub?url=..., skip" + exit 0 +fi + +TMP_OUT="$Temp_Dir/.clash_config.converted.yaml" +rm -f "$TMP_OUT" 2>/dev/null || true + +# 4) 拼接 /sub 参数(尽量通用) +CONVERT_URL="${SUBCONVERTER_URL}/sub?target=${SUB_TARGET}&url=${SUB_URL}" +if [ "$SUB_UDP" = "true" ]; then CONVERT_URL="${CONVERT_URL}&udp=true"; fi +if [ "$SUB_EMOJI" = "true" ]; then CONVERT_URL="${CONVERT_URL}&emoji=true"; fi +if [ "$SUB_SORT" = "true" ]; then CONVERT_URL="${CONVERT_URL}&sort=true"; fi + +# 5) 执行转换(失败则回退) +set +e +curl -fsSL --connect-timeout 3 -m 25 "$CONVERT_URL" -o "$TMP_OUT" +rc=$? +set -e + +if [ "$rc" -ne 0 ] || [ ! -s "$TMP_OUT" ]; then + echo "[WARN] convert failed (rc=$rc), keep original" + rm -f "$TMP_OUT" 2>/dev/null || true + exit 0 +fi + +mv -f "$TMP_OUT" "$OUT_FILE" +echo "[OK] converted via subconverter -> $OUT_FILE (target=${SUB_TARGET})" + +true \ No newline at end of file diff --git a/scripts/resolve_subconverter.sh b/scripts/resolve_subconverter.sh index 808b468..20631f8 100755 --- a/scripts/resolve_subconverter.sh +++ b/scripts/resolve_subconverter.sh @@ -1,100 +1,78 @@ -#!/bin/bash +#!/usr/bin/env bash +set -euo pipefail -Subconverter_Bin="" +# 作用: +# - 检测 tools/subconverter/subconverter 是否存在 +# -(可选)以 daemon 模式启动本地 subconverter(HTTP 服务) +# - 导出统一变量给后续脚本使用: +# SUBCONVERTER_BIN / SUBCONVERTER_READY / SUBCONVERTER_URL +# +# 设计原则: +# - 永不 exit 1(不可用就 Ready=false,主流程继续) +# - 不阻塞 start.sh(快速启动,不等待健康检查) + +Server_Dir="${Server_Dir:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}" +Temp_Dir="${Temp_Dir:-$Server_Dir/temp}" + +mkdir -p "$Temp_Dir" + +Subconverter_Bin="$Server_Dir/tools/subconverter/subconverter" Subconverter_Ready=false -Subconverter_Dir="${Server_Dir}/tools/subconverter" -Default_Bin="${Subconverter_Dir}/subconverter" +# 配置项(可放 .env) +SUBCONVERTER_MODE="${SUBCONVERTER_MODE:-daemon}" # daemon | off +SUBCONVERTER_HOST="${SUBCONVERTER_HOST:-127.0.0.1}" +SUBCONVERTER_PORT="${SUBCONVERTER_PORT:-25500}" +SUBCONVERTER_URL="${SUBCONVERTER_URL:-http://${SUBCONVERTER_HOST}:${SUBCONVERTER_PORT}}" -resolve_subconverter_arch() { - local raw_arch="$1" - case "$raw_arch" in - x86_64|amd64) - echo "linux-amd64" - ;; - aarch64|arm64) - echo "linux-arm64" - ;; - armv7*|armv7l) - echo "linux-armv7" - ;; - *) - echo "" - ;; - esac -} +# pref.ini:不存在就从示例生成 +SUBCONVERTER_PREF="${SUBCONVERTER_PREF:-$Server_Dir/tools/subconverter/pref.ini}" +PREF_EXAMPLE_INI="$Server_Dir/tools/subconverter/pref.example.ini" -try_subconverter_bin() { - local candidate="$1" - if [ -n "$candidate" ] && [ -x "$candidate" ]; then - Subconverter_Bin="$candidate" - Subconverter_Ready=true - return 0 - fi - return 1 -} +PID_FILE="$Temp_Dir/subconverter.pid" -# ------------------------------------------------------------ -# FIX: SUBCONVERTER_PATH may be unbound when parent shell uses `set -u` -# Use ${SUBCONVERTER_PATH:-} to avoid "unbound variable" -# ------------------------------------------------------------ -SUBCONVERTER_PATH_SAFE="${SUBCONVERTER_PATH:-}" - -if [ -n "$SUBCONVERTER_PATH_SAFE" ]; then - try_subconverter_bin "$SUBCONVERTER_PATH_SAFE" && return 0 +# 1) 二进制存在性 +if [ -x "$Subconverter_Bin" ]; then + Subconverter_Ready=true else - try_subconverter_bin "$Default_Bin" && return 0 + Subconverter_Ready=false fi -Detected_Arch="${CpuArch:-$(uname -m 2>/dev/null)}" -Resolved_Arch="$(resolve_subconverter_arch "$Detected_Arch")" - -if [ -n "$Resolved_Arch" ]; then - try_subconverter_bin "${Subconverter_Dir}/subconverter-${Resolved_Arch}" && return 0 - try_subconverter_bin "${Subconverter_Dir}/bin/subconverter-${Resolved_Arch}" && return 0 - try_subconverter_bin "${Subconverter_Dir}/${Resolved_Arch}/subconverter" && return 0 +# 2) pref.ini 生成(仅当准备启用 daemon) +if [ "$Subconverter_Ready" = "true" ] && [ "$SUBCONVERTER_MODE" = "daemon" ]; then + if [ ! -f "$SUBCONVERTER_PREF" ] && [ -f "$PREF_EXAMPLE_INI" ]; then + cp -f "$PREF_EXAMPLE_INI" "$SUBCONVERTER_PREF" + fi fi -Default_Template="https://github.com/tindy2013/subconverter/releases/latest/download/subconverter_{arch}.tar.gz" -Auto_Download="${SUBCONVERTER_AUTO_DOWNLOAD:-auto}" - -if [ "$Auto_Download" != "false" ] && [ -n "$Resolved_Arch" ]; then - Download_Template="${SUBCONVERTER_DOWNLOAD_URL_TEMPLATE:-$Default_Template}" - if [ -z "$Download_Template" ]; then - echo -e "\033[33m[WARN] 未设置 SUBCONVERTER_DOWNLOAD_URL_TEMPLATE,跳过 subconverter 自动下载\033[0m" - return 0 - fi - - Download_Url="${Download_Template//\{arch\}/${Resolved_Arch}}" - - # Ensure temp dirs exist - mkdir -p "${Server_Dir}/temp" "${Subconverter_Dir}" - - Download_Archive="${Server_Dir}/temp/subconverter-${Resolved_Arch}.tar.gz" - Extract_Dir="${Server_Dir}/temp/subconverter-${Resolved_Arch}" - mkdir -p "${Extract_Dir}" - - if command -v curl >/dev/null 2>&1; then - curl -L -sS -o "${Download_Archive}" "${Download_Url}" - elif command -v wget >/dev/null 2>&1; then - wget -q -O "${Download_Archive}" "${Download_Url}" - else - echo -e "\033[33m[WARN] 未找到 curl 或 wget,无法自动下载 subconverter\033[0m" - return 0 - fi - - # Only extract if archive exists and is non-empty - if [ -s "${Download_Archive}" ]; then - tar -xzf "${Download_Archive}" -C "${Extract_Dir}" 2>/dev/null - Downloaded_Bin="$(find "${Extract_Dir}" -maxdepth 3 -type f -name "subconverter" -print -quit)" - if [ -n "${Downloaded_Bin}" ]; then - mv "${Downloaded_Bin}" "${Subconverter_Dir}/subconverter-${Resolved_Arch}" - chmod +x "${Subconverter_Dir}/subconverter-${Resolved_Arch}" - try_subconverter_bin "${Subconverter_Dir}/subconverter-${Resolved_Arch}" && return 0 - fi - fi - - echo -e "\033[33m[WARN] subconverter 自动下载失败,跳过订阅转换\033[0m" +# 3) daemon 启动(只在需要时) +if [ "$Subconverter_Ready" = "true" ] && [ "$SUBCONVERTER_MODE" = "daemon" ]; then + # pid 存活则认为已启动 + if [ -f "$PID_FILE" ] && kill -0 "$(cat "$PID_FILE" 2>/dev/null)" 2>/dev/null; then + : + else + # 端口已监听则不重复起(可能是之前启动的) + if command -v ss >/dev/null 2>&1 && ss -lnt | awk '{print $4}' | grep -q ":${SUBCONVERTER_PORT}\$"; then + : + else + ( + cd "$Server_Dir/tools/subconverter" + # 注意:subconverter 读取 base/rules/snippets 等目录,必须在其目录下启动更稳 + nohup "$Subconverter_Bin" -f "$SUBCONVERTER_PREF" >/dev/null 2>&1 & + echo $! > "$PID_FILE" + ) + # 给一点点启动时间(不要长等,避免阻塞) + sleep 0.2 + fi + fi fi -return 0 \ No newline at end of file +# 4) 统一导出(给后续脚本用) +export Subconverter_Bin +export Subconverter_Ready +export SUBCONVERTER_BIN="$Subconverter_Bin" +export SUBCONVERTER_READY="$Subconverter_Ready" +export SUBCONVERTER_URL + +# 永不失败 +true \ No newline at end of file