bump
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+16
-99
@@ -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
|
||||
}
|
||||
|
||||
+86
-70
@@ -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
|
||||
if $common_params_ok; then
|
||||
mapfile -d '' video_list_raw < <(find "$VIDEO_SRC_DIR" -maxdepth 1 -name "$VIDEO_FILE_PATTERN" -print0 | sort -z)
|
||||
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
|
||||
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 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
|
||||
|
||||
# Собираем триплеты
|
||||
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
|
||||
|
||||
# Собираем пары (аудио-поле оставляем пустым)
|
||||
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")
|
||||
|
||||
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}"
|
||||
|
||||
local output_path="${OUTPUT_BASE_DIR}/${output_name}"
|
||||
|
||||
# Сохраняем триплет в массив
|
||||
FILE_TRIPLETS+=("$video_file"$'\t'"$audio_file"$'\t'"$output_path")
|
||||
done
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
# Функция для отображения предпросмотра
|
||||
logic_show_preview() {
|
||||
if ! logic_prepare_file_lists; then
|
||||
# Сообщение об ошибке уже было показано внутри logic_prepare_file_lists
|
||||
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
|
||||
|
||||
|
||||
+20
-21
@@ -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 $?
|
||||
}
|
||||
|
||||
+90
-56
@@ -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" "ЗАПУСТИТЬ обработку"
|
||||
)
|
||||
CURRENT_ACTION=$(ui_select_initial_action)
|
||||
|
||||
choice=$(ui_main_menu "${menu_items[@]}")
|
||||
|
||||
# Выход из скрипта по кнопке Cancel или Esc
|
||||
if [[ -z "$choice" ]]; then
|
||||
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") ;;
|
||||
# 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" "_не задан_")
|
||||
|
||||
VIEW)
|
||||
# Вызываем предпросмотр
|
||||
logic_show_preview
|
||||
;;
|
||||
# Формируем пункты меню ДИНАМИЧЕСКИ
|
||||
local menu_items=()
|
||||
menu_items+=("V" "Видео каталог : $display_video_path")
|
||||
|
||||
RUN)
|
||||
# Вызываем меню выбора действия и запускаем обработку
|
||||
run_processing
|
||||
;;
|
||||
esac
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user