Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
How to get the MP3 metadata (StreamTitle) from a streaming URL
<?php
/**
* Please be aware. This gist requires at least PHP 5.4 to run correctly.
* Otherwise consider downgrading the $opts array code to the classic "array" syntax.
*/
function getMp3StreamTitle($streamingUrl, $interval, $offset = 0, $headers = true)
{
$needle = 'StreamTitle=';
$ua = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.110 Safari/537.36';
$opts = [
'http' => [
'method' => 'GET',
'header' => 'Icy-MetaData: 1',
'user_agent' => $ua
]
];
if (($headers = get_headers($streamingUrl))) {
foreach ($headers as $h) {
if (strpos(strtolower($h), 'icy-metaint') !== false && ($interval = explode(':', $h)[1])) {
break;
}
}
}
$context = stream_context_create($opts);
if ($stream = fopen($streamingUrl, 'r', false, $context)) {
$buffer = stream_get_contents($stream, $interval, $offset);
fclose($stream);
if (strpos($buffer, $needle) !== false) {
$title = explode($needle, $buffer)[1];
return substr($title, 1, strpos($title, ';') - 2);
} else {
return getMp3StreamTitle($streamingUrl, $interval, $offset + $interval, false);
}
} else {
throw new Exception("Unable to open stream [{$streamingUrl}]");
}
}
var_dump(getMp3StreamTitle('http://str30.creacast.com/r101_thema6', 19200));
echo "\n\n";
@fracasula
Copy link
Author

fracasula commented Jun 14, 2013

This little gist provides a simple PHP function that lets you to extract the MP3 metadata (StreamTitle) from a streaming URL.
Usually in the response headers, the streaming server will put the "icy-metaint" header that tells us how often the metadata is sent in the stream. So, the function checks the response headers and, if the "icy-metaint" is present, the interval parameter will be replaced. Elsewhere the function calls the streaming URL respecting your interval and, if metadata isn't present, the function will try again through recursion starting from the offset parameter.

@NeighNeighNeigh
Copy link

NeighNeighNeigh commented Jun 24, 2013

Hey there, I'm hoping you could help me out with the following error I'm getting:

     Parse error: syntax error, unexpected '[' in /index.php on line 8

I looked over the syntax and the square brackets look like they are matched properly... But having nearly no experience with php, I must be missing something. (Basically I just saved the contents of your gist as index.php and hoped it would work.) Any help you could give would be awesome, thanks.

@dokbua
Copy link

dokbua commented Jun 24, 2013

Same problem here.

@luckydonald
Copy link

luckydonald commented Jul 4, 2013

yup I have both problems too. Neither PHP experience nor does line 8 work.

@luckydonald
Copy link

luckydonald commented Jul 4, 2013

Got that fixed ;)
In line 8 use

$opts = array('http' => array(
            'method' => 'GET',
            'header' => 'Icy-MetaData: 1',
            'user_agent' => $ua
        )
    );

@luckydonald
Copy link

luckydonald commented Jul 4, 2013

<?php

function getMp3StreamTitle($streamingUrl, $interval, $offset = 0, $headers = true)
{
    $needle = 'StreamTitle=';
    $ua = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.110 Safari/537.36';

    $opts = array('http' => array(
            'method' => 'GET',
            'header' => 'Icy-MetaData: 1',
            'user_agent' => $ua
        )
    );

    if (($headers = get_headers($streamingUrl)))
        foreach ($headers as $h){
            $currentSection = explode(':', $h);
            if (strpos(strtolower($h), 'icy-metaint') !== false && ($interval = $currentSection[1]))
                break;
        }
    $context = stream_context_create($opts);

    if ($stream = fopen($streamingUrl, 'r', false, $context))
    {
    $buffer = stream_get_contents($stream, $interval, $offset);
    fclose($stream);

    if (strpos($buffer, $needle) !== false)
    {
    $currentSectionTwo = explode($needle, $buffer);
    $title = $currentSectionTwo[1];
    return substr($title, 1, strpos($title, ';') - 2);
    }
    else
    return getMp3StreamTitle($streamingUrl, $interval, $offset + $interval, false);
    }
    else
    throw new Exception("Unable to open stream [{$streamingUrl}]");
    }

    var_dump(getMp3StreamTitle('http://radio.luckydonald.de', 19200));

    echo "\n\n";

?>

@luckydonald
Copy link

luckydonald commented Jul 4, 2013

@piconano , @ReneCD does this solve your problems?

@fracasula
Copy link
Author

fracasula commented Dec 9, 2013

This gist needs at least PHP 5.4 to run correctly as you can see from the array declaration. If you want to use a previous version of PHP you can just convert the $opts array using the classic declaration as showed by @luckydonald.

@benjaminbalo
Copy link

benjaminbalo commented Feb 15, 2014

Hi!

I try to get your code work on a Icecast Server, but he prints me some strange things on my site:

string(22) "Befour - How Do You Do"

What das the string mean?
The number changes with every title.

Thanks for your help, 'cause my coding really sucks! :D

P.S.: I tried both scripts; the original one above and the last one from luckydonald.
same results with both scripts!

@haykuro
Copy link

haykuro commented Apr 30, 2015

https://gist.github.com/haykuro/2de8d98031a3cd651754/revisions

I changed your callback function to a while loop. This saves you from making unneeded HTTP requests and completes the task much quicker.

@velusopensource
Copy link

velusopensource commented Oct 13, 2016

how would one get the artist from the stream too? also, would this need to be called regular? or would i need an ajax call to it

@alexhackney
Copy link

alexhackney commented May 18, 2017

This is awesome. Thank you.

@AlfredNeuman
Copy link

AlfredNeuman commented Jul 30, 2017

To get a result without the "string(n)" infront of the title, change var_dump to echo in the line at the end of the script.
Thanx for a great script!

@kellito14
Copy link

kellito14 commented Sep 25, 2017

I tried your code and for a while it worked but all of a sudden, it stopped working.

@brdn
Copy link

brdn commented Feb 10, 2019

curl version?

@ko-n
Copy link

ko-n commented Apr 5, 2020

curl version?
cURL version.

@two-t-fruity
Copy link

two-t-fruity commented Jun 25, 2020

Hi

Can anybody tell me why this code works on localhost, all stations I try, but 3 or 4 will not when on a server

PHP version 7.4 on localhost all stations work - server PHP version 5.5 about 3-4 do not work.

I tried all PHP versions available on the server and only 5.5 works at all

@rthymis
Copy link

rthymis commented Dec 11, 2021

Hello,
When I run the script it returns the Artist and Title correctly but it does not update automatically when the next track plays.
Is this possible or do I need extra code to achieve this?

@aigarspl
Copy link

aigarspl commented Dec 14, 2021

@rthymis You can achieve this by 2 ways (okay, there are more ways, but I'm giving common ones) - create cron task on server or call this script with AJAX request.

@RadioActive913
Copy link

RadioActive913 commented Feb 8, 2022

Hello, I’ve been using this snippet in my foreign music internet radio for few months now and its working great… !
I’m not a php programmer, a developer did this work a while ago. I have basic programming skills.
Right now, I ‘m trying to build a new internet radio that will play Greek music.
All the tracks that play there have Greek characters for Artist and Title in their tags.
AIMP player plays the stream and picks up the Artist / Title metadata (directly from the mp3 stream), and displays them correctly in Greek no problem as Artist and Title.
The Greek radio stream is : https://streamyourdream.org:8138/stream and the foreign music radio is https://streamyourdream.org:8050/radioactive

Using this php snippet (from fracasula) (which is a part of my API), see here https://radioactivefm.gr/live/api/getSongJson.php, I modified the calling code for the new Greek radio and created this https://radioactivefm.gr/live/api/getGreekSongJson.php but it never returns anything!

When I changed the MP3 tags to English characters in one track and called the getGreekSongJson.php then it did work fine, so the function isnt't working with the Greek character set.

Here's the phps that I use:
grk_functions.php

`<?php

$xml = simplexml_load_file("grk_config.xml");
$StreamingUrl = '';
$BackupUrl = '';
$ThirdUrl =  '';
$NoPlayerMessage = '';
$MessageSent = '';
$ReplyPageReturn = '';
$SongLiked = '';
$SongAlreadyLiked = '';
$MiniUrl = '';
$MiniMessage = '';
$SlideShowVisible = 'false';
$LikeFacebookMessage = '';
if (is_object($xml))
{
foreach($xml->children() as $child) {
	if ($child->getName() == 'Url') $StreamingUrl =  $child;
	if ($child->getName() == 'BackupUrl') $BackupUrl =  $child;
	if ($child->getName() == 'ThirdUrl') $ThirdUrl =  $child;
	if ($child->getName() == "NoPlayer") $NoPlayerMessage =  $child;
	if ($child->getName() == 'MessageSent') $MessageSent =  $child;
	if ($child->getName() == 'ReplyPageReturn') $ReplyPageReturn =  $child;
	if ($child->getName() == 'SongLiked') $SongLiked =  $child;
	if ($child->getName() == 'SongAlreadyLiked') $SongAlreadyLiked =  $child;
	if ($child->getName() == 'MiniUrl') $MiniUrl =  $child;
	if ($child->getName() == 'MiniMessage') $MiniMessage =  $child;
	if ($child->getName() == 'SlideShowVisible') $SlideShowVisible =  $child;
	if ($child->getName() == 'LikeFacebookMessage') $LikeFacebookMessage =  $child;
}
}


function getMp3StreamTitle($streamingUrl, $interval, $offset = 0, $headers = true)
{
	$needle = 'StreamTitle=';
	$ua = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.110 Safari/537.36';

	$opts = [
			'http' => [
			'method' => 'GET',
			'header' => 'Icy-MetaData: 1',
			'user_agent' => $ua
		]
	];

	if (($headers = get_headers($streamingUrl))) {
		foreach ($headers as $h) {
			if (strpos(strtolower($h), 'icy-metaint') !== false && ($interval = explode(':', $h)[1])) {
				break;
			}
		}
	}

	$context = stream_context_create($opts);

	if ($stream = fopen($streamingUrl, 'r', false, $context)) {
		$buffer = stream_get_contents($stream, $interval, $offset);
		fclose($stream);

		if (strpos($buffer, $needle) !== false) {
			$title = explode($needle, $buffer)[1];
			return substr($title, 1, strpos($title, ';') - 2);
		} else {
			return getMp3StreamTitle($streamingUrl, $interval, $offset + $interval, false);
		}
	} else {
		throw new Exception("Unable to open stream [{$streamingUrl}]");
	}
}

`

grk_api_func.php
`<?php
chdir('..');
include "grk_functions.php";
/**
*

  • Array 2 XML class
  • Convert an array or multi-dimentional array to XML
  • @author Kevin Waterson
  • @copyright 2009 PHPRO.ORG

*/
class array2xml extends DomDocument
{

public $nodeName;

private $xpath;

private $root;

private $node_name;


/**
* Constructor, duh
*
* Set up the DOM environment
*
* @param    string    $root        The name of the root node
* @param    string    $nod_name    The name numeric keys are called
*
*/
public function __construct($root='root', $node_name='node')
{
    parent::__construct();

    /*** set the encoding ***/
    $this->encoding = "utf-8";

    /*** format the output ***/
    $this->formatOutput = true;

    /*** set the node names ***/
    $this->node_name = $node_name;

    /*** create the root element ***/
    $this->root = $this->appendChild($this->createElement( $root ));

    $this->xpath = new DomXPath($this);
}

/*
* creates the XML representation of the array
*
* @access    public
* @param    array    $arr    The array to convert
* @aparam    string    $node    The name given to child nodes when recursing
*
*/
public function createNode( $arr, $node = null)
{
    if (is_null($node))
    {
        $node = $this->root;
    }
    foreach($arr as $element => $value) 
    {
        $element = is_numeric( $element ) ? $this->node_name : $element;

        $child = $this->createElement($element);
        $node->appendChild($child);

        if (is_array($value))
        {
            self::createNode($value, $child);
        }
		else if (strlen($value) > 0)
		{
			$child->appendChild($this->createCDataSection($value));
		}
    }
}
/*
* Return the generated XML as a string
*
* @access    public
* @return    string
*
*/
public function __toString()
{
    return $this->saveXML();
}

/*
* array2xml::query() - perform an XPath query on the XML representation of the array
* @param str $query - query to perform
* @return mixed
*/
public function query($query)
{
    return $this->xpath->evaluate($query);
}

} // end of class

?>`

getGreekSongJson.php

`<?
header('Access-Control-Allow-Origin: *');

include "grk_api_func.php";

$song = getMp3StreamTitle('https://streamyourdream.org:8138/stream', 19200);

$array = array(
array(
'songTitle'=>htmlentities($song))
);

try
{
echo json_encode($array, JSON_UNESCAPED_SLASHES);
}
catch (Exception $e)
{
echo $e->getMessage();
}

?>`

grk_config.xml

<?xml version="1.0" encoding="utf-8"?> <Config> <Url>https://streamyourdream.org:8138/stream</Url> <BackupUrl>https://streamyourdream.org:8138/stream</BackupUrl> <ThirdUrl>https://streamyourdream.org:8138/stream</ThirdUrl> <MiniUrl>https://streamyourdream.org:8138/stream</MiniUrl> <SlideShowVisible>true</SlideShowVisible> <NoPlayer>We are sorry but currently there's no streaming server available. Please try again later by refreshing this page</NoPlayer> <MessageSent>Message Sent!</MessageSent> <ReplyPageReturn>Click here to return</ReplyPageReturn> <SongLiked>was added to database. Thank you!</SongLiked> <SongAlreadyLiked>has been already added to database. Thank you!</SongAlreadyLiked> <LikeFacebookMessage>#GnisiosTracks</LikeFacebookMessage> <MiniMessage>This is the NON-Members Player!!!</MiniMessage> </Config>

I would appreciate any help in fixing this!

Thank you again…

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment