Простая реализация раздела «сейчас играет» для Icecast2 с использованием JSON

в 20:33, , рубрики: icecast2, Веб-разработка, интернет-радио, информация, Медиа, метки: , ,

Здравствуй!
Сегодня мне пришлось столкнуться с проблемой отображения текущего трека и основной информации на страничке с радио.
В поисках оптимального способа отображения я везде натыкался на жуткие PHP-скрипты, которые тупо парсят страницу статуса Icecast. Более того, на одном из форумах об интернет-радио я наткнулся на очень интересный вопрос — «а зачем вы дёргаете пыху?». Действительно, зачем?
И я решил во что бы то ни стало сделать выдачу информации о станции в формате JSON, а заодно и поделиться со всеми своими идеями.

Итак, что нам для этого потребуется: собственно сервер, XSLT-файл для Icecast, скрипт JS для разбора данных и один файлик PHP (позже расскажу зачем).

Шаг первый: настраиваем Icecast

Сервер Icecast2 позволяет создавать пользовательские файлы в формате XSL для вывода информации о станции. Кстати говоря, базовая страничка тоже написана в этом формате.
Для начала узнаем, где icecast ищет свои файлы. Этот путь прописан в файле настроек (по умолчанию это /usr/share/icecast2).
Переходим в каталог /usr/share/icecast2/www и создаём там файл info.xsl — файл, который будет выдавать информацию о точках монтирования.
Методом проб и ошибок я составил XSLT-файл, генерирующий валидный JSON-код с информацией о точках монтирования, представленной в виде именованных объектов:

<xsl:stylesheet xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" version = "1.0" >
<xsl:output omit-xml-declaration="yes" method="text" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" indent="no" encoding="UTF-8" /><xsl:strip-space elements="*"/>

<xsl:template match = "/icestats" >
{<xsl:for-each select="source">
"<xsl:value-of select="<hh user=mount>" />":
      { <!-- составляем информацию о точке монтирования -->
      "name" : "<xsl:value-of select="server_name"/>",
      "listeners" : "<xsl:value-of select="listeners" />",
      "description" : "<xsl:value-of select="server_description" />",
      "title" : "<xsl:value-of select="title" />",
      "genre" : "<xsl:value-of select="genre" />",
      "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>

Чтобы получить информацию, обращаемся к серверу в вот таком формате: example.com:8000/info.xsl

В ответ получаем структурированную информацию обо всех существующих точках монтирования. В моём случае это выглядит так:

{
"/ns":
      {
      "name" : "Nyan-nyan :3",
      "listeners" : "3",
      "description" : "This is sparta~",
      "title" : "Freaking On Shpongle - Dorset Perception Remix",
      "genre" : "Kircore",
      "url" : "http://*********.com"
      }
}

Шаг 2: настройка «прокси»

Информацию-то мы сгенерировали, но вот беда: при помощи JavaScript нельзя обращаться к другому домену или даже порту. Не беда, сделаем вот такой вот скрипт-«переходник»:

<?php
$s = file_get_contents("http://example.com:8000/info.xsl");
echo($s);
?>

Думаю, PHP будет всяко легче просто запросить и отдать, чем запросить, распарсить, вытащить регулярками нужные данные и потом только отдать информацию. Назовём этот скрипт «get.php» и положим на сервер в удобное место.

Шаг 3: настройка JavaScript

Итак, теперь мы можем и генерировать, и принимать информацию, дело за малым — вывести её пользователю.
Здесь я ограничусь кодом с комментариями к нему:


// Функция для упрощения написания, задаёт содержимое элементу DOM. Лень - двигатель прогресса!
function set (id, dat)
{
	var d = document.getElementById(id);
	d.innerHTML = dat;
}


function getXmlHttp() // получаем объект XMLHttpRequest, код взят из многочисленных примеров
{
  var xmlhttp;
  try {
    xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
  } catch (e) {
    try {
      xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
    } catch (E) {
      xmlhttp = false;
    }
  }
  if (!xmlhttp && typeof XMLHttpRequest!='undefined') {
    xmlhttp = new XMLHttpRequest();
  }
  return xmlhttp;
}


function req () // запрос данных
{
	var xmlhttp = getXmlHttp()
	xmlhttp.open("GET", "get.php", true); // просим данные у сервера в асинхронном режиме
	xmlhttp.onreadystatechange = function() 
		{
		  if (xmlhttp.readyState == 4) {
			 if(xmlhttp.status == 200)
			   processResult(xmlhttp.responseText); // отдаём на обработку
		}
	};
	xmlhttp.send(null);
}


function processResult (res) // обработка входящих данных
{
	var csRes = eval("(" + res + ")"); // да-да, тут должен использоваться jQuery, ибо безопасность и так далее. Но, так как это просто скрипт с примером, не буду ударяться в подробности.
	var a = []; // пустой массив, для дальнейших действий

// У меня на радио два маунтпоинта - собственно эфир (/stream) и тот, который играет, пока нет диджеев (/ns).
	if (csRes["/stream"] != null) // Диджей в эфире?
	{
		a = csRes["/stream"]; // если да, работаем с данными от него
		set("sName", "прямой эфир"); // графа "режим" на страничке радио
	}
	else // иначе берём данные от точки нон-стоп
	{
		a = csRes["/ns"];
		set("sName", "Non-Stop (без диджея)"); // графа "режим" на страничке
	}
	
	set("trackholder", a["title"]); // Отображаем текущий трек

        // Задаём все нужные нам поля (жанр, кол-во слушателей, описание станции и т.д.)
	set("sGenre", a["genre"]);
	set("sListeners", a["listeners"]);
	set("sDescr", a["description"]);
	
	setTimeout("req()", 15000); // Если всё прошло удачно, через 15 секунд обновим информацию о станции
// Здесь вместо setInterval используем setTimeout, чтобы при неработающем icecast скрипт не просил данные впустую.
}

req(); // первый запрос

Шаг 4: настройка странички

Здесь всё проще простого. Верстаем страничку, задаём элементы div с именами, используемыми в скрипте выше и после этих элементов указываем ссылку на скрипт:

...
<div id="sDescr">тут описание</div>
<script type="text/javascript" src="./track.js"></script>

Итог

А в итоге мы имеем менее загруженный сервер, более переносимый формат вывода информации (например, для мобильных приложений, написанных для прослушивания вашей станции). Зная специфику XSLT-файлов для Icecast можно написать ещё великое множество интересных вещей для этого замечательного открытого медиасервера.

Автор: namikiri

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js