From 0553ef74930275bfbe76ac9e7df955eca383ef5c Mon Sep 17 00:00:00 2001 From: Vyacheslav K Date: Mon, 15 Jun 2026 15:43:01 +0300 Subject: [PATCH] refactor: rename xray-sub to xtui, optimize selection tracking with index - Rename project from xray-sub to xtui (app name, state dir, user-agent) - Add selected_index to config for O(1) saved server lookup - Add migrate_saved_index() for backward compatibility - Replace fingerprint-based comparison with index-based in menu and apply --- README.md | 14 ++++++------- main.sh | 63 +++++++++++++++++++++++++++++++++++-------------------- 2 files changed, 47 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index f3235dc..546c004 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# xray-sub +# xtui Небольшой TUI-скрипт для выбора сервера из JSON-подписки Xray. @@ -21,7 +21,7 @@ chmod +x main.sh По желанию установите как команду: ```bash -sudo install -m 755 main.sh /usr/local/bin/xray-sub +sudo install -m 755 main.sh /usr/local/bin/xtui ``` ## Запуск @@ -35,7 +35,7 @@ sudo install -m 755 main.sh /usr/local/bin/xray-sub Если установлено в систему: ```bash -xray-sub +xtui ``` Первый запуск попросит ссылку на JSON-подписку, путь к конфигу Xray и имя systemd-службы. @@ -43,9 +43,9 @@ xray-sub Полезные команды: ```bash -xray-sub --setup # заново пройти настройку -xray-sub --refresh # обновить подписку -xray-sub --help # справка +xtui --setup # заново пройти настройку +xtui --refresh # обновить подписку +xtui --help # справка ``` ## Что делает @@ -56,7 +56,7 @@ xray-sub --help # справка ## Где хранит файлы -Пользовательские файлы хранятся в `~/.xray-sub/`: +Пользовательские файлы хранятся в `~/.xtui/`: - `config` — настройки скрипта и последний выбранный сервер. - `sub.json` — кэш скачанной подписки. diff --git a/main.sh b/main.sh index c015f13..d45a67c 100644 --- a/main.sh +++ b/main.sh @@ -1,8 +1,8 @@ #!/usr/bin/env bash set -Eeuo pipefail -APP_NAME="xray-sub" -STATE_DIR="${HOME}/.xray-sub" +APP_NAME="xtui" +STATE_DIR="${HOME}/.xtui" CONFIG_FILE="${STATE_DIR}/config" SUB_JSON="${STATE_DIR}/sub.json" BACKUP_DIR="${STATE_DIR}/backups" @@ -135,7 +135,7 @@ write_cfg() { --arg sub_url "$sub_url" \ --arg xray_config "$xray_config" \ --arg service "$service" \ - '{sub_url:$sub_url, xray_config:$xray_config, service:$service, selected_fp:"", selected_name:""}' >"$tmp" + '{sub_url:$sub_url, xray_config:$xray_config, service:$service, selected_index:"", selected_fp:"", selected_name:""}' >"$tmp" install -m 600 "$tmp" "$CONFIG_FILE" rm -f "$tmp" } @@ -163,7 +163,7 @@ fetch_subscription() { info "Скачиваю подписку…" if ! curl -fLsS --connect-timeout 10 --max-time 45 --retry 2 --retry-delay 1 \ -H 'Accept: application/json, */*' \ - -A 'xray-sub-selector/1.2' \ + -A 'xtui/1.2' \ "$url" -o "$tmp"; then rm -f "$tmp" return 1 @@ -332,21 +332,36 @@ profile_fingerprint() { } find_saved_index() { - local saved_fp count i fp + local saved_index saved_fp count i fp + saved_index="$(cfg_get selected_index)" saved_fp="$(cfg_get selected_fp)" count="$(profile_count)" - [[ -n "$saved_fp" ]] || { echo 0; return; } - for ((i=0; i/dev/null +} + build_selected_config() { local idx="$1" out="$2" jq -e --argjson i "$idx" "${PROFILES_DEF}"' @@ -418,10 +433,10 @@ restart_xray_service() { } apply_selection() { - local idx="$1" dest service tmp fp old_fp name proto addr port changed=1 service_state + local idx="$1" dest service tmp fp old_index name proto addr port changed=1 service_state dest="$(cfg_get xray_config)" service="$(cfg_get service)" - old_fp="$(cfg_get selected_fp)" + old_index="$(cfg_get selected_index)" fp="$(profile_fingerprint "$idx")" IFS=$'\t' read -r name proto addr port < <(profile_summary "$idx") @@ -444,12 +459,13 @@ apply_selection() { ok "В ${dest} уже записан этот конфиг: ${name}" fi + cfg_set selected_index "$idx" cfg_set selected_fp "$fp" cfg_set selected_name "$name" rm -f "$tmp" service_state="$(service_state_raw "$service")" - if (( changed )) || [[ "$old_fp" != "$fp" ]]; then + if (( changed )) || [[ "$old_index" != "$idx" ]]; then if [[ "$service_state" == "active" || "$service_state" == "activating" ]]; then restart_xray_service "$service" else @@ -622,17 +638,16 @@ render_table_header() { } render_profile_row() { - local idx="$1" cursor="$2" saved_fp="$3" - local fp name proto addr port pointer mark row_style name_short addr_short + local idx="$1" cursor="$2" saved_index="$3" + local name proto addr port pointer mark row_style name_short addr_short IFS=$'\t' read -r name proto addr port < <(profile_summary "$idx") - fp="$(profile_fingerprint "$idx")" pointer=" " mark=" " row_style="" [[ "$idx" -eq "$cursor" ]] && { pointer="➤"; row_style="$C_INV"; } - [[ -n "$saved_fp" && "$fp" == "$saved_fp" ]] && mark="✓" + [[ "$saved_index" =~ ^[0-9]+$ && "$idx" -eq "$saved_index" ]] && mark="✓" name_short="$(shorten "$name" 31)" addr_short="$(shorten "$addr" 20)" @@ -646,11 +661,11 @@ render_profile_row() { } render_menu() { - local cursor="$1" count saved_fp i cache_info selected_name + local cursor="$1" count saved_index i cache_info selected_name local service service_state service_enabled xray_config socks_endpoint count="$(profile_count)" - saved_fp="$(cfg_get selected_fp)" + saved_index="$(cfg_get selected_index)" selected_name="$(cfg_get selected_name)" service="$(cfg_get service)" xray_config="$(cfg_get xray_config)" @@ -676,7 +691,7 @@ render_menu() { render_table_header for ((i=0; i