Add Album Art to music files via the command line

My digital music collection goes all the way back to 2002 when I bought my first laptop. Over the years, I downloaded various tracks, ripped copies of my cds using windows media player, copied family members cds to my computer. I bought music in a variety of sources, encoded new cds in multiple formats, transferred the collection from one operation system to another and from iPhone to Android and back again.

My collection isn’t huge but it’s mine. It had taken years to collect and almost every file had some sort of sentimental value. There was always a time or place that I had bought the album or a song that had a certain resonance to it. As I spend more time purchasing new music instead of just streaming it, I feel a greater affinity to the music I do have. I want to invest more time listening to an album in it’s entirety. It makes me appreciate the effort put into the album and it makes me want to talk about it with others more.

It is a mess though. I have been cleaning things up. There is something about having accurate track data and album art that enhances the listening experience. I wanted better quality files. My hearing isn’t great but I’ve been trying to opt for better quality files even if I personally can’t tell the difference. Apologies to audiophiles.

Implementation

I opted to use the command line tool abcde to do this re-copy the cds. It allowed my to set a higher bitrate and took care of things like embedding album art.

What was left was a collection in mp3, m4a and ogg formats. Some had album art, some didn’t. Some albums had been split into different folders due to issues with iTunes or other software that I had used over the years. Some albums lacked any track data at all!!

I also opted to use the command line to embed album art. Googling didn’t return any ui options and I wasn’t about to pay for a program without being sure it would solve my problem. I added track data manually with VLC. Luckily there where only a couple of albums where I had to do that.

The below was all run on a mac. Command line tools where installed using brew . I suggest making a backup of your files before running any scripts against them

MP3

For MP3s I used ffmpeg and followed the following steps

  1. Find the album art online
  2. Download it into the folder where the album is stored. I’ve named it cover.jpg, png files will work too
  3. Run the below script inside the folder
  4. Delete the original folder and rename the copy that is created
thisdir=$(pwd)
dircp="${thisdir}-copy"
mkdir -p "$dircp"

for filename in ./*.mp3; do
	basefile=$(basename "$filename" ".mp3")
	ffmpeg -i "${basefile}.mp3" -i cover.jpg -map_metadata 0 -map 0 -map 1 -acodec copy "${dircp}/${basefile}.mp3"

done

M4A

For the m4a files, I used AtomicParsley. I was pretty happy to be able to copy in place for this option

  1. Find the album art online
  2. Download it into the folder where the album is stored. I’ve named it cover.jpg, png files will work too
  3. Run the below script inside the folder
for filename in ./*.m4a; do
	basefile=$(basename "$filename")
	AtomicParsley "$basefile" --artwork "cover.jpg" --overWrite
done

for filename in ./cover-resized*; do
	rm $filename
done

OGG

The ogg files where a bit tougher. I had to dive into abcde’s source code. to see how they did it. The below is slightly altered to work on my mac. It uses vorbiscomment to add meta data. All thanks to abcde for the below. You should definitely make a copy of your files before running this. It’s easy to mess up the metadata and tricky to get it back

  1. Find the album art online
  2. Download it into the folder where the album is stored. I’ve named it cover.jpg, png files will work too
  3. Run the below script inside the folder
#!/bin/bash

thisdir=$(pwd)
ABCDETEMPDIR="${thisdir}-tmp"
mkdir -p "$ABCDETEMPDIR"


VORBISCOMMENT=vorbiscomment

ALBUMARTFILE=cover.jpg
MIMETYPECOVER=$(file -b --mime-type "$ALBUMARTFILE")
EXPORTTAGS="${ABCDETEMPDIR}/export_ogg_tags"
BUILDHEADER="${ABCDETEMPDIR}/build_header"

# Now build the header, gory details are here:
# https://xiph.org/flac/format.html#metadata_block_picture
# Picture Type:

printf "0: %.8x" 3 | xxd -r -g0 > "$BUILDHEADER"

# Mime type length

printf "0: %.8x" $(echo -n "$MIMETYPECOVER" | wc -c) | xxd -r -g0 >> "$BUILDHEADER"
# Mime type:

echo -n "$MIMETYPECOVER" >> "$BUILDHEADER"
printf "0: %.8x" $(echo -n "Cover Image" | wc -c) | xxd -r -g0 >> "$BUILDHEADER"

# Description:

echo -n "Cover Image" >> "$BUILDHEADER"

# Picture width:
printf "0: %.8x" 0 | xxd -r -g0 >> "$BUILDHEADER"

# Picture height:
printf "0: %.8x" 0 | xxd -r -g0 >> "$BUILDHEADER"

# Picture color depth:
printf "0: %.8x" 0 | xxd -r -g0 >> "$BUILDHEADER"

# Picture color count:
printf "0: %.8x" 0 | xxd -r -g0 >> "$BUILDHEADER"

# Image file size:
printf "0: %.8x" $(wc -c "$ALBUMARTFILE" | awk '{print $1}') | xxd -r -g0 >> "$BUILDHEADER"

# cat the image file:
cat "$ALBUMARTFILE" >> "$BUILDHEADER"

# Now process each ogg file by first exporting the original tags then
# appending the cover image and finally copying the whole thing back
# to the original image:

for i in *.ogg

do

# Make a backup of the existing tags:

	"$VORBISCOMMENT" -d metadata_block_picture $i
	
	"$VORBISCOMMENT" --list --raw "$i" > "$EXPORTTAGS-$i.txt"
	
	cat "$EXPORTTAGS-$i.txt"
	
	# base64 the file and then mix it all together with the exported tags:
	
	echo "metadata_block_picture=$(base64 < "$BUILDHEADER")" >> "$EXPORTTAGS-$i.txt"
	
	# # Update the original ogg file with exported tags and the appended base64'd image:
	
	"$VORBISCOMMENT" --write --raw --commentfile "$EXPORTTAGS-$i.txt" "$i"
	
	# Delete the EXPORTTAGS file ready to be recreated for the next file in the loop,
	
	# note that the header file BUILDHEADER will be reused for each file in the loop:
	
done

rm -rf "$ABCDETEMPDIR"

And that’s it! Let me know if this helps you out or if you run into bugs!

This was a fun diversion for me. It reminded me about all the things I love about programming, like diving into source code, learning about new tools, implementing simple solutions. I didn’t have to write any complex code to solve my problem or re-invent the wheel. Research and patience went so much further than trying to write a script that would fix everything at once. Maybe if there is market for aging millennials and gen-xers desperately hanging on to decades old digital media, I can revisit this.

Advertisement