regexbashawksedtr

Bash script or function to rename mp3 files


Want to batch rename mp3 files to following scheme my-artist_my-title.mp3

E.g.: Let's suppose that I have the mp3 file: of the song Underground‐Čoček, by Goran Bregović. But the file is named s3 24)3ü6 Dț67 .mp3 (for exaple, because it was lost on a drive and recovered with testdisk. Supposing the mp3 metadata is correct, the script should change the filename to: goran-bregovic_underground‐cocek.mp3

Ideally I would prefer to have a bash script which I would trigger with:

$ rename-mp3 directory-with-subdirectories-with-mp3-files

Searching for similar solutions I found this function. I modified it and it works, partially! The regex modifications, marked with #? are not working.

  function rename-mp3() {       # input is an mp3 file: i.e. find -name 's3  24)3ü6   Dț67  .mp3' -exec bash -c 'rename-mp3 "$@"' bash {} \;
                                # extract metadata from file
  artist=`ffprobe -loglevel error -show_entries format_tags=artist -of default=noprint_wrappers=1:nokey=1 "$1"`
  title=`ffprobe -loglevel error -show_entries format_tags=title -of default=noprint_wrappers=1:nokey=1 "$1"`
  artist="${artist/\&/}"        #? delete all non-numeral, non-latin, and non-space characters (&, %, !, ', ä, ö, ü, ă, ț, etc.)
  title="${title/\&/}"          #? delete all non-numeral, non-latin, and non-space characters (&, %, !, ', ä, ö, ü, ă, ț, etc.)
  artist="${artist// +/\-}"     #? replace all spaces with "-", if more then one in sequence substitute only one "-"
  title="${artist// +/\-}"      #? replace all spaces with "-", if more then one in sequence substitute only one "-"
  filename="$artist_$title.mp3" # paste artist and title together
  filename="${name,,}"          # lowercase everything
  echo "$filename"              # display new filename
  cp "$1" "renamed/$name"       # copy renamed file to the renamed folder
}

Solution

  • I'm using a similar script (but really more complicated) to sort all my audio files.

    Based on your script:

    #! /usr/bin/env bash
    
    declare -r input_filename="$1"
    declare -r output_dirname="$2"
    
    function rationalize() {
        local s="$1"
        # extglob option is necessary for pattern matching in bash expansions
        shopt -s extglob
        # delete all non-ascii characters
        s="${s//[^[:ascii:]]/}"
        # keep only all alpha, numeral and space characters
        s="${s//[^[:alnum:]|[:space:]]/}"
        # replace all spaces with "-", if more then one in sequence substitute only one "-"
        s="${s//+([[:space:]])/-}"
        shopt -u extglob
        printf "%s" "$s"
    }
    
    function get_tag() {
        local -r tag="$1"
        local -r filename="$2"
        local value=$(ffprobe -loglevel error -show_entries format_tags="$tag" -of default=noprint_wrappers=1:nokey=1 "$filename")
        value=$(rationalize "$value")
        printf "%s" "$value"
    }
    
    function copy_audiofile() {
        local -r from_filname="$1"
        local -r to_dir="$2"
        # get filename extension
        local -r ext="${from_filname##*.}"
        # get artist for file
        local artist=$(get_tag "artist" "$from_filname")
        # get title for file
        local title=$( get_tag "title" "$from_filname")
        # paste artist and title together
        local filename_to="${artist}_$title.$ext"
        # lowercase everything
        filename_to="${filename_to,,}"
        # display new from_filname
        echo "Rename '$from_filname' to '$filename_to'"
        # copy renamed file to the renamed folder
        cp "$1" "$to_dir/$filename_to"
    }
    
    copy_audiofile "$input_filename" "$output_dirname"