Monday, February 18, 2013

PowerShell: Invoke-RestMethod, API's, and your music collection

Gathering ID3 tag information for your music files has become easier by embracing PowerShell 3's Invoke-RestMethod (IRM) command.  Many online databases use Application Programming Interfaces (API's) for developers to access their information.  These API's used to use XML but now have moved to the JSON language. IRM embraces JSON and allows us to create these API connections and query information about our music files.  In this article, I'm accessing Discogs Creative Commons open-source database.
Original Extended File Information
Updated after IRM and Discog's API

In my earlier article, I explained how to access shell.application in order to retrieve the extended file properties we use to rename and organize our files.  Combined with this method, we'll grab the extended file information and use it to find the correct file information and insert it into our files.  One point to make is that the API wants exact query information.  You'll receive no information if searching for an artist name of Quean instead of Queen.

 Syntax for Discogs allows us to search for song titles, artist names or IDs, album names and their release data plus much more.  The following demonstration script is fragile and incomplete.  It's to show how we embrace the power of PowerShell 3's Invoke-RestMethod.  Become familiar with IRM and API's to pull in online information into your scripts. It'll enhance the CURRENT information you're capable of accessing.  I'm really liking PS3's capabilities.

Disclaimer: Use this script at your own risk!  You'll be accessing Discogs live database and manipulating your computers music files.  This script uses the Windows 7 extended file information numbers.

##Start Script

$shell = new-object -com shell.application
$dirname = (get-item .\).fullname

#A quick and dirty way to find a song to use.  Run your script from your music folder

gi .\*.mp3
write "Type the exact (case-sensitive) song title you wish to query"
$fileinput read-host
$filename = gi .\$fileinput.mp3|?{(!($_.psiscontainer))}|foreach-object{$_.name}
$shellfolder = $shell.namespace($dirname).parsename($filename)

#now you can retrieve the extended file information and use those objects for your queries

$songtitle = $shell.namespace($dirname).getdetailsof($shellfolder,21)
$contribartist = $shell.namespace($dirname).getdetailsof($shellfolder,13)
$albumartist = $shell.namespace($dirname).getdetailsof($shellfolder,217)
$album = $shell.namespace($dirname).getdetailsof($shellfolder,14)
$genre = $shell.namespace($dirname).getdetailsof($shellfolder,16)
$year = $shell.namespace($dirname).getdetailsof($shellfolder,15)
$track = $shell.namespace($dirname).getdetailsof($shellfolder,26)
$length = $shell.namespace($dirname).getdetailsof($shellfolder,27)
$bitrate = $shell.namespace($dirname).getdetailsof($shellfolder,28)
$conductors = $shell.namespace($dirname).getdetailsof($shellfolder,17)
$bpm = $shell.namespace($dirname).getdetailsof($shellfolder,218)
$composers = $shell.namespace($dirname).getdetailsof($shellfolder,219)

#An example of how to replace spaces for your online queries. %20 is the variable used for space.

$apicontribartist = $contribartist -replace (' ','%20')
$apisongtitle = $songtitle -replace (' ','%20')

#Every artist in their database has an ID.  We query to find the ID.

$artistid = (irm http://api.discogs.com/artist/$apicontribartist).resp.artist.id

#We use the Artist ID to find the song's original release date.  Master is the keyword for original recording.

if ((invoke-restmethod http://api.discogs.com/database/search?artist=$artistid"&"type=master"&"title=$songtitle).results){
$origdate = ((invoke-restmethod http://api.discogs.com/database/search?artist=$artistid"&"type=master"&"title=$songtitle).results.resource_url|select -unique -first 1|%{invoke-restmethod $_}).year}else{
$origdate = ((invoke-restmethod http://api.discogs.com/database/search?artist=$apicontribartist"&"track=$songtitle).results.year)|sort|select -first 1}

#Locate Original Album hosting the audio track by using the 
#artist ID, releases, master, and date.

$master = ((irm http://api.discogs.com/artists/$artistid/releases).releases|?{$_.year -eq $origdate -and $_.type -eq "Master"})
$master = $master[0]
$origalbum = (irm $master.resource_url).title

#Now we can use the master album the song appeared on to find additional song information
#We find track number, released on Vinyl, 45, CD, etc., and I like to combine the styles and genres 
#& is a reserved PowerShell character.  We wrap it "&" so it will be passed to Discogs.

$origtracknumber = 1 + (((irm ((irm http://api.discogs.com/database/search?artist=$apicontribartist"&"title=$origalbum"&"track=$songtitle"&"year=$origdate"&"type=master).results).resource_url).tracklist|?{$_.title -eq $songtitle}).position.count)
$origformat = ((irm http://api.discogs.com/database/search?artist=$apicontribartist"&"title=$origalbum"&"track=$songtitle"&"year=$origdate"&"type=master).results)
$origformat = ((irm http://api.discogs.com/database/search?artist=$apicontribartist"&"title=$origalbum"&"track=$songtitle"&"year=$origdate"&"type=master).results).label|select -first 1
$origstyles = (irm ((irm http://api.discogs.com/database/search?artist=$apicontribartist"&"title=$origalbum"&"track=$songtitle"&"year=$origdate"&"type=master).results).resource_url).styles
$origgenres = (irm ((irm http://api.discogs.com/database/search?artist=$apicontribartist"&"title=$origalbum"&"track=$songtitle"&"year=$origdate"&"type=master).results).resource_url).genres

#I combined the styles and genre information so I can create better
#future genre searches.

$origgenre = ($origstyles + $origgenres)|sort

#write the updates back to the original file
#Try taglib for inserting tags back into your audio file.
#Here's a site which goes over some of its abilities.
#For Windows 7, when you download taglib, find the taglib-sharp.dll
#file, right-click it, select properties, and "unblock" it so you can load 
#the DLL into your ref.assembly command

$taglib = "c:\powershell\taglib\libraries\taglib-sharp.dll"
[system.reflection.assembly]::loadfile($taglib)
$tags = [taglib.file]::create($filename)

#type $tags to see the file properties
#type $tags.tag to see the extended file properties
#I'll let you take it from here :-)
##End Script




No comments: