Play MP3 on Website!

2005-08-03: To play songs in a random order, we need to create a randomized playlist. Before you continue, I strongly recommend that you read How do I play a list of several MP3 songs on a web page? to understand the basics.

Now that you've read about playlists, you know that we need to create a .m3u file that lists the URLs of the songs we want to play. But how can we put those songs in a random order for every user? The answer: we'll use PHP. Using PHP code, we can easily open up a .m3u file, shuffle the songs with PHP's built-in shuffle function, and deliver the result to the web browser as a playlist. We'll use a very cool feature of PHP called PATH_INFO to avoid creating any temporary playlist files. See the technical notes at the end of this entry for details on how it all works.

PHP is a great choice for this job. But users who are required to use Perl/CGI or ASP programming for web development can still use the ideas presented here in their own code. And the PATH_INFO trick demonstrated here works just as well for Perl CGI programs. But if you are using a free web hosting service that doesn't allow any kind of server-side programming... well, it's time to start paying for hosting that includes PHP support. Hosting services that include PHP can be really, really cheap these days. Less than $10 a month in fact. So skip a pizza once in a while and get a real web host. When you pay for your PHP hosting, the host doesn't stuff your page with annoying ads or, even worse, "spyware" programs that will make your visitors very angry.
Follow these steps to play MP3 files in a random order on your web page:

1. Create a playlist file called playlist.m3u containing all of the songs you want, in any order. See How do I play a list of several MP3 songs on a web page? for complete instructions on how to make a playlist file.

2. Upload playlist.m3u to your web site. When you do, make a note of the file system path to playlist.m3u on the server. If your FTP program shows that the folder is /home/sites/yourname/www.example.com/examples/, then the path to playlist.m3u is /home/sites/yourname/www.example.com/examples/playlist.m3u. Note: this is NOT a URL. It is the place on the server's hard drive where the file lives. If you don't understand this, reread it until you do!

3. Create the file randomsongs.php, containing the following. There must be ABSOLUTELY NO BLANK LINES OR WHITE SPACES AT THE BEGINNING! Otherwise it will be too late for the script to output a playlist instead of an HTML page when that is appropriate.

$playlist = "/home/sites/myname/www.example.com/examples/playlist.m3u";
if ($_SERVER['PATH_INFO'] == "/playlist.m3u") {
# This a request for the actual playlist.
playlist();
} else {
# Fall through to end of script and display
# the player HTML.
}
function playlist() {
header("Content-type: audio/mpeg");

# Needed for PHP versions OLDER than 4.2.0 only.
# If your host still has PHP older than 4.2.0, shame on them.
# Find a better web host.
srand(make_seed());

# Fetch our list of songs from a file.
$songs = file($playlist);
shuffle($songs);
# Now output the URLs in random order.
foreach ($songs as $song) {
# Remove newline and any other leading and trailing
# whitespace from URL of song.
$song = trim($song);
echo "$song\n";
}
# Now exit before any HTML is produced.
exit(0);
}
# Needed only for very old versions of PHP,
# see srand call earlier.
function make_seed()
{
list($usec, $sec) = explode(' ', microtime());
return (float) $sec + ((float) $usec * 100000);
}
?>

MP3s Playing in Random Order


hidden="true"
autostart="true"
type="audio/mpeg"
loop="true">

4. At the top of randomsongs.php change the setting of the $playlist variable to match the file system path of your playlist file... which you wrote down in step two.

5. randomsongs.php assumes you will be placing your PHP page in a folder on your web site called /examples. If that's not right, change the src attribute of the embed element. DO NOT remove /playlist.m3u from the end of the URL. If you want to know why it's there, read the technical notes at the end of this entry.

5. Upload randomsongs.php to your web server.

6. Upload your .mp3 files if you have not already done so.

7. Access randomsongs.php with your web browser. In our example, the URL would be http://www.example.com/examples/randomsongs.php, but of course your URL will be different. Your songs will begin to play in a random order.

Insert the embed element anywhere inside the body element of your page. If you choose to make the player visible, place the embed element at an appropriate location within the page.

Congratulations, you're the proud owner of an HTML Shuffle. Much cheaper than the iPod Shuffle! (Just kidding, Apple.)

If your songs don't play:

1. Make sure you have successfully completed How do I play a list of several MP3 songs on a web page? first. Make sure you are successful with an ordinary playlist file before you move on to using randomsongs.php.

2. If you are using a very old version of PHP, or PHP compiled for CGI use instead of Apache module use, it's possible that $_SERVER['PATH_INFO'] doesn't work properly in your environment. With a little ingenuity, the script can be modified to create temporary .m3u files instead of outputting playlists directly to the browser.

Technical Notes

You could write a PHP page that creates a new .m3u file on the server for every visitor. Then your PHP code would output the HTML for a player that uses the src attribute of the element to point to that newly created file. But then you would have piles of temporary .m3u files lying around. And you would need more code to clean them up every so often. Yuck.

Fortunately, there's a simpler way.

PATH_INFO to the Rescue

When a user accesses, let's say, http://www.example.com/examples/mypage.php, the PHP code in mypage.php runs. That's not surprising to anyone.

But this might surprise you: when a user accesses http://www.example.com/examples/mypage.php/extrastuff, the user does not get an error. Instead, mypage.php still runs... and the "extra" part of the URL, /extrastuff, shows up in the PHP variable $_SERVER['PATH_INFO'].

How does this help us? We can use the src attribute of the embed element for our player to point right back to the PHP script... with /playlist.m3u tacked on to the end.

Before you blame our use of the MIME type audio/mpeg instead of audio/mpegurl for this pickiness about file extensions, you'd better read How do I play streaming audio on my web page? very carefully. In a nutshell, audio/mpegurl is the "right" way, but the fact is it doesn't work for lots of users.
That helps us in two ways:

First, even though they shouldn't do this and mime types should absolutely always tell browsers what to do, the fact is that many web browser/MP3 player combinations just won't accept a playlist unless the file appears to have a .m3u file extension. That means we need that /playlist.m3u at the end of the URL to make, for instance, Firefox for Windows happy.

Second, we can make this work for us by recognizing the /playlist.m3u part of the URL and generating the randomized playlist directly from PHP, writing it straight to the browser when the browser actually asks for it. No temporary files, no muss, and no fuss.

Remote Scripting with PHP

Remote Scripting with PHP

Remote scripting lets you pull data from a database and display it without loading a new Web page

When you sign for a Microsoft Passport account, at the registration page you are prompted to select your country and state, which are shown in a select box. When you select a country, the entire page first reloads and then shows you the corresponding states. This happens because when a country is selected, the form (containing the select boxes) is posted to a server-side script. The script connects to the database, fires a query to retrieve the states for the selected country and populates the 'state select box' with the state names. For the user, the reloading of the page is not a pleasant experience.

For a better user experience, you can use remote scripting. Using JSRS (JavaScript Remote Scripting), a JavaScript on a Web page can call a server-side script written in PHP, Perl, ASP, ASP .Net or JSP. So, you can set up the onChange event of a select box (the one for countries) to call a JavaScript function, which in turn communicates with a server-side script using JSRS. The server-side script will return the data (name of states) to the JavaScript, which will in turn populate the page with the data, all without reloading the page. To get a taste, we will now code the same countries-states example. For the server-side script, we will use PHP.

Setup
We assume that you have set up PHP and MySQL database on your machine. Next, create a database named rs in MySQL as:

mysqladmin –u root –p create rs

In the rs database, create two tables for the countries and states using the following SQL.

create table countries (id int auto_increment not null, name varchar(50), primary key(id));
create table states (id int auto_increment not null, name varchar(50), country_id int, primary key(id));

Here, the country_id is the foreign key. Populate both the tables with some values.
On our this month's CD, we have given out the JSRS library. Extract the file named jsrs23.zip present in the directory system/cdrom/src_code on the CD. Copy the files named jsrsClient.js and jsrsServer.php.inc to a directory on your Web server. Here, jsrsClient.js is the client side or JavaScript library and jsrsServer.php.inc is the server-side (PHP in our case) library.

The RS code
Next, write a PHP file (say, rstest.php), containing the two selected boxes for country and state. Name the select boxes as country and states, respectively. Populate the country select box with the country names and IDs (use them as the option's value) from the database. Include the client-side library by adding the following line between the tag pair.

Set the onChange event handler for the country select box to getStates( ). Within the getStates( ) function we will call the server-side script using a function (namely jsrsExecute) in the JSRS client-side library. The getStates( ) function (which should be present within the tag pair) looks as follows.

function getStates()
{
country=document.forms[0].country.options[document.forms[0].country.selectedIndex].value;
jsrsExecute( "get_states.php", listStates, "getStates" , country );
}

The parameters to the jsrsExecute function are as follows.
l Name of the PHP script
l Name of the JavaScript function to call when the server-side PHP script has returned results (the name of the states)
l Name of the PHP function to call within the PHP script, which is specified as the first parameter
l Parameters to pass to the PHP function specified as the third parameter

The get_states.php file will look as follows.
include("jsrsServer.php.inc");
jsrsDispatch( "getStates" );
function getStates($country)
{
$return_value="";
$dblink=mysql_connect("localhost","root","pcq");
mysql_select_db("rs",$dblink);
$query="select id,name from states where country_id=$country";
$query_result=mysql_query($query,$dblink);
while($row=mysql_fetch_array($query_result,MYSQL_ASSOC))
$return_value.=$row["name"]."~".$row["id"]."|";
return $return_value;
}
?>

The JSRS specific code has been marked in bold. The first line includes the server-side PHP library for JSRS and with the second line you declare the method to expose, to be called via JSRS. The syntax of jsrsDispatch( ) function is as follows.

jsrsDispatch( )

Note that getStates( ) PHP function returns the results in a form where each row is separated by a Pipe (|) and the states' IDs and names within each row are separated by a tilde (~).

What is left is coding the listStates function in rstest.php. The listStates function (again, preferably located between ) looks as follows.

function listStates(states)
{
jsrsExecute ("foo.php", callBack, "blankFunc");
document.forms[0].states.options.length=0;
options = states.split("|");
for( var i = 0; i < namevalue="options[i].split(">);
document.forms[0].states.options[i] = new Option(nameValue[0], nameValue[1]); }}

Note that in the first line we are calling the jsrsExecute( ) function again. This is just an ugly fix without which the browser will keep showing a loading status even after the states list has been populated. You should define a function named callBack with empty body as:

function callBack() {}

Besides PHP, JSRS supports numerous other server-side scripting languages, which include ASP, ASP .Net, JSP, Perl and Python. If you look at the extracted archive of JSRS, you will find server-side library files for ASP, Perl and Python. JSRS, however, supports only JavaScript (as the name indicates) at the client side. For more information on JSRS, go to www.ashleyit.com/rs/.

COUNTER