97f8b72251
Повторный запуск после докачки сезона не должен заново склеивать старые файлы. VIEW помечает такие серии как пропущенные. Co-authored-by: Cursor <cursoragent@cursor.com>
197 lines
7.8 KiB
Bash
197 lines
7.8 KiB
Bash
#!/bin/bash
|
||
|
||
#==============================================================================
|
||
# Модуль основной логики: поиск файлов, сопоставление, генерация имён
|
||
#==============================================================================
|
||
|
||
declare -a FILE_TRIPLETS
|
||
|
||
# Функция стала "умной" и проверяет CURRENT_ACTION
|
||
logic_prepare_file_lists() {
|
||
FILE_TRIPLETS=()
|
||
local common_params_ok=true
|
||
if [[ -z "$VIDEO_SRC_DIR" || -z "$OUTPUT_BASE_DIR" || -z "$OUTPUT_SERIES_NAME" ]]; then
|
||
common_params_ok=false
|
||
fi
|
||
|
||
local video_list_raw
|
||
if $common_params_ok; then
|
||
mapfile -d '' video_list_raw < <(find "$VIDEO_SRC_DIR" -maxdepth 1 -name "$VIDEO_FILE_PATTERN" -print0 | sort -z)
|
||
fi
|
||
|
||
# РЕЖИМ: СКЛЕЙКА
|
||
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") || continue
|
||
|
||
# --- ИСПРАВЛЕНИЕ ЗДЕСЬ: ИСПОЛЬЗУЕМ ЯВНЫЙ ЗАПОЛНИТЕЛЬ ---
|
||
# Это гарантирует, что `read` всегда увидит три поля.
|
||
local placeholder="__SYMLINK_AUDIO_PLACEHOLDER__"
|
||
FILE_TRIPLETS+=("$video_file"$'\t'"$placeholder"$'\t'"$output_path")
|
||
done
|
||
fi
|
||
|
||
logic_check_output_paths || return 1
|
||
return 0
|
||
}
|
||
|
||
# Уже готовый результат: обычный файл или симлинк (в том числе битый).
|
||
# Каталог сюда не входит: это не серия, а ошибка пути.
|
||
output_file_exists() {
|
||
local path="$1"
|
||
[[ -f "$path" || -L "$path" ]]
|
||
}
|
||
|
||
# Одинаковые имена и путь-каталог ломают пропуск: вторая серия увидела бы
|
||
# файл, созданный первой, и отчёт засчитал бы её как уже готовую.
|
||
logic_check_output_paths() {
|
||
local -A seen=()
|
||
local triplet video audio output
|
||
local -a dir_errors=()
|
||
local -a dup_errors=()
|
||
|
||
for triplet in "${FILE_TRIPLETS[@]}"; do
|
||
IFS=$'\t' read -r video audio output <<< "$triplet"
|
||
if [[ -d "$output" && ! -L "$output" ]]; then
|
||
dir_errors+=("$(basename "$video") → $output")
|
||
continue
|
||
fi
|
||
if [[ -n ${seen["$output"]+x} ]]; then
|
||
dup_errors+=("$(basename "${seen[$output]}") и $(basename "$video") → $(basename "$output")")
|
||
else
|
||
seen["$output"]="$video"
|
||
fi
|
||
done
|
||
|
||
if [ ${#dir_errors[@]} -gt 0 ]; then
|
||
local text="Путь вывода указывает на каталог, а не на файл:\n\n"
|
||
local line
|
||
for line in "${dir_errors[@]}"; do
|
||
text+="$line\n"
|
||
done
|
||
ui_show_message "Ошибка" "$text"
|
||
return 1
|
||
fi
|
||
|
||
if [ ${#dup_errors[@]} -gt 0 ]; then
|
||
local text="Несколько серий получают одно и то же имя:\n\n"
|
||
local line
|
||
for line in "${dup_errors[@]}"; do
|
||
text+="$line\n"
|
||
done
|
||
ui_show_message "Ошибка" "$text"
|
||
return 1
|
||
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}"
|
||
|
||
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 "Предпросмотр" "Нет файлов для обработки по текущим настройкам."
|
||
return
|
||
fi
|
||
|
||
local body=""
|
||
local count=1
|
||
local skip_count=0
|
||
for triplet in "${FILE_TRIPLETS[@]}"; do
|
||
IFS=$'\t' read -r video audio output <<< "$triplet"
|
||
body+="$(printf "%02d" $count). \n"
|
||
|
||
local exists=false
|
||
if output_file_exists "$output"; then
|
||
exists=true
|
||
skip_count=$((skip_count + 1))
|
||
fi
|
||
|
||
if [ "$CURRENT_ACTION" == "MERGE" ]; then
|
||
body+=" \ZbВИДЕО:\Zn $(basename "$video")\n"
|
||
body+=" \ZbАУДИО:\Zn $(basename "$audio")\n"
|
||
body+=" \Zb-> ВЫВОД:\Zn $(basename "$output")\n"
|
||
else # SYMLINK
|
||
body+=" \ZbИСТОЧНИК:\Zn $(basename "$video")\n"
|
||
body+=" \Zb-> ССЫЛКА:\Zn $(basename "$output")\n"
|
||
fi
|
||
|
||
if $exists; then
|
||
body+=" \Z3ПРОПУСК:\Zn операция пропущена — файл уже есть\n\n"
|
||
else
|
||
body+="\n"
|
||
fi
|
||
count=$((count + 1))
|
||
done
|
||
|
||
local pending=$((${#FILE_TRIPLETS[@]} - skip_count))
|
||
local preview_text="РЕЖИМ: $CURRENT_ACTION\n"
|
||
preview_text+="К обработке: $pending, пропуск (уже есть): $skip_count\n\n"
|
||
preview_text+="$body"
|
||
|
||
ui_show_message "Предпросмотр" "$preview_text"
|
||
}
|