#!/usr/bin/env bash # 2025 Hyperling # Clean up a music library. Improved version of rename_music_files.sh. # Steps: # 0) Ensure an OLD folder exists, it is what will be refactored. # 1) Convert all non-mp3 files to mp3 and put them in an INTERIM folder. # 2) Loop over all OLD and INTERIM's mp3 files and move them to NEW based on data. # - Name the file as "${ZERO_LED_TRACK_NUMBER}. ${SONG_TITLE} # - Name parent folder as "${YEAR} - ${ALBUM}" if both exist, otherwise just one or the other "${YEAR}${ALBUM}". # - Name grandparent folder as "${ARTIST} - ${GENRE}" if both exist, otherwise "${ARTIST}${GENRE}". # TODO list: # - Organize tracks by Artist + Album folders. # - Name parent folder as "$YEAR - $ALBUM" if both exist, otherwise just one or the other. # - Name grandparent folder as ARTIST if it exists, otherwise GENRE. ## Facts and Variables ## DIR="`pwd`" PROG="$(basename -- "${BASH_SOURCE[0]}")" FAIL=".$PROG.exit-error" OLD="$DIR/OLD" NEW="$DIR/NEW" INT="$DIR/INTERIM" ## Checks and Setup ## # System Packages # echo "* Checking if necessary tools are installed." function needed_progs { cat <<- EOF ffmpeg exiftool EOF } needed_progs | while read prog; do if [[ -z "`which $prog`" ]]; then echo "ERROR: $prog not found, please install." touch $FAIL fi done if [[ -e $FAIL ]]; then rm -fv $FAIL exit 1 fi echo "** Tools are available." # Old Directory # echo "* Checking whether OLD directory exists." if [[ "`pwd`" == *"/OLD" ]]; then echo "** We are in the folder, moving up a directory." cd .. fi if [[ ! -d "OLD" ]]; then echo "ERROR: Could not find a folder named 'OLD'." echo " Please create it and add your library there." fi echo "** Folder exists appropriately." # Other Directories # echo "* Ensuring working directories NEW and INTERIM exist." mkdir -pv "$NEW" "$INT" ## Convert Media ## echo "* Convert all files to mp3." find "$OLD" ! -name "*".mp3 | while read file; do ffmpeg -nostdin -hide_banner -loglevel quiet -i "$file" "$file".converted.mp3 done echo "** Done converting mp3s." ## Transform Media ## function migrate_music { file="$1" command="$2" if [[ -z $file ]]; then echo "ERROR: No filename retrieved, cannot migrate." return 1 fi if [[ -z "$command" ]]; then command="cp" fi echo "** $file **" # Retrieve and clean the Title title="" title="`exiftool -Title "$file"`" title="${title//Title /}" title="${title// : /}" title="${title//[^[:alnum:][:space:].]/}" title="`echo $title`" while [[ "$title" == *" "* ]]; do title="${title// / }" done echo "*** Title=$title" new_file="$title$EXT" # Retrieve and clean the Track Number track="" # Get raw value track="`exiftool -Track "$file"`" # Filter the header track="${track//Track /}" track="${track// : /}" # Remove disk designations track="${track%%/*}" # Remove any whitespace before/after track="`echo $track`" # Add a leading 0 to single digits. [[ ${#track} == 1 ]] && track="0$track" echo "*** Track=$track" new_file="$track. $new_file" # Add the Album and Year. album="" album="`exiftool -Album "$file"`" album="${album//Album /}" album="${album// : /}" album="${album//[^[:alnum:][:space:].]/}" album="`echo $album`" while [[ "$album" == *" "* ]]; do album="${album// / }" done echo "*** Album=$album" year="" year="`exiftool -Year "$file"`" year="${year//Year /}" year="${year// : /}" year="${year//[^[:alnum:][:space:].]/}" year="`echo $year`" while [[ "$year" == *" "* ]]; do year="${year// / }" done echo "*** Year=$year" parent="" if [[ -s $year && -s $album ]]; then parent="$year - $album" else parent="$year$album" fi new_file="$parent/$new_file" # Add the Artist. artist="" artist="`exiftool -Artist "$file"`" artist="${artist//Artist /}" artist="${artist// : /}" artist="${artist//[^[:alnum:][:space:].]/}" artist="`echo $artist`" while [[ "$artist" == *" "* ]]; do artist="${artist// / }" done echo "*** Artist=$artist" band="" band="`exiftool -Band "$file"`" band="${band//Band /}" band="${band// : /}" band="${band//[^[:alnum:][:space:].]/}" band="`echo $band`" while [[ "$band" == *" "* ]]; do band="${band// / }" done echo "*** Band=$band" album_artist="" album_artist="`exiftool -Albumartist "$file"`" album_artist="${album_artist//Albumartist /}" album_artist="${album_artist// : /}" album_artist="${album_artist//[^[:alnum:][:space:].]/}" album_artist="`echo $album_artist`" while [[ "$album_artist" == *" "* ]]; do album_artist="${album_artist// / }" done echo "*** Albumartist=$album_artist" # Prefer Artist, then Band, then Albumartist grandparent="" if [[ -s $album ]]; then grandparent="$album" elif [[ -s $band ]]; then grandparent="$band" elif [[ -s $album_artist ]]; then grandparent="$album_artist" else grandparent="Unknown" fi new_file="$grandparent/$new_file" # Move the file. new_file="$NEW/$new_file" $command -v "$file" "$new_file" echo "*** Finished file!" } echo "* Moving INTERIM files to NEW." find "$INT" -name "*".mp3 | while read file; do migrate_music "$file" mv done echo "** Done with INTERIM, deleting." rm -rfv "$INT" echo "* Copying OLD files to NEW." find "$OLD" -name "*".mp3 | while read file; do migrate_music "$file" cp done