tags:

views:

863

answers:

4

I'm working on a way to serve up MP3 files through PHP and after some help form the SO massive, I got it working here

However, that example doesn't appear to work when I use it as the source in an audio tag like this

<html>
    <head>
     <title>Audio Tag Experiment</title>
    </head>
    <body>

    <audio id='audio-element' src="music/mp3.php" autoplay controls>
    Your browser does not support the audio element.
    </audio>

    </body>
</html>

and here's the PHP

<?php

$track = "lilly.mp3";

if(file_exists($track))
{
header("Content-Transfer-Encoding: binary"); 
header("Content-Type: audio/mpeg, audio/x-mpeg, audio/x-mpeg-3, audio/mpeg3");
header('Content-length: ' . filesize($track));
header('Content-Disposition: filename="lilly.mp3"');
header('X-Pad: avoid browser bug');
Header('Cache-Control: no-cache');

readfile($track);
}else{
    echo "no file";
}

So I'm thinking (and this may be a really bad idea, you tell me) that I might be able to set up Apache to serve a PHP file when someone requests an .MP3.

So I've three questions

  1. Will this work
  2. Good Idea / Bad Idea?
  3. What would I need to do? Would putting "AddType application/x-httpd-php .mp3" int he httpd conf do it?
+1  A: 

You could simply make it so that you have a mod_rewrite rule to run every request for music/*.mp3 through your mp3.php file.

For example, something like this

RewriteEngine on
RewriteRule ^/music/(.*\.mp3)    /music/mp3.php?file=$1 [L]

mp3.php can then pick up the requested file from $_GET['file'], but if you go with this approach I recommend you sanity check the filename, to ensure it only references a file in the desired directory.

//ensure filename just uses alphanumerics and underscore
if (preg_match('/^[a-z0-9_]+\.mp3$/i', $_GET['file']))
{
    //check file exists and serve it

    $track=$_SERVER['DOCUMENT_ROOT'].'/music/'.$_GET['file'];
    if(file_exists($track))
    {
        header("Content-Type: audio/mpeg");
        header('Content-length: ' . filesize($track));
        //insert any other required headers...

        //send data
        readfile($track);
    }
    else
    {
        //filename OK, but just not here
        header("HTTP/1.0 404 Not Found");
    }

}
else
{
    //bad request
    header("HTTP/1.0 400 Forbidden");
}
Paul Dixon
That works too, but the other answer was... purer I guess. Although that did remind me to use .htaccess to prevent people accessing the files directly. Cheers.
gargantaun
This solution lets you have .mp3 URLs, but allows you to intercept those requests with some PHP. It's not quite what you wanted, but I started it so I finished!
Paul Dixon
Good effort. Someone somewhere will find that useful.
gargantaun
+3  A: 

There are some errors in your code:

  • A resource can only have one single Content-Type value. So you have to decide what media type you want to use. I suggest audio/mpeg.
  • You forgot to specify the disposition in Content-Disposition. If you just want give a filename and don’t want to change the disposition, use the default value inline.

The rest looks fine. But I would also send the 404 status code if the file cannot be found.

$track = "lilly.mp3";

if (file_exists($track)) {
    header("Content-Type: audio/mpeg");
    header('Content-Length: ' . filesize($track));
    header('Content-Disposition: inline; filename="lilly.mp3"');
    header('X-Pad: avoid browser bug');
    header('Cache-Control: no-cache');
    readfile($track);
    exit;
} else {
    header($_SERVER['SERVER_PROTOCOL'].' 404 Not Found', true, 404);
    echo "no file";
}
Gumbo
Genius! Inline did the trick and I've tidied up the Content-Type and I'm now sending a 404.
gargantaun
+1  A: 

Use the header x-sendfile instead of readfile for better performanse.

http://john.guen.in/past/2007/4/17/send%5Ffiles%5Ffaster%5Fwith%5Fxsendfile/

wojtekk
A: 

This one works for me (in .htaccess):

<FilesMatch "mp3$">
    SetHandler application/x-httpd-php5
</FilesMatch>
djn