How to read/write ID3 Tags in MP3 files using UltraID3Lib

Started by wulli, October 02, 2014, 11:48:36 PM

Previous topic - Next topic

wulli

The UltraID3Lib.dll which is available for .Net offers read/write functionality to many ID3 Tags including artwork and lyrics in MP3 files. Since recent versions of Winbatch are able to interact with .Net functionality i hope somebody can give me sample code how to use it (e.g. like the SelectNode example for XML-Files). Here is the link to the UltraID3Lib: http://home.fuse.net/honnert/UltraID3Lib/
Thank you VERY much for your Support :-)

td

Take a look at the ObjectClrNew, ObjectClrOption, and ObjectClrType functions in the WIL Consolidated Help file. You use ObjectClrOptions to set the 'Appbase' to the location of your assembly dll and to load the assembly.  You can then use ObjectClrNew to create instances of the assembly's class or classes.  Both the help file and the Tech DB have examples of using these functions.  If you get stuck, post what you have and someone will help you. 

Or you can simple use the binary buffer functions to read the tags directly from the file.  Examples can be found here

http://techsupt.winbatch.com/webcgi/webbatch.exe?techsupt/nftechsupt.web+WinBatch/Miscellaneous+Reading~MP3s~ID3~Tags.txt 
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

wulli

Wow, 25 views within 24 hours: This topic seems to be of interest to many people.

Thank you for the quick reply.
I was aware of "http://techsupt.winbatch.com/webcgi/webbatch.exe?techsupt/nftechsupt.web+WinBatch/Miscellaneous+Reading~MP3s~ID3~Tags.txt"  but this example is read only and works with the ancient and long outdated ID3v1.1 tags from 1996 with fixed structure and length of 128 bytes at the end of the MP3 file. The currently most used ID3v2.3 tags are at the beginning of the MP3 file with variable structure and length and are likely not to be handled that easy way.
The more up-to-date article "http://techsupt.winbatch.com/webcgi/webbatch.exe?techsupt/nftechsupt.web+WinBatch/OLE~COM~ADO~CDO~ADSI~LDAP+Get~Audio~File~Information.txt" uses COM and Windows Media Player but is read only too.

Since i need tag-write functionality i will try my luck with UltraID3Lib and the ObjectClrNew, ObjectClrOption, and ObjectClrType functions and see if i get stuck :-)

wulli

Here is my intermediate result (in case somebody is interested in this topic):

Functionality implemented so far (see script):
1) Load non GAC assemblies into the WinBatch process
2) Create the UltraID3 Class
3) Read the specified Mp3 file and set all of the ID3v1 and ID3v2.3 properties
4) Get tags (common tags in ID3v1 and ID3v2.3 only)
5) Clear the ID3v1 and ID3v2.3 common tag metadata values
6) Set tags (common tags in ID3v1 and ID3v2.3 only)
7) Update the Mp3 file

;================================================================================================================
;***************************************************************************
;** Mp3 ID3v1 and ID3V2.3 common tag manipulation
;**
;** Purpose: Read and write standard tags using UltraID3Lib
;** Inputs:  Mp3 file containing old tag values
;** Outputs: Mp3 file containing updated tag values
;** Comment: Requires WinBatch 2013A or newer
;** Comment: Requires UltraID3Lib.dll
;** Comment: Download UltraID3Lib package here: http://home.fuse.net/honnert/UltraID3Lib/
;** Comment: Unzip the package (i put it here: C:\WINDOWS\UltraID3Lib0968\)
;**
;** Developer: Kay 2014.10.04
;***************************************************************************

If Version( )< '2013A'
   Pause('Notice', 'Need 2013A or Newer Version of WinBatch')
   Exit
EndIf

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; Load non GAC assemblies into the WinBatch process
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
assemblydir = 'C:\WINDOWS\UltraID3Lib0968\'
ObjectClrOption('appbase',assemblydir)
ObjectClrOption('use','UltraID3Lib, Version=0.9.6.6, Culture=neutral, PublicKeyToken=null')

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; Create the UltraID3 Class
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
objID3 = ObjectClrNew('HundredMilesSoftware.UltraID3Lib.UltraID3')

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; Read the specified Mp3 file and set all of the ID3v1 and ID3v2.3 properties
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
FileMp3WithPath = 'Z:\Test\Data\Artist - Album - TrackNr - Title.mp3'
objID3.Read(FileMp3WithPath)

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; Get tags (common tags in ID3v1 and ID3v2.3 only)
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Artist = objID3.Artist
Album = objID3.Album
Title = objID3.Title
TrackNr = objID3.TrackNum
Year = objID3.Year
Genre = objID3.Genre
Comment = objID3.Comments

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; Clear the ID3v1 and ID3v2.3 common tag metadata values
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
objID3.Clear

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; Set tags (common tags in ID3v1 and ID3v2.3 only)
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
objID3.Artist = 'NewArtist'
objID3.Album = 'NewAlbum'
objID3.SetTrackNum('1')            ;Special method used to set this tag
objID3.Title = 'NewTitle'
objID3.SetYear('2222')            ;Special method used to set this tag
objID3.Genre = 'Classic Rock'      ;Genres according to ID3v1 spec allowed only, since ID3v1 is updated too
objID3.Comments = 'NewComment'

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; Update the Mp3 file
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
objID3.Write

ObjectClose(objID3)
Exit
;================================================================================================================

Functionality i will work on next weeks:
1) Implement the ID3 tags unique to ID3v2.3 i need (subset of the 67 additional  tags supported by UltraID3Lib e.g. "Composer")
2) Add frames not included in the Input Mp3 and set values for them
3) Manipulate embedded CoverArt
4) Manipulate embedded Lyrics
5) Error handling

td

Quote from: wulli on October 03, 2014, 10:18:54 PM
Wow, 25 views within 24 hours: This topic seems to be of interest to many people.

25 views in 24 hours is typical for a topics. If a topic gets 300+ views over a slightly longer time period then it has interest.

Quote
Thank you for the quick reply.
I was aware of "http://techsupt.winbatch.com/webcgi/webbatch.exe?techsupt/nftechsupt.web+WinBatch/Miscellaneous+Reading~MP3s~ID3~Tags.txt"  but this example is read only and works with the ancient and long outdated ID3v1.1 tags from 1996 with fixed structure and length of 128 bytes at the end of the MP3 file. The currently most used ID3v2.3 tags are at the beginning of the MP3 file with variable structure and length and are likely not to be handled that easy way.

Just because you wish to use a newer version of the standard doesn't mean the article is not relevant.  Variable length data structures can be managed with some additional scripting.  You would need to spend a few moments learning the file format.  Your favorite search engine is your friend here.  In fact, if you happen to be using the latest version of WinBatch with the new DllStruct* functions, you might be able to construct data structures on the fly without having to perform many of the tedious buffer offset calculations.

Quote
The more up-to-date article "http://techsupt.winbatch.com/webcgi/webbatch.exe?techsupt/nftechsupt.web+WinBatch/OLE~COM~ADO~CDO~ADSI~LDAP+Get~Audio~File~Information.txt" uses COM and Windows Media Player but is read only too.

Since i need tag-write functionality i will try my luck with UltraID3Lib and the ObjectClrNew, ObjectClrOption, and ObjectClrType functions and see if i get stuck :-)

Taking the initiative to solve the problem yourself after investigating 'prior art' often ends up being the most productive solution in the immediate and the long term.
   
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

ChuckC

Just a little side note...

Outside of the discussion about using UltraID3Lib via WinBatch's feature to consume .NET based libraries, there is also an alternate method available to to interrogate & manipulate ID3 tags in MP3 files.  A couple of years ago, I wrote an extender, the Property Store Utility extender, and donated it for free to WWW.  The PS Utility extender makes use of the built-in Property Store functionality that is part of the Windows Explorer Shell.  The Property Store is what the Windows Explorer uses to display the content of the "Details" tab on the properties page for for a file when you right-click and choose "properties" in the explorer.  For any media file that the Windows Explorer can provide meta-data, such as the ID3 tag data in an MP3 file, this extender can provide the same access to that same meta-data.  The Property Store has access to many more types of media files, though, so music/video files downloaded via iTunes, Windows Media files, Microsoft Office documents, etc..., can all have their properties interrogated and possibly modified depending on what level of functionality the Property Store has implemented for them.  Microsoft provides default support for numerous types of files and their associated meta-data, and 3rd Party vendors can extend the Property Store system by adding new plug-in DLLs to support new types of files with new meta-data.

Here's the link for the WWW Tech Support Database article about this 3rd Party extender:

http://techsupt.winbatch.com/webcgi/webbatch.exe?techsupt/nftechsupt.web+WIL~Extenders/_Third~Party~Extenders/Property~Store~Utility+Property~Store~Extender.txt

In the help file content for the extender there is a sample script called the Property Store Explorer, and it demonstrates the usage of the extender's functions to get/set properties in a file.

td

"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

kdmoyers

I'd totally forgotten about that extender -- Thanks Chuck!
The mind is everything; What you think, you become.

ChuckC

Is there any chance that the tech support database article for it could be tagged with additional keywords to so that the article shows up in search results more readily when it would be an appropriate thing to read?

td

"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

ChuckC

Hmm...

I suppose that "id3" and "mp3" would be the 1st ones that come to mind given the topic of this thread.  Perhaps "shell properties" and "details tab" would be good ones, too.

I'm just looking for a better way to connect up the extender's useful features and purpose with the things that script developers would really need it for.  Unfortunately, Microsoft's "Property Store" terminology isn't so great at being descriptive to anybody but another engineer [who would understand it perfectly fine at a glance].

td

"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

wulli

After several hours trying to get the UltraID3Lib working i gave up and implemented a more pragmatic solution which does the job.
I incuded the command line tool "ID3 Editor" into my Winbatch program. Here is the code i tested it with.
Best thing is that the major functions "Remove tags", "Set tags", "Insert cover art" and "Insert lyrics" are just a single command this way each.
The function "Read all tags" requires some more effort.

Thanks for your support.

;***************************************************************************
;** Mp3 ID3v2.3 and ID3v1 tag read / write / delete
;**
;** Purpose: Read / write / delete Mp3 file tags (including cover art and lyrics)
;** Inputs:  mp3 file, cover art import jpg file, lyrics import txt file
;** Outputs: Updated mp3 file, tag export xml file, lyrics export txt file
;** Comment: Requires ID3 Editor package (license: $15)
;** Comment: Download ID3 Editor package here (32 and 64bit version available): http://www.pa-software.com/downloads.php
;
;** Comment: This program demonstrates straightforward functionality without error handling only
;**
;** Developer: Kay 2014.10.09
;***************************************************************************

;Set variables
;---------------------------------------------------------------------------
ProgID3Edit = "C:\Programme\Pa-software\Windows ID3 Editor\id3edcmd.exe"
FileMp3WithPath = "L:\Test\Data\Artist - Album - TrackNr - Title.mp3"
FileCoverArtImportWithPath = "L:\Test\Data\CoverArtImport.jpg"
FileLyricsImportWithPath = "L:\Test\Data\LyricsImport.txt"
FileLyricsExportWithPath = "L:\Test\Data\LyricsExport.txt"
FileXmlWithPath = "L:\Test\Data\Export.xml"
;These are all tags i need
TagArtist = "ACDC"
TagAlbumArtist = "ACDC"
TagAlbum = "HIGHWAY TO HELL"
TagSet = 1
TagNumberOfSet = 1
TagTrackNr = 1
TagTitle = "HIGHWAY TO HELL"
TagYear = 1979
TagGenre = "CLASSIC HARD ROCK"
TagBPM = ""
TagComment = "FILEID-D20030822-T22531367"
TagComposer = "ANGUS & MALCOM YOUNG"
TagPublisher = "ELEKTRA"

;Function 1: Remove existing ID3v2.3 tags (including picture and lyrics) and ID3v1 tags. Existing tags created by Mp3Gain are fortunately not touched
;---------------------------------------------------------------------------
RunShell(ProgID3Edit, `-rm1 -rm2 "%FileMp3WithPath%"`, "", @HIDDEN, @WAIT)

;;Function 2: Set tags: ID3v2.3 (and ID3v1 with built-in truncation if a tag is too long)
;;---------------------------------------------------------------------------
;RunShell(ProgID3Edit, `-ar "%TagArtist%" -or "%TagAlbumArtist%" -al "%TagAlbum%" -sn "%TagSet%" -ss "%TagNumberOfSet%" -tr "%TagTrackNr%" -ti "%TagTitle%" -yr "%TagYear%" -gn "%TagGenre%" -bm "%TagBPM%" -co "%TagComment%" -cm "%TagComposer%" -pb "%TagPublisher%" -a "%TagArtist%"  -b "%TagAlbum%" -n "%TagTrackNr%" -t "%TagTitle%" -y "%TagYear%" -c "%TagComment%" "%FileMp3WithPath%"`, "", @HIDDEN, @WAIT)
;
;;Function 3: Insert cover art
;;---------------------------------------------------------------------------
;RunShell(ProgID3Edit, `-im "%FileCoverArtImportWithPath%" "%FileMp3WithPath%"`, "", @HIDDEN, @WAIT)
;
;;Function 4: Insert lyrics
;;---------------------------------------------------------------------------
;RunShell(ProgID3Edit, `-ly "%FileLyricsImportWithPath%" "%FileMp3WithPath%"`, "", @HIDDEN, @WAIT)

;Function 5: Do functions 2, 3 and 4 with a single update of the mp3 file to save time
;---------------------------------------------------------------------------
Param = `-ar "%TagArtist%" -or "%TagAlbumArtist%" -al "%TagAlbum%" -sn "%TagSet%" -ss "%TagNumberOfSet%" -tr "%TagTrackNr%" -ti "%TagTitle%" -yr "%TagYear%" -gn "%TagGenre%" -bm "%TagBPM%" -co "%TagComment%" -cm "%TagComposer%" -pb "%TagPublisher%" -a "%TagArtist%"  -b "%TagAlbum%" -n "%TagTrackNr%" -t "%TagTitle%" -y "%TagYear%" -c "%TagComment%"`
If FileExist(FileCoverArtImportWithPath) == @TRUE Then Param = StrCat(Param, ` -im "%FileCoverArtImportWithPath%"`)
If FileExist(FileLyricsImportWithPath) == @TRUE Then Param = StrCat(Param, ` -ly "%FileLyricsImportWithPath%"`)
Param = StrCat(Param, ` "%FileMp3WithPath%"`)
RunShell(ProgID3Edit, Param, "", @HIDDEN, @WAIT)

;Function 6: Read all tags
;---------------------------------------------------------------------------
;Export tag info into a xml file
RunShell(ProgID3Edit, `-xml "%FileXmlWithPath%" "%FileMp3WithPath%"`, "", @HIDDEN, @WAIT)
;Parse the xml file according to http://techsupt.winbatch.com/webcgi/webbatch.exe?techsupt/nftechsupt.web+WinBatch/dotNet/System_XML+SelectNode.txt
objXml = ""
objXnList = ""
objXn = ""
ObjectClrOption("use", "System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")
objXml = ObjectClrNew("System.Xml.XmlDocument")
StrXml = FileGet(FileXmlWithPath)
StrXml = StrReplace(StrXml, "</br>", @CRLF)   ;the lyrics tag contains </br> (representing a line feed) without corresponding opening
 which causes "objXml.LoadXml(StrXml)" to give error 1261
objXml.LoadXml(StrXml)

; TagArtist
TagArtist = ""
objXnList = objXml.SelectNodes("/tag[@version='1.0']/id3[@version='2']/value [@name='Artist']")
ForEach objXn In objXnList
   objXn = ObjectClrType("System.Xml.XmlNode", objXn)
   TagArtist = objXn.InnerText
Next
TagArtist = StrReplace(TagArtist, "&amp;", "&")

; Other tags => copy and adjust the TagArtist example according to your needs

; Export lyrics into a readable txt file
TagLyrics = ""
objXnList = objXml.SelectNodes("/tag[@version='1.0']/id3[@version='2']/value [@name='Lyrics']")
ForEach objXn In objXnList
   objXn = ObjectClrType("System.Xml.XmlNode", objXn)
   TagLyrics = objXn.InnerText
Next
TagLyrics = StrReplace(TagLyrics, "&amp;", "&")
FilePut(FileLyricsExportWithPath, TagLyrics)

; Export cover art is not supported (no issue for me since i store the input.jpg files separately anyway)

If objxn != "" Then ObjectClose(objXn)
If objXnList != "" Then ObjectClose(objXnList)
If objXml != "" Then ObjectClose(objXml)

Exit