You may remember from my earlier post on Audiobox.fm’s uselessness that I was looking for something with which to replace it. Well, I didn’t have a lot of luck finding a similar service which I could stand to use, and I have a long-standing partiality for foobar2000, which doesn’t interoperate with any of the “cloud sharing” services anyway.
(Sennheisers and foobar2000 and USB DACs? Good God, I am an audiophile!)
Anyway, this being the case, I decided it was time to start hacking at the problem for myself. I knew that foobar2000 can play remotely hosted files via HTTP, and that M3U playlists can contain URLs to such files, as well as paths to local files. Why not expose
~/music via HTTP, with an M3U generator to produce playlists of a directory’s worth of files at a time? Most all my music is organized in a directory per album, so that should cover ca. 90% of my needs, with only a little manularity required to handle the rest. How hard could it be?
Turns out it’s actually not all that hard.
I chose lighttpd for my HTTP daemon, because my home box is a Windows machine with Cygwin, and while Apache can be made to work under Cygwin, I saw no need for anything so heavyweight. (I have a couple of Linux VMs on that box, one for Emacs and one for general-purpose hackery, but I try to avoid using them for stuff like this when I can, because it’s just another layer of indirection — and because they have passphrase-less SSH keys granting access to my account on the host machine, something I’m really not inclined to risk exposing to the web.)
If you’ve used lighttpd, you know how easy it is to get up and going with it, and I found it likewise. Once I had it running, the next trick was to write the M3U generator, which took about eighty lines of Perl, and worked nicely with a URL rewrite in lighttpd.conf, such that I can visit e.g.
/Miscellaneous/Sousa Marches/ to browse the directory, or
/Miscellaneous/Sousa Marches.m3u to download a freshly autogenerated M3U playlist of all the FLAC, Ogg, and/or MP3 files in that directory. Having a working knowledge of RFC 2616 makes it really easy to write basic CGI scripts, as for example one which presents a file of a given format for download; just a
Content-Type: audio/x-mpegurl header and a
Content-Disposition: attachment; filename=$DIR.m3u header and spit out the M3U text, and you’re done and dusted.
But I didn’t want to have to hand-hack URLs in the browser, and the stock lighttpd directory-index generator isn’t configurable enough for me to have it present a “Download .m3u” link when browsing a directory containing files eligible for appearance in an M3U. So I wrote my own one of those, too, which hews closely to the format which the lighttpd stock generator uses. Mine’s a little niftier, though; it uses
/bin/file to find out file types, which means they’re a lot more detailed than you get from stock lighttpd, and it generates a couple of <abbr> tags per row, where they do the most good. Its output looks roughly like this, which I grabbed out of the listing for a recording of the Brandenburg Concerti which I purchased years back from eClassical — so far back, in fact, that they didn’t yet offer FLAC. The styling isn’t the same, but the content is, and here’s how it looks:
(No, those aren’t real links to the audio files, and you don’t have the right credentials anyway. Nice try.)
So that’s pleasant and workable, and I can grab a playlist of any directory I like and point foobar2000 at it. So far, so good.
Then I started running into problems — three major ones, in fact, two of which I was able to resolve, the third of which still tasks me.
The first problem revolved around HTTP authentication. I don’t want half the Internet gorging itself on my music collection, so credentials are needed to access the site. But foobar2000 has no mechanism for presenting such credentials. I solved this one by means of a cheap trick I’m not going to tell you about; suffice to say that the trick I used works, and doesn’t open quite as gaping a hole as you probably think it does.
The second problem revolved around getting foobar2000 to recognize and correctly interpret UTF-8 strings in playlist files, which matters a lot because most of my music is very well tagged, and UTF-8 is the only sensible string encoding to use for such matters. (Or any others, actually. Sure, I could’ve gotten away with sticking to Latin-1 for most of it, but why?) If you’ve been following the back numbers of this aperiodic little periodical of mine, then you know how I solved that one. (If not, suffice to say, it involved some tearing of hair.)
And then there’s the third problem, the one I still have, which is that foobar2000 is incredibly picky about the TLS certificates it will honor, and has no mechanism to allow its user to override or ignore a failed certificate check. Try to fob it off with a self-signed cert, like any reasonable person would first try to do, and it stubbornly spits up “Unable to open item for playback (Security error)” and goes and sits in the corner and sulks.
“Well,” says I, “I know how to solve this one, don’t I? Foobar2000 is a Windows-native application, which means that, if it’s doing certificate checks and insisting on a trusted root CA cert, it’s using the Windows certificate store as its source for said CA certs, right? Same as Internet Explorer does. So all I should have to do is to generate my own CA, root cert and all, then generate a CSR and sign it with my CA; if I then add my CA’s root cert to the Windows trusted root CA store, and tell lighttpd to present the site cert I signed with it, then I should be all set! Foobar2000 will see a chain of trust, and it’ll no longer feel any need to complain.”
Makes sense, right? I thought so, too. So I did all this, and it worked out fine, and once I’d added the CA root cert to the certificate store, Internet Explorer no longer pitched a fit about “untrusted site” when I pointed it at my music server. Importing the root CA into Firefox quelled its complaints about the site cert, too, and between the two of them both behaving as expected, I figured I must’ve got it right, and that foobar2000 would be just as satisfied.
If only. No, foobar2000 still pitches a fit when I try to play music via HTTPS, and I can’t understand why, because it doesn’t seem to be keeping its own certificate store anywhere, and everything it can see in the Windows cert store is telling it that my self-signed cert is every bit as hunky-dory as one purchased straight from Verisign.
Unfortunately, foobar2000 is also closed-source (its one true flaw), which means that, short of installing Visual Studio and its associated debugger, attaching the latter to the foobar2000 process, and trying to pick my way through what a stripped binary is doing on a platform where I’ve never compiled so much as a native “Hello World”, I’m pretty much SOL unless and until the developer either gives foobar2000 a prompt to override the cert check, or extends the foobar2000 SDK so I can build a component myself which does the same. (The feature’s been requested before, but there’s no telling when, or if, it’ll ever be added.) In the meantime, I’m considering either wrapping the connection in TLS via an stunnel instance at either end, so that foobar2000 doesn’t have to deal with that side of things at all, or running an actual VPN endpoint on my home box, which seems like taking more effort to set up but less to actually use.
All that’s for later, though; right now, I’m actually pretty well satisfied with how this is working out, and it only took me a few hours, silly problems included, to hack the whole thing together. Eventually, I’ll clean up my little Perl scripts and put them up on my Github account, and I’ll link them here when I do. In the meantime, Nonexistent Reader, here’s hoping something in this post has been of use to you.