#!/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]}")"

EXT="mp3"

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 "*".$EXT | 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='$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}".$EXT
	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}\".$EXT"
	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`"
	year_source="Year"
	if [[ -z $year ]]; then
		year=""
		year="`exiftool -RecordingTime "$file"`"
		year="${year//Recording Time   /}"
		year="${year//   : /}"
		year="${year//[^[:alnum:][:space:].]/}"
		year="`echo $year`"
		year_source="RecordingTime"
	fi
	if [[ -z $year ]]; then
		year=""
		year="`exiftool -DateTimeOriginal "$file"`"
		year="${year//Date\/Time Original   /}"
		year="${year//   : /}"
		year="${year//[^[:alnum:][:space:].]/}"
		year="`echo $year`"
		year_source="DateTimeOriginal"
	fi
	while [[ "$year" == *"  "* ]]; do
		year="${year//  / }"
	done
	echo "*** Year=$year ($year_source)"

	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 "*".$EXT | 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 "*".$EXT | sort | while read file; do
	migrate_music "$file" cp
done