Automatic iTunes Folder Playlist in Javascript and F#
The other day, I was struggling with my playlist on itunes. As you may know iTunes doesn’t have the notion of the folder structure of your mp3s. So if you do organise your music in folders you won’t have them in iTunes. I decided to write a script using the iTunes COM SDK for doing that.
Here is the scenario all the music is stored in d:\Music. So the script starts exploring everything inside in the folder and create an entry in iTunes, and it happens only in one level. First I did it with JavaScript with a lot of pain. I was pain because of the ugly interface and also unclear documentation. Luckily there were the samples in the SDK to play with it.
var iTunesApp = WScript.CreateObject("iTunes.Application"); var mainLibrary = iTunesApp.LibraryPlaylist; var mainLibrarySource = iTunesApp.LibrarySource; var tracks = mainLibrary.Tracks; var numTracks = tracks.Count; var numPlaylistsCreated = 0; var ITTrackKindFile = 1; var albumArray = new Array(); var playlists = mainLibrarySource.Playlists; for (var i = 1; i <= numTracks; i++) { var currTrack = tracks.Item(i); if (currTrack.Kind == ITTrackKindFile) { if (currTrack.Location != "") { var p =currTrack.Location var location = p if ((location != undefined) && (location != "")) { if (albumArray[location] == undefined) { albumArray[location] = new Array(); } albumArray[location].push(currTrack); } } } } WScript.Echo(" Tracks Read " + numTracks); for (var albumNameKey in albumArray) { var trackArray = albumArray[albumNameKey]; var p = albumNameKey; var ignoreFirst = p.indexOf("music\\",0); //3 var firstChar = 0; var secondChar = 0; var firstChar = ignoreFirst +5 ; var myArr = new Array(); while (true) { var firstChar = p.indexOf("\\", firstChar); var secondChar = p.indexOf("\\", firstChar+1); if (secondChar == -1 ) break; var newStr = p.substring(firstChar+1, secondChar) myArr.push(newStr); firstChar = secondChar; } var plist = null; if (myArr.length == 0) { if (playlists.ItemByName("_") != null) { plist = playlists.ItemByName("_") ; } else { plist = iTunesApp.CreatePlaylist("_"); numPlaylistsCreated++; } } else if ( playlists.ItemByName(myArr[0]) != null) { plist = playlists.ItemByName(myArr[0]) } else { plist = iTunesApp.CreatePlaylist( myArr[0]); numPlaylistsCreated++; } for (var trackIndex in trackArray) { var currTrack = trackArray[trackIndex]; plist.AddTrack(currTrack); } } //create playlist if (numPlaylistsCreated > 0) { WScript.Echo( numPlaylistsCreated + " (s) created."); } else { WScript.Echo("No playlist createds"); }
(It is written in a quick way without caring about the beauty
)
It was doing its job that I wanted but I also wanted to have my folder structure in itunes rather than a single entry for a folder. This time I implemented in F#. I have chosen F# mainly because of the type inference and the intellisense. In JavaScript without the documentation, you couldn’t do anything. F# is also chosen because of interoperability with COM and friends. So I have to admit that I didn’t like the COM interface of iTunes. All the best F# implementation with more functionality gave %40 less code
#light #r "Interop.iTunesLib.dll"open iTunesLib open System.IO open System.Text.RegularExpressions let itunes = new iTunesLib.iTunesAppClass() let playLists = itunes.LibrarySource.Playlists let tracks = itunes.LibraryPlaylist.Tracks let track = tracks.Item(2) for track in tracks do if track.Kind = ITTrackKind.ITTrackKindFile && (track:?> IITFileOrCDTrack).Location <> null then let track = track :?> IITFileOrCDTrack let regex = new System.Text.RegularExpressions.Regex( @"^( ?<Drive>([a-zA-Z])):\\_music\\ ((?< directory>[\w\W]+)\\)*((?< playlist>[\w\W]+)\\)(?< filename>([\w\W]+.mp3))", RegexOptions.IgnoreCase) printf "%d\n" track.Index let resMatch = regex.Match(track.Location) if resMatch.Length = 0 () else let lastFolder = resMatch.Groups.Item("directory").Captures > Seq.untyped_fold (fun (acc:IITPlaylist) (a: (System.Text.RegularExpressions.Capture)) -> match (playLists.ItemByName(a.Value)) with null -> if acc.Index = 1 then itunes.CreateFolder(a.Value) else (acc :?> IITUserPlaylist).CreateFolder(a.Value) a1 -> if (a1 :?> IITUserPlaylist).SpecialKind = ITUserPlaylistSpecialKind.ITUserPlaylistSpecialKindFolder then a1 else itunes.CreateFolder(a.Value) ) (playLists.Item(1) ) let pListName = resMatch.Groups.Item("playlist").Captures.Item(0).Value let lastPlayList= match (playLists.ItemByName(pListName)) with null -> if lastFolder.Index = 1 then itunes.CreatePlaylist(pListName) :?> IITUserPlaylist else let fol = lastFolder :?> IITUserPlaylist fol.CreatePlaylist(pListName) :?> IITUserPlaylist a2 -> if (a2 :?> IITUserPlaylist).SpecialKind = ITUserPlaylistSpecialKind.ITUserPlaylistSpecialKindFolder then match (playLists.ItemByName("_" + pListName)) with null -> (a2:?> IITUserPlaylist).CreatePlaylist("_" + pListName) :?> IITUserPlaylist a3 ->a3 :?> IITUserPlaylist else a2 :?> IITUserPlaylist lastPlayList.AddTrack(ref (track :> obj)) > ignore done
The folder playlist is not implemented on iPod. Although it make sense to use in iTunes, there is no point doing it for iPod. I was a bit frustrated, it was my sole purpose. Anyway it was nice to play with it…
So I hope it will be helpful in a way, the folder playlist didn’t help me, maybe in the next firmware update… On the other hand, I still use the first implementation for my automatic one-level folder playlist.


