Update jPlayer track/listeners on a webpage with jsonp

With version 2.4, IceCast is shipping their own xml2json template. IceCast 2.4 doesn’t yet deal with the CORS issue so if you want to parse the metadata/listener count etc. client-side, you will still need to output JSONP. IceCast 2.4.1 has fixed this.

I recently found myself working on a small web-radio project for some friends, and I stumbled upon some interesting technical “challenges” (to say so).

I wanted a HTML5 Web-Radio Player (I used the excellent jPlayer project, which supports html5’s <audio>in a wide range of browsers, but also has a fall-back to Adobe Flash for older browsers), but I also wanted the current playing track and a listener count on it. I modified a default jPlayer skin to show up the elements I needed, but I was still left with the issue of actually getting the data from the icecast server to my webpage (player).

From my searches I found some PHP scripts that connect to the stream and retrieve the data using the ICY protocol, but those were really slow based on my tests.

Keeping on searching, I found some good ideas of using Icecast’s templating system that is used to generate its admin & stats pages, which are all output as XML, to generate JSON instead.

Now, this would be all fine and I would have considered this matter fixed, except… there’s the “dreaded” Access-Control-Allow-Origin: * issue, which prevents you from loading JSON data from another domain other than the one the page is hosted, unless you send the Access-Control header, which you can’t really do using Icecast.

The other solution involved using JSONP (which is basically JSON wrapped in a function, that is loaded by the browser as a script and interpreted), and this looked perfect!

Without further explanations (because I suck at writing these things), here’s the rundown.

Tools needed:

  • An Icecast server that you have access to (ie: you can modify it’s config file and it’s web templates)
  • A page where you can use jQuery (not hard, really, it’s hard to find a project these days that doesn’t use jQuery)
  • Some ideas on how stuff works ©

First step is to create the XSL file that is used to generate the JSONP data we’re going to use.

Create a file called json.xsl with the following contents. I placed mine (on ubuntu-server) in /usr/share/icecast2/web/

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
   <xsl:output omit-xml-declaration="yes" method="text" indent="no" media-type="text/javascript" encoding="UTF-8" />
   <xsl:strip-space elements="*" />
   <xsl:template match="/icestats">
      <!-- <xsl:param name="callback" /> <xsl:value-of select="$callback" /> -->
      parseMusic({
      <xsl:for-each select="source">
         "
         <xsl:value-of select="@mount" />
         ":{"server_name":"
         <xsl:value-of select="server_name" />
         ","listeners":"
         <xsl:value-of select="listeners" />
         ","description":"
         <xsl:value-of select="server_description" />
         ","title":"
         <xsl:if test="artist">
            <xsl:value-of select="artist" />
            -
         </xsl:if>
         <xsl:value-of select="title" />
         ","genre":"
         <xsl:value-of select="genre" />
         ","bitrate":"
         <xsl:value-of select="bitrate" />
         ","url":"
         <xsl:value-of select="server_url" />
         "}
         <xsl:if test="position() != last()">
            <xsl:text>,</xsl:text>
         </xsl:if>
      </xsl:for-each>
      });
   </xsl:template>
</xsl:stylesheet>

Updated: Added SL’s suggestion to the xml:output tag.

If everything’s good, you should be able to go to http://yourserver:port/json.xsl and receive a JSONP string like this:

parseMusic({"/radio":{"server_name":"Amea Radio GR","listeners":"75","description":"Unspecified description","title":"Unknown","genre":"Misc","bitrate":"128","url":"http://amea-radio.gr"}});

In this JSONP you can see the mount point listed as /radio and then the stats. These can be extended to support more data (everything that icecast is able to show about a specific mount point).

Now, to actually make an use of this data, we will use some simple HTML elements and some simple AJAX with jQuery.

The HTML code that interests us:

Listeners: <span id="listeners">00</span>
Current track: <span id="track-title">LIVE</span>

And the javascript code that does all the magic:

function radioTitle() {

    // this is the URL of the json.xml file located on your server.
    var url = 'http://stream.amea-radio.gr:9000/json.xsl';
    // this is your mountpoint's name, mine is called /radio
    var mountpoint = '/radio';

    $.ajax({  type: 'GET',
          url: url,
          async: true,
          jsonpCallback: 'parseMusic',
          contentType: "application/json",
          dataType: 'jsonp',
          success: function (json) {
            // this is the element we're updating that will hold the track title
            $('#track-title').text(json[mountpoint].title);
            // this is the element we're updating that will hold the listeners count
            $('#listeners').text(json[mountpoint].listeners);  
        },
          error: function (e) {    console.log(e.message);  
        }
    });

}

This defines a function called radioTitle() that does all the magic needed.

As for updating the data while the page is opened, we will need to add 2 timers to be run on the webpage every 10 seconds. We will also do this using jQuery, because it’s more convenient

$(document).ready(function () {

    setTimeout(function () {radioTitle();}, 2000);
    // we're going to update our html elements / player every 15 seconds
    setInterval(function () {radioTitle();}, 15000); 

});

And that’s pretty much all the work.

You can see it in action (and play around) on this JSFiddle: http://jsfiddle.net/Znuff/vL5TM/

I will try to extend the json.xsl file whenever I will have the time with all the possible stats, but for now this will work.

Credits for the ideas:

You can see it in action on this page: http://stream.amea-radio.gr/

Subscribe
Notify of
guest
34 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
SL

Hello

A small addition to avoid a “Resource interpreted as script but transferred with MIME type text/plain” warning in Chrome: add media-type=”text/javascript” to the xsl:output tag.

Znuff

Good point!

Phil

Hi

Congratulations, your work is nice, a very interesting solution to avoid the use of php, and opening broadcast software ports to everybody.

Thank you very much for sharing it.

Bests
Phil

geox

You really save dud, tanks!

geox

*You really save me dud, tanks!*

Danut

Salut, ma tot chinui de ceva vreme sa-mi fac si eu o simpla pagina web cu un player radio acolo (shoutcast/icecast) dar nici ca reusesc, sunt prea in afara ca sa ma descurc, si de aceea daca te rog sa-mi trimiti si mie un exemplu cu toate fisierele incluse, in format .zip se poate? (poate-ti cer prea mult dar multumesc oricum)

Znuff

Poți să copii tot de pe pagina asta: http://stream.amea-radio.gr/ și să înlocuiești datele.

lee

is it possible to extend this script so it detects fallback mounts?

Name

+1 interedted to that also. We would like ability to check one mount point before another.

Fuzzy Mannerz
Fuzzy Mannerz

Try this in the javascript file…
http://pastebin.com/2hWUUNSc 🙂

gnfb

OMG as the yunguns say! I think this is my solution icecast server at home/edcast/winamp/
http://101.98.206.223:8000/ to also play on ipads http://radio.wlg.co.nz/sound/ . But was having problems getting to work. Am a real novice at this just a hobby anyway lets see if I can follow what you did………..

AndRec

Hey, thank you very much for the tutorial.

This is exactly what I want.

Did you use WordPress for this?

I’m asking that because I’m trying to do it using WordPress.

The first problem is that I haven’t found any tutorial to install Jplayer in WordPress. Then I installed the plugin mp3-jplayer, which is based on jplayer and it is working fine.

So now I just am not sure about where am I supposed to put the javascript file.

I put this code in functions.php:

function RadioTitle() {

wp_register_script( ‘RadioTitle’, get_template_directory_uri() . ‘/jvs/jsmeta.js’, array(‘jquery’),’1.0.0′,true );

wp_enqueue_script( ‘RadioTitle’ );

}

add_action(‘wp_enqueue_scripts’, ‘RadioTitle’);

And, of course, I created the jvs directory and put the code that you provided.

Is this the right procedure? It doesn’t seem to work here, but probably I’m doing something wrong.

Thanks again!

Znuff

I wanted to use wordpress in the beginning, but I gave up on that idea.

I’m not familiar with these plugins

AndRed

That’s OK. Thanks.

Getterzinger Hi

Hello.

I’m having some trouble with your code. The json works fine and returns what I want. However, the javascript isn’t working. I tried it on my website, but to no avail. The js fiddle code doesn’t seem to update itself either after running it. What am I doing wrong? I’d be very grateful for some help.

Znuff

The jsfiddle posted doesn’t have a source anymore (the service I made it for is currently down), so it can’t update stuff.

I’ll need a source of data.

tony

Great article! Looks like icecast 2.4.1 supports setting arbitrary HTTP headers per-mount or globally:

http://icecast.org/docs/icecast-trunk/changes.html

“Fixed JSON access by adding support for global and mount specific custom HTTP headers.

The purpose is to fix JSON access from browsers, by supporting basic CORS use cases. This is both important for some HTML5 or use cases and accessing the JSON status API.

The default icecast config contains the very permissive global header: “

Znuff

Thanks. I’ll add this to the article.

Fuzzy Mannerz

Awesome script, thanks!
I have added a line that will report the current title of the fallback/autoDJ stream if there is no reported title for the live mount point. I don’t know much about javascript but it works perfectly. haha
http://pastebin.com/2hWUUNSc

LucaTNT

Very cool, thanks for the tip!

nuess0r

I have to thank you many times for this great tutorial. As a Javascript/JSON newbie it was very helpful and I got a working result in no time.
During testing I found a problem with your json.xsl: It does not properly escape reserved characters as ” in the JSON output. This results sometimes (When the title contains a “) in a malformed JSON object and no track information is displayed.
I found solutions to this problem. But I was not successful in implementing it myself. As a reference, also the status-json.xsl provided with newer icecast versions includes code to correctly escape characters.

Znuff

As a recommendation, always use the latest tools provided by IceCast. I don’t really use IceCast myself that much, and I’ve kept this post mostly for historical reasons.

Why not just use their status-json.xsl template and just switch to using JSON instead of JSONP with my code?

nuess0r

I had the same idea and tried the status-json.xsl provided by icecast. But I struggled with the resulting JSON. With your xsl it is easy to directly index the string you are interested in. With the icecast xsl I would have to search through first, to find the correct index for a certain mountpoint.

Ivaylo Georgiev

Hello , could you please let me know which version of jplayer I need to download for this to work ?
When I test it with my stream info on jfiddle it works but when I build the javascript and html on my end nothing is updated

Ivaylo Georgiev

also , do I need to add the java code to main.js ? It is not clear to me.

Danny Hermans

i used the 380 player from the soundmanager project than create some divs and create your content blocks important is that you create it as a php page with html elements
it does auto update for me.. you can see it in working order here ….
http://player.vrienden-cafe.nl/demo/360-player/testj.php

Danny Hermans

also make sure your icecast.xml is set right this is mine setup i have change the passwords for security reasons….

53
10
5
12400
5
30
5
10356

radiopass
radiopass
radiopass

/radio.mp3
50
1
/tmp.mp3
1
/home/mscplitescripts/disc
/home/mscplitescripts/connect
1024k

/live2.mp3
50
/trtmp.mp3
1
/home/mscplitescripts/disconnecttrans</on-discon$
/home/mscplitescripts/connecttrans
1024k

.
./log
./web
./admin

ban.txt

Danny Hermans

can it be made that it has a scrolling or color wavy text ?
that would be super
this script works well on the latest icecast KS version not sure on the regular version have not tested it with the normal icecast … anyway i would like to have it output with some text effects thanks

Nikos Maraslis

Hi, I don’t know if I am late for this discussion. I am interesting to use this script in a blog with different radios. amea stream is not working any more. Accidently I found a stream that it works and the script is working too.
The stream is http://reusable-box.me:8001 with mount point /mpd
I copy your code and this stream in my test page and it works.
But when I try other Icecast streams the script is not working. I am thinking now that the problem is around that “parseMusic” term. Could it be something to work, with the given by Icecast, “icestats”.. status-json.xsl? (I am not programmer and I’m not owner of any station)
Thank you.

Ian Laborte

Hey bro, im so bad in english hope you can help me , i know imma bit late, im working on a project like this for thesis, i copy your codes and do all the requirements for this code will work, but in the end it doesnt work, the title wont display. thank you

ds

What if there are two mountpoints (one is fallback)? It can only get meta data from one. Is there any way to apply some condition in script? Or maybe in the icecast configuration

Next ArticleWindows 8: Enable/Disable Hibernate via PowerShell