Здравствуй!
Сегодня мне пришлось столкнуться с проблемой отображения текущего трека и основной информации на страничке с радио.
В поисках оптимального способа отображения я везде натыкался на жуткие 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