Здравствуй, Хабр!
Сегодня мне пришлось столкнуться с проблемой отображения текущего трека и основной информации на страничке с радио.
В поисках оптимального способа отображения я везде натыкался на жуткие PHP-скрипты, которые тупо парсят страницу статуса Icecast. Более того, на одном из форумах об интернет-радио я наткнулся на очень интересный вопрос — «а зачем вы дёргаете пыху?». Действительно, зачем?
И я решил во что бы то ни стало сделать выдачу информации о станции в формате JSON, а заодно и поделиться со всеми своими идеями.
Итак, что нам для этого потребуется: собственно сервер, XSLT-файл для Icecast, скрипт JS для разбора данных и один файлик PHP (позже расскажу зачем).
Для начала узнаем, где icecast ищет свои файлы. Этот путь прописан в файле настроек (по умолчанию это /usr/share/icecast2).
Переходим в каталог /usr/share/icecast2/www и создаём там файл info.xsl — файл, который будет выдавать информацию о точках монтирования.
Методом проб и ошибок я составил XSLT-файл, генерирующий валидный JSON-код с информацией о точках монтирования, представленной в виде именованных объектов:
UPDATE: Парсер Хабрахабра чуть испортил код. Вроде исправил.
Чтобы получить информацию, обращаемся к серверу в вот таком формате:
В ответ получаем структурированную информацию обо всех существующих точках монтирования. В моём случае это выглядит так:
Думаю, PHP будет всяко легче просто запросить и отдать, чем запросить, распарсить, вытащить регулярками нужные данные и потом только отдать информацию. Назовём этот скрипт «get.php» и положим на сервер в удобное место.
Здесь я ограничусь кодом с комментариями к нему:
В конечном итоге у меня получилась симпатичная страничка с информацией о текущем треке:

При нажатии на строчку с треком можно посмотреть информацию о самой станции:

Вся эта информация обновляется без перезагрузки страницы.
Сегодня мне пришлось столкнуться с проблемой отображения текущего трека и основной информации на страничке с радио.
В поисках оптимального способа отображения я везде натыкался на жуткие PHP-скрипты, которые тупо парсят страницу статуса Icecast. Более того, на одном из форумах об интернет-радио я наткнулся на очень интересный вопрос — «а зачем вы дёргаете пыху?». Действительно, зачем?
И я решил во что бы то ни стало сделать выдачу информации о станции в формате JSON, а заодно и поделиться со всеми своими идеями.
Итак, что нам для этого потребуется: собственно сервер, XSLT-файл для Icecast, скрипт JS для разбора данных и один файлик PHP (позже расскажу зачем).
Шаг первый: настраиваем Icecast
Сервер Icecast2 позволяет создавать пользовательские файлы в формате XSL для вывода информации о станции. Кстати говоря, базовая страничка тоже написана в этом формате.Для начала узнаем, где icecast ищет свои файлы. Этот путь прописан в файле настроек (по умолчанию это /usr/share/icecast2).
Переходим в каталог /usr/share/icecast2/www и создаём там файл info.xsl — файл, который будет выдавать информацию о точках монтирования.
Методом проб и ошибок я составил XSLT-файл, генерирующий валидный JSON-код с информацией о точках монтирования, представленной в виде именованных объектов:
UPDATE: Парсер Хабрахабра чуть испортил код. Вроде исправил.
<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="@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 можно написать ещё великое множество интересных вещей для этого замечательного открытого медиасервера.В конечном итоге у меня получилась симпатичная страничка с информацией о текущем треке:

При нажатии на строчку с треком можно посмотреть информацию о самой станции:

Вся эта информация обновляется без перезагрузки страницы.