|
5. Getting the images out of the mp3 files
The trickiest part of the job was getting the images. Since
the images are not in the XML file we need to get them out
of the mp3 files themselves. iTunes uses the correct mp3 v2
tag to store the image. Now, initially I wrote a perl
script to extract the images based on a perl module. But,
you need to install the perl modules MP3::Info and URI::URL
in order for this to work. This is not too hard to do, but
is still some effort. I could have packaged the perl
modules I guess, but I found it easier to create a little
Cocoa command line tool that does the same based on
libid3,
the library that is distributed by http://www.id3.org.
Both approaches are
still explained however.
Using perl to get the images out of the mp3 files
Warning: This section is outdated with respect to CCTunes. Read on for the
current approach.
|
A link that got me on the way was on the ever interesting
macosxhints website,
http://www.macosxhints.com/article.php?story=20030429003250559,
in which one of the comments describes almost perfectly what needs to be
done. Almost, but for the fact on how to get to open a file based on the
url. Of course, this is not hard to do, using some parsing, and I have
my "Perl in a Nutshell" book on my desktop, but getting used to this perl
thing I thought there must be a module already out there that does this
for me. Now, that's the URI::URL module we've installed, and it was
mentioned in the book.
There we have our perl script, which takes as argument a unix file path
and a filename to write the png file to. If this script does not find
the corresponding tag, it just creates a link to a nopic.png file instead
of the destination file, so that we can put in a nice icon when the image
is missing in the mp3.
Code listing 5.1: The getPicture script gets the image from the mp3 file |
#!/usr/bin/perl
no strict 'refs';
use Getopt::Long;
use IO::File;
use MP3::Info;
use URI::URL;
$url1 = new URI::URL shift;
$unixpath = $url1->unix_path();
$e = $ARGV[0];
if ( -e $e || -l $e ) {
system("/bin/rm " . $e);
}
if (my $mp3tag_id3v2 = get_mp3tag($unixpath, 2, 1)) {
my $tag = "PIC";
my $result = $mp3tag_id3v2->{$tag};
$result =~ s/^(.....).//;
if (length($result) > 0)
{
open $f, "> $e";
print $f $result;
}
else
{
system("/bin/ln -s ./nopic.png " . $e);
}
}
else
{
system("/bin/ln -s ./nopic.png " . $e);
}
|
To invoke the correct sequence of calls to this perl command, we need a little
hack. If you take a closer look at the XML file we used to sort the original
cleaned up XML file per album, you will see that we generated some output in
XML comments, which invokes the getPicture script in a way to generate
the png file we want in the images directory. So now, with a simple bash
command we can invoke the whole set of getPicture's that will generate us
the total directory of images:
Code listing 5.2: Bash command to generate directory of images |
bash-2.05a$ cat mymusic.xml | grep getPicture | /bin/bash
|
I have understood that a Microsoft implementation of the XSLT parsing implements
something like callbacks, in which you can call a script from within your
XSL file. That's a good idea, nice, but since it isn't in the XSLT specification
it is not very compatible to depend upon it. But if you do, you might want
to check that out.
Using a custom command line tool to get the image out of the mp3 file
Another approach is taken in the downloadable package however. The getPicure
command is not a perl command, but a compiled tool, based on id3v2 which already
uses the libid3 library and has a great set of functionalities, except... extracting
images.
Adapting the sources to eliminate all we don't need and adding the extra little bit
of functionality to extract the image was easy however, since we basically know
how to do this in perl already. In order to compile it you will need to have the
libid3 headers in place and have a libid3 library that you can link to. The commands
needed to compile it with the statically linked library are included in comments
in the source code.
Code listing 5.3: Source code to getPicture command line tool |
/**
* Based on id3v2 ... compile something like
*
* g++ -I/usr/local/include/ -O3 -c -o getpic.o getpic.cpp
* c++ -L/usr/local/lib/ -O3 -pedantic -Wall -framework CoreFoundation -lz -liconv
* -g -o id3v2 getpic.o ./libid3.a
*
* Part of CCTunes, distributed under the GPL.
* details at http://www.coin-c.com/CCTunes/
*/
#include <CoreFoundation/CoreFoundation.h>
#include <id3/misc_support.h>
#include <id3/tag.h>
#include "frametable.h"
#include "genre.h"
int main( int argc, char *argv[]);
int main( int argc, char *argv[])
{
CFURLRef theSourceUrl = CFURLCreateWithBytes(kCFAllocatorDefault,
(unsigned char*)argv[1],
strlen(argv[1]),
kCFStringEncodingUTF8,
NULL);
unsigned char sourceFilePath[500];
if (CFURLGetFileSystemRepresentation(theSourceUrl,true,sourceFilePath,500))
{
bool tags = false;
ID3_Tag myTag;
myTag.Link((char*)sourceFilePath, ID3TT_ID3V2);
const ID3_Frame * myFrame = 0;
const ID3_Tag myTagConstRef = myTag;
ID3_Tag::ConstIterator *Iter = myTagConstRef.CreateIterator();
for (size_t nFrames = 0; nFrames < myTag.NumFrames(); nFrames++)
{
myFrame = Iter->GetNext();
if (NULL != myFrame && myFrame->GetID() == ID3FID_PICTURE)
{
myFrame->Field(ID3FN_DATA).ToFile(argv[2]);
tags = true;
break;
}
}
if(!tags)
std::cout << (unsigned char*)argv[1] << ": No ID3 tag" << std::endl;
}
}
|
Generating Thumbnails
This might be all you need if you are on your own private server, have lots of
disk space and don't really care on how fast your collection loads in the browser.
Since I have been using this, I found that the size of the images tends to be too
big to view all of the images for all of the albums at once, so it would be
nice to generate thumbnails for the overview.
If you have a utility like GraphicConverter, which came for free with my laptop,
you can easily generate 60 by 60 thumbnails from the images by using the batch
mode to set the Max Size. Also ImageMagick
can do things like this. You will need to install it using fink or compile
it yourself, both should be easy and with a simple set of commands on the prompt
you convert all images like this:
Code listing 5.4: Generating thumbnails with ImageMagick convert |
mv images images_high
mkdir images
cd images_high; find . -type f -exec convert -resize 60x60 {} ../images/{} \;
cd -
|
However, for your ease of use, the currently downloadable package comes with a little
command line tool programmed in cocoa and based on
pdf2png by Evan Jones which does all of this for you. It simply takes an image
and draws it in an offline buffer of the specified size and saves that as a png
file. I must say however, you will see that the quality is not good. It is
the same quality you can see if you use iPhoto to look at the images you've
taken with your digital camera. Uuuugly. I must provide some feedback to Apple on that
someday.
Code listing 5.5: Little Cocoa command line tool to resize images, based on pdf2png |
// A tiny program that resizes images to PNG images with certain dimensions.
// based on pdf2png by Evan Jones, http://evanjones.ca/pdf2png.m
// modified by Kristof Van Landschoot, Coin-C to fit the purpose of resizing.
//
// gcc --std=c99 -g -o resize resize.m -framework Cocoa
//
// Written originally by Evan Jones <ejones@uwaterloo.ca> Februrary, 2004
// http://www.eng.uwaterloo.ca/~ejones/
//
// Released under the GNU Public License
#include <Cocoa/Cocoa.h>
int main( int argc, char* argv[] )
{
int destinationSize = 36; // in DPI
int page = 1;
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// Package all arguments as NSStrings in an NSArray
NSMutableArray* args = [NSMutableArray arrayWithCapacity: argc - 1];
for ( int i = 1; i < argc; ++ i )
{
[args addObject: [NSString stringWithCString: argv[i]] ];
}
// If we have a "--dpi" along with a corresponding argument ...
unsigned int index = NSNotFound;
if ( (index = [args indexOfObject: @"--dpi"]) != NSNotFound && index + 1 < [args count] )
{
// Parse it as an integer
destinationSize = [[args objectAtIndex: index + 1] intValue];
[args removeObjectAtIndex: index + 1];
[args removeObjectAtIndex: index];
}
if ( [args count] != 2 || [args indexOfObject: @"--help"] != NSNotFound || destinationSize <= 0 )
{
fprintf( stderr, "resizePicture [options] file\n" );
fprintf( stderr, "\t--dpi dpi\tSpecifies the destination size of the image in pixels\n" );
fprintf( stderr, "\t--help\tPrint this help message\n" );
return 1;
}
NSString* sourcePath = [args objectAtIndex: 0];
NSImage* source = [ [NSImage alloc] initWithContentsOfFile: sourcePath ];
[source setScalesWhenResized: YES];
// Tip from http://www.omnigroup.com/mailman/archive/macosx-dev/2002-February/023366.html
// Allows setCurrentPage to do anything
[source setDataRetained: YES];
if ( source == nil )
{
fprintf( stderr, "Source image '%s' could not be loaded\n", argv[1] );
return 2;
}
// The output file name
NSString* outputFilePath = [args objectAtIndex: 1];
NSSize sourceSize = [source size];
NSSize size = NSMakeSize( destinationSize, destinationSize );
[NSApplication sharedApplication];
[[NSGraphicsContext currentContext] setImageInterpolation: NSImageInterpolationHigh];
[source setSize: size];
NSRect destinationRect = NSMakeRect( 0, 0, size.width, size.height );
NSImage* image = [[NSImage alloc] initWithSize:size];
[image lockFocus];
NSEraseRect( destinationRect );
[source drawInRect: destinationRect
fromRect: destinationRect
operation: NSCompositeCopy fraction: 1.0];
NSBitmapImageRep* bitmap = [ [NSBitmapImageRep alloc]
initWithFocusedViewRect: destinationRect ];
NSData* data = [bitmap representationUsingType:NSPNGFileType properties:nil];
[bitmap release];
[[NSFileManager defaultManager]
createFileAtPath: outputFilePath
contents: data
attributes: nil ];
[image unlockFocus];
[image release];
[pool release];
}
|
|
|
 |
|
Updated $LastChangedDate: 2004-11-18 23:41:16 +0100 (Thu, 18 Nov 2004) $ |
 |
Kristof Van Landschoot
Author
|
 |
|
Summary:
We need to get the images out of the mp3 files, they're in there
somewhere. This section explains how to get them out.
|
 |
|