Bluesky

Email

Tumblr

Webtip:

Hover over a link or an image to show a tooltip!

<--

prefix_files_by_track.sh

Author: Packrat

Version: 1.0

Download

Summary

For every file in the directory inside which this script gets executed:
A query is made using ffmpeg to find a track number in that file's meta data.
If one is found, that file's name will be prefixed with the track number, preceded with a number of zeros based on the determined maximum number of zeros.

Why?

Some audio players ignore the track number metadata when listing audio files in a directory, and instead order by filename. I don't know why this is done. This over-elaborate script helps with that issue.


Dependencies


P.S.: in case you don't know, editing tags is pretty simple:
$ ffmpeg -i file.mp3 -metadata TRACK={TRACK NUMBER HERE} -codec copy output.mp3
This can edit any metadata you'd like, such as TRACKTOTAL.

Note: -codec copy simply copies encoding to the new file. Without this option, ffmpeg will re-encode, which takes time and reduces sound quality.

Code Preview

						#!/bin/bash
# iterates on files in current directory
# takes the first audio file found, unless one is specified and stores artist/album metadata
# renames all other audio files in directory sharing the same data based on track# metadata

# search the document for instances of "#echo"
# those are all debug messages

echo ""
echo "Prefix Songs By Track v1.0"
echo "--------------------------------"
echo "AUTHOR : Packrat"
echo "COMPANY: GMR Toolz & Software"
echo "LICENSE: UNLICENSED"
echo ""
echo '+----------------------------------+
| ### # # #### |
| # # ## ## # # |
| # # ## # #### |
| # ## # # # # |
| # # # # # # |
| #### # # # # |
+----------------------------------+'
echo ""
echo "in using this script, you're waiving me, packrat, of any liability for damages done to your computer"
echo ""
echo 'Continue?'
read -p 'y/n : '



if [ $REPLY != "y" ]; then
echo "did not receive 'y', exiting.."
exit
fi

declare -A TRACKS
ALBUM=""
ARTIST=""
let MAX_TRACK=0 # stores the biggest track number found
let GREATEST_TEN=10000 # edit this value if your album has 10,001+ tracks

sample_file() {
if [ $# -gt 0 ]; then
TRACK="$(ffmpeg -i "$1" 2>&1>/dev/null | grep -iwe 'ALBUM *:' -iwe 'ARTIST *:')"
ALBUM=$(echo "$TRACK" | grep -iw "ALBUM" | sed -E 's/^[^:]*://')
ARTIST=$(echo "$TRACK" | grep -iw "ARTIST" | sed -E 's/^[^:]*://')
fi
}

if [ "$1" ]; then
file "$1" | grep -i "audio" > /dev/null
if [ $? == 0 ]; then
sample_file "$1"
else
echo "sample file: $1: is not an audio file"
echo "giving up.."
exit
fi

else
for FILE in *; do
file "$FILE" | grep -i "audio" > /dev/null
if [ $? == 0 ]; then
echo "sampling metadata from $FILE"
sample_file "$FILE"
break
fi
done
fi

echo "ARTIST: $ARTIST"
echo "ALBUM : $ALBUM"

for FILE in *; do
file "$FILE" | grep audio -i > /dev/null
if [ $? -gt 0 ]; then
#echo "$FILE is not an audio file. skipping.."
continue
fi

TRACKDATAS="$(ffmpeg -i "$FILE" 2>&1>/dev/null | grep -iwe 'TRACK *:' -iwe 'ALBUM *:' -iwe 'ARTIST *:')"
#echo $TRACKDATAS
if [ "$(echo "$TRACKDATAS" | grep -iw "ARTIST" | sed -E 's/^[^:]*://')" != "$ARTIST" ]; then
#echo "$FILE: Artist doesn't match the sample file..giving up"
continue
fi
if [ "$(echo "$TRACKDATAS" | grep -iw "ALBUM" | sed -E 's/^[^:]*://')" != "$ALBUM" ]; then
#echo "$FILE: Album doesn't match the sample file..giving up"
continue
fi
# check that grep found an instance of "track". returns non-zero exit if it did not
if [ $? -gt 0 ]; then
continue
fi
# remove whitespace, ensure its interpreted as a base10 integer
let TRACKI=10#$(echo "$TRACKDATAS" | grep -iw "track" | sed -E 's/^[^:]*://' | sed -E 's/\ //g')
if [ $TRACKI -gt $MAX_TRACK ]; then
let MAX_TRACK=$TRACKI
fi
TRACKS["$FILE"]=$TRACKI
done

# count number of zeros to pad filenames with
for file in ${!TRACKS[@]}; do
while true; do
let A=$MAX_TRACK/$GREATEST_TEN
if [ $A -ge 1 ]; then
break
fi
let GREATEST_TEN=$GREATEST_TEN/10
if [ $GREATEST_TEN -le 1 ]; then
break
fi
done
done
declare -a NEW_TRACKS # hold the new tracks in a new array ordered from lowest to highest
# rename the files
for FILENAME in "${!TRACKS[@]}"; do
#echo "$FILENAME"
TRACKPREFIX=${TRACKS["$FILENAME"]}
INDEX=$TRACKPREFIX
I=0
while true; do
let B=10**$I
let A=$TRACKPREFIX*$B
let A=$A/$GREATEST_TEN
if [ $A -lt 1 ]; then
let I=$I+1
continue
fi
break
done
for ((x=0; x < $I; x++)); do
TRACKPREFIX="0$TRACKPREFIX"
done
NEW_TRACKS[$INDEX]="$TRACKPREFIX.$FILENAME"
done
# iterate the files to verify nothing's getting overwritten
for FILE in *; do
for NEW_TRACK in "${NEW_TRACKS[@]}"; do
if [ "$FILE" == "$NEW_TRACK" ]; then
echo "ERROR: $FILE is identical to an existing file"
echo "this script can't handle that scenario"
echo "giving up..."
exit
fi
done
done
echo ""
for NEW_TRACK_NAME in "${NEW_TRACKS[@]}"; do
echo "$NEW_TRACK_NAME"
done
echo "Commit?"
read -p "y/n : "
if [ "$REPLY" != 'y' ]; then
echo "did not receive 'y', giving up.."
exit
fi
# execute the cp operations
# i know having filenames as the key in one array, yet track numbers as the key in the
# other is confusing..but it's written and it works. sorry :(
for INDEX in "${!NEW_TRACKS[@]}"; do
for FILENAME in "${!TRACKS[@]}"; do
if [ ${TRACKS[$FILENAME]} -eq $INDEX ]; then
cp "$FILENAME" "${NEW_TRACKS[$INDEX]}"
echo "$FILENAME -> ${NEW_TRACKS[$INDEX]}"
fi
done
done

©gunmetalrats 2026-2026
Last update:
00:00:00 01-01-1970