diff --git a/core_actions.sh b/core_actions.sh index ac28e9a..fd07b15 100644 --- a/core_actions.sh +++ b/core_actions.sh @@ -9,12 +9,39 @@ action_merge_mkv() { local video_file="$1" local audio_file="$2" local final_output_file="$3" + local subtitle_file="$4" - mkvmerge \ - -o "$TEMP_MERGE_FILE" \ - --track-order 1:0,0:0 \ - --language 0:rus "$audio_file" \ - "$video_file" \ + # Файл 0 — аудио, 1 — видео, 2 — субтитры (если заданы). + # Порядок дорожек: видео, новая аудиодорожка, затем субтитры. + local -a merge_cmd=() + + if [[ -n "$subtitle_file" ]]; then + merge_cmd=( + mkvmerge + -o "$TEMP_MERGE_FILE" + --track-order 1:0,0:0,2:0 + --language 0:rus "$audio_file" + "$video_file" + --language 0:rus + ) + local subtitle_lower="${subtitle_file,,}" + case "$subtitle_lower" in + *.srt|*.ass|*.ssa|*.sub|*.txt) + merge_cmd+=(--sub-charset 0:UTF-8) + ;; + esac + merge_cmd+=("$subtitle_file") + else + merge_cmd=( + mkvmerge + -o "$TEMP_MERGE_FILE" + --track-order 1:0,0:0 + --language 0:rus "$audio_file" + "$video_file" + ) + fi + + "${merge_cmd[@]}" \ && \ mv "$TEMP_MERGE_FILE" "$final_output_file" } @@ -37,12 +64,12 @@ run_processing() { local skip_count=0 local pending_count=0 - local triplet video audio output + local triplet video audio output subtitle # Снимок на момент старта. Файл, появившийся уже в этом запуске, # нельзя считать «и так был» — иначе отчёт завысит число обработанных. local -A ALREADY_PRESENT=() for triplet in "${FILE_TRIPLETS[@]}"; do - IFS=$'\t' read -r video audio output <<< "$triplet" + IFS=$'\t' read -r video audio output subtitle <<< "$triplet" if output_file_exists "$output"; then ALREADY_PRESENT["$output"]=1 skip_count=$((skip_count + 1)) @@ -82,7 +109,7 @@ run_processing() { local total_files=${#FILE_TRIPLETS[@]} local current_file_num=0 - while IFS=$'\t' read -r video audio output; do + while IFS=$'\t' read -r video audio output subtitle; do ((current_file_num++)) local progress_before=$(( ((current_file_num - 1) * 100) / total_files )) @@ -104,7 +131,7 @@ run_processing() { case "$CURRENT_ACTION" in "MERGE") - action_merge_mkv "$video" "$audio" "$output" >/dev/null 2>&1 ;; + action_merge_mkv "$video" "$audio" "$output" "$subtitle" >/dev/null 2>&1 ;; "SYMLINK") action_create_symlink "$video" "$output" >/dev/null 2>&1 ;; esac diff --git a/core_logic.sh b/core_logic.sh index d24b857..fbdacad 100644 --- a/core_logic.sh +++ b/core_logic.sh @@ -41,12 +41,26 @@ logic_prepare_file_lists() { return 1 fi + local -a sub_list_raw=() + if [[ -n "$SUB_SRC_DIR" ]]; then + mapfile -d '' sub_list_raw < <(find "$SUB_SRC_DIR" -name "$SUB_FILE_PATTERN" -print0 | sort -z) + if [ ${#sub_list_raw[@]} -eq 0 ]; then + ui_show_message "Ошибка" "Субтитры по шаблону '$SUB_FILE_PATTERN' не найдены." + return 1 + fi + if [ ${#video_list_raw[@]} -ne ${#sub_list_raw[@]} ]; then + ui_show_message "Ошибка" "Количество видео (${#video_list_raw[@]}), аудио (${#audio_list_raw[@]}) и субтитров (${#sub_list_raw[@]}) не совпадает." + return 1 + fi + fi + for i in "${!video_list_raw[@]}"; do local video_file="${video_list_raw[$i]}" local audio_file="${audio_list_raw[$i]}" + local subtitle_file="${sub_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") + FILE_TRIPLETS+=("$video_file"$'\t'"$audio_file"$'\t'"$output_path"$'\t'"$subtitle_file") done # РЕЖИМ: СИМЛИНКИ @@ -161,7 +175,7 @@ logic_show_preview() { local count=1 local skip_count=0 for triplet in "${FILE_TRIPLETS[@]}"; do - IFS=$'\t' read -r video audio output <<< "$triplet" + IFS=$'\t' read -r video audio output subtitle <<< "$triplet" body+="$(printf "%02d" $count). \n" local exists=false @@ -173,6 +187,9 @@ logic_show_preview() { if [ "$CURRENT_ACTION" == "MERGE" ]; then body+=" \ZbВИДЕО:\Zn $(basename "$video")\n" body+=" \ZbАУДИО:\Zn $(basename "$audio")\n" + if [[ -n "$subtitle" ]]; then + body+=" \ZbСУБТИТРЫ:\Zn $(basename "$subtitle")\n" + fi body+=" \Zb-> ВЫВОД:\Zn $(basename "$output")\n" else # SYMLINK body+=" \ZbИСТОЧНИК:\Zn $(basename "$video")\n" diff --git a/core_ui.sh b/core_ui.sh index 5ca2481..fa5d5c0 100644 --- a/core_ui.sh +++ b/core_ui.sh @@ -32,7 +32,7 @@ ui_select_initial_action() { dialog "${DIALOG_OPTS[@]}" --title "Выбор действия" \ --cancel-label "Выход" \ --menu "Выберите, что вы хотите сделать:" 15 70 2 \ - "MERGE" "Склеить видео и аудио (mkvmerge)" \ + "MERGE" "Склеить видео и аудио, субтитры по желанию" \ "SYMLINK" "Создать симлинки с новыми именами" } diff --git a/universal_merger.sh b/universal_merger.sh index d3940c4..2620da8 100755 --- a/universal_merger.sh +++ b/universal_merger.sh @@ -23,11 +23,13 @@ TEMP_MERGE_FILE="/tmp/tmp.mkv" # ... (все переменные состояния остаются без изменений) VIDEO_SRC_DIR="" AUDIO_SRC_DIR="" +SUB_SRC_DIR="" OUTPUT_BASE_DIR="" OUTPUT_SERIES_NAME="" SEASON_NUMBER="01" VIDEO_FILE_PATTERN="*.mkv" AUDIO_FILE_PATTERN="*.mka" +SUB_FILE_PATTERN="*.ass" EPISODE_NUMBER_REGEX='.* ([0-9]{2}) .*' OUTPUT_FILENAME_TEMPLATE="{SERIES_NAME} s{SEASON}e{EPISODE}.mkv" CURRENT_ACTION="" @@ -76,6 +78,7 @@ main() { 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_sub_path=$(truncate_path_for_display "$SUB_SRC_DIR" "$max_display_len" "_не задан_") local display_output_path=$(truncate_path_for_display "$OUTPUT_BASE_DIR" "$max_display_len" "_не задан_") # Формируем пункты меню ДИНАМИЧЕСКИ @@ -84,6 +87,7 @@ main() { if [ "$CURRENT_ACTION" == "MERGE" ]; then menu_items+=("A" "Аудио каталог : $display_audio_path") + menu_items+=("B" "Субтитры каталог : $display_sub_path") fi menu_items+=( @@ -96,6 +100,7 @@ main() { if [ "$CURRENT_ACTION" == "MERGE" ]; then menu_items+=("U" "Шаблон аудиофайлов : $AUDIO_FILE_PATTERN") + menu_items+=("W" "Шаблон субтитров : $SUB_FILE_PATTERN") fi menu_items+=( @@ -118,6 +123,7 @@ main() { 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") ;; + B) SUB_SRC_DIR=$(ui_get_directory "Выберите каталог с субтитрами" "$SUB_SRC_DIR" "$VIDEO_SRC_DIR") ;; O) OUTPUT_BASE_DIR=$(ui_get_directory "Выберите БАЗОВЫЙ каталог для результата" "$OUTPUT_BASE_DIR" "$DEFAULT_ARCHIVE_DIR") OUTPUT_BASE_DIR="${OUTPUT_BASE_DIR%/}" @@ -129,6 +135,7 @@ main() { 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") ;; + W) SUB_FILE_PATTERN=$(ui_get_input "Введите шаблон для поиска субтитров (*.ass, *.srt)" "$SUB_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 ;;