how to list bitrate of all my mp3's on command line?
Linux - NewbieThis Linux forum is for members that are new to Linux.
Just starting out and have a question?
If it is not in the man pages or the how-to's this is the place!
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
how to list bitrate of all my mp3's on command line?
I ripped all my CD's ages ago into 128Kbs MP3s . I used audiograbber on a Windows laptop at the time. I've since ripped some of my most listened to albums in 320kbps and now i'm seeing the benefits of flac. I am trying to find out which CDs need to be ripped again in 320 and Flac. All my ripped CDs are in their respective folders and then in a base folder called 'Music'.
I can probably use mp3tag, I use it with Wine and love it, but I would like to get to know the bash script better so I am looking for a way to write a script that will get the bitrate from the mp3 and echo it to the screen, which i can then echo to a file. Ideally I will run it once and it will recursively?? go into each folder and check for mp3s and add them to the list.
I have mediainfo installed and I am sure I can use that. It has a command line version and will display the bitrate but whether or not i can display just the filename or folder name and bitrate is another thing.
I would really only have to read one file in the folder and then I would know if the folder is 128kbps or 320.
Any suggestions? I am not averse to a bit of programming and I have used C and python in the past. A long time ago! I'm sure a bash script would be a hell of a lot handier to do.
You can use find to recursively find any file named *.mp3 and then have that feed exiftool via a pipe. In turn, you can have awk extract and display the relevant fields from the resulting metadata.
Which field shown by exiftool do you want?
While you're re-ripping you might consider CD quality, which would be 44100 samples per second.
thanks for the reply Turbocapitalist, I never heard of them tools so I have them all installed. Awk was already on there but the other 2 weren't. The field I'm looking for from exiftool is the 'Audio Bitrate' field. I can use the wildcard *.mp3 to display the bitrate with exiftool.
Code:
exiftool -AudioBitrate *.mp3
will give me what I am looking for but how would I do this for all folders?
Also I'm sure there is a way then to add this information to the folder name? That would be real handy
Edit:
Well I sort of figured it out. It is not perfect but it should do the trick. It is quite slow for whatever reason but hey. It is a complete new area for me. Here is the command if it helps anyone. Did alot of researching to find out how to use the find command properly. Them escapes and backslashes are painful.
Sorry Sefyir, only seen your post just now. Think you posted as I was editing my last post. It looks interesting! I'll try that. The thing I thought up is really basic and I would love to use grep!
It looks interesting! I'll try that. The thing I thought up is really basic and I would love to use grep!
I've written up a small guide of what the one-liner does
Code:
while read i
do
echo "$(ffprobe -i "$i" 2>&1 | egrep -o 'bitrate: [0-9]{1,3} .{4}' | cut -d' ' -f2-3)" "$i"
done < <(find . -type f \( -iname \*.ogg -o -iname \*.mp3 \))
This creates the while loop. for each line in the file do something. Each line is represented by variable i in this case
Code:
while read i; do something; done < file
This writes out to the screen the output of "$(commands)" and "$i". A good way of understanding $() is echo $(ls), this will echo out the current files in your directory in a single echo statement. $i represents the current line in the while loop.
Code:
echo "$(commands)" "$i"
This takes ffprobe and outputs all information about it to stdout. You may notice 2>&1. This means take the stderr and redirect it to stdout. This is critical since we will be piping the output! A lot of programs write their output to stdout but ffprobe is funny that way.
Code:
ffprobe -i "$i" 2>&1
This takes the stdin (input data) from ffprobe and matches a string starting with "bitrate: ", then matches any numbers between 0 to 9 between 1 to 3 times. So 1 24 and 932 counts, but 32523 or 2352 does not. Then it matches any character with "." exactly 4 times (so fasd and fsa9 is fine, dd is not).
It then pipes the output (with |) to cut
Code:
egrep -o 'bitrate: [0-9]{1,3} .{4}'
This sets the deliminator to whitespace so you can say. "I want fields 2 to 3. For example, if you had 3 blocks and assumed the space between each block was the deliminator, you can could say "I want blocks 2 through 3" by specifying field 2 through 3.
Code:
cut -d' ' -f2-3"
This is called "Process Substitution". Essentially, it writes the output of commands to a special FIFO or named pipe file so that it may be used by the while loop. More info -> https://en.wikipedia.org/wiki/Named_pipe
Code:
<(commands)
This takes the current directory referenced as ".", looks for only regular files (not FIFO's, directories, sockets, etc), then using \( \), makes a test then if either the file starts with .ogg or .mp3 (case insensitive) it will match
I would really only have to read one file in the folder and then I would know if the folder is 128kbps or 320.
Makes sense - but
Quote:
Also I'm sure there is a way then to add this information to the folder name? That would be real handy
... this could get difficult - especially if the name contains special characters or spaces. Not impossible, but given your apparent shell skills, not trivial.
Another thought might be just to create a file either named or containing the bit rate in each directory. Simple redirect your output to that file - you might need to invoke basename though to resolve the directory name if using a simple "find . " command.
wow thanks everyone, this is why I love linux. there's so many ways to do everything and there's so many people waiting to help too. The command line is so powerful when you know what you're doing!
wow sefyir that's unreal. There's alot going on in that one liner! I'll try get my head round it.
Thanks ondoho for the mediainfo arguments. I didn't realise i could printf out specific headers but of course I shouldn't be surprised! I tried that command you posted and it just outputs a long number. I tried it on one folder and it was so random.
228848220557221879208113197145212806
I didn't know what it was doing so I tired it on a different folder and I get
So at least now I know the previous folders MP3s are variable bitrate and the bitrate is outputted with the three trailing zeroes. Alot of trial and error!
Still wasn't great so I added the name of the album to the mediainfo output and this is eventually what I came up with. Not great but maybe a bit better. If i add all this to a file I can easily spot which albums need to be ripped again.
Now I just have to add this line to the find command and I might be a bit happier
One tip regarding find is that there are two ways to invoke -exec
Code:
time find /home/ed/Music -type f \( -name '*.ogg' -o -name '*.mp3' \) -exec mediainfo --Output="General; %OverallBitRate% %CompleteName%\n" {} \;
time find /home/ed/Music -type f \( -name '*.ogg' -o -name '*.mp3' \) -exec mediainfo --Output="General; %OverallBitRate% %CompleteName%\n" {} +
The top line sends the file names one at a time to mediainfo. The bottom line sends them in larger batches. So you might find the + method much faster than the \; method for -exec.
The same applies to -execdir if you use that instead.
One tip regarding find is that there are two ways to invoke -exec
Code:
time find /home/ed/Music -type f \( -name '*.ogg' -o -name '*.mp3' \) -exec mediainfo --Output="General; %OverallBitRate% %CompleteName%\n" {} \;
time find /home/ed/Music -type f \( -name '*.ogg' -o -name '*.mp3' \) -exec mediainfo --Output="General; %OverallBitRate% %CompleteName%\n" {} +
The top line sends the file names one at a time to mediainfo. The bottom line sends them in larger batches. So you might find the + method much faster than the \; method for -exec.
The same applies to -execdir if you use that instead.
To be honest I didn't know what the \ was for. I assumed it was an escape character so the shell would be able to read the ;. It is good to know though. I timed the two commands and there is a huge difference. I tried just with a handful of folders and the first command "exec \;" took 13.9s and the second way "exec +" took only 3.07s! Not that it really matters but it is a massive time saving.
rateis="$(mp3info -r m -p "%r\n" "$script_dir"/"${newFile}")"
I do not have mp3info installed on slack so I cannot check my script code against the cli. But I'd say all you'd have to write is this. Cd into your mp3 dir or set pathname after arguments.
Code:
$mp3info -r m -p "%r/n" /path/to/mp3/*
Quote:
-r a|m|v
Report bit rate of Variable Bit Rate (VBR) files as one of the
following (See the section below entitled Bit Rates for more
information):
here is something that may help you make a list of all the files at whatever bit rate you want to find out, and store the names or yo could mod it and make it move the files to a location so you can have them in one place to work on.
Code:
#!/bin/bash
mp3_path="path to mp3s"
while read FILENAME
do
f=$FILENAME
path=${f%/*}
xfile=${f##*/}
title=${xfile%.*}
ext=${xfile##*.}
rateis="$(mp3info -r m -p "%r\n" "${FILENAME}")"
if [[ "$rateis" -eq '320' ]] ; then
printf "$rateis - $title\n"
echo '$rateis - $FILENAME" >> /pathTOList/ListName
done < <(find "$mp3_path" -type f -name "*.mp3")
you could also just write a script to re sample or do whatever it is you want done to them while in the directory they are in. Leaving them all in the order you created and organized them.
that last bit of code strips off the kbs at the tail end leaving you with just the number to check.
LOOKUP: string manipulation BASH for more info on how to manipulate strings in BASH.
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.