#!/bin/bash # Includes . $(miniade) || { echo "${0##*/}: ERROR: miniade failed (hint: run 'miniade' to see error)" >&2; exit 1; } # Configurable stuff TUPLES=( '^Various - .* - ([0-9]{2,3}) - (.* \(.*\))\.mp3$' unpack_format_standard_various '^.* - .* - ([0-9]{2,3}) - (.*)\.mp3$' unpack_format_standard '^(.*) \[[-A-Za-z0-9_]{11}\]\.mp3$' unpack_format_ytdlp '^(.*)\.mp3$' unpack_format_unknown ) # Other globals TUPLE_LEN=2 main() { local MY_ARGS local # Defaults for options # Process options special_opts_handler() { case $1 in esac } miniade_process_options --help-handler=help --special-opts-handler=special_opts_handler MY_ARGS "$@" && set -- "${MY_ARGS[@]}" # Process arguments [ $# -ge 1 ] || miniade_bad_usage THINGS=( "$@" ) # Sanity checks and derivations # Guts for THING in "${THINGS[@]}"; do process "${THING%/}" done } process() { local THING THING2 OLD_ABS_THING NEW_ABS_THING I MATCH MUSIC_DIR ARTIST_SUBDIR ALBUM_SUBDIR OLD_BASENAME local OLD_TRACKNO OLD_TRACK UNMUNGED_ARTIST_SUBDIR NEW_BASENAME # Process arguments [ $# = 1 ] || miniade_internal "process: bad argument count ($#)" THING=$1 miniade_debug 10 "process: THING=$THING" # Sanity checks and derivations case $THING in /*) OLD_ABS_THING=$THING ;; *) OLD_ABS_THING=$(pwd)/$THING ;; esac # If artist recurse to do albums by artist. if [ -d "${OLD_ABS_THING%/*}/KLF" ]; then miniade_debug 10 "process: $THING: is an artist" for THING2 in "$THING"/*; do process "$THING2" done # If album recurse to do tracks on album. elif [ -d "${OLD_ABS_THING%/*}/../KLF" ]; then miniade_debug 10 "process: $THING: is an album" for THING2 in "$THING"/*; do process "$THING2" done # If track then process it. elif [ -d "${OLD_ABS_THING%/*}/../../KLF" ]; then if [[ $THING =~ .*\.jpg$ ]]; then miniade_debug 10 "process: $THING: is skippable; skipping ..." return fi miniade_debug 10 "process: $THING: is a track" # Sanity checks and derivations [ -f "$OLD_ABS_THING" ] || miniade_error "$OLD_ABS_THING: not accessible" [[ $OLD_ABS_THING =~ (.*)/([^/]+)/([^/]+)/([^/]+)$ ]] || miniade_error "$OLD_ABS_THING: failed to disect" MUSIC_DIR=${BASH_REMATCH[1]} ARTIST_SUBDIR=${BASH_REMATCH[2]} ALBUM_SUBDIR=${BASH_REMATCH[3]} OLD_BASENAME=${BASH_REMATCH[4]} unmunge_artist_subdir "$ARTIST_SUBDIR" UNMUNGED_ARTIST_SUBDIR if [ "$ARTIST_SUBDIR" = Various -a "$ALBUM_SUBDIR" = Singles ]; then TRACKNO_WIDTH=3 else TRACKNO_WIDTH=2 fi # Guts # Match filename against templates in order to extract old track number and old # track title, which will be used as defaults when prompting for new ones. for ((I=0; I<${#TUPLES[*]}; I+=TUPLE_LEN)); do MATCH=false if [[ $OLD_BASENAME =~ ${TUPLES[$I]} ]]; then MATCH=true break fi done $MATCH || miniade_internal "$OLD_BASENAME: failed to find match in TUPLES table" ${TUPLES[$((I+1))]} "${BASH_REMATCH[@]}" # Enquire, validate, rationalise echo "$OLD_BASENAME" read -e -p "Track-No: " -i "$OLD_TRACKNO" NEW_TRACKNO read -e -p "Track: " -i "$OLD_TRACK" NEW_TRACK [[ $NEW_TRACKNO =~ ^[0-9]{1,3}$ ]] || miniade_error "$NEW_TRACKNO: badly formatted track number" # strip leading zeroes in order that printf doesn't interpret '08' as invalid octal. [[ $NEW_TRACKNO =~ ^0*(.*) ]] NEW_TRACKNO=${BASH_REMATCH[1]} printf -v NEW_TRACKNO %0${TRACKNO_WIDTH}d $NEW_TRACKNO # Combine fixed filesystem-derived fields and user-decided fields. NEW_BASENAME="$UNMUNGED_ARTIST_SUBDIR - $ALBUM_SUBDIR - $NEW_TRACKNO - $NEW_TRACK.mp3" miniade_debug 10 "process: NEW_BASENAME=[$NEW_BASENAME]" NEW_ABS_THING="$MUSIC_DIR/$ARTIST_SUBDIR/$ALBUM_SUBDIR/$NEW_BASENAME" if [ "X$OLD_ABS_THING" = "X$NEW_ABS_THING" ]; then miniade_debug 10 "process: no rename necessary" else miniade_debug 10 "process: moving ..." miniade_evaler "mv --no-clobber \"$OLD_ABS_THING\" \"$NEW_ABS_THING\"" fi miniade_debug 10 "process: clearing ID3v2 tags ..." miniade_evaler "id3v2 -D \"$NEW_ABS_THING\" > /dev/null" miniade_debug 10 "process: setting ID3v2 tags ..." miniade_evaler "id3v2 -a \"$UNMUNGED_ARTIST_SUBDIR\" -A \"$ALBUM_SUBDIR\" -T $NEW_TRACKNO -t \"$NEW_TRACK\" \"$NEW_ABS_THING\"" echo else miniade_internal "can't find KLF (KLF is reference for determining what you're trying to rename" fi } unmunge_artist_subdir() { local ARTIST_SUBDIR REF LOCAL_UNMUNGED_ARTIST_SUBDIR ARTIST_SUBDIR=$1 REF=$2 # Change 'X, The' to 'The X' if [[ $ARTIST_SUBDIR =~ (.*),\ (The)$ ]]; then LOCAL_UNMUNGED_ARTIST_SUBDIR="${BASH_REMATCH[2]} ${BASH_REMATCH[1]}" # Change 'secondname, firstname' to 'firstname secondname' elif [[ $ARTIST_SUBDIR =~ ^([^, ]+),\ ([^, ]+)$ ]]; then LOCAL_UNMUNGED_ARTIST_SUBDIR="${BASH_REMATCH[2]} ${BASH_REMATCH[1]}" else LOCAL_UNMUNGED_ARTIST_SUBDIR=$ARTIST_SUBDIR fi eval "$REF=\"\$LOCAL_UNMUNGED_ARTIST_SUBDIR\"" } unpack_format_ytdlp() { OLD_TRACKNO=1 OLD_TRACK=${BASH_REMATCH[1]} } unpack_format_standard_various() { OLD_TRACKNO=${BASH_REMATCH[1]} OLD_TRACK=${BASH_REMATCH[2]} } unpack_format_standard() { OLD_TRACKNO=${BASH_REMATCH[1]} OLD_TRACK=${BASH_REMATCH[2]} } unpack_format_unknown() { OLD_TRACKNO=1 OLD_TRACK=${BASH_REMATCH[1]} } help() { local PROGNAME miniade_get_progname PROGNAME echo "$PROGNAME [ ] { | | } ..." >&2 exit 1 } main "$@"