Sunday, February 24, 2013

PowerShell: ID3 Tag Editing via Taglib-sharp.dll and Discogs API


First of all, I've been searching for a good way to read, manipulate, then write values to the ID3 and other extended file information properties.  TagLib is perfect.  It integrates easily into scripts and doesn't have any problems other than trying to read a wrongly-named MP3 file.  It'll throw an error and I haven't found a way of suppressing or sending the error value to a variable other than to set $erroractionpreference = ignore, which sets the action for the whole script.

(Note: I've created a new script using taglib and get-hash to filter out duplicate files.  Click here)

Getting started:
Download the latest taglib: http://download.banshee.fm/taglib-sharp/















Download the most current version and look for the taglib-sharp.dll file in the libraries folder.















Windows 7+ has security protecting the system from "foreign" dlls.  Right-click on the taglib-sharp.dll file and click on the "unblock" button.  This will allow you to load the file into PowerShell.



#Create a variable for the tag-lib dll file

$taglib = "C:\PowerShell\taglib\libraries\taglib-sharp.dll"

#Load it into Powershell

[system.reflection.assembly]::loadfile($taglib)

#Find an MP3 and either assign it to a variable or put it's name into the create field


$media = [taglib.file]::create("e:\test\body.mp3")

#Now you can view, edit, and save ID3 information

PS E:\test> $media.properties
Codecs          : {TagLib.Mpeg.AudioHeader}
Duration        : 00:05:12.3120000
MediaTypes      : Audio
Description     : MPEG Version 1 Audio, Layer 3
AudioBitrate    : 128
AudioSampleRate : 44100
BitsPerSample   : 0
AudioChannels   : 2
VideoWidth      : 0
VideoHeight     : 0
PhotoWidth      : 0
PhotoHeight     : 0
PhotoQuality    : 0


Compare Taglib field entries to Windows Explorers Detail tab:
PS E:\test> $media.tag
StartTag                   : TagLib.NonContainer.StartTag
EndTag                     : TagLib.NonContainer.EndTag
TagTypes                   : Id3v1, Id3v2
Tags                       : {, }
Title                      : Body
Performers                 : {Bush}
PerformersSort             : {}
AlbumArtistsSort           : {}
AlbumArtists               : {Bush}
Composers                  : {Gavin Rossdale}
ComposersSort              : {}
TitleSort                  :
AlbumSort                  :
Album                      : Sixteen Stone
Comment                    :
Genres                     : {Alternative}
Year                       : 1994
Track                      : 6
TrackCount                 : 0
Disc                       : 1
DiscCount                  : 1
Lyrics                     :
Grouping                   :
BeatsPerMinute             : 0
Conductor                  :
Copyright                  :
MusicBrainzArtistId        :
MusicBrainzReleaseId       :
MusicBrainzReleaseArtistId :
MusicBrainzTrackId         :
MusicBrainzDiscId          :
MusicIpId                  :
AmazonId                   :
MusicBrainzReleaseStatus   :
MusicBrainzReleaseType     :
MusicBrainzReleaseCountry  :
Pictures                   : {}
IsEmpty                    : False
Artists                    : {Bush}
FirstArtist                : Bush
FirstAlbumArtist           : Bush
FirstAlbumArtistSort       :
FirstPerformer             : Bush
FirstPerformerSort         :
FirstComposerSort          :
FirstComposer              : Gavin Rossdale
FirstGenre                 : Alternative
JoinedArtists              : Bush
JoinedAlbumArtists         : Bush
JoinedPerformers           : Bush
JoinedPerformersSort       :
JoinedComposers            : Gavin Rossdale
JoinedGenres               : Alternative

PS E:\test> $media.properties.duration
Days              : 0
Hours             : 0
Minutes           : 5
Seconds           : 12
Milliseconds      : 312
Ticks             : 3123120000
TotalDays         : 0.00361472222222222
TotalHours        : 0.0867533333333333
TotalMinutes      : 5.2052
TotalSeconds      : 312.312
TotalMilliseconds : 312312

#An example of how I load variables from my music files

$filename = $file.basename
$fileextension = $file.name.split('.')[1]
$filetitle = $media.tag.title
$fileperformers = $media.tag.performers
$filealbumartists = $media.tag.albumartists
$filealbum = $media.tag.album
$filegenres = $media.tag.genres
$fileyear = $media.tag.year
$filetrack = $media.tag.track
$filetrackcount = $media.tag.trackcount
$fileaudiobitrate = $media.properties.audiobitrate
$fileconductor = $media.tag.conductor
$filecomposers = $media.tag.Composers
$fileBPM = $media.tag.BeatsPerMinute

$filedurationminutes = $media.properties.duration.minutes
$filedurationseconds = $media.properties.duration.seconds
$filedurationtotalseconds = $media.properties.duration.totalseconds


#Here's a way to clean the title tag. It cleans the annoying buggy brackets
#then it restricts all characters except the ranges inside the brackets
#Lifted clip explaining some regex expressions:


# Expression:
# ([0-9a-zA-Z\.]+) --> Gets all chars, numbers and '.'. Discard comma and others.


if ($filetitle){
$filetitle = $filetitle -replace ("\[","")
$filetitle = $filetitle -replace ("\]","")
$filetitle = $filetitle -replace ("[^0-9a-zA-Z-&\']"," ")}


#inserting Album Photo:
#(requires PowerShell 3.0 for this album photo catching section)
#To populate the picture tag, I call upon Discogs API via Invoke-Restmethod
#Please refer to the Discogs developer pages for information how to search for your music
#In this example, I used the weighted results and select the first hit for my album search

$filealbum = $media.tag.album
$apialbum = $filealbum -replace (' ','%20')
$albumfound = (invoke-restmethod http://api.discogs.com/database/search?title=$apialbum).results[0]
$thumb = $currentfolder + "\" + $albumfound.thumb.split('/')[-1]
invoke-webrequest -uri $albumfound.thumb -outfile $thumb
$pic = $pic + [taglib.picture]::createfrompath("$thumb")

#writing back to the file

$media.tag.title = [string]$filetitle
$media.tag.performers = [string]$fileperformers
$media.tag.albumartists = [string]$filealbumartists
$media.tag.album = [string]$filealbum
$media.tag.genres = [string]$filegenres
$media.tag.year = $fileyear
$media.tag.track = [string]$filetrack
$media.tag.trackcount = [string]$filetrackcount
$media.tag.conductor = [string]$fileconductor
$media.tag.composers = [string]$filecomposers
$media.tag.BeatsPerMinute = [string]$filebpm
$media.tag.pictures = $pic
$media.save()

References:
http://www.powershell.nu/2009/09/04/scripting-mp3-metadata-through-powershell/
http://vallery.net/2012/04/27/organizing-your-music-with-powershell/







10 comments:

BloggerBoy said...

I don't trust the taglib-sharp windows zip file that you're supposed to download! I ran a SHA256 checksum on the download and it does not match the websites reported value. Anybody else getting this problem?

BloggerBoy said...

You can ignore (or delete!) the last comment. My original SHA256 checksum calculation was probably flawed. I used a different checksum executable and I got the right value, plus if I use my original checksum script in default mode (no algorithm option declarations), I get the right value. I'll notify the author, Boe Prox, who wrote the script and put it online. Either I messed up or maybe his script is funny. Probably me, but who knows.

Unknown said...

How do you get access to the "Subtitle" meta data in windows? It does not appear in $media.tag?

George said...

I haven't found a way to manipulate the subtitle field.

Superimpose said...

Thanks for the great post!
I have a question: Is it possible to display the libFLAC version in FLAC files?

If I use "> $media.tag.tags" I see all file informations and under Tags the libFLAC version. (e.g. {reference libFLAC 1.3.0 20130526})
But I do not manage to leave only this information to evaluate.
Is there a command only for this information?

Thanks

Unknown said...

I know this is an old post, but I was able to get the subtitle. This should also work with any other tags as listed at https://taglib.github.io/api/classTagLib_1_1ID3v2_1_1TextIdentificationFrame.html


$TagLib = 'C:\TagLib\taglib-sharp.dll'
[System.Reflection.Assembly]::LoadFile($TagLib)
$Media = [TagLib.File]::Create('C:\Temp\test.mp3')
[TagLib.Id3v2.Tag]$currId3v2 = $media.GetTag([TagLib.TagTypes]::Id3v2)
$currId3v2.SetTextFrame('TIT3',$mp3TagSubtitle)
$Media.Save()

Unknown said...

Thanks for sharing, this is a great way to solve the problem. I advise everyone, it helped me. After that I had a system dll file error, but it is very easy to eliminate. You need to replace the corrupted file http://fix4dll.com/xinput1_3_dll. I would be glad if someone helped. Good luck.

Pat Richard, MCSE MVP said...

Has anyone gotten this to work in Windows 10? I keep getting this in an elevated session, after unblocking the files:

PS C:\test> [system.reflection.assembly]::loadfile($taglib)
Exception calling "LoadFile" with "1" argument(s): "An attempt was made to load an assembly from a network location
which would have caused the assembly to be sandboxed in previous versions of the .NET Framework. This release of the
.NET Framework does not enable CAS policy by default, so this load may be dangerous. If this load is not intended to
sandbox the assembly, please enable the loadFromRemoteSources switch. See
http://go.microsoft.com/fwlink/?LinkId=155569 for more information."
At line:1 char:1
+ [system.reflection.assembly]::loadfile($taglib)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : NotSupportedException

George said...

Hi Pat, I wrote this three years ago and am intending on updating for Windows 10. There might be another program that can extract Tag information but haven't investigated much.

Faeb35 said...

Hi all,
@Pat: Again a very late reply here in this comment section, probably it will help someone at one day...
I also had the loadFromRemoteSources error. Cause: taglib was on a network drive, after moving to c-drive it worked for me.
@George: I tested several ways (found by google) and taglib-sharp was the only solution I found to read the track numbers by using PowerShell.