Novice scripting - updating info from Musicbrainz

This forum is for questions / discussions regarding development of addons / tweaks for MediaMonkey.

Moderator: Gurus

mhendu
Posts: 81
Joined: Thu Jan 12, 2006 11:18 am

Novice scripting - updating info from Musicbrainz

Post by mhendu » Thu Oct 26, 2017 8:11 am

I'm a complete novice at writing code and not sure what I'm doing. Am hoping that one or more folks here with more experience might be willing to point me in the right direction, maybe review the script as I'm writing it and provide feedback.

Thanks to the efforts of other users we have scripts that will find an AcoustID for a given track. We also have a script that will search Musicbrainz to find the earliest release date for a given track (which I would use if the below lookups don't return results). I'd like to build a script that will do the following:

1. Search AcoustID metadata to find a Musicbrainz recording ID (e.g., http://api.acoustid.org/v2/lookup?clien ... ee12d5718c)
2. For a given recording ID, store the Work ID (if available) in a custom field - this is both for the classical lookup mentioned below, as well as to allow me to find covers of tracks in my library
3. If the track is a classical work, find the composition date and store it in the year (if available, assuming work ID returned a result)
4. If the track is not a classical work, find the recording date and store it in the year (if available)

This query at Musicbrainz will return the recording date, work ID and composition date if they're accessible:

http://musicbrainz.org/ws/2/recording/3 ... place-rels

I'm looking at borrowing some code from either the MusicIP tagger (which was based on the now-deprecated PUID) or the MusicBrainz NGS tagger, or maybe both, but again, not really sure what I'm doing.

*******

First issue I'm looking to tackle is how to load the currently selected song list and, for each track in the list, query AcoustID to find the recording ID. I store the AcoustID in Custom5 but if the script is to be more broadly applicable I'd have to incorporate either fpcalc or allow a user to specify which field the AcoustID is stored in to perform the lookup.

I could use some pointers on how to do the above so I can get started - also not sure how to integrate debugging into the code to identify any problems that might come up.

*******

Next issue would be querying to find the Work ID (I'm not sure how to get this data from the query I posted above). Then storing that ID in a custom field (I use custom 4 but again, might need to make this user-defined).

I use the Grouping field to specify whether a track is classical, although others may use the newer functionality in MediaMonkey 4 to separate classical tracks from other tracks. That would be my next problem to solve, figuring out whether a track is classical and, if so, looking up and storing the composition date.

Final issue would be looking up and storing the recorded date if the track is not classical.

Thanks in advance for any help!

mhendu
Posts: 81
Joined: Thu Jan 12, 2006 11:18 am

Re: Novice scripting - updating info from Musicbrainz

Post by mhendu » Fri Oct 27, 2017 1:18 am

Shockingly, I actually was able to make this work, more or less. Biggest problem is that the script will effectively freeze MediaMonkey if run against a large number of tracks, but otherwise seems to work OK. Would appreciate if anyone happens to look to see what could be improved here. For simplicity, I just built it for myself, but could be extended to allow for user inputs if someone has the knowledge to do that.

Code: Select all

Sub SearchAcoustID
  ' Define variables
  Dim list, itm, i
  Dim APIKey : APIKey = "[insert your API key here]"
  Dim Grouping
  Dim AcoustID
  Dim Worktemp
  Dim WorkID
  Dim RecordingID
  Dim acoustidurl
  Dim mbrainzurl
  Dim xml : Set xml = CreateObject("Microsoft.XMLDOM")
  xml.async = False
  xml.preserveWhiteSpace = True
  Dim mbrainz : Set mbrainz = CreateObject("Microsoft.XMLDOM")
  mbrainz.async = False
  mbrainz.preserveWhiteSpace = True
  Dim mbrainznode
  Dim recordingidtemp
  Dim mbrainztemp
  Dim workidtemp
  RecordingID = ""
  WorkID = ""

  ' Get list of selected tracks from MediaMonkey
  Set list = SDB.CurrentSongList 
  If list.Count=0 Then
    Exit Sub
  End If
    ' Process all selected tracks
  For i=0 To list.count-1
    Do
      Set itm = list.Item(i)
  	  Grouping = itm.Grouping
	  AcoustID = itm.Custom5
	  acoustidurl = "http://api.acoustid.org/v2/lookup?format=xml&client=" & APIKey & "&meta=recordingids+recordings&trackid=" & AcoustID
	  Call xml.Load(acoustidurl)
      Dim cnt : cnt = 0
      While (xml.readyState < 4 And cnt < 300)
        Call SDB.Tools.Sleep(100)
        SDB.ProcessMessages
        cnt = cnt+1
      WEnd
      If xml.readyState < 4 Then
        Exit Sub  
      End If
      Set recordingidtemp = xml.getElementsByTagName("id").Item(0)
      If recordingidtemp Is Nothing Then Exit Do
	  RecordingID = xml.getElementsByTagName("id").Item(0).Text
	  mbrainzurl = "http://musicbrainz.org/ws/2/recording/" & RecordingID & "?inc=place-rels+artist-rels+work-rels+work-level-rels"
	  Call mbrainz.Load(mbrainzurl)
      Dim cnta : cnta = 0
      While (mbrainz.readyState < 4 And cnta < 300)
        Call SDB.Tools.Sleep(100)
        SDB.ProcessMessages
        cnta = cnta+1
      WEnd
      If mbrainz.readyState < 4 Then
        Exit Sub  
      End If
	  If Grouping = "Classical" Then
	    Set Worktemp = mbrainz.getElementsByTagName("work").Item(0)
		If Worktemp Is Nothing Then Exit Do
	    WorkID = Worktemp.getAttribute("id")
	    itm.Custom4 = WorkID
		Set mbrainztemp = mbrainz.SelectSingleNode("//recording/relation-list/relation/work/relation-list/relation/end")
		If mbrainztemp Is Nothing Then
		  itm.UpdateDB
		  itm.WriteTags
		  Exit Do
		End If
	    mbrainznode = mbrainz.SelectSingleNode("//recording/relation-list/relation/work/relation-list/relation/end").Text
  	    itm.Year = mbrainznode
	  Else
        Set mbrainztemp = mbrainz.SelectSingleNode("//recording/relation-list/relation/end")
		If mbrainztemp Is Nothing Then
          Set Worktemp = mbrainz.getElementsByTagName("work").Item(0)
		  If Worktemp Is Nothing Then Exit Do
	      WorkID = Worktemp.getAttribute("id")
 	      itm.Custom4 = WorkID
		  itm.UpdateDB
		  itm.WriteTags
		  Exit Do
		End If
   	    mbrainznode = mbrainz.SelectSingleNode("//recording/relation-list/relation/end").Text
	    itm.Year = Left(mbrainznode,4)
        Set Worktemp = mbrainz.getElementsByTagName("work").Item(0)
		If Worktemp Is Nothing Then
		  itm.UpdateDB
		  itm.WriteTags
 		  Exit Do
		End If
	    WorkID = Worktemp.getAttribute("id")
	    itm.Custom4 = WorkID
	  End If
	Loop While False
  Next
  list.UpdateAll
End Sub

mhendu
Posts: 81
Joined: Thu Jan 12, 2006 11:18 am

Re: Novice scripting - updating info from Musicbrainz

Post by mhendu » Fri Oct 27, 2017 1:31 am

Oh, just to set good expectations, the script won't commonly return results, unfortunately. I think this is because the fields I'm looking up are not often filled out and associated with an AcoustID fingerprint, so your mileage may vary. If I'm incorrect and this is due to a coding error would love to have some help to make this more productive.

mhendu
Posts: 81
Joined: Thu Jan 12, 2006 11:18 am

Re: Novice scripting - updating info from Musicbrainz

Post by mhendu » Fri Oct 27, 2017 8:29 am

Last post (I think) - made some improvements. Now the script will also search by Work ID for classical works, if that information is stored in Custom4. Also limited the loop to go once per second to avoid hitting Musicbrainz too often. I created a separate version of the script just to tag the Work ID from the AcoustID, which mostly involved stripping out a bunch of code. If anyone's interested in that I can post it separately.

Code: Select all

Sub SearchAcoustID
  ' Define variables
  Dim list, itm, i
  Dim APIKey : APIKey = "[REPLACE WITH ACOUSTID API KEY]"
  Dim Grouping
  Dim AcoustID
  Dim Worktemp
  Dim WorkID
  Dim RecordingID
  Dim acoustidurl
  Dim mbrainzurl
  Dim xml : Set xml = CreateObject("Microsoft.XMLDOM")
  xml.async = False
  xml.preserveWhiteSpace = True
  Dim mbrainz : Set mbrainz = CreateObject("Microsoft.XMLDOM")
  mbrainz.async = False
  mbrainz.preserveWhiteSpace = True
  Dim mbrainznode
  Dim recordingidtemp
  Dim mbrainztemp
  Dim workidtemp
  Dim workurl
  Dim workxml : Set workxml = CreateObject("Microsoft.XMLDOM")
  workxml.async = False
  workxml.preserveWhiteSpace = True
  Dim mbrainzlist
  RecordingID = ""
  WorkID = ""

  ' Get list of selected tracks from MediaMonkey
  Set list = SDB.CurrentSongList 
  If list.Count=0 Then
    Exit Sub
  End If
    ' Process all selected tracks
  For i=0 To list.count-1
    Do
      Set itm = list.Item(i)
  	  Grouping = itm.Grouping
	  AcoustID = itm.Custom5
	  acoustidurl = "http://api.acoustid.org/v2/lookup?format=xml&client=" & APIKey & "&meta=recordingids+recordings&trackid=" & AcoustID
	  Call xml.Load(acoustidurl)
      Dim cnt : cnt = 0
      While (xml.readyState < 4 And cnt < 300)
        Call SDB.Tools.Sleep(100)
        SDB.ProcessMessages
        cnt = cnt+1
      WEnd
      If xml.readyState < 4 Then
        Exit Sub  
      End If
      Set recordingidtemp = xml.getElementsByTagName("id").Item(0)
      If recordingidtemp Is Nothing Then
	    If itm.Custom4 <> "" AND Grouping = "Classical" Then
	  WorkID = itm.Custom4
 	  workurl = "http://musicbrainz.org/ws/2/work/" & WorkID & "?inc=artist-rels"
	  Call workxml.Load(workurl)
      Dim cntb : cntb = 0
      While (workxml.readyState < 4 And cntb < 300)
        Call SDB.Tools.Sleep(100)
        SDB.ProcessMessages
        cntb = cntb+1
      WEnd
      If workxml.readyState < 4 Then
        Exit Sub  
      End If
		Set mbrainztemp = workxml.SelectSingleNode("//work/relation-list/relation/end")
		If mbrainztemp Is Nothing Then Exit Do
		mbrainznode = workxml.SelectSingleNode("//work/relation-list/relation/end").Text
  	    itm.Year = Left(mbrainznode,4)
		  itm.UpdateDB
		  itm.WriteTags
		End If
		Exit Do
	  End If
	  RecordingID = xml.getElementsByTagName("id").Item(0).Text
	  mbrainzurl = "http://musicbrainz.org/ws/2/recording/" & RecordingID & "?inc=place-rels+artist-rels+work-rels+work-level-rels"
	  Call mbrainz.Load(mbrainzurl)
      Dim cnta : cnta = 0
      While (mbrainz.readyState < 4 And cnta < 300)
        Call SDB.Tools.Sleep(100)
        SDB.ProcessMessages
        cnta = cnta+1
      WEnd
      If mbrainz.readyState < 4 Then
        Exit Sub  
      End If
	  If Grouping = "Classical" Then
	    Set Worktemp = mbrainz.getElementsByTagName("work").Item(0)
		If Worktemp Is Nothing Then Exit Do
	    WorkID = Worktemp.getAttribute("id")
	    itm.Custom4 = WorkID
		Set mbrainztemp = mbrainz.SelectSingleNode("//recording/relation-list/relation/work/relation-list/relation/end")
		If mbrainztemp Is Nothing Then
		  itm.UpdateDB
		  itm.WriteTags
		  Exit Do
		End If
	    mbrainznode = mbrainz.SelectSingleNode("//recording/relation-list/relation/work/relation-list/relation/end").Text
  	    itm.Year = Left(mbrainznode,4)
	  Else
        Set mbrainztemp = mbrainz.SelectSingleNode("//recording/relation-list/relation/end")
		If mbrainztemp Is Nothing Then
          Set Worktemp = mbrainz.getElementsByTagName("work").Item(0)
		  If Worktemp Is Nothing Then Exit Do
	      WorkID = Worktemp.getAttribute("id")
 	      itm.Custom4 = WorkID
		  itm.UpdateDB
		  itm.WriteTags
		  Exit Do
		End If
   	    mbrainznode = mbrainz.SelectSingleNode("//recording/relation-list/relation/end").Text
	    itm.Year = Left(mbrainznode,4)
        Set Worktemp = mbrainz.getElementsByTagName("work").Item(0)
		If Worktemp Is Nothing Then
		  itm.UpdateDB
		  itm.WriteTags
 		  Exit Do
		End If
	    WorkID = Worktemp.getAttribute("id")
	    itm.Custom4 = WorkID
	  End If
	Loop While False
	Call SDB.Tools.Sleep(1000)
  Next
  list.UpdateAll
End Sub

mhendu
Posts: 81
Joined: Thu Jan 12, 2006 11:18 am

Re: Novice scripting - updating info from Musicbrainz

Post by mhendu » Sat Oct 28, 2017 12:43 am

Found that there were some instances where the script would return the incorrect date or Work ID. Have adjusted accordingly.

Code: Select all

Sub SearchAcoustID
  ' Define variables
  Dim list, itm, i, j, jnum, jcnt, jmax, jpos
  Dim APIKey : APIKey = "[ENTER ACOUSTID API KEY HERE]"
  Dim Grouping
  Dim AcoustID
  Dim Worktemp
  Dim WorkID
  Dim RecordingID
  Dim acoustidurl
  Dim mbrainzurl
  Dim xml : Set xml = CreateObject("Microsoft.XMLDOM")
  xml.async = False
  xml.preserveWhiteSpace = True
  Dim mbrainz : Set mbrainz = CreateObject("Microsoft.XMLDOM")
  mbrainz.async = False
  mbrainz.preserveWhiteSpace = True
  Dim mbrainznode
  Dim recordingidtemp
  Dim recordingidtemp1
  Dim mbrainztemp
  Dim workidtemp
  Dim workurl
  Dim workxml : Set workxml = CreateObject("Microsoft.XMLDOM")
  workxml.async = False
  workxml.preserveWhiteSpace = True
  Dim mbrainzlist
  RecordingID = ""
  WorkID = ""

  ' Get list of selected tracks from MediaMonkey
  Set list = SDB.CurrentSongList 
  If list.Count=0 Then
    Exit Sub
  End If
    ' Process all selected tracks
  For i=0 To list.count-1
    Do
      Set itm = list.Item(i)
  	  Grouping = itm.Grouping
	  AcoustID = itm.Custom5
	  acoustidurl = "http://api.acoustid.org/v2/lookup?format=xml&client=" & APIKey & "&meta=recordingids+sources&trackid=" & AcoustID
	  Call xml.Load(acoustidurl)
      Dim cnt : cnt = 0
      While (xml.readyState < 4 And cnt < 300)
        Call SDB.Tools.Sleep(100)
        SDB.ProcessMessages
        cnt = cnt+1
      WEnd
      If xml.readyState < 4 Then
        Exit Sub  
      End If
	  jnum = 0
	  jcnt = -1
	  jmax = 0
	  jpos = 0
	  For Each j In xml.selectNodes("//sources")
	    jnum = CInt(j.Text)
		jcnt = jcnt + 1
		If jnum > jmax Then jpos = jcnt
		If jnum > jmax Then jmax = jnum
	  Next
      Set recordingidtemp = xml.getElementsByTagName("id").Item(jpos)
      If recordingidtemp Is Nothing Then
	    If itm.Custom4 <> "" AND Grouping = "Classical" Then
	      WorkID = itm.Custom4
 	      workurl = "http://musicbrainz.org/ws/2/work/" & WorkID & "?inc=artist-rels"
	      Call workxml.Load(workurl)
          Dim cntb : cntb = 0
          While (workxml.readyState < 4 And cntb < 300)
            Call SDB.Tools.Sleep(100)
            SDB.ProcessMessages
            cntb = cntb+1
          WEnd
          If workxml.readyState < 4 Then
          Exit Sub  
          End If
		  xpath = "//relation[@type='composer']/end"
		  Set mbrainztemp = workxml.SelectSingleNode(xpath)
		  If mbrainztemp Is Nothing Then Exit Do
		  mbrainznode = mbrainztemp.Text
  	      itm.Year = Left(mbrainznode,4)
		  itm.UpdateDB
		  itm.WriteTags
        End If
	    Exit Do
	  End If
	  RecordingID = recordingidtemp.Text
	  mbrainzurl = "http://musicbrainz.org/ws/2/recording/" & RecordingID & "?inc=place-rels+artist-rels+work-rels+work-level-rels"
      Call mbrainz.Load(mbrainzurl)
	  Dim cnta : cnta = 0
      While (mbrainz.readyState < 4 And cnta < 300)
        Call SDB.Tools.Sleep(100)
        SDB.ProcessMessages
        cnta = cnta+1
      WEnd
      If mbrainz.readyState < 4 Then
        Exit Sub  
      End If
	  If Grouping = "Classical" Then
	    Set Worktemp = mbrainz.getElementsByTagName("work").Item(0)
		If Worktemp Is Nothing Then Exit Do
	    WorkID = Worktemp.getAttribute("id")
	    itm.Custom4 = WorkID
		xpath = "//relation[@type='composer']/end"
		Set mbrainztemp = mbrainz.SelectSingleNode(xpath)
		If mbrainztemp Is Nothing Then
		  itm.UpdateDB
		  itm.WriteTags
		  Exit Do
		End If
	    mbrainznode = mbrainztemp.Text
  	    itm.Year = Left(mbrainznode,4)
	  Else
        xpath = "//relation-list[@target-type='place']/relation/end"
		Set mbrainztemp = mbrainz.SelectSingleNode(xpath)
		If mbrainztemp Is Nothing Then
          Set Worktemp = mbrainz.getElementsByTagName("work").Item(0)
		  If Worktemp Is Nothing Then Exit Do
	      WorkID = Worktemp.getAttribute("id")
 	      itm.Custom4 = WorkID
		  itm.UpdateDB
		  itm.WriteTags
		  Exit Do
		End If
   	    mbrainznode = mbrainztemp.Text
	    itm.Year = Left(mbrainznode,4)
        Set Worktemp = mbrainz.getElementsByTagName("work").Item(0)
		If Worktemp Is Nothing Then
		  itm.UpdateDB
		  itm.WriteTags
 		  Exit Do
		End If
	    WorkID = Worktemp.getAttribute("id")
	    itm.Custom4 = WorkID
	  End If
	Loop While False
	Call SDB.Tools.Sleep(1000)
  Next
  list.UpdateAll
End Sub

mhendu
Posts: 81
Joined: Thu Jan 12, 2006 11:18 am

Re: Novice scripting - updating info from Musicbrainz

Post by mhendu » Mon Oct 30, 2017 9:40 pm

Err, OK, found instances where the AcoustID search returns no recording IDs but then the script still treats classical works like other works and doesn't search by work ID if present. Fixed in this version, so more results will be returned for classical works (assuming Grouping=Classical and the work ID is present and stored in Custom4).

Code: Select all

Sub SearchAcoustID
  ' Define variables
  Dim list, itm, i, j, jnum, jcnt, jmax, jpos
  Dim APIKey : APIKey = "[INSERT ACOUSTID API KEY HERE]"
  Dim Grouping
  Dim AcoustID
  Dim Worktemp
  Dim WorkID
  Dim RecordingID
  Dim acoustidurl
  Dim mbrainzurl
  Dim xml : Set xml = CreateObject("Microsoft.XMLDOM")
  xml.async = False
  xml.preserveWhiteSpace = True
  Dim mbrainz : Set mbrainz = CreateObject("Microsoft.XMLDOM")
  mbrainz.async = False
  mbrainz.preserveWhiteSpace = True
  Dim mbrainznode
  Dim recordingidtemp
  Dim recordingidtemp1
  Dim mbrainztemp
  Dim workidtemp
  Dim workurl
  Dim workxml : Set workxml = CreateObject("Microsoft.XMLDOM")
  workxml.async = False
  workxml.preserveWhiteSpace = True
  Dim mbrainzlist
  RecordingID = ""
  WorkID = ""

  ' Get list of selected tracks from MediaMonkey
  Set list = SDB.CurrentSongList 
  If list.Count=0 Then
    Exit Sub
  End If
    ' Process all selected tracks
  For i=0 To list.count-1
    Do
      Set itm = list.Item(i)
  	  Grouping = itm.Grouping
	  AcoustID = itm.Custom5
	  acoustidurl = "http://api.acoustid.org/v2/lookup?format=xml&client=" & APIKey & "&meta=recordingids+sources&trackid=" & AcoustID
	  Call xml.Load(acoustidurl)
      Dim cnt : cnt = 0
      While (xml.readyState < 4 And cnt < 300)
        Call SDB.Tools.Sleep(100)
        SDB.ProcessMessages
        cnt = cnt+1
      WEnd
      If xml.readyState < 4 Then
        Exit Sub  
      End If
	  jnum = 0
	  jcnt = -1
	  jmax = 0
	  jpos = 0
	  For Each j In xml.selectNodes("//sources")
	    jnum = CInt(j.Text)
		jcnt = jcnt + 1
		If jnum > jmax Then jpos = jcnt
		If jnum > jmax Then jmax = jnum
	  Next
      Set recordingidtemp = xml.getElementsByTagName("id").Item(jpos)
      If recordingidtemp Is Nothing OR jmax = 0 Then
	    If itm.Custom4 <> "" AND Grouping = "Classical" Then
	      WorkID = itm.Custom4
 	      workurl = "http://musicbrainz.org/ws/2/work/" & WorkID & "?inc=artist-rels"
	      Call workxml.Load(workurl)
          Dim cntb : cntb = 0
          While (workxml.readyState < 4 And cntb < 300)
            Call SDB.Tools.Sleep(100)
            SDB.ProcessMessages
            cntb = cntb+1
          WEnd
          If workxml.readyState < 4 Then
          Exit Sub  
          End If
		  xpath = "//relation[@type='composer']/end"
		  Set mbrainztemp = workxml.SelectSingleNode(xpath)
		  If mbrainztemp Is Nothing Then Exit Do
		  mbrainznode = mbrainztemp.Text
  	      itm.Year = Left(mbrainznode,4)
		  itm.UpdateDB
		  itm.WriteTags
        End If
	    Exit Do
	  End If
	  RecordingID = recordingidtemp.Text
	  mbrainzurl = "http://musicbrainz.org/ws/2/recording/" & RecordingID & "?inc=place-rels+artist-rels+work-rels+work-level-rels"
      Call mbrainz.Load(mbrainzurl)
	  Dim cnta : cnta = 0
      While (mbrainz.readyState < 4 And cnta < 300)
        Call SDB.Tools.Sleep(100)
        SDB.ProcessMessages
        cnta = cnta+1
      WEnd
      If mbrainz.readyState < 4 Then
        Exit Sub  
      End If
	  If Grouping = "Classical" Then
	    Set Worktemp = mbrainz.getElementsByTagName("work").Item(0)
		If Worktemp Is Nothing Then Exit Do
	    WorkID = Worktemp.getAttribute("id")
	    itm.Custom4 = WorkID
		xpath = "//relation[@type='composer']/end"
		Set mbrainztemp = mbrainz.SelectSingleNode(xpath)
		If mbrainztemp Is Nothing Then
		  itm.UpdateDB
		  itm.WriteTags
		  Exit Do
		End If
	    mbrainznode = mbrainztemp.Text
  	    itm.Year = Left(mbrainznode,4)
	  Else
        xpath = "//relation-list[@target-type='place']/relation/end"
		Set mbrainztemp = mbrainz.SelectSingleNode(xpath)
		If mbrainztemp Is Nothing Then
          Set Worktemp = mbrainz.getElementsByTagName("work").Item(0)
		  If Worktemp Is Nothing Then Exit Do
	      WorkID = Worktemp.getAttribute("id")
 	      itm.Custom4 = WorkID
		  itm.UpdateDB
		  itm.WriteTags
		  Exit Do
		End If
   	    mbrainznode = mbrainztemp.Text
	    itm.Year = Left(mbrainznode,4)
        Set Worktemp = mbrainz.getElementsByTagName("work").Item(0)
		If Worktemp Is Nothing Then
		  itm.UpdateDB
		  itm.WriteTags
 		  Exit Do
		End If
	    WorkID = Worktemp.getAttribute("id")
	    itm.Custom4 = WorkID
	  End If
	Loop While False
	Call SDB.Tools.Sleep(1000)
  Next
  list.UpdateAll
End Sub

Post Reply