Fill itunes genre with tags from last.fm

Hello everyone,

First (real) post. I’m quite a novice with Applescript. I’ve been fiddling around a bit and I am reading Rosenthal’s SE Applescript guide (at page 43 :rolleyes:).

I am now experimenting with a script I found floating around on the net to adjust it to my liking. It fetches the top tags and should place the first hit (highest count) in the genre field in itunes, and a few other high hits in the comment field (at least, that’s how I am intepreting the ‘thestrength’ in the script).

I want to change the script so it uses the tags for tracks instread of the artitst and when that is empty after filtering use the artists’ or albums’ tags. Some tags are often not genres (especially for tracks and albums) so I want to filter those in an ‘if’ loop where I can add unwanted results.

But before I get to the end I’d like to fix a few things first and try and speed it up.

I’ve been at it for hours and the result comes up with the wrong tags in the genre field.
Also, I have no idea how to limit the tags read to a maximum of the first ten to speed things up.

here is the original script:

tell application "iTunes"
if selection = {} then
display dialog "You must select one or more tracks first." buttons {"Cancel"} default button 1 with icon 1
end if
set trackList to selection
set lastartist to ""
set lastgenre to ""
set lastcomment to ""
repeat with i from 1 to count of trackList
set thisTrack to item i of trackList
set theartist to artist of thisTrack

set genre of thisTrack to ""
set comment of thisTrack to ""
set thiscomment to ""


if theartist = lastartist then
set genre of thisTrack to lastgenre
set comment of thisTrack to lastcomment
else
set theurl to "http://ws.audioscrobbler.com/1.0/artist/"; & my encode_text(theartist) & "/toptags.xml"

set tags to {}

set thexml to do shell script "curl " & theurl
if thexml does not start with "No artist exists with this name:" then


tell application "System Events"
delete every XML data
set this_data to make new XML data with properties {name:"toptags", text:thexml}
tell this_data
every XML element of XML element 1
repeat with x in every XML element of XML element 1
set thetag to value of XML element "name" of x
set thestrength to value of XML element "count" of x

if thestrength ≥ 50 then

set the end of tags to thetag
end if
end repeat
end tell
delete every XML data
end tell
if tags ≠ {} then
set genre of thisTrack to item 1 of tags
repeat with i from 1 to count of tags
set thiscomment to thiscomment & "'" & item i of tags & "' "
end repeat
set comment of thisTrack to thiscomment
end if
delay 1
end if
set lastartist to theartist
set lastgenre to genre of thisTrack
set lastcomment to thiscomment

end if
end repeat

end tell


-- this sub-routine is used to encode text
on encode_text(this_text)
set the unacceptable_characters to " &+%'/\"\\"
set the encoded_text to ""
set the character_list to {}
repeat with this_char in this_text
set this_char to the contents of this_char
if this_char is not in the unacceptable_characters then
set the end of the character_list to this_char
else
set the end of the character_list to encode_char(this_char)
end if
end repeat
return (the character_list) as string
end encode_text

-- this sub-routine is used to encode a character
on encode_char(this_char)
set the ASCII_num to (the ASCII number this_char)
set the hex_list to {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"}
set x to item ((ASCII_num div 16) + 1) of the hex_list
set y to item ((ASCII_num mod 16) + 1) of the hex_list
return ("%" & x & y) as string
end encode_char

I think that this line
set theurl to “http://ws.audioscrobbler.com/1.0/artist/”; & my encode_text(theartist) & “/toptags.xml”
has the ; as a typo. It won’t compile unless I remove it, after which it runs fine.

The url in this script should become something like
http://ws.audioscrobbler.com/1.0/artist/Peter Gabriel/toptags.xml

The url I need for the track would be something like
http://ws.audioscrobbler.com/1.0/track/Peter%20Gabriel/Solsbury%20Hill/toptags.xml

So this is the script I’ve come up with so far to fetch the track tags:

tell application "iTunes"
	if selection = {} then
		display dialog "You must select one or more tracks first." buttons {"Cancel"} default button 1 with icon 1
	end if
	set trackList to selection
	set lastartist to ""
	set lasttrack to ""
	set lastgenre to ""
	set lastcomment to ""
	repeat with i from 1 to count of trackList
		set thisTrack to item i of trackList
		set theartist to artist of thisTrack
		set thename to name of thisTrack
		
		set genre of thisTrack to ""
		set comment of thisTrack to ""
		set thiscomment to ""
		
		
		if thisTrack = lasttrack then
			set genre of thisTrack to lastgenre
			set comment of thisTrack to lastcomment
		else
			set theurl to "http://ws.audioscrobbler.com/1.0/track/" & my encode_text(theartist) & "/" & my encode_text(thename) & "/toptags.xml"
			
			set tags to {}
			
			set thexml to do shell script "curl " & theurl
			if thexml does not start with "No such track" then
				
				
				tell application "System Events"
					delete every XML data
					set this_data to make new XML data with properties {name:"toptags", text:thexml}
					tell this_data
						every XML element of XML element 1
						repeat with x in every XML element of XML element 1
							set thetag to value of XML element "name" of x
							set thestrength to value of XML element "count" of x
							
							if thestrength ≥ 50 then
								
								set the end of tags to thetag
							end if
						end repeat
					end tell
					delete every XML data
				end tell
				if tags ≠ {} then
					set genre of thisTrack to item 1 of tags
					repeat with i from 1 to count of tags
						set thiscomment to thiscomment & "'" & item i of tags & "' "
					end repeat
					set comment of thisTrack to thiscomment
				end if
				delay 1
			end if
			set lastartist to theartist
			set lasttrack to thisTrack
			set lastgenre to genre of thisTrack
			set lastcomment to thiscomment
			
		end if
	end repeat
	
end tell


-- this sub-routine is used to encode text
--same as original but removed to shorten the post

The issues I’m having with both scripts is that it is not taking the the tags with the highest count to set the genre, but one of the lower ones. The highest count does show up in the comment field but obviously that is not where I want it.

So finally, the questions.
Can someone help me figure out why the top count does not end up in the genre field and how I can correct that;
Can someone help me figure out how to limit the tags processed to the first 10 or so to speed things up.

Once I get past those issues I’ll focus on filtering and fetching other tags in case track tags are empty or unwanted.

TIA!

Ingemar.

Model: alu iMac 24"
AppleScript: 2.2
Browser: Firefox 3.0
Operating System: Mac OS X (10.5)

Wow and you rewrote the script too :smiley:

Many thanks Jacques. And fast it is.

I see I have some debugging to do to figure out how it all works. I just woke up though and need more caffeine to uncloud my somewhat unwilling brain on this sunday morning. :stuck_out_tongue:

Oke, I think I came a long way but now I’m really stuck at filtering unwanted tags.

In the subroutine get_genres() I evaluate thetag to unwanted tag, and if returned true set thetag to “unwanted” and exit the repeat. However, it always comes out false, even though in the display dialogs I use to “debug” it shows as true.

Also, I’m not sure if I understand the return “” correctly. I would think the return '“” ends the subroutine as a whole or does it depend on the use in nested if/repeat loops?

Basically what I am trying to accomplish is if the if thetag is equal to unwanted_tag I want to skip it and continue with the next in the list. I then want the first result in the genre and the remaining results in the comments (I think I got that working right).

Still, something is wrong because item 1 of these_genres is returned as a single character but maybe that will become obvious once the evaluation works properly.

Rosenthal’s Applescript book is excellent for learning but not so good for a reference.
/me moves a good applescript reference book to the top of his wanted-list.

Thx again!

set {lastartist, lastalbum, lastname, lastgenre, lastcomment} to {"", "", "", "", ""}

tell application "iTunes"
	set trackList to selection
	if trackList = {} then display dialog "You must select one or more tracks first." buttons {"Cancel"} default button 1 with icon 1
	repeat with thisTrack in trackList
		tell thisTrack
			set {theartist, thealbum} to {artist, album}
			if thealbum = lastalbum then
				set {genre, comment} to {lastgenre, lastcomment}
			else
				set t_result to my get_Genres(thealbum, theartist)
				if t_result ≠ "" then
					set {genre, comment} to t_result
					set {lastgenre, lastcomment} to t_result
				else
					set {genre, comment} to {"", ""}
					set {lastgenre, lastcomment} to {"", ""}
				end if
			end if
		end tell
	end repeat
end tell

on get_Genres(a_album, a_artist)
	set unwanted_list to {"albums I own", "singer-songwriter", "female vocalists"}
	set unwanted_count to (count unwanted_list)
	set theurl to quoted form of ("http://ws.audioscrobbler.com/1.0/album/" & encode_text(a_artist) & "/" & encode_text(a_album) & "/toptags.xml")
	set theresult to do shell script "/usr/bin/curl " & theurl & " | /usr/bin/grep -m20 '\\(<name>\\|\\<count>\\)' | /usr/bin/sed 's/<\\/.*//;s/.*>//'"
	if theresult = "" then return ""
	-- display dialog theresult
	set theresult to paragraphs of theresult
	set thecount to (count theresult)
	set these_genres to ""
	set these_comments to ""
	set allscores to ""
	set alltags to ""
	set thetag to ""
	set thescore to ""
	repeat with c from 2 to thecount by 2
		set thescore to item c of theresult
		set thetag to item (c - 1) of theresult
		-- display dialog thescore
		if thescore as integer < 50 then exit repeat
		if thetag = "" then exit repeat
		repeat with unwanted_tag in unwanted_list
			set thecounter to c
			-- display dialog thetag & " = " & unwanted_tag
			if thetag is equal to unwanted_tag then
				set thetag to "unwanted"
				-- display dialog thetag
				exit repeat
			else
				if thecounter > 3 then set these_comments to these_comments & thetag & ", "
			end if
		end repeat
		set these_genres to these_genres & thetag & ", "
	end repeat
	return {item 1 of these_genres, these_genres}
end get_Genres


-- this sub-routine is used to encode text
on encode_text(aString)
	set character_list to (characters of aString)
	repeat with this_char in character_list
		tell this_char to if it is not in "$-_.+!*'(),1234567890abcdefghijklmnopqrstuvwxyz" then set contents to my encode_char(it)
	end repeat
	return character_list as string
end encode_text

-- this sub-routine is used to encode a character
on encode_char(c)
	set n to (ASCII number c)
	tell "0123456789ABCDEF" to return "%" & (character ((n div 16) + 1)) & (character ((n mod 16) + 1))
end encode_char

You’re a great help, Jacques. Thanks.

I’ll look at it tomorrow afternoon. Right now is sleepy time :wink:

So basically you take 12 lines out of my script and redo it with 5 :rolleyes:

It all makes perfect sense but I wouldn’t come up with it myself, not yet at least. I need to finish that book and get some more experience under my belt :wink:

One more question. Why are certain variables defined before use and others are not? For instance, these_comments is left in your rewrite, but you left out thetag. Why is that?

Cheers!