Embed album art in OGG through command line in linux

15

6

I want to convert my music from flac to ogg, and currently oggenc does that perfectly except for album art. Metaflac can output album art, however there seems to be no command line tool to embed album art into ogg. MP3Tag and EasyTag are able to do it, and there is a specification for it here which calls for the image to be base64 encoded. However so far I have been unsuccessful in being able to take an image file, converting it to base64 and embedding it into an ogg file.

If I take a base64 encoded image from an ogg file that already has the image embedded, I can easily embed it into another image using vorbiscomment:

vorbiscomment -l withimage.ogg > textfile
vorbiscomment -c textfile noimage.ogg

My problem is taking something like a jpeg and converting it to base64. Currently I have:

base64 --wrap=0 ./image.jpg

Which gives me the image file converted to base64, using vorbiscomment and following the tagging rules, I can embed that into an ogg file like so:

echo "METADATA_BLOCK_PICTURE=$(base64 --wrap=0 ./image.jpg)" > ./folder.txt
vorbiscomment -c textfile noimage.ogg

However this gives me an ogg whose image does not work properly. I noticed when comparing the base64 strings that all properly embedding pictures have a header line but all the base64 strings I generate are lacking this header. Further analysis of the header:

od -c header.txt
0000000  \0  \0  \0 003  \0  \0  \0  \n   i   m   a   g   e   /   j   p
0000020   e   g  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
0000040  \0  \0  \0  \0  \0  \0  \0  \0 035 332
0000052

Which follows the spec given above. Notice 003 corresponds to front cover and image/jpeg is the mime type.

So finally, my question is, how can I base64 encode a file and generate this header along with it for embedding into an ogg file?

dmikalova

Posted 2010-07-29T03:56:57.007

Reputation: 153

Answers

5

i've just written a script that export / import images from OGG/Vorbis files using vorbiscomment. It is part of a music library conversion tool.

The revelent script is in the 'mussync-tools-transfert_images' function of this tool:

https://github.com/biapy/howto.biapy.com/blob/master/various/mussync-tools

Basically, i've written a reader and a writer for the metadata_block_picture format.

The code is quite complex:

      OUTPUT_FILE="/path/to/my-ogg-file.ogg"
      IMAGE_PATH="/path/to/my-cover-art.jpg"
      IMAGE_MIME_TYPE="image/jpeg"
      # Export existing comments to file.
      local COMMENTS_PATH="$(command mktemp -t "tmp.XXXXXXXXXX")"
      command vorbiscomment --list --raw "${OUTPUT_FILE}" > "${COMMENTS_PATH}"

      # Remove existing images.
      command sed -i -e '/^metadata_block_picture/d' "${COMMENTS_PATH}"

      # Insert cover image from file.

      # metadata_block_picture format.
      # See: https://xiph.org/flac/format.html#metadata_block_picture

      local IMAGE_WITH_HEADER="$(command mktemp -t "tmp.XXXXXXXXXX")"
      local DESCRIPTION=""

      # Reset cache file.
      echo -n "" > "${IMAGE_WITH_HEADER}"

      # Picture type <32>.
      command printf "0: %.8x" 3 | command xxd -r -g0 \
              >> "${IMAGE_WITH_HEADER}"
      # Mime type length <32>.
      command printf "0: %.8x" $(echo -n "${IMAGE_MIME_TYPE}" | command wc -c) \
                | command xxd -r -g0 \
              >> "${IMAGE_WITH_HEADER}"
      # Mime type (n * 8)
      echo -n "${IMAGE_MIME_TYPE}" >> "${IMAGE_WITH_HEADER}"
      # Description length <32>.
      command printf "0: %.8x" $(echo -n "${DESCRIPTION}" | command wc -c) \
                | command xxd -r -g0 \
              >> "${IMAGE_WITH_HEADER}"
      # Description (n * 8)
      echo -n "${DESCRIPTION}" >> "${IMAGE_WITH_HEADER}"
      # Picture with <32>.
      command printf "0: %.8x" 0 | command xxd -r -g0 \
              >> "${IMAGE_WITH_HEADER}"
      # Picture height <32>.
      command printf "0: %.8x" 0 | command xxd -r -g0 \
              >> "${IMAGE_WITH_HEADER}"
      # Picture color depth <32>.
      command printf "0: %.8x" 0 | command xxd -r -g0 \
              >> "${IMAGE_WITH_HEADER}"
      # Picture color count <32>.
      command printf "0: %.8x" 0 | command xxd -r -g0 \
              >> "${IMAGE_WITH_HEADER}"
      # Image file size <32>.
      command printf "0: %.8x" $(command wc -c "${IMAGE_PATH}" \
                | command cut --delimiter=' ' --fields=1) \
                | command xxd -r -g0 \
              >> "${IMAGE_WITH_HEADER}"
      # Image file.
      command cat "${IMAGE_PATH}" >> "${IMAGE_WITH_HEADER}"

      echo "metadata_block_picture=$(command base64 --wrap=0 < "${IMAGE_WITH_HEADER}")" >> "${COMMENTS_PATH}"

      # Update vorbis file comments.
      command vorbiscomment --write --raw --commentfile "${COMMENTS_PATH}" "${OUTPUT_FILE}"

      # Delete cache file.
      command rm "${IMAGE_WITH_HEADER}"
      # Delete comments file.
      command rm "${COMMENTS_PATH}"

Biapy

Posted 2010-07-29T03:56:57.007

Reputation: 959

6

Here's my solution for the /usr/bin/vorbiscomment: Argument list too long problem. I created a script and named it oggart. Just run it from the command line like this:

oggart /path/to/music_file.ogg /path/to/image_file

This tags your ogg file with METADATA_BLOCK_PICTURE field. Easytag uses the old way of doing this with COVERART field instead of METADATA_BLOCK_PICTURE. If you want Easytag compatibility then you can run the script like this:

oggart /path/to/music_file.ogg /path/to/image_file -e

Here's the script:

#!/bin/sh

FILE1="`basename \"$1\"`"
EXT1=${FILE1##*.}
EXTTYPE1=`echo $EXT1 | tr '[:upper:]' '[:lower:]'`

FILE2="`basename \"$2\"`"
EXT2=${FILE2##*.}
EXTTYPE2=`echo $EXT2 | tr '[:upper:]' '[:lower:]'`

OGG=""
if [ "$EXTTYPE1" = ogg ]; then
OGG="$1"
elif [ "$EXTTYPE2" = ogg ]; then
OGG="$2"
fi
if [ "$OGG" = "" ]; then
echo no ogg file selected
exit 0
fi

PIC=""
array=(jpeg jpg png)
for item in ${array[*]}
do
if [ "$item" = "$EXTTYPE1" ]; then
PIC="$1"
elif [ "$item" = "$EXTTYPE2" ]; then
PIC="$2"
fi
done
if [ "$PIC" = "" ]; then
echo no jpg or png file selected
exit 0
fi

if [ "$3" = -e ]; then
EASYTAG=Y
else
EASYTAG=N
fi

DESC=`basename "$PIC"`
APIC=`base64 --wrap=0 "$PIC"`
if [ "`which exiv2`" != "" ]; then
MIME=`exiv2 "$PIC" | grep 'MIME type ' | sed 's/: /|/' | cut -f 2 -d '|' | tail -n 1`
fi
if [ "$MIME" = "" ]; then
MIME="image/jpeg"
fi

vorbiscomment -l "$OGG" | grep -v '^COVERART=' | grep -v '^COVERARTDESCRIPTION=' | grep -v '^COVERARTMIME=' | grep -v 'METADATA_BLOCK_PICTURE=' > "$OGG".tags

if [ "$EASYTAG" = N ]; then
echo METADATA_BLOCK_PICTURE="$APIC" > "$OGG".tags2
else
echo COVERART="$APIC" > "$OGG".tags2
fi
vorbiscomment -w -R -c "$OGG".tags2 "$OGG"
vorbiscomment -a -R -t COVERARTDESCRIPTION="$DESC" "$OGG"
vorbiscomment -a -R -t COVERARTMIME="$MIME" "$OGG"
vorbiscomment -a -R -c "$OGG".tags "$OGG"

rm -f "$OGG".tags
rm -f "$OGG".tags2

Jason

Posted 2010-07-29T03:56:57.007

Reputation: 69

The script posted funny here. You can download oggart.tar.gz @ http://www.murga-linux.com/puppy/viewtopic.php?mode=attach&id=44270

– Jason – 2011-08-04T16:49:51.173

I fixed the script formatting in the post. – Gaff – 2011-08-04T16:53:02.393

1If you get a "Syntax error: "(" unexpected" in Ubuntu it's probably something to do with the shell being used to run. I changed the first line to #!/bin/bash and it worked. – Dan Gravell – 2012-08-23T11:41:49.540

1this script is not working for me. As I can see it only uses base64 of the image but there needs to be a special header before it – Sergey – 2013-08-24T18:01:52.680

2

I'm not aware of anything that does it automatically by just pointing to the image.

However vorbiscomment can embed arbitrary tags, you just need to encode the image in base64 and then construct the tag in the correct format.

e.g vorbiscomment -a -t 'METADATA_BLOCK_PICTURE=...' file.ogg newfile.ogg

you'll have to hack these steps into a script of some kind for it to be useful.

sml

Posted 2010-07-29T03:56:57.007

Reputation: 1 582

This would be doable, but sadly if the picture is over 64kb then vorbiscomments returns "/usr/bin/vorbiscomment: Argument list too long ". Any idea how to get around this? – dmikalova – 2010-07-29T06:31:31.580

what is your system, and what is the output of getconf ARG_MAX? Unfortunately there is no way around this limit without recompiling the kernel.

Here on 64-bit 2.6.32-24, I have 2 MB. – sml – 2010-07-29T17:14:46.763

I'm running arch linux 64-bit 2.6.34.1-1 and I have 2mb as well.

Would it be possible to put in a marker e.g. vorbiscomment -a -t 'METADATA_BLOCK_PICTURE=marker' file.ogg newfile.ogg, then have something read the ogg file and replace the marker with the base64 image? – dmikalova – 2010-07-29T17:16:04.280

Absolutely. If you see the tag format spec I linked, you could use vorbiscomment to insert a temporary (small) image, and then directly write to the file updating the last two parts of the tag - data length, and the data itself. Obviously you'll have to hack something together yourself, however. – sml – 2010-07-30T04:00:51.080

I'm trying out mutagen, a low level python library for audio tagging and my preliminary looks seem like it can do what I need. I'll report back when I figure things out in it. – dmikalova – 2010-07-30T05:07:15.943

I was able to write script for vorbiscomment and it works, however I still don't know how to convert an image to base64 properly. Here's what I have so far:

base64 --wrap=0 ./image.jpg > ./image . ./image echo $Picture vorbiscomment -a -t METADATA_BLOCK_PICTURE="$Picture" ./1.ogg

However base64 seems to take the image and include the headers and then the image doesn't work in ogg. Otherwise the rest of the script works. – dmikalova – 2010-07-30T15:52:07.743