Reading MP3 ID3 tags with ColdFusion

You may have heard of a file format called MP3. It is an audio format that is small and compact and easy to share. If you don't have any MP3s, you can contact that RIAA. They seem to be pretty good at finding them. If you do have a few MP3s, you may know that some programs, like iTunes, will show you information about the MP3, like song title, artist, duration, etc. This information is stored in the MP3 file itself and is called ID3. ID3 is a format to embed information in the MP3 file itself. (For more information, see the site.) Since I have a few MP3s on my hard drive, I decided to see how hard it would be to get ColdFusion to read and parse these tags. It's been done before. Christian Cantrell released code on one of the first DRKs that did this. Since this isn't public domain though, let's start from scratch and see how hard it is.

Of course, the nice thing is that most of the work is done for us. I did a quick Google search and came across the Java ID3 Tag Library. I downloaded the JAR and copied it to $CFMX_HOME/runtime/servers/lib/. I then restarted ColdFusion. So what next? I checked the project's quick start guide and saw this Java example:

File sourceFile;
MP3File mp3file = new MP3File(sourceFile);

I knew this could be translated to ColdFusion as:

<cfset mp3file = createObject("java", "xxxx")>
<cfset mp3file.init(somefilepath)>

The problem was - what to use for the class name? I fumbled around a bit and found the API documentation. I looked up MP3File in the API and saw that it's path was org.farng.mp3.MP3File. I'm not sure how folks would be able to figure that out without documentation, but I was finally able to create an instance of the object using this path.

I then checked the API and looked into how it returned ID3 tag information. The code supports two versions of ID3 information, ID3v1 and ID3v2. (There are actually subversions for each of these two versions.) For a quick test, I wrote some code to examine one of my folders and grab the information from the ID3v1 tag:

<cfset dir = "g:\music\80s\">

<cfdirectory action="list" directory="#dir#" filter="*.mp3" name="music">

<cfloop query="music">
   <cfoutput>filename = #name#<br></cfoutput>
   
   <cftry>
      <cfset mp3 = createObject("java", "org.farng.mp3.MP3File")>
      <cfset mp3.init(dir & name)>
      
      <cfset tag = mp3.getID3v1Tag()>
      
      <cfoutput>
      artist=#tag.artist#<br>
      album=#tag.album#<br>
      comment=#tag.comment#<br>
      genre=#tag.genre#<br>
      title=#tag.title#<br>
      year=#tag.year#<br>
      has v1? #mp3.hasID3v1Tag()#<br>
      has v2? #mp3.hasID3v2Tag()#<br>
      </cfoutput>
      <cfcatch>
      bad file
      </cfcatch>
   </cftry>
   <hr>
</cfloop>

How did I know what methods and fields I could use? It all came from the API. So - not the cleanest of code, but you can see how it works. I create an instance of the MP3 code, and then fetch the ID3v1 tag information. To be safer, I should have used the convenience function, hasID3v1Tag() first. Notice the try/catch around the code. From what I could tell, there was no easy way to tell the code to check first for a valid MP3 file.

So what next? This works easily enough - but what if I switch to a different Java library? Tomorrow I'll write a ColdFusion Component and show how we can wrap this into a nicer and easier to use system. What I want to end up with is a tool that is 100% ColdFusion based. The developer using the tool should have no idea that behind the scenes I'm just using a JAR file.

Related Entries

Comments

i've started to use mark's javaLoader for everythng java these days: http://www.compoundtheory.com/?action=javaloader.i...

even if i can get to the classpath, this approach makes upgrading the backend java bits much easier.
# Posted By PaulH | 6/13/06 6:06 AM
Ray,

As I understand it, .jar files are the same as .zip files. So you can just open a jar file using winzip or whatever and see the name of all the class files and their paths. The class name is just the path with the class name in dot notation.
# Posted By Nelson Winters | 6/13/06 7:05 AM
Nice!
# Posted By Scott Stroz | 6/13/06 7:45 AM
Nelson, yep, you are right. My issue was that I saw a lot of files, and couldn't find the mp3file one. The API was just a bit easier.

Of course, the easiest will be the CFC approach I'll write about tonight.
# Posted By Raymond Camden | 6/13/06 7:58 AM
Forgive me if I'm totally off base on this, but would it be possible to apply this concept (modified of course) to say a Word doc or Excel spreadsheet to read the metadata saved in those files? I'd love to be able to read the author, comments, etc from Coldfusion.
# Posted By todd | 6/13/06 8:10 AM
Do a Google search for POI. It's a Java based Excel reader. I'm sure it could handle the metadata no problem. Also, CF's built in Verity will read it as well, but can't be used for simple "get one doc and return just the md" type things.

I've never used POI, but I know I've heard others recommend it.
# Posted By Raymond Camden | 6/13/06 8:19 AM
Well, you _could_ write a CFC. Or you could just use the cfID3 cfc that you edited for DRK3. ;)
# Posted By Adam | 6/13/06 9:02 AM
No, see my first paragraph. That's not open sourced. It wasn't my project. (I just QAed it.)
# Posted By Raymond Camden | 6/13/06 9:06 AM
Sorry, missed that part. It wasn't Christian though... it was actually the first CFC I ever wrote (following the launch of CFMX). Your QA taught me some tremendoulsy good lessons that I still follow. Thanks.
# Posted By Adam | 6/13/06 9:44 AM
Oh, sorry! It was so long ago. :)
# Posted By Raymond Camden | 6/13/06 9:44 AM
Todd,

If you are on a windows box, you can use the dsofile.dll (from Microsoft) to read the OLE properties from Office documents.

See the following code:

objDSOFile = CreateObject("com","DSOleFile.PropertyReader");
try {
   objDocProp = objDSOFile.GetDocumentProperties(arguments.file);
   flObj.Author = objDocProp.author;
   flObj.Name = objDocProp.name;
   flObj.Title = objDocProp.title;
   flObj.Company = objDocProp.company;
   flObj.Comments = objDocProp.Comments;
} catch(any e) {
   //
}
releaseCOMObject(objDSOFile);
# Posted By Mario Rodrigues | 6/13/06 9:54 AM
It might be worth noting the cfxid3 custom tag in the developer's exchange. It would let you read and write ID3 tags on mp3 files.

(I hope this link works )

www.adobe.com/cfusion/exchange/index.cfm#view=sn10...

I've used it for both reading and writing w/ pretty good results.
# Posted By Jeff Houser | 6/13/06 11:58 AM
I was playing around with the provided code but nothing seemed to work. I ended up cfdump'ing the variables at each step and noticed that the line <cfset mp3.init(dir & name)> was actually causing a problem. If 'dir' was 'C:\music' and 'name' was 'cool.mp3' what was being passed to the instance of the java class init was 'C:\musiccool.mp3'... something that it couldn't successfully use.

I changed the line to read <cfset mp3.init(dir & '\' & name)> and all is well.

I also used Mark's javaloader to handle grabbing and managing the *.jar file - very handy and useful for those of us on hosted servers which don't allow access to the Admin control panel but enable the cfobject tag.
# Posted By Matthew Reinbold | 6/13/06 10:41 PM
I'm glad you got it working. In the CFC version, which is done btw, I have code in there that verified the file exists. It would have caught that earlier for you. I plan on writing about the CFC either today or tomorrow. I will say one thing - the author did a darn good job cuz my job was incredibly easy.
# Posted By Raymond Camden | 6/14/06 6:41 AM
Just an FYI guys. I did write the CFC for part 2. I just haven't written the article yet. The good news is that it will be short and sweet as the code that the Java project supplied was -super- helpful.
# Posted By Raymond Camden | 6/17/06 10:04 AM
Hi Ray, have you had a chance to write the CFC that reads ID3 tags yet? I'm working on a project and would love to integrate this CFC.
# Posted By Jeff Lemmon | 6/29/06 8:57 PM
Ugh. Sorry for taking so long. If I don't get this published next week, someone shoot me, ok?
# Posted By Raymond Camden | 6/30/06 6:12 AM
*bang*
# Posted By Joe P | 7/7/06 3:39 PM
I hate to keep busting your chops about this! I'd love to use the existing java tool, but it doesn't work in a shared environment. Thanks man!
# Posted By Jeff Lemmon | 7/9/06 9:08 PM
Posted. Sorry guys.
# Posted By Raymond Camden | 7/9/06 9:43 PM
This presumably works for mp3 files with version 1 tags.

I have files from iTunes on my server. They return mp3.hasID3v1Tag() as false and mp3.hasID3v2Tag() as true. So this code creates an error when you access tag.artists because tag is undefined because getID3v1Tag fails.

The obvious solution was replacing tag = mp3.getID3v1Tag() with tag = mp3.getID3v2Tag(). That "works" though what is returned is a string (with some interesting gibberish) rather than a structure.

Any ideas? Maybe I'm just being dense.
# Posted By Tim Gill | 7/13/06 2:20 PM
Tom, check out my second blog entry. I talk about the API the author created to let you talk to v1 or v2 tags in a simple fashion.
# Posted By Raymond Camden | 7/13/06 2:31 PM
Is there any easy way to write mp3s with Java? I have two mp3s that I want to overlay into a single mp3, but I can't find any libraries for doing this. I've seen the JMF, but it appears to be a dead project, and doesn't look like it offers much support anyways.
# Posted By Chris | 5/1/07 12:50 PM
I haven't seen anything like that. _Maybe_ ffmpeg could do it.
# Posted By Raymond Camden | 5/1/07 8:52 PM
How can I use this with my server. I don't have ColdFusion admin rights but I do have admin rights to my domain space. I am at the point where I need to reference the class:

<cfset mp3 = createObject("java", "org.farng.mp3.MP3File")>
# Posted By Mike Guzzo | 3/21/08 2:27 PM
see the first comment.
# Posted By PaulH | 3/21/08 9:18 PM
The code read ID3 info from mp3's perfectly does anyone know how it could be modified to read id3 info from m4a or m4b files ?
# Posted By JakeE | 3/27/08 1:31 PM
is there a way to do this with a WMV file? anyone know of a way to read the artist or metadata from a movie file?
# Posted By Ric | 6/2/08 5:57 PM
@Ric: I'd recommend looking at ffmpeg.
# Posted By Raymond Camden | 6/2/08 7:48 PM