Can Someone Help Me With This?

cc333

Tinkerer
Dec 22, 2021
59
33
18
California, USA
OK, I'm running a little Internet Radio stream for my own enjoyment, and I made up a little web player for it that displays some metadata.

Anyway, the problem is that when there's only one stream running on my server, everything's under 'source' and the metadata routine works fine, but when the header is something like '0' – as is the case when there's more than one active stream on the server – it breaks because what it's looking for is no longer under 'source.'

For example, when the JSON is like this:
JSON:
icestats
    admin    "***"
    host    "***"
    location    "Nowhere"
    server_id    "Icecast 2.4.4"
    server_start    "Sat, 22 Aug 2026 13:23:01 -0700"
    server_start_iso8601    "2026-08-22T13:23:01-0700"
    source
        audio_info    "ice-bitrate=320;ice-channels=2;ice-samplerate=44100"
        bitrate    320
        genre    "various"
        ice-bitrate    320
        ice-channels    2
        ice-samplerate    44100
        listener_peak    1
        listeners    0
        listenurl    "***"
        server_description    "Unspecified description"
        server_name    "Unspecified name"
        server_type    "audio/mpeg"
        stream_start    "Sat, 22 Aug 2026 13:42:09 -0700"
        stream_start_iso8601    "2026-08-22T13:42:09-0700"
        title    "Nicolette Larson - Rio de Janeiro Blue - In The Nick Of Time"
        dummy    null
My webplayer's metadata grabbing routine works fine, but if it's like this:
JSON:
icestats    
    admin    "***"
    host    "***"
    location    "Nowhere"
    server_id    "Icecast 2.4.4"
    server_start    "Sat, 22 Aug 2026 13:23:01 -0700"
    server_start_iso8601    "2026-08-22T13:23:01-0700"
        source    
            0    
                audio_info    "ice-bitrate=320;ice-channels=2;ice-samplerate=44100"
                bitrate    320
                genre    "various"
                ice-bitrate    320
                ice-channels    2
                ice-samplerate    44100
                listener_peak    1
                listeners    0
                listenurl    "***"
                server_description    "Unspecified description"
                server_name    "Unspecified name"
                server_type    "audio/mpeg"
                stream_start    "Sat, 22 Aug 2026 13:42:09 -0700"
                stream_start_iso8601    "2026-08-22T13:42:09-0700"
                title    "James Taylor - Country Road - Sweet Baby James"
                dummy    null
            1    
                audio_info    "ice-bitrate=256;ice-channels=2;ice-samplerate=44100"
                bitrate    256
                genre    "Various"
                ice-bitrate    256
                ice-channels    2
                ice-samplerate    44100
                listener_peak    0
                listeners    0
                listenurl    "***"
                server_description    "Unspecified description"
                server_name    "Radio1610"
                server_type    "audio/aac"
                stream_start    "Sat, 22 Aug 2026 13:59:16 -0700"
                stream_start_iso8601    "2026-08-22T13:59:16-0700"
                title    "James Taylor - Country Road - Sweet Baby James"
                dummy    null
It doesn't work.

Here's the code for the metadata routine:
JavaScript:
function fetchCurrentlyPlaying ()
            {jQuery.get('***/status-json.xsl', {}, function (response)
                {$('.currently-playing-title').html(response.icestats.source['title']);
                    document.title = response.icestats.source['title'];});}

Any ideas? While I could use AI to try fixing it, I'd rather not (I'm old-fashioned... I like communicating with real humans), but if any of you want to plug it in to your model of choice and give it a try, feel free.

c
 
Last edited:

jibsaramnim

Tinkerer
Mar 16, 2024
42
28
18
South Korea
davejansen.com
Small caveat up front that I'm on my phone and may make a typo.

Technically it might be cleaner to check if `source` is an array and then pull from its first entry if it is, but it might just be enough to add a bit of OR logic and call it a day? Something along the lines of;

JavaScript:
document.title = response.icestats.source[0]['title'] || response.icestats.source['title'];

I'm not sure what browser environment you're targeting, so if something like the above throws warnings as errors, you could wrap the whole thing in a try/catch.

Would this work for your needs?
 
  • Like
Reactions: cc333

cc333

Tinkerer
Dec 22, 2021
59
33
18
California, USA
@jibsaramnim Hmm, it might. I'll give it a try and let you know!

For this I'm targeting any recent browser, which I guess means pretty much Firefox and Chrome?

As an aside, I've tested the stream itself, and it works on SoundJam MP on Mac OS 9, so this project is somewhat vintage related insomuch as I've bothered to test that it works :)

That said, if I can eventually make a web-accessible player similar to this one, but compatible with Mac OS 9-era browsers (Mozilla, Netscape and IE 4.x and 5.x), that would be neat.

c
 
  • Like
Reactions: jibsaramnim

cc333

Tinkerer
Dec 22, 2021
59
33
18
California, USA
OK, I added in the OR logic you suggested, and while it may not be the cleanest, it worked!

This thing could stand a rewrite once I learn more, so I don't care if it's a bit clunky at present.

For reference, here's what it looks like on Firefox ESR 140.12 (the name of my station is "Radio1610"):
Screen Shot 2026-08-22 at 6.50.56 PM.png

The big light blue square is supposed to be for album artwork, but there's no logic because I didn't know how to make it work. All it is is a placeholder.

c
 

cc333

Tinkerer
Dec 22, 2021
59
33
18
California, USA
I have an idea!

Now that I have learned to make metadata retrieval work when more than one stream is active on my server (thanks the help of @jibsaramnim!), I'm wondering if I can create something like a drop down menu where I can select which stream the player, uh, plays.

It's academic at this point because both streams (I have two at present) are playing the same thing, but eventually, it might be fun to have a separate programming for each, and I'd like a single player to be able to connect to both of them (I could have two players on separate pages, each hardcoded to their respective streams, which is practically foolproof, but it seems rather inelegant).

If anyone has some advice, I'd appreciate it!

In the meantime, I'm going to check out W3 Schools to see if I can come up with some sort of proof of concept.

c
 

jibsaramnim

Tinkerer
Mar 16, 2024
42
28
18
South Korea
davejansen.com
Awesome, I'm glad it's working for you!

(...) I'm wondering if I can create something like a drop down menu where I can select which stream the player, uh, plays.

Very possible, yes! If I understand your idea right, you'd want to (optionally?) display a drop-down box if there are more than one stream available, and then use the selection in this dropdown box to render out the "now playing" UI, right? I'm not sure what you want to show in the dropdownbox as the stream indicators (numerical, for stream 1, 2, etc., or by currently playing title?), but either way, with a bit of event listening goodness added in, this should be fairly straight-forward, and all totally possible with jQuery too :).

Today my caveat is not that I'm on mobile (yay), but it has been maybe 15 years since I last used jQuery, and I'm jotting all this down in this here comment field, sans testing. So if I make any mistake or miss something obvious there, apologies.

I've written the following out as a way to hopefully get you started, with the aforementioned caveat in place. I split out certain responsibilities into separate functions as some parts are now potentially triggered from more than one place (ie. rendering the UI would happen after a presumably timed fetch of currently playing info, as-well as after making a selection the dropdown box), but you don't have to do it this way of course. I just thought it might make things a bit easier to glance over and borrow from.

JavaScript:
// Prepare an empty array outside of the below functions we can store fetched streams into.
var streams = [];

// We'll use this value to store which stream has been selected.
var streamSelection = 0;

// Removes all previously created menu entries and creates new ones based on what is currently in the above values.
function updateStreamSelectionMenu() {
    // Saved to more easily reference the same element multiple times
    var selection = $('.stream-selection');

    // Clean the select menu so we can populate it with the up-to-date list of streams
    $('.stream-selection').empty();
  
    // Walk through each stream, adding it to the selection menu
    $.each(streams, function(i, stream) {
        // You can set the value of .text() something like the currently playing song (`stream.title`) or
        // perhaps `Stream #${i+1}` to render it out as "Stream #1", "Stream #2", etc.
        var option = $('option').value(i).text(`Stream #${i+1}`);
        $('.stream-selection').append(option);
    });
}

// Handles storing the newly selected stream index, and triggers the render function.
function handleStreamSelection() {
    var newStreamSelection = $(this).val();
  
    // Ensure we only handle this event if the selection has actually changed
    // from what was handled previously.
    if (streamSelection != newStreamSelection) {
        streamSelection = newStreamSelection;
        renderCurrentlyPlaying();
    }
}

// Fetches currently playing information, and updates the above variables.
function fetchCurrentlyPlaying () {
    jQuery.get('***/status-json.xsl', {}, function (response){
        // Ensure we're always working with an array
        streams = (Array.isArray(response.icestats.source)) ? response.icestats.source : [response.icestats.source];
      
        updateStreamSelectionMenu();
      
        renderCurrentlyPlaying();
    });
}

// Renders the currently playing information of the currently selected stream
function renderCurrentlyPlaying() { 
    if (sources[streamSelection]) {
        var source = sources[streamSelection];
      
        $('.currently-playing-title').html(source['title']);
        document.title = source['title'];
    }
}


// This would be placed wherever you have your other event/timer trigger initialization code
// Note that this should only be run once, as you'd otherwise end up in a situation where one action may trigger
// listeners multiple times.
$('.stream-selection').on("change", handleStreamSelection);

If you end up giving all this a try, I'd love to hear how it ends up working out for you! And again, for any mistakes I've made along the way, I do apologize :D.
 
  • Like
Reactions: cc333

cc333

Tinkerer
Dec 22, 2021
59
33
18
California, USA
No worries!

You know more than I do! I'm pretty much learning as I go (I know some basic HTML and CSS, but javascript has always mystified me somewhat).

Now that I've got a samba share set up on my server (a VM running Debian without a GUI) so I can edit index.html from the comfort of MacOS, I'm going to try wrapping my head around your code and see if I can use any of it.

I was thinking of something relatively simple like radio buttons to choose which stream the player will connect to, with each radio button hardcoded to the server.

Perhaps doing it dynamically would be better, so it can adapt to the number of streams, but hardcoding is simpler :)

Is there a method which would be simpler/easier/more robust than JQuery that could be implemented here? WebSockets?

I really do appreciate your help, so please keep it coming!

c
 

jibsaramnim

Tinkerer
Mar 16, 2024
42
28
18
South Korea
davejansen.com
My pleasure!

Is there a method which would be simpler/easier/more robust than JQuery that could be implemented here? WebSockets?

There's probably fancier ways you could communicate with your streaming server and render it all live and whatnot, but I'd honestly say it's not worth the hassle, unless the coding experience is what you're after. jQuery is an older choice, and modern JavaScript kind of made it redundant in many ways, but as you can tell it's still working fine.

I'd not sweat it, unless you're looking for the experience of course. If that's the case, I'd suggest looking into doing everything you're currently doing with vanilla JavaScript, and see how that feels. Things like jQuery's `.get()` would be replaced by .fetch(), jQuery's element selector (ie. `$(".foo")`) by querySelector (and querySelectorAll), and jQuery's easy way to create new HTML elements with the perhaps slightly clunkier feeling createElement. Things like that. In the modern internet development days folk tend to be overly eager in suggesting heavy frameworks, but it's really not necessary unless, again, you're looking for the experience :).
 

cc333

Tinkerer
Dec 22, 2021
59
33
18
California, USA
OK, I've been puzzling over this code, and I must admit that I'm kind of stumped by it.

I think I'll just keep it as a single-stream player for now, but I'm still going to study your code and eventually figure it out.

In the meantime, I'm thinking It would be nice to be able to parse the single line "title" I get from Icecast into separate fields for artist, song, album, yer, etc., so I can have some more flexibility regarding how to display stuff. Is there a simple way to do that? I know a very tiny amount of PHP, so I could use that, but if you have better ideas, I'm all ears.

I currently use " - " (space dash space) as the delimiter, but I could get rid of the spaces if that would make it any easier.

I also made up a parser in PHP awhile back that can translate POST requests, clean it up a bit, and then write it to a file. I wouldn't mind doing something cleaner, but it works. At any rate, here it is, if you'd like to take a look:

PHP:
<?php
//Get the POST data
$postdata = fopen("php://input", "r");
$npFile = fopen("nowplaying.txt", "w");
while ($data = fread($postdata, 1024))
    fwrite($npFile, $data);
fclose($npFile);
fclose($postdata);

//Clean it up...
$npFile = fopen("nowplaying.txt", "r");
$passOne = fread($npFile, filesize("nowplaying.txt"));
$passTwo = str_ireplace("+"," ",$passOne);
$passThree = str_ireplace("%27","'",$passTwo);
$passFour = str_ireplace("%22","\"",$passThree);
$passFive = str_ireplace("%3f","?",$passFour);
$passSix = str_ireplace("%26","&",$passFive);
$passSeven = str_ireplace("%2c",",",$passSix);
$passEight = str_ireplace("%20"," ",$passSeven);
$passNine = str_ireplace("%28","(",$passEight);
$passTen = str_ireplace("%29",")",$passNine);
fclose($npFile);

//...and write it to nowplaying.txt
$npFile = fopen("nowplaying.txt", "w");
fwrite($npFile,$passTen);
fclose($npFile);
?>

c
 
Last edited: