PDA

View Full Version : ID3 Tag reading



father of monstermagnet
April 4th, 2005, 08:31 AM
I was searching for a com object that can write/read id3 tags.
I found cddbcontrol.dll and thought that it is a dll made by creative.

But after searching with a com explorer i saw it's name is CDDBControlAOL.CddbID3Tag.

AOL ?

But it's working...

http://www.dlldump.com/download-dll-files.php/dllfiles/C/cddbcontrol.dll/download.html

Don't forget to register :-)

Write:


if not luacom then
UnloadLuaCom ()
end
LoadLuaCom ()

id3 = luacom.CreateObject ("CDDBControlAOL.CddbID3Tag")

id3:LoadFromFile ("D:\\MP 3\\3-Doors-Down-The-better-life\\3 Doors Down - [05] Be like that.mp3",0)
id3.LeadArtist = "3 Doors Down"
id3.Album = "The better life"
id3.Title = "Be like that"
id3.Genre = "Rock"
id3.Year = "2000"
id3.Comments = "Cool"
id3:SaveToFile ("D:\\MP 3\\3-Doors-Down-The-better-life\\3 Doors Down - [05] Be like that.mp3")

Read:


id3:LoadFromFile ("D:\\MP 3\\3-Doors-Down-The-better-life\\3 Doors Down - [05] Be like that.mp3",0)
print (id3.LeadArtist.."\n",id3.Album.."\n",id3.Title.."\n",id3.Genre.."\n",id3.Year.."\n",id3.Comments)

Promixis
April 4th, 2005, 08:57 AM
does it do id3 v2 tags as well?

father of monstermagnet
April 4th, 2005, 09:05 AM
Yes.

But why AOL ?

Here's a list:


Interfaces:
Interface ICddbID3Tag
GUID: {0306D2A8-B7E2-4EA2-ADC6-78F80D65B1E2}
HelpString: ICddbID3Tag Interface
# Members: Sub QueryInterface(riid as GUID, ppvObj as Void)
# Function AddRef as VT_UI4
# Function Release as VT_UI4
# Sub GetTypeInfoCount(pctinfo as VT_UINT)
# Sub GetTypeInfo(itinfo as VT_UINT, lcid as VT_UI4, pptinfo as Void)
# Sub GetIDsOfNames(riid as GUID, rgszNames as VT_I1, cNames as VT_UINT, lcid as VT_UI4, rgdispid as Long)
# Sub Invoke(dispidMember as Long, riid as GUID, lcid as VT_UI4, wFlags as VT_UI2, pdispparams as DISPPARAMS, pvarResult as Variant, pexcepinfo as EXCEPINFO, puArgErr as VT_UINT)
# Property Get Album as String [property Album]
# Property Put Album as String [property Album]
# Property Get Movie as String [property Movie]
# Property Put Movie as String [property Movie]
# Property Get Title as String [property Title]
# Property Put Title as String [property Title]
# Property Get CopyrightYear as String [property CopyrightYear]
# Property Put CopyrightYear as String [property CopyrightYear]
# Property Get CopyrightHolder as String [property CopyrightHolder]
# Property Put CopyrightHolder as String [property CopyrightHolder]
# Property Get Comments as String [property Comments]
# Property Put Comments as String [property Comments]
# Property Get Label as String [property Label]
# Property Put Label as String [property Label]
# Property Get BeatsPerMinute as String [property BeatsPerMinute]
# Property Put BeatsPerMinute as String [property BeatsPerMinute]
# Property Get LeadArtist as String [property LeadArtist]
# Property Put LeadArtist as String [property LeadArtist]
# Property Get PartOfSet as String [property PartOfSet]
# Property Put PartOfSet as String [property PartOfSet]
# Property Get TrackPosition as String [property TrackPosition]
# Property Put TrackPosition as String [property TrackPosition]
# Property Get Year as String [property Year]
# Property Put Year as String [property Year]
# Property Get Genre as String [property Genre]
# Property Put Genre as String [property Genre]
# Property Get FileId as String [property FileId]
# Property Put FileId as String [property FileId]
# Property Get ISRC as String [property ISRC]
# Property Put ISRC as String [property ISRC]
# Sub LoadFromFile(Filename as String, Readonly as Long) [method LoadFromFile]
# Sub BindToFile(Filename as String, Readonly as Long) [method BindToFile]
# Sub SaveToFile(Filename as String) [method SaveToFile]
# Sub Commit [method Commit]
# Sub Clear [method Clear]
# Sub LoadFromBuffer(Buffer as Variant, BufferSize as Long) [method LoadFromBuffer]
# Function GetBufferSize as Long [method GetBufferSize]
# Sub SaveToBuffer(Buffer as Variant, BufferSize as Long) [method SaveToBuffer]
# Function GetTextFrame(Frame as String) as String [method GetTextFrame]
# Sub SetTextFrame(Frame as String, Text as String) [method SetTextFrame]

Promixis
April 4th, 2005, 09:14 AM
I don't see AOL here. Does the file come with any license info?

father of monstermagnet
April 4th, 2005, 09:20 AM
http://www.motobit.com/tips/detpg_list-id3-tags-script/

father of monstermagnet
April 4th, 2005, 09:27 AM
http://ask.americas.creative.com/SRVS/CGI-BIN/WEBCGI.EXE?New,Kb=ww_english,Company={CEAE216D-8719-4C00-AC9F-03BC258F7B70},VARSET=ws:http://us.creative.com,case=2495

danward79
April 4th, 2005, 11:05 AM
This looks very interesting...

father of monstermagnet
April 4th, 2005, 11:25 AM
Version 1.2.0.48 == CDDBControl Core Module (AOL), company Gracenote (formerly CDDB, Inc.)

Version 1.1.0.27 == CDDBControl Core Module, company CDDB, Inc.

Are there any other com objects that can write/read ?

father of monstermagnet
April 4th, 2005, 11:50 AM
Before i go to bed:
http://www.gracenote.com/gn_products/cddb.html

Found the id3lib com object.

Someone using it ?

A COM wrapper (id3com) is also supplied allowing VB, VBA, VBScript and other COM-enabled languages to use the library.

http://prdownloads.sourceforge.net/id3lib/id3com.dll?use_mirror=puzzle


Good night...

father of monstermagnet
April 9th, 2005, 04:59 AM
The id3com.dll sucks.
The gracenote dll works fine.


The AOL inside the name was a modified version.

But there's an other way ActiveXObject("Shell.Application") to read :lol:

Javascript:


var mp3_file = "D:\\MP 3\\Soundgarden Superunknown\\Kickstand.mp3";

var DETAILS =
{
"FILENAME" :0x0, //0
"SIZE" :0x1, //1
"TITLE" :0xA, //10
"COMMENT" :0xE, //14
"INTERPRET":0x10, //16
"ALBUM" :0x11, //17
"YEAR" :0x12, //18
"TRACK" :0x13, //19
"GENRE" :0x14, //20
"LENGTH" :0x15, //21
"BITRATE" :0x16 //22
};

var VALUES = new Object();

var sh = new ActiveXObject("Shell.Application");
var p = mp3_file.lastIndexOf('\\');
var parent_dir = mp3_file.substr(0,p);
var mp3_name = mp3_file.substr(p+1);

var dir = sh.NameSpace(parent_dir);
var mp3 = dir.ParseName(mp3_name);

var str = '';
for (detail in DETAILS)
{
VALUES[detail] = dir.GetDetailsOf (mp3, DETAILS [detail]);
}
for (value in VALUES)
{
str += value + " = " + VALUES[value] + "\n";
}
WScript.Echo (str);

father of monstermagnet
April 9th, 2005, 06:42 AM
Maybe someone is interessed in this, it's a modified version of the famous
m3u script.

Requires: CDDBControl.CddbID3Tag ActiveX control

Creates a xml file ...

Scans 15 gig in about 1 min !

VBScript:


'***********************************
'BEGIN
'***********************************
Option Explicit
Const ForReading = 1, ForWriting = 2, ForAppending = 8
Dim fso,id3, WshShell, cptTot, objArgs, arrFiles(), sExtToGet
Dim driveLetter, pathToScan, fold, nTime, sAppName
Dim IExec, playlistPath
Set fso = CreateObject("Scripting.FileSystemObject")
Set id3 = CreateObject("CDDBControl.CddbID3Tag")
Set WshShell = WScript.CreateObject("WScript.Shell")
sAppName = "Mp3Playlister - Recursive playlist generator"

'-- lowercase file extension to search for
sExtToGet = "mp3"

'***********************************
'Path to Scan
'***********************************

pathToScan = "D:\MP 3\"

'***********************************

nTime = Timer

'-- start scanning
Call startScanning()

'-- clean
Set fso = nothing
Set WshShell = nothing
Set id3 = nothing
'***********************************
'END
'***********************************

'***********************************
'FUNCTIONS:
'***********************************

Sub startScanning()
Dim i, cpt, playlistPath
cptTot = 0
If fso.FolderExists(pathToScan) Then
ReDim arrFiles(0)
Set fold = fso.Getfolder(pathToScan)
playlistPath = fold.path &"\"& fold.Name & ".xml"
'-- recurse folder
Call DoIt(fold)
Else
WshShell.Popup "Folder """& pathToScan &""" does not exist. ", 5, sAppName, 48
Wscript.quit
End If

'-- save playlist if more than 0 entry in it
If (UBound(arrFiles) > 0) Then

Call createAndSavePlaylist(arrFiles, playlistPath)
End If


End Sub
'***********************************

Sub AddFiles(fold)
'-- process all mp3 files in the fold folder
Dim strExt, mpFiles, strName, foldName, foldPath, f, Artist, Album, Genre, id3Title, File

foldPath = fold.Path
Set mpfiles = fold.Files

For each f in mpfiles
strName = f.Name


strExt = LCase(fso.GetExtensionName(strName))
If strExt = sExtToGet Then

id3.LoadFromFile foldPath &"\"& UCase(Left(strName, 1)) & Mid(strName,2,Len(strName)),False

Artist = Replace (id3.LeadArtist,Chr(38),"&")
If Artist = "" Then
Artist = "unknown"
End If

Album = Replace (id3.Album,Chr(38),"&")
If Album = "" Then
Album = "unknown"
End If

id3Title = Replace (id3.Title,Chr(38),"&")
If id3Title = "" Then
id3Title = "unknown"
End If

Genre = Replace (id3.Genre,Chr(38),"&")
If Genre = "" Then
Genre = "unknown"
End If

File = Replace (foldPath & "\" & UCase(Left(strName, 1)) & Mid(strName,2,Len(strName)),Chr(38),"&")


arrFiles&#40;cptTot&#41; = &#40;"<CD><ID3ARTIST>" & Artist & "</ID3ARTIST><ID3ALBUM>" & Album & "</ID3ALBUM><ID3TITLE>" & Id3title & "</ID3TITLE><ID3GENRE>" & Genre & "</ID3GENRE><FILE>" & File & "</FILE></CD>"&#41;
ReDim Preserve arrFiles&#40;UBound&#40;arrFiles&#41;+1&#41;
cptTot = cptTot + 1 '-- global counter for processed files
End If
Next

End Sub
'***********************************

Sub createAndSavePlaylist&#40;arrFiles, playlistPath&#41;
Dim txt, txtFile


If Not fso.FileExists&#40;playlistPath&#41; Then
Set txtFile = fso.CreateTextFile&#40;playlistPath,true,false&#41; 'ASCII !!
End If
Set txtFile = fso.GetFile&#40;playlistPath&#41;
Set txt = txtFile.OpenAsTextStream&#40;ForWriting, 0&#41; 'ForWriting , 0 for ASCII &#40;-1 for Unicode&#41;
txt.write&#40;"<?xml version='1.0' encoding='ISO-8859-1'?><CATALOG>"&#41;
txt.write Join&#40;arrFiles,vbCrLf&#41;
txt.write&#40;"</CATALOG>"&#41;
txt.close
Set txtFile = nothing
End Sub
'***********************************

Sub DoIt&#40;fold&#41;
'-- recursive scan
Dim sfold, sfoo
Call AddFiles&#40;fold&#41; 'process files in current folder
Set sfold = fold.subfolders
for each sfoo in sfold 'process files in subfolders
Call DoIt&#40;sfoo&#41;
Next
End Sub
'***********************************
'Show created file in IE
'***********************************
Set IExec = CreateObject&#40;"InternetExplorer.Application"&#41;
playlistPath = fold.path &"\"& fold.Name & ".xml"
IExec.navigate playlistPath
IExec.Visible = 1

Promixis
April 12th, 2005, 06:06 PM
There is an opensource project called mediainfo which looks great for doing id3 and other formats...

quixote
April 12th, 2005, 06:29 PM
That would be cool if you could enter a search string and have the song or album play automatically. I would like to make it voice activated so that I could just say "play prodigy album fat of the land", for example, or "play prodigy song mindfields".

father of monstermagnet
April 12th, 2005, 11:03 PM
That should be possible, if you wanna give it a try.

Here's what i've done so far.


Requires CDDBControl.CddbID3Tag
and
Microsoft XML Core Services
http://www.microsoft.com/downloads/details.aspx?familyid=3144b72b-b4f2-46da-b4b6-c5d7485f2b42&displaylang=en

The vbscript that scans and read the music directory.


'***********************************
'BEGIN
'***********************************
Option Explicit
Const ForReading = 1, ForWriting = 2, ForAppending = 8
Dim fso,id3, WshShell, cptTot, objArgs, arrFiles&#40;&#41;, sExtToGet
Dim driveLetter, pathToScan, fold, nTime, sAppName
Dim IExec, playlistPath
Set fso = CreateObject&#40;"Scripting.FileSystemObject"&#41;
Set id3 = CreateObject&#40;"CDDBControl.CddbID3Tag"&#41;
Set WshShell = WScript.CreateObject&#40;"WScript.Shell"&#41;
sAppName = "Mp3Playlister - Recursive playlist generator"

'-- lowercase file extension to search for
sExtToGet = "mp3"

'***********************************
'Path to Scan
'***********************************

pathToScan = "D&#58;\MP 3\"

'***********************************

nTime = Timer

'-- start scanning
Call startScanning&#40;&#41;

'-- clean
Set fso = nothing
Set WshShell = nothing
Set id3 = nothing
'***********************************
'END
'***********************************

'***********************************
'FUNCTIONS&#58;
'***********************************

Sub startScanning&#40;&#41;
Dim i, cpt, playlistPath
cptTot = 0
If fso.FolderExists&#40;pathToScan&#41; Then
ReDim arrFiles&#40;0&#41;
Set fold = fso.Getfolder&#40;pathToScan&#41;
playlistPath = fold.path &"\"& fold.Name & ".xml"
'-- recurse folder
Call DoIt&#40;fold&#41;
Else
WshShell.Popup "Folder """& pathToScan &""" does not exist. ", 5, sAppName, 48
Wscript.quit
End If

'-- save playlist if more than 0 entry in it
If &#40;UBound&#40;arrFiles&#41; > 0&#41; Then

Call createAndSavePlaylist&#40;arrFiles, playlistPath&#41;
End If


End Sub
'***********************************

Sub AddFiles&#40;fold&#41;
'-- process all mp3 files in the fold folder
Dim strExt, mpFiles, strName, foldName, foldPath, f, Artist, Album, Genre, id3Title, File

foldPath = fold.Path
Set mpfiles = fold.Files

For each f in mpfiles
strName = f.Name


strExt = LCase&#40;fso.GetExtensionName&#40;strName&#41;&#41;
If strExt = sExtToGet Then

id3.LoadFromFile foldPath &"\"& UCase&#40;Left&#40;strName, 1&#41;&#41; & Mid&#40;strName,2,Len&#40;strName&#41;&#41;,False

Artist = Replace &#40;id3.LeadArtist,Chr&#40;38&#41;,"&amp;"&#41;
If Artist = "" Then
Artist = "unknown"
End If

Album = Replace &#40;id3.Album,Chr&#40;38&#41;,"&amp;"&#41;
If Album = "" Then
Album = "unknown"
End If

id3Title = Replace &#40;id3.Title,Chr&#40;38&#41;,"&amp;"&#41;
If id3Title = "" Then
id3Title = "unknown"
End If

Genre = Replace &#40;id3.Genre,Chr&#40;38&#41;,"&amp;"&#41;
If Genre = "" Then
Genre = "unknown"
End If

File = Replace &#40;foldPath & "\" & UCase&#40;Left&#40;strName, 1&#41;&#41; & Mid&#40;strName,2,Len&#40;strName&#41;&#41;,Chr&#40;38&#41;,"&amp;"&#41;


arrFiles&#40;cptTot&#41; = &#40;"<cd><artist>" & Artist & "</artist><album>" & Album & "</album><title>" & Id3title & "</title><genre>" & Genre & "</genre><file>" & File & "</file></cd>"&#41;
ReDim Preserve arrFiles&#40;UBound&#40;arrFiles&#41;+1&#41;
cptTot = cptTot + 1 '-- global counter for processed files
End If
Next

End Sub
'***********************************

Sub createAndSavePlaylist&#40;arrFiles, playlistPath&#41;
Dim txt, txtFile


If Not fso.FileExists&#40;playlistPath&#41; Then
Set txtFile = fso.CreateTextFile&#40;playlistPath,true,false&#41; 'ASCII !!
End If
Set txtFile = fso.GetFile&#40;playlistPath&#41;
Set txt = txtFile.OpenAsTextStream&#40;ForWriting, 0&#41; 'ForWriting , 0 for ASCII &#40;-1 for Unicode&#41;
txt.write&#40;"<?xml version='1.0' encoding='ISO-8859-1'?><catalog>"&#41;
txt.write Join&#40;arrFiles,vbCrLf&#41;
txt.write&#40;"</catalog>"&#41;
txt.close
Set txtFile = nothing
End Sub
'***********************************

Sub DoIt&#40;fold&#41;
'-- recursive scan
Dim sfold, sfoo
Call AddFiles&#40;fold&#41; 'process files in current folder
Set sfold = fold.subfolders
for each sfoo in sfold 'process files in subfolders
Call DoIt&#40;sfoo&#41;
Next
End Sub
'***********************************
'Show created file in IE
'***********************************
Set IExec = CreateObject&#40;"InternetExplorer.Application"&#41;
playlistPath = fold.path &"\"& fold.Name & ".xml"
IExec.navigate playlistPath
IExec.Visible = 1

That prints a xml file.

Then process the xml file and have a sorted html output.
At the moment i link only to the file source to play the files.

Adding more search options is easy, adding a textinput for a string
search a little bit harder...

Output Htm Page based on the DataIsland.htm sample.

Link to the created xml file.



<HTML>
<HEAD>
<TITLE>Untitled</TITLE>
<STYLE>
.catalog_genre_head &#123;background-color&#58;darkGreen;font-size&#58;24pt;color&#58;white;font-family&#58;Impact;&#125;
.catalog_head &#123;background-color&#58;green;font-size&#58;18pt;color&#58;white;font-family&#58;Impact;&#125;
.catalog_row0 &#123;background-color&#58;lightGreen;&#125;
.catalog_row1 &#123;background-color&#58;white;&#125;
.catalog_row_end &#123;background-color&#58;darkGreen;&#125;
</STYLE>
</HEAD>

<BODY>
<H1>Music Catalog</H1>


Select a genre to see all songs in that genre&#58;</P>
<DIV id="catalog_table"></DIV>

<xml id="music_catalog" src="MP 3.xml">
</xml>

<xml id="catalog_filter">
<xsl&#58;stylesheet xmlns&#58;xsl="http&#58;//www.w3.org/1999/XSL/Transform"
xmlns&#58;msxsl="urn&#58;schemas-microsoft-com&#58;xslt"
version="1.0">

<xsl&#58;output method="xml" omit-xml-declaration="yes"/>

<xsl&#58;param name="selected_genre" select="'all'"/>

<xsl&#58;template match="/">
<div>
<form method="post" action="catalog.asp">
Genre
<select name="genre" value="&#123;$selected_genre&#125;" onchange="showGenre&#40;this.value&#41;">
<option value="all"><xsl&#58;if test="$selected_genre='all'"><xsl&#58;attribute name="selected">Selected</xsl&#58;attribute></xsl&#58;if>All</option>
<option value="Rock"><xsl&#58;if test="$selected_genre='Rock'"><xsl&#58;attribute name="selected">Selected</xsl&#58;attribute></xsl&#58;if>Rock</option>
<option value="Punk Rock"><xsl&#58;if test="$selected_genre='Punk Rock'"><xsl&#58;attribute name="selected">Selected</xsl&#58;attribute></xsl&#58;if>Punk Rock</option>
<option value="Pop"><xsl&#58;if test="$selected_genre='Pop'"><xsl&#58;attribute name="selected">Selected</xsl&#58;attribute></xsl&#58;if>Pop</option>
<option value="Heavy Metal"><xsl&#58;if test="$selected_genre='Heavy Metal'"><xsl&#58;attribute name="selected">Selected</xsl&#58;attribute></xsl&#58;if>Heavy Metal</option>
<option value="Comedy"><xsl&#58;if test="$selected_genre='Comedy'"><xsl&#58;attribute name="selected">Selected</xsl&#58;attribute></xsl&#58;if>Comedy</option>
</select>
</form>


<xsl&#58;apply-templates select="catalog"/>
</div>
</xsl&#58;template>

<xsl&#58;template match="catalog">
<table width="750" class="catalog_table">
<xsl&#58;apply-templates select="cd&#91;&#40;$selected_genre='all'&#41; or &#40;$selected_genre=./genre&#41;&#93;">
<xsl&#58;sort select="artist"/>
</xsl&#58;apply-templates>
</table>
</xsl&#58;template>

<xsl&#58;template match="cd">
<xsl&#58;if test="position&#40;&#41;=1">
<tr class="catalog_genre_head"><td colspan="5">
<xsl&#58;choose>
<xsl&#58;when test="$selected_genre='all'">
All Genres
</xsl&#58;when>
<xsl&#58;otherwise>
Genre&#58; <xsl&#58;value-of select="genre"/>
</xsl&#58;otherwise>
</xsl&#58;choose>
</td></tr>
<tr class="catalog_head">
<td>#</td>
<td>Artist</td>
<td>Album</td>
<td>Title</td>
<xsl&#58;if test="$selected_genre='all'">
<td>Genre</td>
</xsl&#58;if>
</tr>
</xsl&#58;if>
<tr class="catalog_row&#123;position&#40;&#41; mod 2&#125;">
<td><xsl&#58;value-of select="position&#40;&#41;"/></td>
<td class="catalog_cell"><xsl&#58;value-of select="artist"/></td>
<td class="catalog_cell"><xsl&#58;value-of select="album"/></td>
<td class="catalog_cell"><xsl&#58;value-of select="title"/> (&#123;file&#125;)</td>
<xsl&#58;if test="$selected_genre='all'">
<td class="catalog_cell"><xsl&#58;value-of select="genre"/></td>
</xsl&#58;if>
</tr>
<xsl&#58;if test="position&#40;&#41;=last&#40;&#41;">
<tr class="catalog_row_end"><td colspan="4"> </td></tr>
</xsl&#58;if>
</xsl&#58;template>
</xsl&#58;stylesheet>
</xml>


<SCRIPT language="JavaScript">
function loadSource&#40;sourceObj&#41;&#123;
var xmlDoc=new ActiveXObject&#40;"MSXML2.FreeThreadedDOMDocument.4.0"&#41;;
xmlDoc.async=false;
xmlDoc.load&#40;sourceObj.XMLDocument&#41;;
return xmlDoc;
&#125;

var table_proc=null;

function getProcessor&#40;transformObj&#41;&#123;
if &#40;table_proc==null&#41;&#123;
var xslDoc=new ActiveXObject&#40;"MSXML2.FreeThreadedDOMDocument.4.0"&#41;;
var xslTemplate=new ActiveXObject&#40;"MSXML2.XSLTemplate.4.0"&#41;;
xslDoc.async=false;
xslDoc.load&#40;transformObj.XMLDocument&#41;;
xslTemplate.stylesheet=xslDoc;
xslProcessor=xslTemplate.createProcessor&#40;&#41;;
table_proc=xslProcessor;
&#125;
else &#123;
xslProcessor=table_proc;
&#125;
return xslProcessor;
&#125;

function transformData&#40;srcDoc,processor&#41;&#123;
var resultDoc=new ActiveXObject&#40;"MSXML.DOMDocument"&#41;;
processor.input=srcDoc;
processor.output=resultDoc;
processor.transform&#40;&#41;;
return resultDoc;
&#125;

function showGenre&#40;genre&#41;&#123;
var srcDoc=loadSource&#40;music_catalog&#41;;
var processor=getProcessor&#40;catalog_filter&#41;;
processor.addParameter&#40;"selected_genre",genre&#41;;
var rsltDoc=transformData&#40;srcDoc,processor&#41;;
catalog_table.innerHTML=rsltDoc.xml
&#125;

showGenre&#40;"all"&#41;;

</SCRIPT>

</BODY>
</HTML>

quixote
April 13th, 2005, 04:42 AM
That's really nice. I'll give it a shot when I get back from work today. Thanks!