Skip to content

Instantly share code, notes, and snippets.

@Malvineous
Last active January 12, 2025 12:07
Show Gist options
  • Save Malvineous/966f56d2df95e8e9f16f362fadb1f91f to your computer and use it in GitHub Desktop.
Save Malvineous/966f56d2df95e8e9f16f362fadb1f91f to your computer and use it in GitHub Desktop.
Bash script to apply a regex to Ogg Vorbis tags, and rename the files to match the tags, showing a diff of what it changed.
#!/bin/bash
#
# Apply regular expressions to Ogg Vorbis tags and filenames.
# Run with -h for help.
# https://gist.github.com/Malvineous/966f56d2df95e8e9f16f362fadb1f91f
#
set -e
# Defaults
ALBUM_FIRST=0
FULL_ARTIST=0
DRYRUN=0
RENAME=1
TAG=1
function help() {
echo "Apply regular expressions to Ogg Vorbis tags and filenames."
echo ""
echo "vorbis-retag-regex.sh [options] <regex> *.ogg"
echo ""
echo " options:"
echo " -a Rename files to show album first"
echo " -c Don't truncate artist in filename at second comma"
echo " -n Dry run/preview, don't modify or rename any files"
echo " -r Don't rename files at all (e.g. if name is too long)"
echo " -t Don't retag, just rename (i.e. apply regex to filename only)"
echo ""
echo " regex: sed expression over multiline tags, blank (\"\") to leave tags unmodified, e.g."
echo " 's/\(title=\).*/\1New title/' <= Replace title with 'New title'"
echo " 's/\(artist=\).*/\1New artist/' <= Replace artist with 'New artist'"
echo " 's/\(album=\).*/\1New album/' <= Replace album with 'New album'"
echo " '/tracknumber/a date=2000' <= Add new 'date=2000' tag after tracknumber tag"
echo " '/^year=/ { d }' <= Completely remove 'year' tag"
echo " 's/^\([A-Z_][^=]\+\)=/\L&/' <= Convert all tag names to lowercase"
echo " 's/\(\( \(A\|At\|In\|Of\|On\|The\|For\|And\)\)\+ \)/\L\1/g' <= Convert short words to lowercase"
echo ""
echo " examples:"
echo " # Rename all files based on existing tags (dry run, no files changed)"
echo " ./vorbis-retag-regex.sh -n '' *.ogg"
echo ""
echo " # Change album tag and rename files to match"
echo " ./vorbis-retag-regex.sh 's/\(album=\).*/\1My Songs/' *.ogg"
echo ""
echo "Last-modified dates on files are left unchanged."
}
if [ "$1" == "" ]; then
help
exit
fi
while true; do
case "$1" in
-a)
ALBUM_FIRST=1
shift
;;
-c)
FULL_ARTIST=1
shift
;;
-n)
DRYRUN=1
shift
;;
-r)
RENAME=0
shift
;;
-t)
TAG=0
shift
;;
-h)
help
exit
;;
*)
break
;;
esac
done
REGEX=$1
shift
for I in "$@"; do
T_ORIG=`vorbiscomment -e "$I"`
# Get new tags
if [ "$REGEX" != "" ]; then
T_NEW=`echo "$T_ORIG" | sed "$REGEX"`
else
T_NEW="$T_ORIG"
fi
# Get new name
TAGS=`echo "$T_NEW" | sed 'y|/|⁄|'`
TITLE=`echo "$TAGS" | grep -i -e '^title=' | cut -d= -f2`
ARTIST=`echo "$TAGS" | grep -i -e '^artist=' | cut -d= -f2`
if [ $FULL_ARTIST -eq 0 ]; then
# Chop off the artist at the second comma, so if multiple artists are
# specified, only the first two end up in the filename in order to stop them
# from becoming too long and hitting filename length limits.
ARTIST=`echo "$ARTIST" | sed 's/^\([^,]*,[^,]*\),.*$/\1/'`
fi
ALBUM=`echo "$TAGS" | grep -i -e '^album=' | cut -d= -f2`
TRACK=`echo "$TAGS" | grep -i -e '^tracknumber=' | cut -d= -f2`
if [ "$TRACK" == "" ]; then
TRACK=0
TRACKZ=00
else
TRACKZ=`printf %02d $[10#$TRACK]`
fi
if [ $ALBUM_FIRST -eq 0 ]; then
F_NEW="${ARTIST} - ${ALBUM} - ${TRACKZ} - ${TITLE}.ogg"
else
F_NEW="${ALBUM} - ${TRACKZ} - ${ARTIST} - ${TITLE}.ogg"
fi
if [ "$TITLE" == "" ]; then
echo "ERROR: Missing title tag: $I" > /dev/stderr
continue
fi
if [ "$ARTIST" == "" ]; then
echo "ERROR: Missing artist tag: $I" > /dev/stderr
continue
fi
if [ "$ALBUM" == "" ]; then
echo "ERROR: Missing album tag: $I" > /dev/stderr
continue
fi
echo
if [ "$I" != "$F_NEW" ] && [ "$RENAME" -eq 1 ]; then
echo -e "\e[30;101m$I\e[0m"
echo -e "\e[30;102m$F_NEW\e[0m"
else
echo -e "\e[30;104m$I\e[0m"
fi
if [ "$REGEX" != "" ] && [ $TAG -eq 1 ]; then
diff --color=always -Nru <(echo "$T_ORIG") <(echo "$T_NEW") | tail -n +4
fi
if [ $DRYRUN -eq 0 ]; then
if [ "$REGEX" != "" ] && [ $TAG -eq 1 ]; then
echo "$T_NEW" | vorbiscomment -w "$I" _retag_out.ogg
touch -r "$I" _retag_out.ogg && mv _retag_out.ogg "$I"
fi
if [ "$I" != "$F_NEW" ] && [ "$RENAME" -eq 1 ]; then
mv "$I" "$F_NEW"
fi
fi
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment