Building a Web Site with ASP .NET 2.0 to Navigate Your Music Library

Sign in to queue


  This article focuses on a common solution for managing shared music files. It is accomplished by keeping shared music files on a single computer and have other computers' media libraries point to, and stay current with, the files on that computer.

Difficulty: Intermediate
Time Required: 1-3 hours
Cost: Free
Software: Visual Studio Express Editions
Download: Download


It's easy to assume that most people with computers have media libraries. As computers become cheaper, easier to network and easier to use, people are more likely to create home networks. Since servers aren't practical in a home setting, information is distributed amongst the computers in the network, which makes managing and finding formation difficult. This is probably most evident in music libraries.
Most media players maintain a private database containing all of the songs they're aware of. Keeping these databases up-to-date can be a chore on a single computer, and keeping databases current on several computers often seems impossible.
A common solution is to keep all music files on a single computer and have other computers' media libraries point to, and stay current with, the files on that computer. Anyone that has tried this in practice knows that it's an adequate solution, but not an ideal one. Personally, I just want to listen to music, not manage it.
My home setup consists of a Windows Media Center computer that drives my TV and has all of my music on it, my workstation, and occasionally a laptop. Keeping the workstation current with the music on the Media Center has always been a hassle, and doing so on the laptop just isn't practical. I wanted a simple, clean way to listen to music anywhere in my apartment, from any computer, without having to worry about the accuracy of the library. I chose to build a little website whose only purpose would be to get me to the music I wanted to listen to as painlessly as possible. I used Microsoft Visual Web Developer 2005 Express Edition Beta 2 to make this happen.
COM: A friend from the past
Back in the old days we had a technology called COM that let us use libraries written in any language from our code, regardless of the langauge we were using (as long as it supported COM), much like .NET does today. COM did a great job solving some problems, but introduced a few others that became more apparent over time. .NET is, among other things, a solution to many of these problems.
Years ago, Microsoft started exposing operating system and application services via COM. This let common folk write code against Microsoft Word, Windows Scripting Host, Windows Media Player, and countless others. Due to the number of languages, frameworks, etc. that support COM, it's still a major player in the Microsoft world and it's common to find COM support in Microsoft applications before .NET support. Luckily, .NET does a great job talking to COM (most of the time).
This history lesson is relevant because the site uses the Windows Media Player COM component to access its media library. Adding a reference to a COM component is just as easy as adding a reference to a .NET assembly. Click the Website menu and select Add Reference, then click the COM tab on the Add Reference dialog.

Figure 1: Adding a reference to the Windows Media Player COM component

Quick access to a subset of the WMP library's information is required, so an in-memory database consisting of Artist, Album, Database and Track classes is required. The Database class has a static (or shared in Visual Basic) constructor that populates the database with a call to the Refresh method:

Visual C#

private static void Refresh()
        WindowsMediaPlayer wmp = new WindowsMediaPlayer();
        IWMPPlaylist playlist = wmp.mediaCollection.getAll();
        Dictionary<string, Artist> artistDictionary = 
new Dictionary<string, Artist>();

        for (int i = 0; i < playlist.count; i++)
            IWMPMedia media = (IWMPMedia)playlist.get_Item(i);
            string albumArtistName = media.getItemInfo("AlbumArtist");
            string albumName = media.getItemInfo("Album");
            string trackName = media.getItemInfo("Title");
            string trackLocation = media.getItemInfo("SourceUrl");
            string trackNumberString = media.getItemInfo("OriginalIndex");

            Artist artist;
            string artistSortName = Artist.GetSortName(albumArtistName);

            if (!artistDictionary.TryGetValue(artistSortName, out artist))
                artist = new Artist(albumArtistName);
                artistDictionary.Add(artistSortName, artist);

            Album album;

            if (!artist.Albums.TryGetValue(albumName, out album))
                album = new Album(albumName, artist);
                artist.Albums.Add(albumName, album);

            Track track;

            if (!album.Tracks.TryGetValue(trackName, out track))
                int trackNumber;

                if (int.TryParse(trackNumberString, out trackNumber))
                    track = new Track(trackNumber, trackName, trackLocation);
                    track = new Track(trackName, trackLocation);

                track.Album = album;
                album.Tracks.Add(trackName, track);


Visual Basic  

Private Shared Sub Refresh()
        Dim wmp As WindowsMediaPlayer = New WindowsMediaPlayer
        Dim playlist As IWMPPlaylist = wmp.mediaCollection.getAll()
        Dim artistDictionary As Dictionary(Of String, Artist) = _
New Dictionary(Of String, Artist)

        For i As Integer = 0 To playlist.count - 1
            Dim media As IWMPMedia = playlist.Item(i)
            Dim albumArtistName As String = media.getItemInfo("AlbumArtist")
            Dim albumName As String = media.getItemInfo("Album")
            Dim trackName As String = media.getItemInfo("Title")
            Dim trackLocation As String = media.getItemInfo("SourceUrl")
            Dim trackNumberString As String = _

            Dim theArtist As Artist
            Dim artistSortName As String = _

If Not artistDictionary.TryGetValue(artistSortName, theArtist) Then
                theArtist = New Artist(albumArtistName)
                artistDictionary.Add(artistSortName, theArtist)
            End If

            Dim theAlbum As Album

            If Not theArtist.Albums.TryGetValue(albumName, theAlbum) Then
                theAlbum = New Album(albumName, theArtist)
                theArtist.Albums.Add(albumName, theAlbum)
            End If

            Dim theTrack As Track

            If Not theAlbum.Tracks.TryGetValue(trackName, theTrack) Then
                Dim trackNumber As Integer

                If Integer.TryParse(trackNumberString, trackNumber) Then
                    theTrack = New Track(trackNumber, trackName, trackLocation)
                    theTrack = New Track(trackName, trackLocation)
                End If

                theTrack.Album = theAlbum
                theAlbum.Tracks.Add(trackName, theTrack)
            End If

    End Sub

The WMP library is a flat structure of media items, so the Artist -> Albums -> Tracks hierarchy must be created manually while populating the database. Accessing the WMP library is simple; calling the getAll method off of a WindowsMediaPlayer object's mediaCollection property returns a playlist of all items in the library, represented by the IWMPPlaylist interface. Collecting the information needed from each media item (represented by the IWMPMedia interface) is just as easy, via a call to IWMPMedia's getItemInfo method. Once all of the relevant information for the media item is collected, the artist, album and track objects must be retrieved or created.
Note that I've used a generic Dictionary object to store the artists while we're iterating through the playlist. Some media collections consist of tens of thousands of items, and having to do a brute-force search through the ArtistList for every media item would further slow down an already slow operation. (Why the Dictionary object is quicker is beyond the scope of this article. Please see Scott Mitchell's examination of the Hashtable class, Dictionary's non-generic counterpart, in an article from his fantastic series on data structures.)
Designing the website
To keep things as simple as possible, we'll make three pages: Artists, Artist's Albums, and Album.

Figure 2: The Artists page

The Artists page (Figure 2) shows ten groups of artists at a time. Each time a group is clicked, that group is shown and so on until the user gets to the artist she's looking for.

Figure 3: The Albums page

The Albums page (Figure 3) shows all of the albums, including artwork, for an artist. Clicking the album name brings the user to the Album page, and clicking the play button will launch a playlist containing the tracks in the album in Windows Media Player.

Figure 4: The Album page

The Album page shows the large album artwork and tracks. The user can play the entire album or individual tracks.
Each page uses object data binding and shares a common master page. ASP.NET has a new feature called Master Pages, which allows you to create a consistent layout for the Web site and share common functionality without having to duplicate it for every page. The Master Page in this website is used to share Cascading Style Sheet (CSS) information and host the "breadcrumb bar," or SiteMapPath, as it's called in ASP.NET. I won't be covering the SiteMapPath control, but the implementation should be interesting to anyone that needs to drive one programmatically and not through a static definition. More information about master pages can be found in Fritz Onion's Master Your Site Design with Visual Inheritance and Page Templates.
The Albums and Album page also have images and play buttons that launch Windows Media Player playlist files. Album images are cached in a Web-accessible directory, and WMP playlist files are generated on the fly in an HTTP handler. Both of these topics are covered below.
Binding objects to grids
The new ObjectDataSource is the middleman between the grid and objects. It must be configured to work with a class to return data requested by the grid. We'll step through creating the data source for the Albums page.

Figure 5: The ObjectDataSource wizard, first page

The "Binder" class is selected as the business object. The objects used to represent the artists, et al, are simple and primarily represent data, and therefore don't have much in the way of persistence methods. The Binder class provides the functionality that may otherwise be handled by the objects themselves.

Figure 6: The ObjectDataSource wizard, second page

The next page is where we choose which method we would like to use to get the data, so we pick GetAlbums. Update, Insert and Delete methods can also be specified, but are not required.

Figure 7: The ObjectDataSource wizard, third page

The final page specifies where the parameter values will come from. The value can come from one of the following: Cookie, Control, Form, Profile, QueryString and Session.
Once the data source is configured, setting it as the DataSource of the grid is all that's required to make the magic happen.
Generating custom grid values
All of the grids use template columns and custom code to generate their hyperlinks. For example, the artists page (figure 2) consists of a single GridView that's bound to a generic List of RangeListItems. RangeListItem is a custom class that represents the first and last artist IDs in the range, as well as the text that is displayed in the grid. The hyperlinks are created by using a template field in the grid and calling a method in the page's server-side script section.

<asp:TemplateField ItemStyle-HorizontalAlign=Center>
        <asp:HyperLink ID="HyperLink1" runat="server" NavigateURL='<%# CreateNavigateURL(GetDataItem() %>'
            Text='<%# Eval("Text") %>'></asp:HyperLink>
The NavigateUrl attribute is populated by a call to the CreateNavigateUrl method:

Visual C#

private string CreateNavigateUrl(object o)
        RangeListItem listItem = (RangeListItem)o;

        if (listItem.StartIndex == listItem.EndIndex)
            return "albums.aspx?artist=" + listItem.StartIndex;
return string.Format("default.aspx?start={0}&end={1}", listItem.StartIndex, listItem.EndIndex);

Visual Basic

Private Function CreateNavigateUrl(ByVal o As Object) As String
        Dim listItem As RangeListItem = CType(o, RangeListItem)
        If (listItem.StartIndex = listItem.EndIndex) Then
            Return "albums.aspx?artist=" & listItem.StartIndex
            Return String.Format("default.aspx?start={0}&end={1}", listItem.StartIndex, listItem.EndIndex)
        End If
End Function
More information about templates in ASP.NET 2.0 can be found in Dino Esposito's Move Over DataGrid, There's a New Grid in Town!.
Displaying Album Art

Windows Media Player downloads album art, both large and small versions, when possible. These image files are stored in the same folders as the album tracks, but are hidden by default. These images must be copied to a child folder of the Web site so the browser can access them. This is done by first finding the correct image, then copying that file to a subfolder of the Web site and giving it a unique name to so it can be more easily retrieved the next time it's required. If album art isn't found, a small transparent image is used instead.

Visual C#

public string GetAlbumArtUrl(AlbumArtSize size)
        string albumArtFileName = GetCustomAlbumArtFileName(size);
string albumArtFullFilename = Path.Combine(_albumArtDirectory, 

        bool fileExists = File.Exists(albumArtFullFilename);

        if (!fileExists)
            string dir = Path.GetDirectoryName(Tracks[0].Location);
            string filename;

            if (Directory.Exists(dir))
                filename = GetRealAlbumArtFileName(dir, size);

                if (filename != null)
                    File.Copy(filename, albumArtFullFilename);
                    fileExists = true;

        string url;

        if (fileExists)
            url = "Images/AlbumArt/" + albumArtFileName;
            url = "Images/dot.gif";

        return url;

Visual Basic   

Public Function GetAlbumArtUrl(ByVal size As AlbumArtSize) As String
        Dim albumArtFileName As String = GetCustomAlbumArtFileName(size)
        Dim albumArtFullFilename As String = _
            Path.Combine(_albumArtDirectory, albumArtFileName)

        Dim fileExists As Boolean = File.Exists(albumArtFullFilename)

        If Not fileExists Then
            Dim dir As String = Path.GetDirectoryName(Tracks(0).Location)
            Dim filename As String

            If Directory.Exists(dir) Then
                filename = GetRealAlbumArtFileName(dir, size)

                If Not filename Is Nothing Then
                    File.Copy(filename, albumArtFullFilename)
                    File.SetAttributes(albumArtFullFilename, _
                    fileExists = True
                End If
            End If
        End If

        Dim url As String

        If fileExists Then
            url = "Images/AlbumArt/" & albumArtFileName
            url = "Images/dot.gif"
        End If

        Return url
    End Function

The GetCustomAlbumArtFileName method creates a consistent name that is used to save the copy of the image and more easily retrieve it in subsequent requests:

Visual C#   

private string GetCustomAlbumArtFileName(AlbumArtSize size)
        return string.Format("{0}-{1}-{2}.jpg", 

    private string GetAlphanumericString(string s)
        StringBuilder sb = new StringBuilder();

        foreach (char c in s)
            if (char.IsLetterOrDigit(c))

        return sb.ToString();

Visual Basic  

 Private Function GetCustomAlbumArtFileName(ByVal size As AlbumArtSize) As String
        Return String.Format("{0}-{1}-{2}.jpg", _
            GetAlphanumericString(_artist.Name), _
GetAlphanumericString(Name), size.ToString)
    End Function

    Private Function GetAlphanumericString(ByVal s As String) As String
        Dim sb As StringBuilder = New StringBuilder

        For Each c As Char In s
            If Char.IsLetterOrDigit(c) Then
            End If

        Return sb.ToString()
    End Function
To make things even more interesting, album art filenames can match any of the following patterns:







GetRealAlbumArtFileName gets the filename for the album art, if it exists, by searching for the patterns above:

Visual C#   

private string GetRealAlbumArtFileName(string dir, AlbumArtSize size)
        string filename = null;
        string[] filenames = 
Directory.GetFiles(dir, "AlbumArt*" + size + ".jpg");

        if (filenames.Length > 0)
            filename = filenames[0];
        else if (size == AlbumArtSize.Large)
            FileInfo file = new FileInfo(Path.Combine(dir, "Folder.jpg"));
            if (file.Exists)
                filename = file.FullName;
        return filename;

Visual Basic

Private Function GetRealAlbumArtFileName(ByVal dir As String, ByVal size As AlbumArtSize) As String
        Dim filename As String = Nothing
        Dim filenames() As String = _
Directory.GetFiles(dir, "AlbumArt*" & size.ToString() & ".jpg")

        If (filenames.Length > 0) Then
            filename = filenames(0)
        ElseIf (size = AlbumArtSize.Large) Then
            Dim file As FileInfo = _
New FileInfo(Path.Combine(dir, "Folder.jpg"))
            If file.Exists Then
                filename = file.FullName
            End If
        End If

        Return filename
    End Function

Creating a Playlist

Playlists are created by the PlaylistCreator class, which is a lightweight web request handler that implements the IHttpHandler interface. The handler is quite simple: It writes out a header, notifying the browser that the type of content it will be returning is "video/x-ms-asf", then creates and returns an XML playlist. The content type header helps the browser determine what to do with the content, and is a very important step since we want to play the playlist, not show it in the browser.
PlaylistCreator writes directly to the response stream with an XmlTextWriter:

Visual C#   

public void ProcessRequest(HttpContext context)
        _trackIndex = QueryStringHelper.TrackIndex;
        _artistIndex = QueryStringHelper.ArtistIndex;
        _albumIndex = QueryStringHelper.AlbumIndex;

        context.Response.ContentType = "video/x-ms-asf";

        StreamWriter streamWriter = 
new StreamWriter(context.Response.OutputStream);
        _writer = new XmlTextWriter(streamWriter);
        _writer.WriteProcessingInstruction("wpl", "version=\"1.0\"");



        if (_trackIndex.HasValue)
        else if (_albumIndex.HasValue)



Visual Basic   

Public Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
        _trackIndex = QueryStringHelper.TrackIndex
        _artistIndex = QueryStringHelper.ArtistIndex
        _albumIndex = QueryStringHelper.AlbumIndex

        context.Response.ContentType = "video/x-ms-asf"

        Dim streamWriter As StreamWriter = _
New StreamWriter(context.Response.OutputStream)
        _writer = New XmlTextWriter(streamWriter)
        _writer.WriteProcessingInstruction("wpl", "version=\""1.0\""")




        If _trackIndex.HasValue Then
        ElseIf _albumIndex.HasValue Then
        End If


    End Sub

Registering the Playlist Creator
ASP.NET knows how to handle requests to ASPX pages: it finds the page with the same name and executes it. HTTP handlers are handled a bit differently. An entry must be made in the web.config file associating a file name or pattern with the type of the object to use to handle the request. The XML fragment below is from the configuration/system.web section of the web.config file. It's instructing ASP.NET to direct any request with an extention of "wpl" to an object of type "PlaylistCreator". 

          <add path="*.wpl" type="PlaylistCreator" verb="*" validate="false" />


The Discussion

  • User profile image

    This is useful code. I will and test and see if it works with  all the formats.

  • User profile image

    What is the user wants to stop the currentMedia? I have tried to use a 'Stop' button

    Sub button_OnClick()


    End Sub

    But on Page.PostBack I cannot access the Media Player that I was controlling from the previous page. All I can access is the new instance of the media player created after my postback oocurred.

    I am not handling anything via client side.

  • User profile image

    Got it, Save it to a session variable

    Session.Add("mediaPlayer", wmpPlayer)

  • User profile image

    Can I hlep you change skin using window media sdk  ( language C# or VB)

  • User profile image

    @Emmanuel:  Use a BLOB datatype.

  • User profile image

    How do I access the "Add to Library" functionality from C# or c++?

    Help will be highly appreciated.

  • User profile image

    I can only make this work using Cassini.  If i use IIS, it doesn't detect any of my music files.  What am i missing?

  • User profile image

    i need to develop an web application in such a way that the audio files will be stored in a server and i need to play those audio files from an page i.e i should be able to share the music to all user.

    more over you havent given how to create the database.

  • User profile image

    @varadharaj:  I believe we've covered databases in a different area.

    From looking at this, it should play.

    With that said, I think I'm going to create a new version of this article since I tried to run it and couldn't get it to run very easily.

    - clint

  • User profile image

    I have  just begun to use  Microsoft Visual Web Developer 2005 Express Edition version 2.050 727-7600. For development I am usinng  the ASP.NET DEVELOPMENT SERVER that is installed with the developer.

    I have tried to run your code by downloading it from this site and opening in the IDE. I performed a build, resulting in 3 benign looking warnings, that should not really impede execution. Upon execution I received the following server error:

    Server Error in '/VisualBasic' Application.


    Specified cast is not valid.

    Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

    Exception Details: System.InvalidCastException: Specified cast is not valid.

    Source Error:

    Line 25: Private Shared Sub Refresh()

    Line 26: Dim wmp As WindowsMediaPlayer = New WindowsMediaPlayer

    Line 27: Dim playlist As IWMPPlaylist = wmp.mediaCollection.getAll()

    Line 28: Dim artistDictionary As Dictionary(Of String, Artist) = New Dictionary(Of String, Artist)

    Line 29:

    Can  you  comment?

    Can COM controls be conveniently  used in visual web express edition? Some users say no!!

    Thank You!!

  • User profile image

    I'll work on reencoding this project in a VS Friendlier solution.  This is one of our projects from Coding4Fun version 1 so it could very well be much older than the date listed above.

  • User profile image
    College Fun Facts » Coding4Fun : Building a Web Site with ASP .NET 2.0 to Navigate Your …
  • User profile image

    The simplest way to have a media player in your web applicaion is- Download Microsot Silverlight and 3.5 extensions

  • User profile image

    can you please upload the zip file so i can study, thank you.

  • User profile image

    I'm sorry but it is very hard to follow your code and your explanation above does not help. THis is supposed to be coding for fun, not code that will frustrate the hell out of you. What is the WMPLib.dll? What is the starting point of the app? Where are you getting the artist that come up when you run the app? Why in your instructions you say to change a line of code that when you change it HAS NO AFFECT ON THE APP. Why when I click on the config for the ObjectDatasSource it gives me an error saying "The type Binder could not be loaded. If the type is located in the App_Code folder, please check that it compiles." What does that mean?

    I'm sorry but this is very frustrating. Try to explain your code better.

  • User profile image

    How do you point it to your music file. I the directions it says to:

    Replace the following in the web.config with a mapping that's valid on your computer:

     <add key="Mappings" value="g:\music|\\mediacenter\music"/>

    But if I get rid of this line completely and leave it blank the app still gives me that default list of artists. What's going on?????????

  • User profile image

    Have you fixed this yet so it works over the internet?

  • User profile image

    Dummy question: Mr.Jeff Key,

    Your application is loading every single artist and it's all albums,  tracks on default.aspx into the memory.


    Is not that too much to load? if I had 5,000 University students using my music server per minute?

    If it is how would you solve it?

    May be the design patterns you are practicing just off the shelf and not meant for large scale software?



  • User profile image

    i think it's a great article and does say level: intermediate!

    definetly was fun for me!

    If you don't get it, don't worry ...others do!


  • User profile image

    @Avaz, You can cache the page.  I'd cache it for an hour or longer depending on how often the music collection changes or how often you want it to update.

    No matter what, if you have 5000 people accessing your computer, your hard drives may have a hard time keeping up.  Figure 5MB a song, song is 3 minutes long (so 1.66MB/min), that comes out to be 8.3 gigs worth of data transferred every minute.

    I don't think this was designed for the scenario you're describing.

  • User profile image
    Mosh Jahan

    For IIS you may need to configure the Windows Media Player Network Sharing service to run using LocalSystem account.  The IUSR account's media library might also be empty.  I wonder if there is a way to work directly with the WMP database?

  • User profile image
    ganesh todkar

    Husshh..!! Finally my search ends here..!!

    This article helped me a lot..!!

    Thanks for it.!!

  • User profile image
    Bill Roberts

    Sorry I've slammed so many comments but, have been hacking all weekend and i'm generally one to keep on even when i think i've hit a wall.

    so, comes down to... when IIS is involved , seems i have only access to crud like:

    C:\Users\Bill\AppData\Local\Microsoft\Media Player\Sync Playlists\*.*

    but when i use the built in VS server, seems things work as expected.

    I've gone so far are to run my AppPool using my id/pw along with configuring my site also to use my id/pw.

    Odd thing is that the PlayLists exposed don't even show up in the PlayLists tab for Window Media Player.

    Anyway, not sure where to go from here Sad

  • User profile image
    Bill Roberts

    Thanks for this info !

    I've compiled a project in VS2008 against 3.5 on a Windows 7 x64 rig and when run using the VISUAL STUDIO DEVELOPMENT SERVER, it runs without a hitch.

    But when I run in IIS 7 (same machine), I can load the Default.Aspx, but seems no code is actually kicked off... Ideas?

    kinda rusty here ... but, I believe I've have created an Web Application rather then a Web site...

    Thanks again for a wonderful thread Smiley

  • User profile image

    @Bill Roberts RE: IIS7 issue, do you have IIS with dynamic content enabled?  for super easy configuration, I tend to use Web PI. to get IIS setup quick.

Add Your 2 Cents