Wanting to be able to play my music from my computer on my Xbox 360, I installed Microsoft’s Zune Software available for free at www.zune.net. You can configure the Zune Software to setup a Zune Network Sharing Service (NSS) for the Xbox 360. I like the music experience on the Xbox 360 though the Zune NSS better than through Media Center. The only setup step I stumbled on was that the Zune NSS runs by default as the “Network Service” account. In order to have your music available through the service, your must change your music folder permissions to give access to that account.
I’ve used iTunes for years and will need to continue using it for my iPods. However, Zune Software provides a pretty sexy UI. I’ve been using it for a few days now and really like it. In the near future, I need to merge some of my music from iTunes on another computer to this one. With that in mind, I became curious what kind of APIs iTunes and Zune Software might provide.
I found a blog post from 2010-07-03 by Phillip Chistoph explaining how to access the Zune database and then found some more details about how to create queries by David Aoun on 2008-03-03. Here I build upon their work by building a “Zune” F# module that can get a list of all albums and artists. It can be used like so:
use zuneLibrary = Zune.initLibrary()
let albumCount, albums = Zune.queryAllAlbums zuneLibrary
printfn "# of albums: %d" albumCount
albums |> Seq.iter (printfn "%s")
let artistCount, artists = Zune.queryAllAlbumArtists zuneLibrary
printfn "# of artists: %d" artistCount
artists |> Seq.iter (printfn "%s")
The “Zune” F# module to support these basic queries is:
open System
open System.Text
open MicrosoftZuneLibrary
module Zune =
/// returns ZuneLibrary that is IDisposable
let initLibrary() =
let zl = new ZuneLibrary()
let rv, dbReloaded = zl.Initialize null
if rv < 0 then
failwithf "ZuneLibrary.Initialize failed: %d" rv
let i2 = zl.Phase2Initialization()
let hr, rv2 = zl.Phase2Initialization()
if rv2 < 0 then
failwithf "ZuneLibrary.Phase2Initialization failed: %d" rv2
zl.CleanupTransientMedia()
zl
/// returns ZuneQueryList that is IDisposable
let queryDatabase (zuneLibrary:ZuneLibrary) queryType sortOrder (sortField:SchemaMap) =
zuneLibrary.QueryDatabase(queryType, 0, sortOrder, uint32 sortField, null)
let getFieldValue (list:ZuneQueryList) (i:int) typ (field:SchemaMap) =
list.GetFieldValue(uint32 i, typ, uint32 field)
let getString list i field =
getFieldValue list i typeof<string> field :?> string
/// concatenates all string fields that have values
// very useful to see what is available
let getAllStrings list i =
let sb = StringBuilder()
Enum.GetValues typeof<SchemaMap> :?> SchemaMap[]
|> Seq.map (fun field -> field, getString list i field)
|> Seq.filter (fun (field, value) -> not (String.IsNullOrEmpty value))
|> Seq.map (fun (field, value) -> sprintf "%A: %s" field value)
|> Seq.iter (fun s -> sb.AppendLine s |> ignore)
sb.ToString()
let queryAllAlbums zuneLibrary =
let list =
queryDatabase zuneLibrary
EQueryType.eQueryTypeAllAlbums
EQuerySortType.eQuerySortOrderAscending
SchemaMap.kiIndex_WMAlbumTitle
list.Count,
seq {
use l = list // dispose when done with the sequence
for i in 0 .. l.Count-1 do
//yield getAllStrings l i // useful to figure out what field(s) to return
yield getString l i SchemaMap.kiIndex_WMAlbumTitle
}
let queryAllAlbumArtists zuneLibrary =
let list =
queryDatabase zuneLibrary
EQueryType.eQueryTypeAllAlbumArtists
EQuerySortType.eQuerySortOrderAscending
SchemaMap.kiIndex_DisplayArtist
list.Count,
seq {
use l = list // dispose when done with the sequence
for i in 0 .. l.Count-1 do
yield getString l i SchemaMap.kiIndex_DisplayArtist
}
I’m running Windows 7 64-bit. My Visual Studio project is set to build for x64 using .NET 3.5 Client Profile project and an output path of “C:\Program Files\Zune”. Does anyone know of a good way to have the CLR find native libraries in different folder? ZuneDBApi.dll is the assembly that exposes the MicrosoftZuneLibrary namespace I’m using.
There are other managed libraries that Zune Software uses, but I’m not sure what is usable.
C:\Program Files\Zune\UIX.dll
C:\Program Files\Zune\UIX.renderapi.dll
C:\Program Files\Zune\UIXcontrols.dll
C:\Program Files\Zune\ZuneDBApi.dll
C:\Program Files\Zune\ZuneShell.dll
I was able to determine which files were managed by building upon the code from my 2010-07-28 blog post using Mono.Cecil.
let enumerateFiles path searchPattern =
Directory.EnumerateFiles(path, searchPattern, SearchOption.TopDirectoryOnly)
let isModule (path:string) =
try
readModule path |> ignore
true
with
:? BadImageFormatException -> false
| _ -> reraise()
let printModules() =
let dir = @"C:\Program Files\Zune"
enumerateFiles dir "*.dll"
|> Seq.append (enumerateFiles dir "*.exe")
|> Seq.filter isModule
|> Seq.iter (printfn "%s")