Artist TOP TRACKS via last.fm (v1.10c) MM 2.5

Download and get help for different MediaMonkey for Windows 4 Addons.

Moderators: Peke, Gurus

Teknojnky
Posts: 5537
Joined: Tue Sep 06, 2005 11:01 pm
Contact:

Artist TOP TRACKS via last.fm (v1.10c) MM 2.5

Post by Teknojnky »

Script to Enqueue TopTracks via Last.fm

grab it HERE
1.10c - 20070928
* added . to allowed chars in fixurl allowing for correct data retrieval from last.fm
' 1.10
' * toptracks ordered now really returns the top tracks

' 1.10b
' * fixed SortPlaylist error
New Version 1.09
* Fixed Shuffle button
New Versoin 1.08
LATEST CHANGES:
1.08
* now shows hides buttons w/o restarting MM
* added option to try not to use albums with certain keywords

1.07
* added option to choose which buttons to show
NEW VERSION 1.06

Uses the improved fixurl by Trixmoto and now should be able to handle any artist.
When multiple artists are selected if you use the option ordered it will only return the top songs of the first artist selected since it is in order, but if you use the shuffle option it will return a mix of the songs of the selected artists.

If anyone needs i can change the options and make it return the top X from each artist.

Regards,
Red

icon 1: ICON 1 icon 2: ICON 2

===========================================

This script performs a similar function as scrobblerDJ, however instead of auto-enqueueing like auto-dj when the last track of nowplaying is reached, this script currently only on user demand when the toolbar button is pressed.

The idea for this script was, that I wanted a quick and easy way to 'play more from selected/current artist' instead of having to manually navigate to the desired artist and pick some tracks by hand. You can either have top tracks enqueued as returned by last.fm, or have the script shuffle the returned list then enqueue your selected number of tracks.

How it works:
When activated, the script checks the user track selection. If no track, or more than one track is selected, the script uses the currently playing track's artist.

If a single track is selected, then that track's artist will be selected.

The script then requests an xml toptracks feed for the artist from last.fm and tries to match up the returned tracks to tracks in your local library.

The matched tracks are then either shuffled if enabled, or played in order of popularity from last.fm, at this point using the entire matched list up to the max of the xml feed or however many were matched up against your library, whichever is lower. (ie last.fm returned 70, but you only had 40 matches, the list is 40... or if last.fm returned 40, but you have more than 40 tracks in your library, the list is still 40)

After being shuffled, if enabled, the script then builds a final queue list to the max set in the script option, or whatever the total tracks matched, whichever is less. (ie the matched list is 40, but the max enqueue tracks is 5, then the top 5 popular or the top 5 after being shuffled will be enqueued).

============================================
' UPDATED: March 28, 2007

' LATEST CHANGES:

New version 1.03: HERE!
icon 1: ICON 1 icon 2: ICON 2

This release also include two icons that need to be in the same folder as the script (auto folder). If you don't copy them then u'll get two identical icons in the toolbar.

changelog:
1.03
* Added option panel for comfort setup
* Added another button to toolbar for easy selection between shuffle and normal
* Added status bar
* started working on future auto enqueue
* Added two custom icons

' 1.01
' * function URLEncode() added to better handle artist names for last.fm
' * Some logging changes/tweaks

' 1.0
' * separated logging and debug, this allows you to continue to log even if
' debug pop up's are disabled
' * Removed max counts from get top tracks, and match top tracks functions.
' This allows the full set of last.fm tracks to be added, queried, matched to
' the library instead of being limited to the max tracks.
' * added a final "EnqueueTracks" loop to create the final list of tracks with
' the max of MaxTracks.
' This allows this the shuffle function to generate a wider variety
' (based on the larger last.fm list) while still being able to restrict the
' final result to a smaller subset.
' It's working very well for me thus far.
============================================

I'd like very much to thank RedX for his help, and Trixmoto for initial query info. I also want to thank psyXonova for his permission to use some bits of code from scrobblerdj!

I consider this to be basically functionally complete, however there are still a few things on the TODO list I may address in the future.

When copying this script, be careful not to let forum wordwrap mangle the query string.

The line:

Code: Select all

  	Set QueryMatch = SDB.Database.QuerySongs ("AND (Artists.Artist LIKE '"&CorrectSt(Artist) & "' AND( Songs.songtitle LIKE '"& CorrectSt(TopTracksList.item(j)) & "'))")
MUST BE SAVED AS ONE LINE

Code: Select all

removed
---------------------------------------------
I was thinking recently that it would be cool if you could hit a toolbar button to look up the current artist and enqueue the top ### (user configurable) of tracks based upon the artists top tracks charts.

I had planned to work on this myself, but I simply haven't had the time to research the details and implement something, and well I thought it was a good enough idea that someone might take up the challenge.

possible options:
- configurable number of tracks to enqueue
- choose from last week chart or last 6 months chart
- choose play tracks in popularity order, or randomize the list after retrieving the specified number of top tracks
- choose to always equeue next, enqueue last, or use follow default enqueue as specified in the MM options
Last edited by Teknojnky on Fri Sep 28, 2007 9:59 am, edited 17 times in total.
Steegy
Posts: 3452
Joined: Sat Nov 05, 2005 7:17 pm

Post by Steegy »

That looks a lot like what ScrobblerDJ (or some similar name) from Psyxonova does. I think you should contact him best.
Extensions: ExternalTools, ExtractFields, SongPreviewer, LinkedTracks, CleanImport, and some other scripts (Need Help with Addons > List of All Scripts).
Teknojnky
Posts: 5537
Joined: Tue Sep 06, 2005 11:01 pm
Contact:

Post by Teknojnky »

Well its similar, but not the same.

ScrobblerDJ automatically/continuously enqueues tracks via last.fm, but what I am thinking of is a "Find More From Same Last.FM Artist" as opposed to scrobblerDJ.

I already use scrobberdj 90% of the time, what I want is an easy way to enqueue some additional tracks from the current or selected artist based on the last.fm charts as I am listening to artists/tracks that scrobblerdj has playing...

I had started on the basics of the script previously, I just hadn't had time to finish out the details for downloading the last.fm xml and crossreferencing those tracks with tracks in the librarary.

Scrobblerdj and Trixmoto's genre finder scripts do alot of this already, its just a matter of implementation of similar code.
Teknojnky
Posts: 5537
Joined: Tue Sep 06, 2005 11:01 pm
Contact:

Post by Teknojnky »

Ok, so I decided to work a bit further on what I already have but I am stuck so far on matching dictionary to sql query to find tracks which match the artist/title list returned by last fm...


first the get top tracks seems to work fine, I get the list of track titles displayed in the debug message boxes

Code: Select all

Function GetTopTracks(Artist)
  'find the artist and return the track chart from last.fm'
  'last.fm xml url @ http://ws.audioscrobbler.com/1.0/artist/" & Artist & "/toptracks.xml'
  If Debug Then msgbox ("entering GetTopTracks()")
  
  'query lastfm data feed
  Dim xmlArtistTopTracksFeedURL, xmlDoc, TopTracksDict
  xmlArtistTopTracksFeedURL = "http://ws.audioscrobbler.com/1.0/artist/" & Artist & "/toptracks.xml"
  Set xmlDoc = CreateObject("Microsoft.XMLDOM")
  Set TopTracksDict = CreateObject("Scripting.Dictionary")
  xmlDoc.Async = False 'false for simplicity'
  
  If xmlDoc.Load (xmlArtistTopTracksFeedURL) Then
    If Debug then msgbox ("artist top tracks feed loaded")
    Dim ele,TrackTitle,count
    count = 0
    For Each ele In xmlDoc.getElementsByTagName("name")
      count = count + 1
      TrackTitle = ele.ChildNodes.Item(0).Text
      If (TrackTitle = "") or (count > MaxTracks) Then Exit For
      If debug then msgbox ("Top Track " & count & ": " & TrackTitle)
      TopTracksDict.Add count, TrackTitle
    Next
       
  Else
    'If Debug then msgbox ("artist top tracks feed FAILED to load")
    'other failure stuff from http://msdn2.microsoft.com/en-us/library/aa468547.aspx'
  End If
End Function

I've been using scrobblerdj/genre finder as references but I'm not figuring out what I need to match up the dictionary to what is available in the library.

Code: Select all

'semi-psuedocode
Function MatchTopTracks(Artist, TopTracksList)

 Set MatchTopTracks = SDB.NewSongList
 loop thru TopTrackList until EOF or MaxTracks is reached
  QueryMatch = SDB.Database.QuerySongs ("AND Songs.ArtistName = Artist AND Songs.Title = TopTracksList.item")
   AddTrack(MatchTopTracks(QueryMatch.item)
 Next
End Function

Here is the entire (INCOMPLETE) code

Code: Select all

Snipped

I'm not even sure I need a dictionary. What I need to happen is for the xml track list to be used to find accessible tracks of the artist in the library, add them to an song list, shuffle them if requested, then enqueue the list to now playing.
Last edited by Teknojnky on Sun Mar 25, 2007 5:01 am, edited 3 times in total.
trixmoto
Posts: 10024
Joined: Fri Aug 26, 2005 3:28 am
Location: Hull, UK
Contact:

Post by trixmoto »

I've not tried this, but would this work...

Code: Select all

Set QueryMatch = SDB.Database.QuerySongs ("AND Artists.Artist LIKE '"&Artist&"' AND Songs.Title LIKE '"&TopTracksList.item&"'")
Don't forget to check you get some results, like this...

Code: Select all

If Not QueryMatch.EOF Then
  ...
End If
Download my scripts at my own MediaMonkey fansite.
All the code for my website and scripts is safely backed up immediately and for free using Dropbox.
Teknojnky
Posts: 5537
Joined: Tue Sep 06, 2005 11:01 pm
Contact:

Post by Teknojnky »

Hmm ok, I'm getting an error with this code

Code: Select all

Function MatchTopTracks(Artist, TopTracksList) 
  'match up the last.fm track chart dictionary to available local library tracks' 
  if debug then msgbox ("entering MatchTopTracks()") 
  Set MatchTopTracks = SDB.NewSongList 
' semi-psuedocode 
'  loop thru TopTrackList until EOF or MaxTracks is reached 
'   QueryMatch = SDB.Database.QuerySongs ("AND Songs.ArtistName = Artist AND Songs.Title = TopTracksList.item") 
'    AddTrack(MatchTopTracks(QueryMatch.item) 
'  Next 
  Set QueryMatch = SDB.Database.QuerySongs ("AND Artists.Artist LIKE '"&Artist&"' AND Songs.Title LIKE '"&TopTracksList.item&"'")
  If Not QueryMatch.EOF Then
    If IsAccessible(QueryMatch.item) Then
      MatchTopTracks.AddTrack (QueryMatch.Item)
    End If
  End If

  
End Function 
Error = object required: TopTracksList
RedX
Posts: 366
Joined: Wed Dec 27, 2006 10:32 am
Location: Germany

Post by RedX »

Code: Select all

Function GetTopTracks(Artist)
  'find the artist and return the track chart from last.fm'
  'last.fm xml url @ http://ws.audioscrobbler.com/1.0/artist/" & Artist & "/toptracks.xml'
  If Debug Then msgbox ("entering GetTopTracks()")
 
  'query lastfm data feed
  Dim xmlArtistTopTracksFeedURL, xmlDoc, TopTracksDict
  xmlArtistTopTracksFeedURL = "http://ws.audioscrobbler.com/1.0/artist/" & Artist & "/toptracks.xml"
  Set xmlDoc = CreateObject("Microsoft.XMLDOM")
  Set TopTracksDict = CreateObject("Scripting.Dictionary")
  xmlDoc.Async = False 'false for simplicity'
 
  If xmlDoc.Load (xmlArtistTopTracksFeedURL) Then
    If Debug then msgbox ("artist top tracks feed loaded")
    Dim ele,TrackTitle,count
    count = 0
    For Each ele In xmlDoc.getElementsByTagName("name")
      count = count + 1
      TrackTitle = ele.ChildNodes.Item(0).Text
      If (TrackTitle = "") or (count > MaxTracks) Then Exit For
      If debug then msgbox ("Top Track " & count & ": " & TrackTitle)
      TopTracksDict.Add count, TrackTitle
    Next
       
  Else
    'If Debug then msgbox ("artist top tracks feed FAILED to load")
    'other failure stuff from http://msdn2.microsoft.com/en-us/library/aa468547.aspx'
    Dim strErrText ' as Text
    Dim xPE ' as MSxml.ixmlDOMParseError
    Set xPE = xmlDoc.ParseError
    With xPE
      strErrText = "Your XML Document failed to load" & _
      "due the following error." & vbCrLf & _
      "Error #: " & .errorCode & ": " & xPE.reason & _
      "Line #: " & .Line & vbCrLf & _
      "Line Position: " & .linepos & vbCrLf & _
      "Position In File: " & .filepos & vbCrLf & _
      "Source Text: " & .srcText & vbCrLf & _
      "Document URL: " & .url
    End With

    MsgBox strErrText, vbExclamation

  End If

End Function 
You don't seem to return the TopTracksDict in this function, it just gets lost

try adding GetTopTracks = TopTracksDict after the For loop where you populate the dic


I just skimmd through the code and other errors will happen i think. ;)
Teknojnky
Posts: 5537
Joined: Tue Sep 06, 2005 11:01 pm
Contact:

Post by Teknojnky »

Hmm, ok you were right I don't think it was returning the dictionary.

I tried the assignment

GetTopTracks = TopTracksDict

but now I'm getting this error:

wrong number of arguments or invalid property assignment

so something is not liking me put the dictionary in the function return variable.

I'm not sure I need the dictionary at all, or if maybe I should put it in a global but that seems poor code.

Thanks for the input. I only know enough to be dangerous and don't get much time to practice.
RedX
Posts: 366
Joined: Wed Dec 27, 2006 10:32 am
Location: Germany

Post by RedX »

The problem AFAIK is that a dictionary is a object and normally to set a variable to an object you ahve to use the Set function and i'm not sure if this works with funcitons but you could try:
Set GetTopTracks = TopTracksDict
and where the output is
Set TopTracksList = GetTopTracks(Artist)

and you will need to set the line below:
Set MatchedTracks = MatchTopTracks(Artist,TopTracksList)

This works, i tried it out
Last edited by RedX on Sat Mar 24, 2007 7:41 am, edited 1 time in total.
Teknojnky
Posts: 5537
Joined: Tue Sep 06, 2005 11:01 pm
Contact:

Post by Teknojnky »

Ok progress :)

Using this to call,

Set TopTracksList = GetTopTracks(Artist)

and

Set GetTopTracks = TopTracksDict

in the function, appeared to work.

Now I'm getting an error on the set query line

Code: Select all

  Set QueryMatch = SDB.Database.QuerySongs ("AND Artists.Artist LIKE '"&Artist&"' AND Songs.Title LIKE '"&TopTracksList.item&"'")
error: wrong number of arguments or invalid property assignment: 'toptrackslist.item'

So I am guessing something right with the query or variable still.

If I change the query to be &TopTracksList.item(0) then I get an ODBC error about too few parameters, expected 1.
RedX
Posts: 366
Joined: Wed Dec 27, 2006 10:32 am
Location: Germany

Post by RedX »

Working function:

Code: Select all

Function MatchTopTracks(Artist, TopTracksList)
  'match up the last.fm track chart dictionary to available local library tracks'
  if debug then msgbox ("entering MatchTopTracks()")
  Set MatchTopTracks = SDB.NewSongList
' semi-psuedocode
'  loop thru TopTrackList until EOF or MaxTracks is reached
'   QueryMatch = SDB.Database.QuerySongs ("AND Songs.ArtistName = Artist AND Songs.Title = TopTracksList.item")
'    AddTrack(MatchTopTracks(QueryMatch.item)
'  Next
	Dim i: i=0
	Dim sql:sql=""
	Dim QueryMatch:QueryMatch = ""
	For i = 1 To TopTracksList.count
		If i=1 Then
				sql = "songs.songtitle LIKE '" & Toptrackslist.item(i) & "'"
		Else
				sql = sql & " OR songs.songtitle LIKE '" & Toptrackslist.item(i) & "'"
		End if
	Next
	If Debug Then MsgBox sql
  Set QueryMatch = SDB.Database.QuerySongs ("AND (Artists.Artist LIKE '"&Artist & "' AND(  "& sql & "))")  
  Do While Not QueryMatch.EOF 
    If IsAccessible(QueryMatch.item) Then
      MatchTopTracks.Add (QueryMatch.Item)
    End If
    QueryMatch.next
  loop
End Function 

Function IsAccessible(SongObj)
    If SongObj Is Nothing Then
	    'logme "* IsAccessible has started but the passed parameter was empty"
	    'logme "* IsAccessble will now call sub ScrobblerDJ and exit"
        'ScrobblerDJ
        If Debug Then MsgBox "SongObj was empty"
        Exit Function
    End If
  	'logme "* IsAccessible has started for song (" & SongObj.ID & ") " & SongObj.ArtistName & " - " & SongObj.Title

    If (Left(SongObj.Path, 1) <> "?") Or (SongObj.Cached) Then
        IsAccessible = True
    Else
        IsAccessible = False
    End If
    If Debug Then MsgBox "Isaccesible: " & IsAccessible & " " &songobj.title
End Function
the only problem atm is when you ahve a song many times in your library. This happens i.e. if one songs shows up in different albums

You should also remove the maxtracks checking from toptracks and put it here after the isaccessible has run! this way you ensure to get all tracks that the suer has on the list, atm waht can happen is that the user does not ahve the top 5 tracks but other ones so he'll get an empty list.

replace

Code: Select all

    For Each ele In xmlDoc.getElementsByTagName("name")
      count = count + 1
      TrackTitle = ele.ChildNodes.Item(0).Text
      If (TrackTitle = "") or (count > MaxTracks) Then Exit For
      'If debug then msgbox ("Top Track " & count & ": " & TrackTitle)
      TopTracksDict.Add count, TrackTitle
    Next
with

Code: Select all

    For Each ele In xmlDoc.getElementsByTagName("name")
      TrackTitle = ele.ChildNodes.Item(0).Text
      'If debug then msgbox ("Top Track " & count & ": " & TrackTitle)
      If Not TopTracksDict.Exists(TrackTitle) then
        count = count+1
      	TopTracksDict.Add count, TrackTitle
      End If
    Next
and

Code: Select all

  Set QueryMatch = SDB.Database.QuerySongs ("AND (Artists.Artist LIKE '"&Artist & "' AND(  "& sql & "))")  
  Do While Not QueryMatch.EOF
    If IsAccessible(QueryMatch.item) Then
      MatchTopTracks.Add (QueryMatch.Item)
    End If
    QueryMatch.next
  loop
with

Code: Select all

	i=0
  Set QueryMatch = SDB.Database.QuerySongs ("AND (Artists.Artist LIKE '"&Artist & "' AND(  "& sql & "))")  
  Do While Not QueryMatch.EOF Or i = MaxTracks
    If IsAccessible(QueryMatch.item) Then
      MatchTopTracks.Add (QueryMatch.Item)
      i = i + 1
    End If
    QueryMatch.next
  loop
Teknojnky
Posts: 5537
Joined: Tue Sep 06, 2005 11:01 pm
Contact:

Post by Teknojnky »

Ok, something I didn't expect.

The code you posted almost works (still get an error more on that later), what I was not expecting is that the query requests all tracks in the list instead of iterating thru each one individually.

ie: artist = pixies
the debug sql msgbox reports:

songs.songtitle LIKE 'Where is my Mind?' OR songs.songtitle LIKE 'Debaser' OR songs.songtitle LIKE 'Here Comes Your Man'

Those being the 3 tracks in the list.

Then it says is accessible and tries to add it when it gets error:

object required 'MatchTopTracks'

on the line
MatchTopTracks.Add (QueryMatch.Item)

edit, will try your other changes
RedX
Posts: 366
Joined: Wed Dec 27, 2006 10:32 am
Location: Germany

Post by RedX »

There seems to be a problem with the query length.
If you try to query the db for all the songs you get an error because the sql is too long.

I'm not sure how to restrict the length of the query without cutting out tracks that the user might have in his db and leaving songs that he doesn't.
Teknojnky
Posts: 5537
Joined: Tue Sep 06, 2005 11:01 pm
Contact:

Post by Teknojnky »

Why trying to query for all tracks in one query, instead of iterating thru the toptrack list and querying each individually and adding them to the matched list if we find an accessible match?
Teknojnky
Posts: 5537
Joined: Tue Sep 06, 2005 11:01 pm
Contact:

Post by Teknojnky »

OK Here is the code as it currently is with various stuff commented out.

I hard coded the artist name to metallica to be able to have consistent top track list.

Here is the xml from last fm @ http://ws.audioscrobbler.com/1.0/artist ... tracks.xml

(top 5 only)

Code: Select all

  <?xml version="1.0" encoding="UTF-8" ?> 
- <mostknowntracks artist="Metallica">
- <track>
  <name>Nothing Else Matters</name> 
  <mbid /> 
  <reach>7263</reach> 
  <url>http://www.last.fm/music/Metallica/_/Nothing+Else+Matters</url> 
  </track>
- <track>
  <name>Enter Sandman</name> 
  <mbid /> 
  <reach>6834</reach> 
  <url>http://www.last.fm/music/Metallica/_/Enter+Sandman</url> 
  </track>
- <track>
  <name>Master of Puppets</name> 
  <mbid /> 
  <reach>6779</reach> 
  <url>http://www.last.fm/music/Metallica/_/Master+of+Puppets</url> 
  </track>
- <track>
  <name>One</name> 
  <mbid /> 
  <reach>5802</reach> 
  <url>http://www.last.fm/music/Metallica/_/One</url> 
  </track>
- <track>
  <name>The Unforgiven</name> 
  <mbid /> 
  <reach>4526</reach> 
  <url>http://www.last.fm/music/Metallica/_/The+Unforgiven</url> 
  </track>
 </mostknowntracks>

The script as current

Code: Select all

snipped
Originally I coded the max tracks into the GetTopTracks(Artist) function temporaily to keep the number of pop up debug dialogs down. I had not planned to limit the list size at this point permanently. MaxTracks is/was the max to enqueued.

I'm still getting an error querying the database, but it seems we are getting close to something working.
Last edited by Teknojnky on Sat Mar 24, 2007 8:54 am, edited 1 time in total.
Post Reply