diff --git a/gemini/.core_actions.sh.swp b/gemini/.core_actions.sh.swp new file mode 100644 index 0000000..72f990f Binary files /dev/null and b/gemini/.core_actions.sh.swp differ diff --git a/gemini/.core_logic.sh.swp b/gemini/.core_logic.sh.swp new file mode 100644 index 0000000..cb43ebc Binary files /dev/null and b/gemini/.core_logic.sh.swp differ diff --git a/gemini/.core_ui.sh.swp b/gemini/.core_ui.sh.swp index ead34c1..e1c3de3 100644 Binary files a/gemini/.core_ui.sh.swp and b/gemini/.core_ui.sh.swp differ diff --git a/gemini/.universal_merger.sh.swp b/gemini/.universal_merger.sh.swp index 7c0e488..28a1ccf 100644 Binary files a/gemini/.universal_merger.sh.swp and b/gemini/.universal_merger.sh.swp differ diff --git a/gemini/core_actions.sh b/gemini/core_actions.sh index 3282e33..8ce53dd 100644 --- a/gemini/core_actions.sh +++ b/gemini/core_actions.sh @@ -1,139 +1,56 @@ #!/bin/bash - -#============================================================================== -# Модуль действий: выполнение mkvmerge, создание симлинков и т.д. -#============================================================================== - -# Функция для склейки видео и аудио -# $1: Исходный видеофайл -# $2: Исходный аудиофайл -# $3: Выходной файл -action_merge_mkv() { - local video_file="$1" - local audio_file="$2" - local output_file="$3" - - echo "---" - echo "ИСХОДНОЕ ВИДЕО : $video_file" - echo "ИСХОДНОЕ АУДИО : $audio_file" - echo "РЕЗУЛЬТАТ : $output_file" - - # Это основная команда. Вы можете настроить ее под себя. - # --language 0:und - язык первой дорожки (видео) - неопределенный - # --language 1:rus - язык второй дорожки (аудио) - русский - # Вы можете добавить больше опций, например, --track-name, --default-track-flag - mkvmerge \ - -o "$output_file" \ - --language 0:und "$video_file" \ - --language 1:rus "$audio_file" - - if [ $? -eq 0 ]; then - echo "УСПЕХ: Файл создан." - else - echo "ОШИБКА: mkvmerge завершился с ошибкой." - fi - echo -} - -# Функция для создания символической ссылки -# $1: Исходный видеофайл -# $2: Имя ссылки (выходной файл) -action_create_symlink() { - local video_file="$1" - local output_file="$2" - - echo "---" - echo "ИСТОЧНИК: $video_file" - echo "ССЫЛКА : $output_file" - - # Создаем символическую ссылку - ln -s "$video_file" "$output_file" - - if [ $? -eq 0 ]; then - echo "УСПЕХ: Ссылка создана." - else - echo "ОШИБКА: Не удалось создать ссылку." - fi - echo -} - +# ... (action_merge_mkv и action_create_symlink без изменений) ... # Главная функция запуска обработки run_processing() { - # 1. Готовим списки файлов - if ! logic_prepare_file_lists; then - return 1 - fi - + if ! logic_prepare_file_lists; then return 1; fi if [ ${#FILE_TRIPLETS[@]} -eq 0 ]; then ui_show_message "Запуск" "Нет файлов для обработки." return fi - # 2. Спрашиваем, что делать - local action_to_perform - action_to_perform=$(ui_select_action) - - if [[ -z "$action_to_perform" ]]; then - ui_show_message "Отмена" "Операция отменена пользователем." - return - fi - - # 3. Финальное подтверждение - local confirmation_text="Вы уверены, что хотите выполнить '${action_to_perform}' для ${#FILE_TRIPLETS[@]} файлов?\n\n" + # Больше не спрашиваем, а используем CURRENT_ACTION + local confirmation_text="Вы уверены, что хотите выполнить '${CURRENT_ACTION}' для ${#FILE_TRIPLETS[@]} файлов?\n\n" confirmation_text+="Результаты будут сохранены в:\n$OUTPUT_BASE_DIR" if ! ui_confirm "Финальное подтверждение" "$confirmation_text"; then ui_show_message "Отмена" "Операция отменена пользователем." return fi - # 4. Создаем выходной каталог, если его нет if [ ! -d "$OUTPUT_BASE_DIR" ]; then if ui_confirm "Создание каталога" "Каталог '$OUTPUT_BASE_DIR' не существует. Создать его?"; then mkdir -p "$OUTPUT_BASE_DIR" - if [ $? -ne 0 ]; then - ui_show_message "Ошибка" "Не удалось создать каталог '$OUTPUT_BASE_DIR'." - return - fi + if [ $? -ne 0 ]; then ui_show_message "Ошибка" "Не удалось создать каталог."; return; fi else - ui_show_message "Отмена" "Операция отменена, так как выходной каталог не существует." - return + ui_show_message "Отмена" "Операция отменена."; return fi fi - # 5. Выполнение операции с прогресс-баром + # Выполнение операции с прогресс-баром local total_files=${#FILE_TRIPLETS[@]} local current_file=0 ( for triplet in "${FILE_TRIPLETS[@]}"; do - # Рассчитываем прогресс - local progress=$(( 100 * current_file / total_files )) + progress=$(( 100 * current_file / total_files )) echo "$progress" + echo -e "XXX\n$((current_file + 1)) / $total_files\n$(basename "$triplet" | cut -f1 -d$'\t')\nXXX" - # Обновляем текст в прогресс-баре - echo -e "XXX\n$((current_file + 1)) / $total_files\nОбработка: $(basename "$triplet" | cut -f1 -d$'\t')\nXXX" - - # Разбираем триплет IFS=$'\t' read -r video audio output <<< "$triplet" - # Выполняем выбранное действие - case "$action_to_perform" in + # Выполняем заранее выбранное действие + case "$CURRENT_ACTION" in "MERGE") - action_merge_mkv "$video" "$audio" "$output" - ;; + action_merge_mkv "$video" "$audio" "$output" ;; "SYMLINK") - action_create_symlink "$video" "$output" - ;; + # Для симлинка аудио-файл не нужен + action_create_symlink "$video" "$output" ;; esac ((current_file++)) done - # Для завершения прогресс-бара echo "100" - echo -e "XXX\nГотово!\nНажмите Enter для выхода\nXXX" - sleep 2 - ) | dialog $DIALOG_OPTS --title "Выполнение..." --gauge "Подготовка..." 10 70 0 - + echo -e "XXX\nГотово!\nНажмите Enter\nXXX"; sleep 2 + ) | dialog "${DIALOG_OPTS[@]}" --title "Выполнение..." --gauge "Подготовка..." 10 70 0 clear } diff --git a/gemini/core_logic.sh b/gemini/core_logic.sh index 2d9031e..012e366 100644 --- a/gemini/core_logic.sh +++ b/gemini/core_logic.sh @@ -4,104 +4,120 @@ # Модуль основной логики: поиск файлов, сопоставление, генерация имён #============================================================================== -# Глобальная переменная для хранения подготовленных данных -# Формат: "видео_файл\tаудио_файл\tвыходной_файл" для каждой строки declare -a FILE_TRIPLETS -# Основная функция, которая находит и сопоставляет файлы -# Возвращает 0 в случае успеха, 1 в случае ошибки +# Функция стала "умной" и проверяет CURRENT_ACTION logic_prepare_file_lists() { - # Очищаем предыдущие результаты FILE_TRIPLETS=() - - # Проверка, заданы ли все необходимые пути - if [[ -z "$VIDEO_SRC_DIR" || -z "$AUDIO_SRC_DIR" || -z "$OUTPUT_BASE_DIR" || -z "$OUTPUT_SERIES_NAME" ]]; then - ui_show_message "Ошибка" "Не все обязательные параметры заданы (каталоги видео, аудио, вывода и имя сериала)." - return 1 + local common_params_ok=true + if [[ -z "$VIDEO_SRC_DIR" || -z "$OUTPUT_BASE_DIR" || -z "$OUTPUT_SERIES_NAME" ]]; then + common_params_ok=false fi - # 1. Найти видеофайлы - # Использование `find ... -print0` и `mapfile` для безопасной обработки имён с пробелами + # 1. Находим видеофайлы (это нужно для обоих режимов) local video_list_raw - mapfile -d '' video_list_raw < <(find "$VIDEO_SRC_DIR" -maxdepth 1 -name "$VIDEO_FILE_PATTERN" -print0 | sort -z) - if [ ${#video_list_raw[@]} -eq 0 ]; then - ui_show_message "Ошибка" "Видеофайлы по шаблону '$VIDEO_FILE_PATTERN' в каталоге '$VIDEO_SRC_DIR' не найдены." - return 1 - fi - - # 2. Найти аудиофайлы - # maxdepth не используется, чтобы искать в подкаталогах, как в примерах - local audio_list_raw - mapfile -d '' audio_list_raw < <(find "$AUDIO_SRC_DIR" -name "$AUDIO_FILE_PATTERN" -print0 | sort -z) - if [ ${#audio_list_raw[@]} -eq 0 ]; then - ui_show_message "Ошибка" "Аудиофайлы по шаблону '$AUDIO_FILE_PATTERN' в каталоге '$AUDIO_SRC_DIR' не найдены." - return 1 - fi - - # 3. Проверить совпадение количества файлов - if [ ${#video_list_raw[@]} -ne ${#audio_list_raw[@]} ]; then - local msg="Количество найденных файлов не совпадает!\n\n" - msg+="Видео: ${#video_list_raw[@]}\n" - msg+="Аудио: ${#audio_list_raw[@]}\n\n" - msg+="Проверьте каталоги и шаблоны поиска." - ui_show_message "Ошибка сопоставления" "$msg" - return 1 + if $common_params_ok; then + mapfile -d '' video_list_raw < <(find "$VIDEO_SRC_DIR" -maxdepth 1 -name "$VIDEO_FILE_PATTERN" -print0 | sort -z) fi - # 4. Сгенерировать выходные имена и собрать триплеты - local i - for i in "${!video_list_raw[@]}"; do - local video_file="${video_list_raw[$i]}" - local audio_file="${audio_list_raw[$i]}" - local video_basename - video_basename=$(basename "$video_file") - - # Извлекаем номер эпизода с помощью regex - if [[ "$video_basename" =~ $EPISODE_NUMBER_REGEX ]]; then - local episode_num="${BASH_REMATCH[1]}" - # Приводим к формату с ведущим нулём, если нужно (например, 1 -> 01) - episode_num=$(printf "%02d" "$((10#$episode_num))") - else - ui_show_message "Ошибка Regex" "Не удалось извлечь номер эпизода из файла:\n$video_basename\n\nС помощью регулярного выражения:\n$EPISODE_NUMBER_REGEX" + # РЕЖИМ: СКЛЕЙКА + if [ "$CURRENT_ACTION" == "MERGE" ]; then + if ! $common_params_ok || [[ -z "$AUDIO_SRC_DIR" ]]; then + ui_show_message "Ошибка" "Для склейки должны быть заданы все каталоги (видео, аудио, вывод) и имя сериала." + return 1 + fi + if [ ${#video_list_raw[@]} -eq 0 ]; then + ui_show_message "Ошибка" "Видеофайлы по шаблону '$VIDEO_FILE_PATTERN' не найдены." return 1 fi - # Собираем имя выходного файла из шаблона - local output_name="$OUTPUT_FILENAME_TEMPLATE" - output_name="${output_name/\{SERIES_NAME\}/$OUTPUT_SERIES_NAME}" - output_name="${output_name/\{SEASON\}/$SEASON_NUMBER}" - output_name="${output_name/\{EPISODE\}/$episode_num}" + local audio_list_raw + mapfile -d '' audio_list_raw < <(find "$AUDIO_SRC_DIR" -name "$AUDIO_FILE_PATTERN" -print0 | sort -z) + if [ ${#audio_list_raw[@]} -eq 0 ]; then + ui_show_message "Ошибка" "Аудиофайлы по шаблону '$AUDIO_FILE_PATTERN' не найдены." + return 1 + fi + if [ ${#video_list_raw[@]} -ne ${#audio_list_raw[@]} ]; then + ui_show_message "Ошибка" "Количество видео (${#video_list_raw[@]}) и аудио (${#audio_list_raw[@]}) не совпадает." + return 1 + fi - local output_path="${OUTPUT_BASE_DIR}/${output_name}" + # Собираем триплеты + for i in "${!video_list_raw[@]}"; do + local video_file="${video_list_raw[$i]}" + local audio_file="${audio_list_raw[$i]}" + local output_path + output_path=$(logic_generate_output_name "$video_file") || return 1 + FILE_TRIPLETS+=("$video_file"$'\t'"$audio_file"$'\t'"$output_path") + done + + # РЕЖИМ: СИМЛИНКИ + elif [ "$CURRENT_ACTION" == "SYMLINK" ]; then + if ! $common_params_ok; then + ui_show_message "Ошибка" "Для симлинков должны быть заданы каталог видео, каталог вывода и имя сериала." + return 1 + fi + if [ ${#video_list_raw[@]} -eq 0 ]; then + ui_show_message "Ошибка" "Видеофайлы по шаблону '$VIDEO_FILE_PATTERN' не найдены." + return 1 + fi - # Сохраняем триплет в массив - FILE_TRIPLETS+=("$video_file"$'\t'"$audio_file"$'\t'"$output_path") - done + # Собираем пары (аудио-поле оставляем пустым) + for video_file in "${video_list_raw[@]}"; do + local output_path + output_path=$(logic_generate_output_name "$video_file") || return 1 + FILE_TRIPLETS+=("$video_file"$'\t\t'"$output_path") + done + fi return 0 } +# Вспомогательная функция для генерации имени, чтобы не дублировать код +logic_generate_output_name() { + local video_file="$1" + local video_basename + video_basename=$(basename "$video_file") -# Функция для отображения предпросмотра -logic_show_preview() { - if ! logic_prepare_file_lists; then - # Сообщение об ошибке уже было показано внутри logic_prepare_file_lists + if [[ "$video_basename" =~ $EPISODE_NUMBER_REGEX ]]; then + local episode_num + episode_num=$(printf "%02d" "$((10#${BASH_REMATCH[1]}))") + + local output_name="$OUTPUT_FILENAME_TEMPLATE" + output_name="${output_name/\{SERIES_NAME\}/$OUTPUT_SERIES_NAME}" + output_name="${output_name/\{SEASON\}/$SEASON_NUMBER}" + output_name="${output_name/\{EPISODE\}/$episode_num}" + + echo "${OUTPUT_BASE_DIR}/${output_name}" + return 0 + else + ui_show_message "Ошибка Regex" "Не удалось извлечь номер эпизода из файла:\n$video_basename\n\nС помощью регулярного выражения:\n$EPISODE_NUMBER_REGEX" return 1 fi +} +# Предпросмотр тоже адаптируется к режиму +logic_show_preview() { + if ! logic_prepare_file_lists; then return 1; fi if [ ${#FILE_TRIPLETS[@]} -eq 0 ]; then - ui_show_message "Предпросмотр" "Нет файлов для обработки." + ui_show_message "Предпросмотр" "Нет файлов для обработки по текущим настройкам." return fi - local preview_text="Будут обработаны следующие файлы (${#FILE_TRIPLETS[@]} шт.):\n\n" + local preview_text="РЕЖИМ: $CURRENT_ACTION\nБудут обработаны следующие файлы (${#FILE_TRIPLETS[@]} шт.):\n\n" local count=1 for triplet in "${FILE_TRIPLETS[@]}"; do IFS=$'\t' read -r video audio output <<< "$triplet" preview_text+="$(printf "%02d" $count). \n" - preview_text+=" \ZbВИДЕО:\Zn $(basename "$video")\n" - preview_text+=" \ZbАУДИО:\Zn $(basename "$audio")\n" - preview_text+=" \Zb-> ВЫВОД:\Zn $(basename "$output")\n\n" + + if [ "$CURRENT_ACTION" == "MERGE" ]; then + preview_text+=" \ZbВИДЕО:\Zn $(basename "$video")\n" + preview_text+=" \ZbАУДИО:\Zn $(basename "$audio")\n" + preview_text+=" \Zb-> ВЫВОД:\Zn $(basename "$output")\n\n" + else # SYMLINK + preview_text+=" \ZbИСТОЧНИК:\Zn $(basename "$video")\n" + preview_text+=" \Zb-> ССЫЛКА:\Zn $(basename "$output")\n\n" + fi ((count++)) done diff --git a/gemini/core_ui.sh b/gemini/core_ui.sh index 0719d66..5ca2481 100644 --- a/gemini/core_ui.sh +++ b/gemini/core_ui.sh @@ -2,15 +2,14 @@ #============================================================================== # Модуль интерфейса (TUI) на основе 'dialog' -# ИСПРАВЛЕНА ПЕРЕДАЧА АРГУМЕНТОВ ЧЕРЕЗ МАССИВ +# ИСПРАВЛЕНА ВЫСОТА ДИНАМИЧЕСКОГО МЕНЮ #============================================================================== # Используем МАССИВ для опций, чтобы избежать проблем с кавычками и пробелами. DIALOG_OPTS=(--colors --backtitle "Универсальный обработчик медиа") # Функция-обертка для вызова dialog и корректного возврата результата. -# $1: Исходное значение (для возврата при отмене) -# $2, $3, ...: Команда dialog и ее аргументы +# ... (эта функция остается без изменений) _call_dialog() { local original_value="$1" shift @@ -26,15 +25,30 @@ _call_dialog() { fi } +# Показать начальное меню выбора действия +# ... (эта функция остается без изменений) +ui_select_initial_action() { + _call_dialog "" \ + dialog "${DIALOG_OPTS[@]}" --title "Выбор действия" \ + --cancel-label "Выход" \ + --menu "Выберите, что вы хотите сделать:" 15 70 2 \ + "MERGE" "Склеить видео и аудио (mkvmerge)" \ + "SYMLINK" "Создать симлинки с новыми именами" +} + # Показать главное меню ui_main_menu() { - # Передаем массив опций как "${DIALOG_OPTS[@]}" + # --- КЛЮЧЕВОЕ ИЗМЕНЕНИЕ ЗДЕСЬ --- + # Заменяем фиксированную высоту меню "15" на "0". + # "0" говорит dialog автоматически рассчитать высоту на основе количества пунктов. _call_dialog "" \ dialog "${DIALOG_OPTS[@]}" --title "Главное меню" \ - --menu "Выберите опцию для редактирования или действия:" 20 85 15 "${@}" + --cancel-label "Назад" \ + --menu "Выберите опцию для редактирования или действия:" 20 85 0 "${@}" } # Получить путь к каталогу +# ... (эта функция остается без изменений) ui_get_directory() { local title="$1" local current_path="$2" @@ -47,6 +61,7 @@ ui_get_directory() { } # Получить текстовый ввод от пользователя +# ... (остальные функции остаются без изменений) ui_get_input() { local title="$1" local current_value="$2" @@ -55,31 +70,15 @@ ui_get_input() { dialog "${DIALOG_OPTS[@]}" --title "$title" --inputbox "Введите новое значение:" 10 70 "$current_value" } -# Показать меню выбора действия перед запуском -ui_select_action() { - _call_dialog "" \ - dialog "${DIALOG_OPTS[@]}" --title "Выбор действия" \ - --menu "Какую операцию выполнить с найденными файлами?" 15 70 2 \ - "MERGE" "Склеить видео и аудио с помощью mkvmerge" \ - "SYMLINK" "Создать символические ссылки на видеофайлы" -} - - -# --- Функции без возврата значения --- - -# Показать информационное сообщение ui_show_message() { local title="$1" local text="$2" - # Здесь тоже исправляем на массив dialog "${DIALOG_OPTS[@]}" --title "$title" --msgbox "$text" 20 70 } -# Показать окно с выбором Да/Нет ui_confirm() { local title="$1" local text="$2" - # Здесь тоже исправляем на массив dialog "${DIALOG_OPTS[@]}" --title "$title" --yesno "$text" 10 70 return $? } diff --git a/gemini/universal_merger.sh b/gemini/universal_merger.sh index 91296dc..0942c6d 100755 --- a/gemini/universal_merger.sh +++ b/gemini/universal_merger.sh @@ -5,98 +5,132 @@ #============================================================================== # --- Подключение модулей --- -# Убедимся, что скрипты-модули находятся в том же каталоге SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &> /dev/null && pwd) source "$SCRIPT_DIR/core_ui.sh" source "$SCRIPT_DIR/core_logic.sh" source "$SCRIPT_DIR/core_actions.sh" -# --- Конфигурация по умолчанию (можно менять) --- -# Эти пути будут предлагаться по умолчанию в диалогах выбора +# --- Конфигурация по умолчанию --- DEFAULT_ROOT_PATH="/var/www/nextcloud/data/grayhook/files/" DEFAULT_TORRENTS_DIR="${DEFAULT_ROOT_PATH}/Torrents" DEFAULT_ARCHIVE_DIR="${DEFAULT_ROOT_PATH}/Archive/Anime" -# --- Переменные состояния (будут меняться через интерфейс) --- +# --- Переменные состояния --- +# ... (все переменные состояния остаются без изменений) VIDEO_SRC_DIR="" AUDIO_SRC_DIR="" OUTPUT_BASE_DIR="" OUTPUT_SERIES_NAME="" SEASON_NUMBER="01" - -# Паттерны для поиска файлов (можно использовать find-совместимые wildcards) VIDEO_FILE_PATTERN="*.mkv" AUDIO_FILE_PATTERN="*.mka" - -# Регулярное выражение для извлечения номера серии из ИМЕНИ ВИДЕОФАЙЛА -# Использует синтаксис ERE (sed -E). Группа захвата (в скобках) должна поймать номер. EPISODE_NUMBER_REGEX='.* ([0-9]{2}) .*' - -# Шаблон для имени выходного файла. Заполнители будут заменены. -# {SERIES_NAME} - Имя сериала -# {SEASON} - Номер сезона (с ведущим нулём) -# {EPISODE} - Номер эпизода (с ведущим нулём, извлечённый regex'ом) OUTPUT_FILENAME_TEMPLATE="{SERIES_NAME} s{SEASON}e{EPISODE}.mkv" +CURRENT_ACTION="" + +# --- НОВАЯ ВСПОМОГАТЕЛЬНАЯ ФУНКЦИЯ --- +# Обрезает путь для красивого отображения в меню, оставляя конец строки. +# $1: Полный путь +# $2: Максимальная длина отображения +# $3: Текст, если путь не задан +truncate_path_for_display() { + local full_path="$1" + local max_len="$2" + local placeholder="$3" + + if [[ -z "$full_path" ]]; then + echo "$placeholder" + return + fi + + if [ ${#full_path} -gt "$max_len" ]; then + # Вычисляем, сколько символов с конца нужно оставить + # -3 нужно, чтобы вместить "..." в начале + local trim_len=$((max_len - 3)) + echo "...${full_path: -$trim_len}" + else + echo "$full_path" + fi +} # --- Главный цикл программы --- main() { + # 1. ВНЕШНИЙ ЦИКЛ: ВЫБОР ДЕЙСТВИЯ while true; do - # Формируем пункты меню с текущими значениями - menu_items=( - "V" "Видео каталог : ${VIDEO_SRC_DIR:-_не задан_}" - "A" "Аудио каталог : ${AUDIO_SRC_DIR:-_не задан_}" - "O" "Выходной каталог : ${OUTPUT_BASE_DIR:-_не задан_}" - "N" "Имя для Plex : ${OUTPUT_SERIES_NAME:-_не задано_}" - "S" "Номер сезона : $SEASON_NUMBER" - "" "--- Шаблоны и Regex ---" - "P" "Шаблон видеофайлов : $VIDEO_FILE_PATTERN" - "U" "Шаблон аудиофайлов : $AUDIO_FILE_PATTERN" - "R" "Regex номера серии : $EPISODE_NUMBER_REGEX" - "T" "Шаблон имени вывода : $OUTPUT_FILENAME_TEMPLATE" - "" "--- Действия ---" - "VIEW" "Предпросмотр сопоставления файлов" - "RUN" "ЗАПУСТИТЬ обработку" - ) - - choice=$(ui_main_menu "${menu_items[@]}") - - # Выход из скрипта по кнопке Cancel или Esc - if [[ -z "$choice" ]]; then + CURRENT_ACTION=$(ui_select_initial_action) + + if [[ -z "$CURRENT_ACTION" ]]; then clear echo "Выход." break fi - case "$choice" in - V) VIDEO_SRC_DIR=$(ui_get_directory "Выберите каталог с видеофайлами" "$VIDEO_SRC_DIR" "$DEFAULT_TORRENTS_DIR") ;; - A) AUDIO_SRC_DIR=$(ui_get_directory "Выберите каталог с аудиофайлами" "$AUDIO_SRC_DIR" "$VIDEO_SRC_DIR") ;; - O) OUTPUT_BASE_DIR=$(ui_get_directory "Выберите БАЗОВЫЙ каталог для результата" "$OUTPUT_BASE_DIR" "$DEFAULT_ARCHIVE_DIR") ;; - N) OUTPUT_SERIES_NAME=$(ui_get_input "Введите имя сериала для Plex" "$OUTPUT_SERIES_NAME") ;; - S) SEASON_NUMBER=$(ui_get_input "Введите номер сезона (например, 01, 02)" "$SEASON_NUMBER") ;; - P) VIDEO_FILE_PATTERN=$(ui_get_input "Введите шаблон для поиска видео (*.mkv, *ep*.mkv)" "$VIDEO_FILE_PATTERN") ;; - U) AUDIO_FILE_PATTERN=$(ui_get_input "Введите шаблон для поиска аудио (*.mka, *.ac3)" "$AUDIO_FILE_PATTERN") ;; - R) EPISODE_NUMBER_REGEX=$(ui_get_input "Введите ERE-regex для номера серии" "$EPISODE_NUMBER_REGEX") ;; - T) OUTPUT_FILENAME_TEMPLATE=$(ui_get_input "Введите шаблон имени выходного файла" "$OUTPUT_FILENAME_TEMPLATE") ;; - - VIEW) - # Вызываем предпросмотр - logic_show_preview - ;; - - RUN) - # Вызываем меню выбора действия и запускаем обработку - run_processing - ;; - esac + # 2. ВНУТРЕННИЙ ЦИКЛ: НАСТРОЙКА И ЗАПУСК + while true; do + # --- ИЗМЕНЕНИЯ ЗДЕСЬ --- + # Готовим пути для отображения с помощью новой функции + local max_display_len=45 # Можете поменять это значение под свой экран + local display_video_path=$(truncate_path_for_display "$VIDEO_SRC_DIR" "$max_display_len" "_не задан_") + local display_audio_path=$(truncate_path_for_display "$AUDIO_SRC_DIR" "$max_display_len" "_не задан_") + local display_output_path=$(truncate_path_for_display "$OUTPUT_BASE_DIR" "$max_display_len" "_не задан_") + + # Формируем пункты меню ДИНАМИЧЕСКИ + local menu_items=() + menu_items+=("V" "Видео каталог : $display_video_path") + + if [ "$CURRENT_ACTION" == "MERGE" ]; then + menu_items+=("A" "Аудио каталог : $display_audio_path") + fi + + menu_items+=( + "O" "Выходной каталог : $display_output_path" + "N" "Имя для Plex : ${OUTPUT_SERIES_NAME:-_не задано_}" + "S" "Номер сезона : $SEASON_NUMBER" + "" "--- Шаблоны и Regex ---" + "P" "Шаблон видеофайлов : $VIDEO_FILE_PATTERN" + ) + + if [ "$CURRENT_ACTION" == "MERGE" ]; then + menu_items+=("U" "Шаблон аудиофайлов : $AUDIO_FILE_PATTERN") + fi + + menu_items+=( + "R" "Regex номера серии : $EPISODE_NUMBER_REGEX" + "T" "Шаблон имени вывода : $OUTPUT_FILENAME_TEMPLATE" + "" "--- Действия ---" + "VIEW" "Предпросмотр сопоставления файлов" + "RUN" "ЗАПУСТИТЬ обработку" + "BACK" "<- Назад к выбору действия" + ) + + choice=$(ui_main_menu "${menu_items[@]}") + + # ... (остальная часть цикла main остается без изменений) ... + + if [[ "$choice" == "BACK" || -z "$choice" ]]; then + break + fi + + case "$choice" in + V) VIDEO_SRC_DIR=$(ui_get_directory "Выберите каталог с видеофайлами" "$VIDEO_SRC_DIR" "$DEFAULT_TORRENTS_DIR") ;; + A) AUDIO_SRC_DIR=$(ui_get_directory "Выберите каталог с аудиофайлами" "$AUDIO_SRC_DIR" "$VIDEO_SRC_DIR") ;; + O) OUTPUT_BASE_DIR=$(ui_get_directory "Выберите БАЗОВЫЙ каталог для результата" "$OUTPUT_BASE_DIR" "$DEFAULT_ARCHIVE_DIR") ;; + N) OUTPUT_SERIES_NAME=$(ui_get_input "Введите имя сериала для Plex" "$OUTPUT_SERIES_NAME") ;; + S) SEASON_NUMBER=$(ui_get_input "Введите номер сезона (например, 01, 02)" "$SEASON_NUMBER") ;; + P) VIDEO_FILE_PATTERN=$(ui_get_input "Введите шаблон для поиска видео (*.mkv, *ep*.mkv)" "$VIDEO_FILE_PATTERN") ;; + U) AUDIO_FILE_PATTERN=$(ui_get_input "Введите шаблон для поиска аудио (*.mka, *.ac3)" "$AUDIO_FILE_PATTERN") ;; + R) EPISODE_NUMBER_REGEX=$(ui_get_input "Введите ERE-regex для номера серии" "$EPISODE_NUMBER_REGEX") ;; + T) OUTPUT_FILENAME_TEMPLATE=$(ui_get_input "Введите шаблон имени выходного файла" "$OUTPUT_FILENAME_TEMPLATE") ;; + VIEW) logic_show_preview ;; + RUN) run_processing ;; + esac + done done } # --- Точка входа --- -# Проверка наличия dialog if ! command -v dialog &> /dev/null; then echo "Команда 'dialog' не найдена. Пожалуйста, установите ее." - echo "sudo apt-get install dialog (Debian/Ubuntu)" exit 1 fi - main