Streaming music over HTTP is very easy, and supported by most media players. The media player you decide to use will determine which file types you will be able to stream. On the server all you need is an HTTP server (like Apache), and the clients will need a playlist linking to all the media files available.

The simplest way to make a playlist is to generate a text file with the web address for each media file, and to save this file with a .m3u extension. The M3U universal playlist does allow for some more options, but simply listing the locations to the files is enough. To generate my playlists I use the Bourne script below, which runs nightly on my server through a cron job, so my playlist stays up to date without me having to think about it. The script lists all the media files in the "LOCAL_SOURCE" directory, replacing the local path with the HTTP path, as well as replacing a few special characters with their URL encoding (for instance, spaces are replaced with %20). I use this script for Apache, other servers may have different requirements. In the days I used Windows, to make my playlists I would go into a DOS prompt and do something like "dir /S /B *.mp3 >> playlist.m3u" within the directory holding all my music, then run a macro in a text editor to make any necessary modifications to produce the playlist.

generate_playlist.sh

#!/bin/sh
LOCAL_SOURCE="/home/nick/files/Media/Audio/Music/"
HTTP_SOURCE="http://randombytes.net/music/"
PLAYLIST="playlist.m3u"
FILE_TYPES="mp3 wma m4a aif wav ogg flac ape"

if [ -e $PLAYLIST ]
then
  echo "Playlist already exists.  Replace with new playlist?"
  rm -i $PLAYLIST
fi

if [ ! -e $PLAYLIST ]
then
  for i in $FILE_TYPES; do
	  echo "Adding" $i "files to playlist."
    find $LOCAL_SOURCE | grep -e '\.'$i'' | sed -e 's#'$LOCAL_SOURCE'#'$HTTP_SOURCE'#g' | sed -e 's/%/%25/g' | sed -e 's/ /%20/g' | sed -e 's/"/%22/g' | sed -e 's/#/%23/g' | sed -e 's/;/%3b/g' | sed -e 's/</%3c/g' | sed -e 's/>/%3e/g' | sed -e 's/?/%3f/g' | sed -e 's/\[/%5b/g' | sed -e 's/\\/%5c/g' | sed -e 's/\]/%5d/g' | sed -e 's/\^/%5e/g' | sed -e 's/`/%60/g' | sed -e 's/{/%7b/g' | sed -e 's/|/%7c/g' | sed -e 's/}/%7d/g' | sort >> $PLAYLIST
  done
fi

A similar script below, written in Python, has the added feature of including extended M3U directives. All that is listed in the M3U directive is the filename, which I included for use with the VLC player. When no M3U directives are given, the playlist viewer in VLC has an annoying behavior of displaying the entire URL with URL encodings, which is ugly to look at. I added the M3U directives merely to fix this. I originally added this function to the Bourne script above, but for some reason it was extremely slow.

generate_playlist.py

#!/usr/bin/env python
# coding=utf-8
# Nick Masluk
# 2013-02-08

local_source = '/home/nick/files/Media/Audio/Music/'
http_source = 'http://randombytes.net/music/'
playlist = 'playlist.m3u'
file_types = ['mp3', 'wma', 'm4a', 'aif', 'wav', 'ogg', 'flac', 'ape']

################################################################################
# Import modules
################################################################################

import sys
import os.path
import commands

################################################################################
# Function definitions
################################################################################

def url_encode(string):
    url = ""
    for char in string:
        if char == ' ':
            url += '%20'
        elif char == '"':
            url += '%22'
        elif char == '#':
            url += '%23'
        elif char == '%':
            url += '%25'
        elif char == ';':
            url += '%3b'
        elif char == '<':
            url += '%3c'
        elif char == '>':
            url += '%3e'
        elif char == '?':
            url += '%3f'
        elif char == '[':
            url += '%5b'
        elif char == '\\':
            url += '%5c'
        elif char == ']':
            url += '%5d'
        elif char == '^':
            url += '%5e'
        elif char == '`':
            url += '%60'
        elif char == '{':
            url += '%7b'
        elif char == '|':
            url += '%7c'
        elif char == '}':
            url += '%7d'
        elif char == '‚':
            url += '%82'
        else:
            url += char
    return url

################################################################################
# Main code
################################################################################

# make sure paths end with a '/'
local_source = local_source + ('/' if local_source[-1] != '/' else '')
http_source = http_source + ('/' if http_source[-1] != '/' else '')

# verify path exists
if os.path.exists(local_source) == False:
    sys.exit("Invalid local_source")

# get list of all files in local_source
files = commands.getoutput('find ' + local_source + '| sort').split('\n')

# open playlist file
playlist_file = open(playlist, 'wb')
playlist_file.write('#EXTM3U\n')

for file_type in file_types:
    for file in files:
        # check if file end matches with file_type
        if file[-len(file_type) - 1:].lower() != '.' + file_type.lower():
            # skip to next file if not the correct file type
            continue

        # get filename from full path
        filename = file[1 + file.rfind('/'):]
        # write EXTINF data
        playlist_file.write('#EXTINF:-1, - ' + filename + '\n')
        # write file URL
        playlist_file.write(url_encode(http_source \
        + file[len(local_source):]) + '\n')

# close playlist file
playlist_file.close()

It is important to make sure when generating a playlist that the media files over HTTP are actually where you think they are. Special characters in the filename may translate to a URL encoding when accessed over HTTP. What these encodings are will depend on the HTTP server being used. The simplest way to check what characters are being encoded, and what the encodings are, is to enable directory listing on the server and observe how the server lists the files. A rather fail-proof method of making a playlist could be to use the directory listing page generated by the server. But once everything is working, I like to keep directory listing disabled as a security measure. This way, even if someone over the internet knows of the location of all your media files, they won't know what's there, and therefore can't download it (unless, of course, they had your playlist).

To avoid problems with special characters, I try not to use special characters in my filenames. When I notice a song in my playlist doesn't load, I'll check why my playlist generator failed, and either add a "find and replace" to my script to insert the correct URL encoding, or rename the file to get rid of the offending characters.

I should also add, if your ISP blocks port 80, it is not necessary to stream over 80. Any open port will do!

Now for the client side... Most media players can stream at least a few file formats through HTTP. However, just because a player can play a file format locally doesn't mean it will be able to play a stream of that same file type. Here is a short list of players I've used, and what I've been able to stream with them:

I'd be interested in hearing about other people's experience streaming in different media players.