Back to blog
Aug 2022·1 min read

Autosort: Schnell und einfach Bilder und Videos sortieren

#autosort#bash#dateien#dateien-sortieren#gopro#ordnerstruktur#shell#skript#sortieren

Dies ist der Nachfolger des hier vorgestellten Skriptes: GoPro & Fotos: Automatisch umbenennen und kopieren

Da ich relativ viel fotografiere und filme ist mir eine organisierte Datei- und Ordnerstruktur extrem wichtig. Ich lege meine Bilder und Videos immer im Format

{video_target|foto_target}/YYYY-MM-DD/YYYY-MM-DD-HH-MM-SS.ext

ab.

Das macht man ein, zwei mal. Dann wird es repetitiv, langweilig und eine automatisierte Lösung muss her... insbesondere da schnell mal > 1000 Fotos geschossen sind und es wirklich keinen Spaß macht, diese manuell zu verschieben und umzubenennen.

So war die Idee geboren, ein kleines Shellskript zu schreiben, welches meine Dateien automatisiert nach Datum sortiert und umbenennt und dabei auch Unterordner durchsucht.

Es wird zunächst eine Menge an Ordnern und ihre Unterordner nach Dateien mit vordefiniertem Medientyp durchsucht. Wird ein Mediendatei gefunden, ermittelt das Skript ob es sich dabei um ein Bild oder ein Video handelt. Abhängig davon wird die Datei in den entsprechenden Zielordner kopiert, und zwar im gleichen Format wie oben.

Ist der Kopiervorgang erfolgreich (= die MD5 Werte von Quelle und Ziel stimmen überein) wird das Original gelöscht.

Wenn der Zielordner nicht gefunden werden kann wird die Datei nicht kopiert und übersprungen.

Folgende Variablen müssen angepasst werden:

  • video_target: Hierhin werden die Videos kopiert
  • foto_target: Hierhin werden die Fotos kopiert
  • sources: Das sind die Ordner, in denen automatisiert nach Videos und Fotos gesucht wird. Es gibt aber auch die Möglichkeit, bei jedem Durchlauf individuell einen Pfad anzugeben.
  • video_extensions: Dateien mit dieser Endung werden als Video erkannt
  • foto_extensions: Dateien mit dieser Endung werden als Foto erkannt

Im folgenden das Skript, welches sich auch direkt herunterladen lässt:

Shell_Copy_Rename_Media.zip herunterladen

Viel Spaß damit.

#!/bin/sh

video_target="/Volumes/Videos/Videos"
foto_target="/Volumes/Extern01/Fotos"
sources=("/Volumes/Untitled/MP_ROOT" "/Volumes/GoPro/DCIM/100GOPRO" "/Volumes/Untitled/DCIM/100MSDCF" "/Volumes/Untitled/private/AVCHD/BDMV/STREAM")
video_extensions=("mp4" "mov" "flac" "webm" "mkv" "flv" "vob" "ogv" "ogg" "drc" "gif" "gifv" "mng" "avi" "mts" "m2ts" "ts" "mov" "qt" "wmv" "yuv" "rm" "rmvb" "viv" "asf" "amv" "mp4" "m4p" "m4v" "mpg" "mp2" "mpeg" "mpe" "mpv" "m2v" "m4v" "svi" "3gp" "3g2" "mxf" "roq" "nsv" "f4v" "f4p" "f4a" "f4b" "wav")
foto_extensions=("jpg" "heic" "arw" "raw" "crw" "jpeg")
total_errors=0

docopy()
{
  path=$1
  log INFO "Copy from path: '$path'"
  num_files=`ls -1q "$path" | wc -l`
  filecount=0
  err_count=0

  for file in $path/*; do
      filecount=$((filecount+1))
      log INFO "File/Folder #$filecount of $num_files"

      if [[ -d ${file} ]]; then
        log INFO "$file is Directory. Check it's subdirectories."
        docopy "$file"
      elif [ ! -f "$file" ]; then
          log INFO "$file does not contain any files. Next."
          continue
      fi

      original_file_datetime=`stat -f "%Sm" -t "%Y-%m-%d-%H-%M-%S" "$file"`
      original_file_name=$(basename ${file})
      original_file_extension="${original_file_name##*.}"

      GetPathByExtension "$original_file_extension"

      if [ "$filetype_path" = "UNKNOWN" ]; then
        log ERR "$original_file_extension is not a valid format. Continue with next file"
        continue
      elif [ ! -d "$filetype_path" ]; then
        log ERR "Target path '$filetype_path' unavailable"
        err_count=$((err_count+1))
        continue
      fi

      target_folder_name=`stat -f "%Sm" -t "%Y-%m-%d" "$file"`
      target_path="$filetype_path/${target_folder_name}"
      target_file="$target_path/$original_file_name"

      log INFO "Source: ${file}"
      log INFO "Destination: ${target_path}"
      log INFO "Copying..."
      
      if [ -f "$file" ] && [ -f "$target_path" ]; then
        md5SrcFile=$(md5 -q "$file")
        md5DstFile=$(md5 -q "$target_file")
        if [ "$md5SrcFile" == "$md5DstFile" ]; then
          log WARN "The file ${target_path} already exists with MD5 Matching. Skip?"
          read -p "Y to Skip, C to Continue " skip
          if [ "$skip" == "Y" ]; then
            continue
          fi
        fi 
      fi

      mkdir -p "${target_path}"
      md5SrcFile=$(md5 -q "$file")
      rsync -ct ${file} ${target_path}
      md5DstFile=$(md5 -q "$target_file")

      if [ "$md5SrcFile" == "$md5DstFile" ] && [ "$md5SrcFile" != "" ] && [ "$md5DstFile" != "" ];
      then
        rm ${file}
      else
        log INFO "ERROR: MD5 MISMATCH"
        exit
      fi

      renamed_file="$target_path/$original_file_datetime.$original_file_extension"
      incrementor=0
      while [ -f "$renamed_file" ]; do
        incrementor=$((incrementor+1))
        renamed_file="$target_path/$original_file_datetime.$incrementor.$original_file_extension"
      done

        mv $target_file $renamed_file

      log IFNO "File Done"
    done

    log INFO "Finished with $err_count errors"
}

GetPathByExtension()
{
  extension=`echo "$1" | dd  conv=lcase 2> /dev/null`
  if [[ " ${video_extensions[*]} " =~ " ${extension} " ]]; then
    filetype_path="$video_target"  
  elif [[ " ${foto_extensions[*]} " =~ " ${extension} " ]]; then
    filetype_path="$foto_target"  
  else
    filetype_path="UNKNOWN"
  fi
}

log()
{
  [ "$1" = "ERR" ] && echo "\033[0;31m$2\033[0m" && err_count=$((err_count+1)) && total_errors=$((total_errors+1))
  [ "$1" = "WARN" ] && echo "\033[0;31m$2\033[0m"
  [ "$1" = "SUCCESS" ] && echo "\033[0;32m$2\033[0m"
  [ "$1" = "INFO" ] && echo "$2"
}

scandefault()
{
  log INFO "Scanning default folders."
  for i in "${sources[@]}"; do
    if [ -d "$i" ]; then
      docopy "$i"
    fi
  done
}

scanindividual()
{
  read -p "Do you want to copy files from an individual path? Press Y or N: " tmp

  if [ "$tmp" = "Y" ]; then
    read -p "Enter Source path: " src
    if [ -d "$src" ]; then
      docopy "$src"
    else
      log ERR "Path $src does not exist. Aborted."
      total_errors=$((total_errors+1))
    fi
  fi
}

scandefault
scanindividual

hasErrors=$false
[ "$total_errors" -gt 0 ] && hasErrors=$true || hasErrors=$false
if [[ $hasErrors -eq $true ]]; then
  log ERR "==========================\r\nFinished with errors\r\nTotal Errors: $total_errors\r\n=========================="
else
  log SUCCESS "==========================\r\nFinished without errors\r\n=========================="
fi
NewerMarlin Firmware: SKR Mini e3 v3OlderPromises