             path:       [@options.preroll.path,@options.preroll.key,key,"?"+query].join("/")
 
         conn = req.connection
-        
+
         console.log "firing preroll request", count
         req = http.get opts, (rres) =>
             console.log "got preroll response ", count
             if rres.statusCode == 200
                 # collect preroll and return it so length can be computed
                 pre_data = new Buffer(0)
-                
+
                 rres.on "data", (chunk) =>
                     buf = new Buffer(pre_data.length + chunk.length)
                     pre_data.copy(buf,0)

                res.writeHead 206, headers
            else
                res.writeHead 200, headers
                    
            console.log "response headers are", headers

            # if we have an id3, write that
            res.write id3 if id3
            
            # write the preroll
            res.write predata if predata


        # after a new deployment, allow a 30 minute grace period for 
        # connected listeners to finish their downloads
        max_zombie_life:    2 * 60 * 1000



    constructor: (opts={}) ->
        @options = _u.defaults opts, @DefaultOptions

        # -- make sure our audio dir is valid -- #
        if !path.existsSync(@options.audio_dir)
            console.error "Audio path is invalid!"
            process.exit()

        # -- set up cache storage -- #
        @key_cache  = {}
        @listeners  = 0
        @_counter   = 0

        # -- set up a server -- #
        console.log "config is ", @options
        @app = express()
        @app.use (req,res,next) => @podRouter(req,res,next)
        @server = @app.listen @options.port

        # -- set up a shutdown handler -- #

        process.on "SIGTERM", =>
            # stop listening 
            @server.close()

            # now start polling to see how many files we're delivering
            # when should we get pushy?
            @_shutdownMaxTime = (new Date).getTime() + @options.max_zombie_life

            console.log "Got SIGTERM. Starting graceful shutdown with #{@listeners} listeners."

            # need to start a process to quit when existing connections disconnect
            @_shutdownTimeout = setInterval =>
                # have we used up the grace period yet?
                force_shut = if (new Date).getTime() > @_shutdownMaxTime then true else false

                if @listeners == 0 || force_shut
                    # everyone's out...  go ahead and shut down
                    console.log "Shutdown complete"
                    process.exit()
                else
                    console.log "Still awaiting shutdown; #{@listeners} listeners"
            , 60 * 1000



    podRouter: (req, res, next) ->
        # if we take prefix away from req.url, does it match an audio file?
        match = ///^#{@options.prefix}(.*)///.exec req.path
        next() and return unless match?[1]

        filename = path.join(@options.audio_dir,match[1]) 
        fs.stat filename, (err,stats) =>
            # if there was a stat error or this isn't a file, move on 
            if err || !stats.isFile()
                next()
                return true

            # is this a file we already know about? if so, has it not been changed?
            if @key_cache[ filename ] && @key_cache[filename]?.mtime == stats.mtime.getTime() && @key_cache[filename].stream_key
                # we're good.  use the cached stream key for preroll, then send our file
                @streamPodcast req, res, filename, stats.size, @key_cache[filename].stream_key, @key_cache[filename].id3
            else
                # never seen this one, or it's changed since we saw it

                # -- validate it and get its audio settings -- #
                ffmpegmeta.get filename, (meta) =>
                    if meta
                        # stash our stream key and mtime
                        key = [meta.audio.codec,meta.audio.sample_rate,meta.audio.bitrate,(if meta.audio.channels == 2 then "s" else "m")].join("-")
                        mtime = stats.mtime.getTime()

                        # -- now look for ID3 tag -- #
                        # we do this by streaming the file into our parser, 
                        # and seeing which comes first: an ID3 tag or an 
                        # mp3 header.  Once we hit first audio, we assume 
                        # no tag and move on.  we cache any tag we get to 
                        # deliver at the head of the combined file
                        @checkForID3 filename, (id3) =>
                            @key_cache[ filename ] = mtime:mtime, stream_key:key, id3:id3
                            @streamPodcast req, res, filename, stats.size, key, id3
                    else
                        @res.writeHead 404, "Content-Type":"text/plain", "Connection":"close"
                        @res.end("File not found.")



    checkForID3: (filename, cb) =>
        parser = new Parser(true)

        cb = _u.once(cb) if cb

        parser.on "id3v2", (buf) =>
            # got an ID3
            cb?(buf)

        parser.on "header", (h,buf) =>
            # got a frame
            cb?()

        # we only read the first 4k
        rstream = fs.createReadStream filename, bufferSize:256*1024, start:0, end:4096
        rstream.pipe parser, end:false

        rstream.on "end", => parser.end()



    streamPodcast: (req, res, filename, size, stream_key, id3) ->
        return false if req.connection.destroyed

        @loadPreroll stream_key, req, (predata = null) =>
            console.log "url is", req.url
            console.log "request method is ", req.method
