property prefs : missing value property prefsPath : missing value on get_tracklength(numBars, isUTF) my loadPrefs() if numBars is 0 then -- just use regular time tell application "iTunes" set thePosition to my convertTime(player position) if duration of current track is missing value then set theDuration to "∞" else set theDuration to my convertTime(duration of current track) end if end tell return "(" & thePosition & " / " & theDuration & ")" else tell application "iTunes" if duration of current track is not missing value then set n to round (player position / (duration of current track) * numBars) else set n to -1 end if end tell if isUTF then set leftedge to "╞" set bar to "═" set marker to "╪" set rightedge to "╡" else set leftedge to "[" set bar to "=" set marker to "|" set rightedge to "]" end if set str to "" & leftedge repeat with b from 0 to numBars if b < n then set str to str & bar else if b = n then set str to str & "" & marker & "" else set str to str & bar end if end repeat set str to str & rightedge & "" return str end if end get_tracklength on convertTime(theTime) set theMinutes to (theTime div 60) as string set theSeconds to ((theTime mod 60 as integer) as string) if length of theSeconds is 1 then set theSeconds to "0" & theSeconds end if return theMinutes & ":" & theSeconds end convertTime on rgbStringOf(theColor) repeat with i from 1 to 3 if item i of theColor < 256 then set item i of theColor to 256 if item i of theColor > 65270 then set item i of theColor to 65270 end repeat set rtn to "rgb(" & (round ((item 1 of theColor) / 256 - 0.5)) & "," & (round ((item 2 of theColor) / 256 - 0.5)) & "," & (round ((item 3 of theColor) / 256 - 0.5)) & ")" return rtn end rgbStringOf on encodeEntities(htmlText) set oldDelims to AppleScript's text item delimiters set AppleScript's text item delimiters to the "&" set the itemList to every text item of htmlText set AppleScript's text item delimiters to the "&" set htmlText to the itemList as string set AppleScript's text item delimiters to the "<" set the itemList to every text item of htmlText set AppleScript's text item delimiters to the "<" set htmlText to the itemList as string set AppleScript's text item delimiters to the ">" set the itemList to every text item of htmlText set AppleScript's text item delimiters to the ">" set htmlText to the itemList as string set AppleScript's text item delimiters to the "\"" set the itemList to every text item of htmlText set AppleScript's text item delimiters to the """ set htmlText to the itemList as string set AppleScript's text item delimiters to the "'" set the itemList to every text item of htmlText set AppleScript's text item delimiters to the "'" set htmlText to the itemList as string set AppleScript's text item delimiters to oldDelims return htmlText end encodeEntities on urlEncodeText(thisText, encodeGroupA, encodeGroupB) set the acceptableCharacters to "abcdefghijklmnopqrstuvwxyz0123456789" set the aCharacters to "$+!'/?;&@=#%><{}[]\"~`^\\|*" set the bCharacters to ".-_:" if encodeGroupA is false then ¬ set the acceptableCharacters to the acceptableCharacters & the aCharacters if encodeGroupB is false then ¬ set the acceptableCharacters to the acceptableCharacters & the bCharacters set the encodedText to "" repeat with thisChar in thisText if thisChar is in the acceptableCharacters then set the encodedText to (the encodedText & thisChar) else if thisChar is in " " then set the encodedText to (the encodedText & "+") else set the encodedText to (the encodedText & urlEncodeCharacter(thisChar)) as string end if end repeat return the encodedText end urlEncodeText on urlEncodeCharacter(thisChar) set the asciiNumber to (the ASCII number thisChar) set the hexList to {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"} set x to item ((asciiNumber div 16) + 1) of the hexList set y to item ((asciiNumber mod 16) + 1) of the hexList return ("%" & x & y) as string end urlEncodeCharacter on get_rating(the_track, EncodingIsUTF) tell application "iTunes" set the_rating to the rating of the_track end tell if the_rating is not missing value and the_rating is not equal to 0 then tell application "iTunes" if not (exists the rating of the_track) or the_rating is missing value then return "" else set stars to (the_rating div 20) as integer if the_rating is greater than 0 and stars is equal to 0 then set stars to 1 end if end if end tell if not EncodingIsUTF then return "(" & stars & " stars)" set starString to "(" set fullstar to "✮" set emptystar to "✩" if stars is equal to 0 then set starString to "" repeat with i from 1 to 5 set starString to starString & emptystar end repeat set starString to starString & "" else repeat with i from 1 to stars set starString to starString & fullstar end repeat repeat with i from stars to 4 set starString to starString & emptystar end repeat end if set starString to starString & ")" return starString else return "" end if end get_rating using terms from application "Colloquy" on process user command c with arguments for view if c is in {"itunes", "music"} then try tell application "System Events" set itunesRunning to ((application processes whose name is equal to "iTunes") count) is greater than 0 end tell if (arguments is "" or arguments is missing value) then set theitunes to my getitunes(encoding of view is UTF8) if theitunes is "not running" then set msg to "isn't running iTunes currently." else if theitunes is "paused" then set msg to "has iTunes paused." else if theitunes is "unknown track type" then set msg to "is listening to an unknown type of track." else set msg to "is listening to " & theitunes end if tell view to send message msg with action tense else if first word of arguments is "open" then ignoring application responses tell application "iTunes" to activate end ignoring else if first word of arguments is "quit" and itunesRunning then ignoring application responses tell application "iTunes" to quit end ignoring else if first word of arguments is "play" then ignoring application responses tell application "iTunes" to play end ignoring else if first word of arguments is "pause" and itunesRunning then ignoring application responses tell application "iTunes" to pause end ignoring else if first word of arguments is "stop" and itunesRunning then ignoring application responses tell application "iTunes" to stop end ignoring else if first word of arguments is "next" and itunesRunning then ignoring application responses tell application "iTunes" to next track end ignoring else if first word of arguments is "previous" and itunesRunning then ignoring application responses tell application "iTunes" to previous track end ignoring else if first word of arguments is "rating" and itunesRunning then set EncodingIsUTF to (encoding of view is UTF8) --if the command is just "/itunes rating", this try block will cause an error in the first line, skip the rest of the block, and no one will be the wiser. Otherwise, it will set the rating then continue the script. try set theRating to (word 2 of arguments) as number tell application "iTunes" if player state is playing then set rating of current track to theRating * 20 end if end tell end try tell application "iTunes" if player state is playing then set starString to my get_rating(current track, EncodingIsUTF) set msg to "iTunes song rating: " & starString set actionTense to false else set msg to "has iTunes paused" set actionTense to true end if end tell if actionTense then tell view to send message msg with action tense else tell view to send message msg end if else if first word of arguments is "stats" and itunesRunning then try set args to words 2 through -1 of arguments on error set args to "" end try tell view to send message "'s " & my getStats(args) with action tense else if first word of arguments is "prefs" then tell view to add event message "Control-click (or right-click) here for iTunes Prefs" with name "itunes" return true end if return true on error err display dialog err end try else if c is "reload" then if arguments is "prefs" then my reloadprefs() end if return false end if return false end process user command (* on process incoming chat message m my loadPrefs() if prefs's getKey("Link Songs") is in {missing value, false} then return m end if if body of m starts with "is listening to " and action tense of m is true and prefs's getKey("Link Songs") is true then set segments to attribute run of m if (count of segments) ≥ 3 and item 1 of segments is "is listening to " then try set theSong to item 2 of segments set theArtist to missing value if (count of segments) is greater than or equal to 4 and item 3 of segments is equal to " by " then set theArtist to item 4 of segments set theAlbum to missing value if (count of segments) is greater than or equal to 6 and item 5 of segments is equal to " from " then set theAlbum to item 6 of segments if (count of segments) is greater than or equal to 4 and item 3 of segments is equal to " from " then set theAlbum to item 4 of segments set initialSearch to "itms://phobos.apple.com/WebObjects/MZSearch.woa/wa/advancedSearchResults?" set artistSearch to initialSearch set albumSearch to initialSearch set delim to "" if theArtist is not missing value then set artistSearch to artistSearch & "artistTerm=" & my urlEncodeText(theArtist, true, true) set albumSearch to artistSearch set delim to "&" set link of attribute run 4 of m to artistSearch set style classes of attribute run 4 of m to {"itunes"} end if if theAlbum is not missing value then set albumSearch to albumSearch & delim & "albumTerm=" & my urlEncodeText(theAlbum, true, true) set delim to "&" if theArtist is missing value then set albumIndex to 4 --which attribute is the album string else set albumIndex to 6 end if set link of attribute run albumIndex of m to albumSearch set style classes of attribute run albumIndex of m to {"itunes"} end if set songSearch to albumSearch & delim & "songTerm=" & my urlEncodeText(theSong, true, true) set link of attribute run 2 of m to songSearch set style classes of attribute run 2 of m to {"itunes"} on error err display dialog err end try end if end if end process incoming chat message *) on build contextual menu for item theItem try if theItem contains "listening" then my loadPrefs() tell prefs to set sm to getContextMenu() return {title:"iTunes Script Settings", submenu:sm} end if on error err -- return {title:"iTunes script got an error!!", submenu:{{title:err, enabled:false}, {title:build of prefs as string}}} end try end build contextual menu for item on handle clicked contextual menu item theItem within theParents if theParents is {"iTunes Script Settings"} then try prefs's changekey(theItem) my savePrefs() on error err display dialog err end try end if end handle clicked contextual menu item end using terms from script prefsTemplate property defaultKeys : {¬ {label:"Show Rating", value:true}, ¬ {label:"Show Colors", value:true}, ¬ {label:"Link Songs", value:true}, ¬ {label:"Show Time", value:false}, ¬ {label:"Show Progress Bar", value:false}, ¬ {label:"--Left Progress Bar Color", value:{40000, 40000, 40000}}, ¬ {label:"--Progress Bar Marker Color", value:{40000, 40000, 40000}}, ¬ {label:"--Right Progress Bar Color", value:{40000, 40000, 40000}}, ¬ {label:"Show Bit Rate", value:false} ¬ } property theKeys : defaultKeys property build : 10 --increment each time you change something where prefs needs to change --returns true if the value was found, false if the value had to be created on setKey(thisLabel, newValue) repeat with k in theKeys if label of k is thisLabel then set value of k to newValue return true end if end repeat set theKeys to theKeys & {{label:thisLabel, value:newValue}} return false end setKey --like setKey, except it handles the interface: if it's boolean it toggles it, if it's a string it will ask for a new value, etc on changekey(thisLabel) set asd to AppleScript's text item delimiters set AppleScript's text item delimiters to " (" set thisLabel to text item 1 of thisLabel set oldvalue to getKey(thisLabel) set AppleScript's text item delimiters to asd if oldvalue is missing value then display dialog "That item (" & thisLabel & ") does not exist" return else if class of oldvalue is boolean then setKey(thisLabel, not oldvalue) else if class of oldvalue is string then set ans to (text returned of (display dialog "New value?" default answer oldvalue) as string) setKey(thisLabel, ans) else if class of oldvalue is integer then set ans to text returned of (display dialog "New value?" default answer oldvalue) setKey(thisLabel, ans as integer) else if class of oldvalue is list then if number of items in oldvalue is 3 then ---must be a color set ans to choose color default color oldvalue setKey(thisLabel, ans) else display dialog "Cannot edit lists of items." end if end if end changekey on getKey(thisLabel) repeat with k in theKeys if label of k is thisLabel then return value of k end if end repeat return missing value end getKey on listKeys() set final to "" repeat with k in theKeys set final to final & label of k & ": " & (value of k as string) & return end repeat return final end listKeys on getContextMenu() set final to {} try repeat with k in theKeys if class of value of k is boolean then set final to final & {{title:label of k, checked:value of k}} else if class of value of k is integer or class of value of k is string then set final to final & {{title:label of k & " (" & value of k & ")"}} else if class of value of k is list then set asd to AppleScript's text item delimiters set AppleScript's text item delimiters to ", " if number of items in value of k < 3 then set final to final & {{title:label of k & " (" & (every item of value of k as string) & ")"}} else if number of items in value of k is 3 then set final to final & {{title:label of k, icon:value of k}} else set final to final & {{title:label of k & " (" & (items 1 through 3 of value of k as string) & ", ...)"}} end if set AppleScript's text item delimiters to asd end if end repeat return final on error err return {{title:"iTunes script got an error: " & err, enabled:false}} end try end getContextMenu end script on loadPrefs() if prefs is missing value then set prefsPath to (path to preferences folder from user domain as Unicode text) & "Colloquy Prefs - iTunes" try set prefs to load script alias prefsPath try set oldbuild to build of prefs on error set oldbuild to -1 end try if oldbuild < build of prefsTemplate then set newPrefs to prefsTemplate set newPrefs to my mergeKeys(prefs, prefsTemplate) set prefs to newPrefs my savePrefs() end if on error set prefs to prefsTemplate end try end if end loadPrefs on savePrefs() if prefs is not missing value then store script prefs in file prefsPath replacing yes end savePrefs --This function is used to merge keys of the old list with the values of the new one. --Note that the first parameter is the old version of the prefs (from which the values need to be kept), and the second contains the new list (with default values to be overwritten by values gotten from oldPrefs --returns the list to set theKeys to on mergeKeys(oldPrefs, newDefaultPrefs) try repeat with k in theKeys of newDefaultPrefs set oldvalue to oldPrefs's getKey(label of k) if oldvalue is not missing value then set value of k to oldvalue end if end repeat end try return newDefaultPrefs end mergeKeys on getitunes(EncodingIsUTF) tell application "System Events" set itunesRunning to ((application processes whose name is equal to "iTunes") count) is greater than 0 end tell if itunesRunning then tell application "iTunes" if player state is playing then return item 1 of my getitunesFor(EncodingIsUTF, current track) else return "paused" end if end tell else return "not running" end if end getitunes on getitunesFor(EncodingIsUTF, trackID) tell application "System Events" set itunesRunning to ((application processes whose name is equal to "iTunes") count) is greater than 0 end tell set fullmsg to {} if itunesRunning then my loadPrefs() tell application "iTunes" if class of trackID is not list then set trackID to {trackID} repeat with thisTrackID in trackID if class of thisTrackID is integer then set thisTrack to (first track of library playlist 1 whose database ID is thisTrackID) else if class of thisTrackID is in {track, file track, URL track, device track, shared track, audio CD track} then set thisTrack to thisTrackID else error "Unknown class" number 3245 end if if class of thisTrack is in {track, file track, URL track, device track, shared track, audio CD track} then if class of thisTrack is URL track then set theSong to current stream title if theSong is missing value then set theSong to name of thisTrack set theAlbum to missing value else set theAlbum to name of thisTrack end if set theArtist to missing value else set theArtist to artist of thisTrack set theAlbum to album of thisTrack set theSong to name of thisTrack end if set showColors to prefs's getKey("Show Colors") if showColors then set msg to "" & my encodeEntities(theSong) & "" else set msg to my encodeEntities(theSong) end if if theArtist is not "" and theArtist is not missing value then if showColors then set msg to msg & " by " & my encodeEntities(theArtist) & "" else set msg to msg & " by " & my encodeEntities(theArtist) end if end if if theAlbum is not "" and theAlbum is not missing value then if showColors then set msg to msg & " from " & my encodeEntities(theAlbum) & "" else set msg to msg & " from " & my encodeEntities(theAlbum) end if end if if prefs's getKey("Show Rating") then set msg to msg & " " & my get_rating(thisTrack, EncodingIsUTF) end if if prefs's getKey("Show Time") then set msg to msg & " " & my get_tracklength(0, EncodingIsUTF) end if if prefs's getKey("Show Progress Bar") then set msg to msg & " " & my get_tracklength(10, EncodingIsUTF) end if if prefs's getKey("Show Bit Rate") then set theKind to kind of thisTrack if theKind contains "MPEG" then set theKind to "MP3" if theKind contains "AAC" then set theKind to "AAC" if theKind contains "WAV" then set theKind to "WAV" if theKind contains "Apple Lossless" then set theKind to "Apple Lossless" set msg to msg & " (" & bit rate of thisTrack & " kbps " & theKind & ")" end if else set msg to "unknown track type" end if set fullmsg to fullmsg & {msg} end repeat end tell end if return fullmsg end getitunesFor on getStats(args) tell application "iTunes" if args is "" then set lib to library playlist 1 set libName to name of library playlist 1 else try set lib to some playlist whose name contains args set libName to "Playlist \"" & name of (some playlist whose name contains args) & "\"" on error set lib to "" end try end if if lib ≠ "" then if size of lib < 1.0E+9 then set mysize to (round (size of lib) / 1024 / 1024 * 100 as string) / 100 & " MB" else set mysize to ((round (size of lib) / 1024 / 1024 / 1024 * 100) / 100 as string) & " GB" end if set res to "iTunes " & libName & " is " & time of lib & " long, with " & (count tracks in lib) & " songs totalling " & mysize & "." else set res to "" end if end tell if res ≠ "" then return res end if end getStats