skip to Main Content

I am running an Icecast server and using a custom audio player to receive the stream and display the currently playing title and artist. The method I am using–JavaScript and Json–makes a request to the server every 15 seconds so that the metadada is updated as each new song plays.

Is there a way to send the changed information from the server to the browser without a request? Even 10 listeners sending requests every 15 seconds are a lot or requests if each listens for half an hour.

//Get the stream meta from xml
function ajax_get(url, callback) {
  'use strict';
  setInterval( function() {

  var xmlhttp = new XMLHttpRequest();
    xmlhttp.onreadystatechange = function() {
        if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
            //console.log('responseText:' + xmlhttp.responseText);
            try {
                var obj = JSON.parse(xmlhttp.responseText);
            } catch(err) {
                console.log(err.message + " in " + xmlhttp.responseText);
                return;
            }
            callback(obj);
        }
    };

    xmlhttp.open("GET", url, true);
    xmlhttp.send();
}, 15000);
}

ajax_get('https://listen.abengnews.com/status-json.xsl', function(obj) {
    document.getElementById("scroll-text").innerHTML = obj.icestats.source.server_name + ", " + obj.icestats.source.genre + ". Now playing: " + obj.icestats.source.title + ". " + obj.icestats.source.server_description;
    });
#scroll-container {
  /*position: absolute;*/
  background-color: #005500;
  border: 1px solid black;
  border-radius: 5px;
  overflow: hidden;
  width: 250px;
  margin: auto;
  font-family: arial,san-serif;
  font-size:10px;
  font-weight: bold;
  font-color: #fff;
}

#scroll-text {
  display: block;
  height:12px;
  white-space: nowrap;
  /* animation properties */
  -moz-transform: translateX(100%);
  -webkit-transform: translateX(100%);
  transform: translateX(100%);

  -moz-animation: my-animation 15s linear infinite;
  -webkit-animation: my-animation 15s linear infinite;
  animation: my-animation 15s linear infinite;
}
/* for Firefox */
@-moz-keyframes my-animation {
  from { -moz-transform: translateX(100%); }
  to { -moz-transform: translateX(-100%); }
}

/* for Chrome */
@-webkit-keyframes my-animation {
  from { -webkit-transform: translateX(100%); }
  to { -webkit-transform: translateX(-100%); }
}

@keyframes my-animation {
  from {
    -moz-transform: translateX(100%);
    -webkit-transform: translateX(100%);
    transform: translateX(100%);
  }
  to {
    -moz-transform: translateX(-100%);
    -webkit-transform: translateX(-100%);
    transform: translateX(-100%);
  }
<div id="scroll-container">
  <span class="scroll-text" id="scroll-text"><span>
</div>

2

Answers


  1. Chosen as BEST ANSWER

    I found the answer to pushing metadata of an Icecast stream to a custom HTML5 audio player by using Server-Sent Events (SSE) as defined by MDN. I learned of this method after searching how to implement websockets as suggested by @Alex Paramonov.

    SSE is far simpler to implement and probably the best option for the use case that does not require two-way full duplex communication between the server and the client. All it took were two simple snippets of JavaScript and PHP.

    The JavaScript listens for changes sent by the PHP, that has a 10 seconds trigger to provide info on the currently playing audio.

    This is the HTML and JavaScript (found here on Stockoverflow in another answer):

        <h1>Getting server updates</h1>
        <div id="result"></div>
    
        <script>
            messages = new Array();
    
            if(typeof(EventSource) !== "undefined") {
                var source = new EventSource("demo_sse.php");
                source.onmessage = function(event) {
                    if (messages.length == 10) messages.splice(-1, 1);
                    messages.splice(0, 0, event.data);
                    document.getElementById("result").innerHTML = messages.join("<br />");
                };
            } else {
                document.getElementById("result").innerHTML = "Sorry, your browser does not support server-sent events...";
            }
        </script>
    

    And this is the PHP that checks the Icecast meta:

    header('Content-Type: text/event-stream');
    header('Cache-Control: no-cache');
    
    // prevent direct access
    //if ( ! defined("ABSPATH") ) die("");
    
    $json = file_get_contents('https://listen.example.com:8000/status-json.xsl');
    $obj = json_decode($json);
    $retry = 10000;
    echo "data:{$obj->icestats->source->server_name}.nretry:{$retry}n ";
    echo "data:{$obj->icestats->source->genre}.  Now playing:nretry:{$retry}n";
    echo "data:{$obj->icestats->source->title}.nretry:{$retry}n";
    echo "data:{$obj->icestats->source->server_description}nnretry:{$retry}nn";
    flush();
    

    And that is it. I am sure that it can be tidied up.


  2. Once I solved it by creating a simple Websocket server/script. The idea is that:

    • You setup a simple Websocket server (I used https://pypi.org/project/simple-websocket-server/ in my case)
    • Create a server-side script that makes GET requests every second to Icecast to get the metadata. Since server-side script is the only one that is making requests – there is no huge load on the server.
    • Once script in (2) sees the metadata change – it pushes the new metadata to the websocket.
    • On JS side in your player you make a Websocket connection to the server and listen to events from the server with the new metadata.

    This approach hugely decreases server load, since you don’t have thousands of player requesting metadata form the server every 15 seconds.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search