278 lines
6.6 KiB
Bash
Executable File
278 lines
6.6 KiB
Bash
Executable File
#!/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="OLD"
|
|
NEW="NEW"
|
|
INT="INTERIM"
|
|
|
|
OLD_DIR="$DIR/$OLD"
|
|
NEW_DIR="$DIR/$NEW"
|
|
INT_DIR="$DIR/$INT"
|
|
|
|
## 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_DIR" "$INT_DIR"
|
|
|
|
## Convert Media ##
|
|
|
|
# TODO: Use the INTERIM folder folder, or remove references to it entirely.
|
|
echo "* Ensure all files are mp3."
|
|
find "$OLD_DIR" -type f ! -name "*".mp3 | sort | while read file; do
|
|
echo "** Working on '$file'."
|
|
|
|
TEMP_DIR="`dirname "$file"`"
|
|
TEMP_DIR="${TEMP_DIR//$OLD/$INT}"
|
|
echo "TEMP_DIR='$TEMP_DIR'"
|
|
mkdir -pv "$TEMP_DIR"
|
|
|
|
TEMP_FILE="`basename "$file"`"
|
|
TEMP_FILE="${TEMP_FILE//[^[:alnum:][:space:].]/}"
|
|
echo "$TEMP_FILE"
|
|
|
|
if [[ -s "$TEMP_DIR/$TEMP_FILE" ]]; then
|
|
echo "*** Skipping file, already exists!"
|
|
continue
|
|
fi
|
|
|
|
# Actual conversion.
|
|
ffmpeg -y \
|
|
-nostdin -hide_banner -loglevel quiet \
|
|
-i "$file" "${file//$OLD/$INT}".mp3
|
|
status="$?"
|
|
if [[ $status != 0 ]]; then
|
|
echo "*** FAILED: Exited with status '$status'."
|
|
echo "**** To troubleshoot, try running this:"
|
|
echo "ffmpeg -y -i \"$file\" \"${file//$OLD/$INT}\".mp3"
|
|
else
|
|
echo "*** Success!"
|
|
fi
|
|
done
|
|
echo "** Done converting any 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"
|
|
|
|
if [[ -n "$track" ]]; then
|
|
new_file="$track. $new_file"
|
|
fi
|
|
|
|
# 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 [[ -n $year && -n $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 [[ -n $album ]]; then
|
|
grandparent="$artist"
|
|
elif [[ -n $band ]]; then
|
|
grandparent="$band"
|
|
elif [[ -n $album_artist ]]; then
|
|
grandparent="$album_artist"
|
|
else
|
|
grandparent="Unknown"
|
|
fi
|
|
|
|
new_file="$grandparent/$new_file"
|
|
|
|
# Check that the file has proper data.
|
|
skip=false
|
|
if [[ -z "$grandparent" ]]; then
|
|
echo "*** WARNING: Could not find Artist information."
|
|
skip="true"
|
|
fi
|
|
if [[ -z "$parent" ]]; then
|
|
echo "*** WARNING: Could not find Album or Year information."
|
|
skip="true"
|
|
fi
|
|
if [[ -z "$track" && -z "$title" ]]; then
|
|
echo "*** WARNING: Could not find Track or Title information."
|
|
skip="true"
|
|
fi
|
|
|
|
if [[ "$skip" == "true" ]]; then
|
|
echo "*** Skipping file, critical data missing."
|
|
return 1
|
|
fi
|
|
|
|
# Create the new directories if they do not already exist.
|
|
mkdir -pv "$NEW_DIR/$grandparent/$parent"
|
|
|
|
# Move the file.
|
|
new_file="$NEW_DIR/$new_file"
|
|
$command -v "$file" "$new_file"
|
|
|
|
echo "*** Finished file!"
|
|
}
|
|
|
|
echo "* Moving INTERIM files to NEW."
|
|
find "$INT_DIR" -name "*".mp3 | sort | while read file; do
|
|
migrate_music "$file" mv
|
|
done
|
|
|
|
echo "** Done with INTERIM, deleting."
|
|
rm -rfv "$INT_DIR"
|
|
|
|
echo "* Copying OLD files to NEW."
|
|
find "$OLD_DIR" -name "*".mp3 | sort | while read file; do
|
|
migrate_music "$file" cp
|
|
done
|