#!/bin/bash # Auto Sort — sortiert Videodateien nach Audio-/Untertitel-Spuren # mit Erkennung zu langer Ordnernamen → generische Gruppennamen set -euo pipefail # -------------------------------------------------------------------- # CONFIG / COLORS # -------------------------------------------------------------------- settings_file="./settings.ini" ffprobe_path="/usr/bin/ffprobe" RED='\033[0;31m' GREEN='\033[0;92m' BLUE='\033[0;94m' YELLOW='\033[0;33m' WHITE_ON_GRAY='\033[0;37;100m' BLACK_ON_WHITE='\033[0;30;47m' WHITE_ON_RED='\033[0;37;41m' YELLOW_ON_WHITE='\033[0;37;43m' NC='\033[0m' declare -A group_map # Map von Kombination → Ordnername declare -A files_by_group # Map von Kombination → Liste von Dateien declare -a combinations # Liste aller eindeutigen Kombinationen # -------------------------------------------------------------------- # FUNKTIONEN # -------------------------------------------------------------------- extract_audio_languages_and_codecs() { local file=$1 local ffprobe_json ffprobe_json=$("$ffprobe_path" -v error -show_streams -of json "$file") local audio_streams=() while IFS= read -r s; do codec_name=$(echo "$s" | jq -r '.codec_name') language=$(echo "$s" | jq -r '.tags.language // "unknown"') audio_streams+=("[$codec_name"_"$language]") done < <(echo "$ffprobe_json" | jq -c '.streams[] | select(.codec_type=="audio")') echo "${audio_streams[@]}" } extract_subtitle_languages_and_forced_tags() { local file=$1 local ffprobe_json ffprobe_json=$("$ffprobe_path" -v error -show_streams -of json "$file") local subtitle_streams=() while IFS= read -r s; do language=$(echo "$s" | jq -r '.tags.language // "unknown"') forced_flag=$(echo "$s" | jq -r '.disposition.forced // 0') if [[ "$forced_flag" == "1" ]]; then subtitle_streams+=("[forced_$language]") fi subtitle_streams+=("[$language]") done < <(echo "$ffprobe_json" | jq -c '.streams[] | select(.codec_type=="subtitle")') echo "${subtitle_streams[@]}" } relativ_to_fullpath() { local path="$1" local scriptpath scriptpath="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" path=$(echo "$path" | sed 's|/+|/|g') if [ ! -e "$path" ]; then realpath "$(dirname "$scriptpath")/$path" 2>/dev/null || echo "$path" else realpath "$path" 2>/dev/null || echo "$path" fi } get_user_variables_from_ini_file() { settings_file=$(relativ_to_fullpath "$settings_file") if [ ! -e "$settings_file" ]; then echo -e "${WHITE_ON_RED} Settingsfile nicht gefunden: $settings_file ${NC}" exit 1 fi input_folder=$(sed -nr "/^\[pathes\]/ { :l /^input_folder[ ]*=/ { s/[^=]*=[ ]*//; p; q;}; n; b l;}" "$settings_file") output_folder=$(sed -nr "/^\[pathes\]/ { :l /^output_folder[ ]*=/ { s/[^=]*=[ ]*//; p; q;}; n; b l;}" "$settings_file") } # -------------------------------------------------------------------- # MAIN # -------------------------------------------------------------------- main() { get_user_variables_from_ini_file output_dir="$input_folder/[0] Sort" output_dir=$(echo "$output_dir" | sed 's|//|/|g') mkdir -p "$output_dir" echo "──────────────────────────────────────────────────────────────" echo -e "${BLACK_ON_WHITE} Auto Sort — Analysephase ${NC}" echo "──────────────────────────────────────────────────────────────" # 1️⃣ Alle Dateien analysieren und Kombinationen erfassen found_any=false # Sortierordner-Pfad in ein sauberes Pattern für find umwandeln escaped_output_dir=$(printf '%s\n' "$output_dir" | sed 's/[][\.*^$(){}?+|/]/\\&/g') while IFS= read -r file; do found_any=true relative_path=$(realpath --relative-to="$input_folder" "$file") # Sample-Dateien erkennen (case-insensitive, am Ende des Dateinamens) basename_lower=$(basename "$file" | tr '[:upper:]' '[:lower:]') if [[ "$basename_lower" =~ \.sample\.(mkv|mp4|avi|vob|ts|mpeg|mov)$ ]]; then echo -e "${YELLOW_ON_WHITE} Überspringe Sample ${NC} $relative_path" echo -e "${YELLOW_ON_WHITE} Grund ${NC} Enthält '.sample' vor Dateiendung${NC}" continue fi audio_streams=$(extract_audio_languages_and_codecs "$file") subtitle_streams=$(extract_subtitle_languages_and_forced_tags "$file") if [ -z "$audio_streams" ] && [ -z "$subtitle_streams" ]; then echo -e "${WHITE_ON_RED} Keine Audio-/Subtitle-Spuren: ${NC} $relative_path" continue fi # Ausgabe der gefundenen Spuren echo -e "${WHITE_ON_GRAY} Datei ${NC} $relative_path" if [ -n "$audio_streams" ]; then echo -e " 🎧 ${GREEN}Audio:${NC} $audio_streams" else echo -e " 🎧 ${RED}Audio:${NC} (keine)" fi if [ -n "$subtitle_streams" ]; then echo -e " 💬 ${YELLOW}Subtitles:${NC} $subtitle_streams" else echo -e " 💬 ${RED}Subtitles:${NC} (keine)" fi combined_streams="$audio_streams | $subtitle_streams" files_by_group["$combined_streams"]+="$file"$'\n' done < <( find "$input_folder" \ -type d -regex "^$escaped_output_dir$" -prune -false -o \ -type d -regex "^$escaped_output_dir/.*" -prune -false -o \ -type f \( \ -iname "*.mkv" -o -iname "*.mp4" -o -iname "*.avi" -o \ -iname "*.ts" -o -iname "*.vob" -o -iname "*.mpeg" -o -iname "*.mov" \ \) -print ) if [ "$found_any" = false ]; then echo "──────────────────────────────────────────────────────────────" echo -e "${YELLOW_ON_WHITE} Keine neuen Videodateien gefunden. ${NC}" echo "──────────────────────────────────────────────────────────────" read -rp "Zum Schließen bitte [ENTER] drücken ..." exit 0 fi # 2️⃣ Ordnernamen prüfen group_counter=1 for combo in "${!files_by_group[@]}"; do folder_name=$(echo "$combo" | tr -s ' ' '_') target_folder="$output_dir/$folder_name" if [ ${#target_folder} -gt 240 ]; then folder_name="Gruppe_${group_counter}" ((group_counter++)) fi group_map["$combo"]="$folder_name" combinations+=("$combo") done echo "──────────────────────────────────────────────────────────────" echo -e "${BLACK_ON_WHITE} Verschiebe Dateien ${NC}" echo "──────────────────────────────────────────────────────────────" # 3️⃣ Dateien verschieben for combo in "${combinations[@]}"; do folder_name="${group_map[$combo]}" target_folder="$output_dir/$folder_name" mkdir -p "$target_folder" while IFS= read -r file; do [ -z "$file" ] && continue relative_path=$(realpath --relative-to="$input_folder" "$file") echo -e "${WHITE_ON_GRAY} → ${NC} ${relative_path}" echo -e " nach ${GREEN}$folder_name${NC}" mv "$file" "$target_folder/" done <<< "${files_by_group[$combo]}" done echo "──────────────────────────────────────────────────────────────" echo -e "${BLACK_ON_WHITE} Fertig! ${NC}" } # Main main echo read -rp "Zum Schließen bitte [ENTER] drücken ..." exit 0 $SHELL