Running the App

And that’s it. To use this app, you just need to load the MP3 database with the load-database function from Chapter 27, publish the CSS style sheet, set *song-source-type* to playlist so find-song-source uses playlists instead of the singleton song source defined in the previous chapter, and start AllegroServe. The following function takes care of all these steps for you, after you fill in appropriate values for the two parameters *mp3-dir*, which is the root directory of your MP3 collection, and *mp3-css*, the filename of the CSS style sheet:

  1. (defparameter *mp3-dir* ...)
  2. (defparameter *mp3-css* ...)
  3. (defun start-mp3-browser ()
  4. (load-database *mp3-dir* *mp3s*)
  5. (publish-file :path "/mp3-browser.css" :file *mp3-css* :content-type "text/css")
  6. (setf *song-source-type* 'playlist)
  7. (net.aserve::debug-on :notrap)
  8. (net.aserve:start :port 2001))

When you invoke this function, it will print dots while it loads the ID3 information from your ID3 files. Then you can point your MP3 client at this URL:

  1. http://localhost:2001/stream.mp3

and point your browser at some good starting place, such as this:

  1. http://localhost:2001/browse

which will let you start browsing by the default category, Genre. After you’ve added some songs to the playlist, you can press Play on the MP3 client, and it should start playing the first song.

Obviously, you could improve the user interface in any of a number of ways—for instance, if you have a lot of MP3s in your library, it might be useful to be able to browse artists or albums by the first letter of their names. Or maybe you could add a “Play whole album” button to the playlist page that causes the playlist to immediately put all the songs from the same album as the currently playing song at the top of the playlist. Or you could change the playlist class, so instead of playing silence when there are no songs queued up, it picks a random song from the database. But all those ideas fall in the realm of application design, which isn’t really the topic of this book. Instead, the next two chapters will drop back to the level of software infrastructure to cover how the FOO HTML generation library works.


1The intricacies of concurrent programming are beyond the scope of this book. The basic idea is that if you have multiple threads of control—as you will in this application with some threads running the shoutcast function and other threads responding to requests from the browser—then you need to make sure only one thread at a time manipulates an object in order to prevent one thread from seeing the object in an inconsistent state while another thread is working on it. In this function, for instance, if two new MP3 clients are connecting at the same time, they’d both try to add an entry to *playlists* and might interfere with each other. The with-process-lock ensures that each thread gets exclusive access to the hash table for long enough to do the work it needs to do.

2This approach also assumes that every client machine has a unique IP address. This assumption should hold as long as all the users are on the same LAN but may not hold if clients are connecting from behind a firewall that does network address translation. Deploying this application outside a LAN will require some modifications, but if you want to deploy this application to the wider Internet, you’d better know enough about networking to figure out an appropriate scheme yourself.

3Unfortunately, because of licensing issues around the MP3 format, it’s not clear that it’s legal for me to provide you with such an MP3 without paying licensing fees to Fraunhofer IIS. I got mine as part of the software that came with my Slimp3 from Slim Devices. You can grab it from their Subversion repository via the Web at http://svn.slimdevices.com/*checkout*/trunk/server/ HT``**ML/EN/html/silentpacket.mp3?rev=2**. Or buy a Squeezebox, the new, wireless version of Slimp3, and you’ll get silentpacket.mp3 as part of the software that comes with it. Or find an MP3 of John Cage’s piece 4’33”.

4The reader supports a bit of syntax, #., that causes the following s-expression to be evaluated at read time. This is occasionally useful in source code but obviously opens a big security hole when you read untrusted data. However, you can turn off this syntax by setting ***READ-EVAL*** to **NIL**, which will cause the reader to signal an error if it encounters #..

5This solution has its drawbacks—if a browse page returns a lot of results, a fair bit of data is going back and forth under the covers. Also, the database queries aren’t necessarily the most efficient. But it does keep the application stateless. An alternative approach is to squirrel away, on the server side, information about the results returned by browse and then, when a request to add songs come in, find the appropriate bit of information in order to re-create the correct set of songs. For instance, you could just save the values list instead of sending it back in the form. Or you could copy the **RANDOM-STATE** object before you generate the browse results so you can later re-create the same “random” results. But this approach causes its own problems. For instance, you’d then need to worry about when you can get rid of the squirreled-away information; you never know when the user might hit the Back button on their browser to return to an old browse page and then hit the “Add all” button. Welcome to the wonderful world of Web programming.