Sunday 26 September 2010

AppleScript for iTunes: Generate a Playlist of Random Songs

When I go for  a drive, I like to listen to music on my iPod. For that purpose I usually create a playlist in iTunes. I've been using an Applescript to randomly fill a playlist with music. I only need to tell it the total duration I need (usually 3hrs worth) and it throws songs into the playlist until the time-limit is reached.  But the problem was that occasionally I'd get playlists filled with songs that I don't like so much. Now for one or two songs, that's no problem. But when it gets more it tends to become annoying.

So, I looked at the script and tweaked it a bit. The Applescript below relies on the rating of the songs in your library. So, if you haven't rated your songs (between * and *****) then this script won't work. If you have, you'll find that the script will generate a playlist that tends to contain higher rated songs more frequently.

-- Default duration of the playlist (in seconds)
property reqDuration : 3 * 60 * 60
-- List with source(s) of playlists to use.
property listMusicPlaylists : {"Music"}
-- Name of the playlist to create
property myPlayList : "RandomMusic"


set cntM to count of listMusicPlaylists

tell application "iTunes"
set TrackCount to count tracks in (item 1 of every playlist)
-- create playlist if it doesn't exist yet
if not (exists playlist myPlayList) then
make new user playlist with properties {name:myPlayList}
end if
-- ask user the required duration of the playlist
set rslt to display dialog "Add to or replace the existing list? Enter number of minutes." default answer (reqDuration / 60) buttons {"Replace", "Add"} default button 2
set reqDuration to ((text returned of rslt) as integer) * 60
-- empty the playlist
if the button returned of rslt is "Replace" then
set thePlayList to item 1 of (every playlist whose name is myPlayList)
set cntSongs to (count tracks in thePlayList)
repeat cntSongs times
delete item 1 of (every track in thePlayList)
end repeat
else
-- action for 2nd button goes here
end if
end tell

try
tell application "iTunes"
set durPlayList to duration of playlist myPlayList
set LoopCnt to 0
repeat while durPlayList < reqDuration
set addFlag to false
set cntSongs to 0
set cntLoops to 0
-- pick a playlist with music
repeat while (cntSongs = 0)
tell me to set ptrM to random number from 1 to cntM
set selM to item ptrM of listMusicPlaylists
set musicPlaylist to item 1 of (every playlist whose name is selM)
set cntSongs to (count tracks in musicPlaylist)
set cntLoops to cntLoops + 1
-- prevent the repeat loop going infinite
if (cntLoops > cntM) then exit repeat
end repeat
-- determine a Rating-threshold for the next song
tell me to set rndm to random number from 1 to 99
set cntLps to 0
-- pick a song in the playlist
repeat until (addFlag = true)
tell me to set randSong to random number from 1 to cntSongs
set selSong to track randSong of musicPlaylist
-- get info on the selected song
set {dbid, onoff, ratSong} to ({database ID, enabled, rating} of selSong)
-- add the song to our playlist if:
-- 1. the song isn't already in our the playlist
-- 2. the song is enabled/selected
-- 3. the song's rating is higher than the Rating-threshold
if not (exists (some track of playlist myPlayList whose database ID is dbid)) and (onoff is true) and (rndmratSong) then
duplicate selSong to playlist myPlayList
set addFlag to true
end if
set cntLps to cntLps + 1
-- prevent the repeat loop going infinite
if (cntLps > 5) then exit repeat
end repeat
set durPlayList to duration of playlist myPlayList
set LoopCnt to LoopCnt + 1
-- prevent the repeat loop going infinite
if (LoopCnt > TrackCount) then exit repeat
end repeat
end tell
say "ready"
on error the error_message number the error_number
say "Oh, Bother. An error occured while compiling the playlist " & myPlayList
my write_log("Error: " & the error_number & ". " & the error_message)
end try

on write_log(this_text)
set myName to name of (info for (path to me))
set the theLog to ((path to desktop) as text) & myName & " Log.txt"
try
open for access file theLog with write permission
write (this_text & return) to file theLog starting at eof
close access file theLog
on error
try
close access file theLog
end try
end try
end write_log