Статьи

Учебник по потоковому радио-плееру с демонстрацией в реальном времени и исходным кодом


Сегодня я подготовил еще один действительно хороший урок для вас.
Недавно я начал разработку собственного программного обеспечения для радио (как модуля для Dolphin CMS) и получил несколько интересных результатов, которыми я хотел бы поделиться с вами. Это будет симпатичный (CSS3) радиосценарий, который состоит из трех основных элементов: заголовок (с анимированной панелью поиска и встроенным радиоплеером), левая сторона (со списком категорий и подкатегорий) и правая сторона (которая будет содержать список последних или отфильтрованных станций).

Вот окончательный результат нашего игрока:

HTML5 видео плеер

Вот наш Live Demo и загружаемый пакет: Хорошо, загрузите наши исходные файлы и давайте начнем кодировать!

Шаг 1. HTML-разметка

Это разметка одного из файлов шаблона. Это шаблон нашей главной (индексной) страницы:

шаблоны / main_page.html

<!DOCTYPE html>
<html lang="en" >
    <head>
        <meta charset="utf-8" />
        <title>Stream Radio Script | Script Tutorials</title>
        <link href="css/main.css" rel="stylesheet" type="text/css" />
        <link href="css/radio.css" rel="stylesheet" type="text/css" />
        <script type="text/javascript" src="https://www.google.com/jsapi"></script>
        <script>
            google.load("jquery", "1.7.1");
        </script>
        <script src="js/script.js"></script>
    </head>
    <body>
        <header>
            <h2>Stream Radio Script</h2>
            <a href="http://www.script-tutorials.com/stream-radio-script/" class="stuts">Back to original tutorial on <span>Script Tutorials</span></a>
        </header>
        <div class="container">

            <form method="get" class="header" action="javascript:void(0)" onsubmit="get_stations_by_keyword(); return false;">
                <input type="text" id="search" value="Search" name="s">
                <span>
                    <div id="rplayer">
                        <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="191" height="46" bgcolor="#FFFFFF">
                        <param name="movie" value="player/ffmp3-config.swf" />
                        <param name="flashvars" value="url=http://scfire-dtc-aa04.stream.aol.com:80/stream/1013/&lang=en&codec=mp3&volume=100&introurl=media/welcome.mp3&autoplay=true&traking=false&jsevents=false&buffering=5&skin=player/def/skin.xml&title=SKY.FM 80s" />
                        <param name="wmode" value="opaque" />
                        <param name="allowscriptaccess" value="always" />
                        <param name="scale" value="noscale" />
                        <embed src="player/ffmp3-config.swf" flashvars="url=http://scfire-dtc-aa04.stream.aol.com:80/stream/1013/&lang=en&codec=mp3&volume=100&introurl=media/welcome.mp3&autoplay=true&traking=false&jsevents=false&buffering=5&skin=player/def/skin.xml&title=SKY.FM 80s" width="191" scale="noscale" height="46" wmode="opaque" bgcolor="#FFFFFF" allowscriptaccess="always" type="application/x-shockwave-flash" />
                        </object>
                    </div>
                </span>
            </form>
            <div class="clear"></div>
            <div class="genres_par">
                <ul class="genres">
                    <li id="1" val="Alternative"><a href="javascript:void(0)">Alternative</a>
                        <ul>
                            <li id="11" val="Classic+Alternative"><a href="javascript:void(0)">Classic Alternative</a></li>
                            <li id="12" val="Industrial"><a href="javascript:void(0)">Industrial</a></li>
                            <li id="13" val="New+Wave"><a href="javascript:void(0)">New Wave</a></li>
                            <li id="14" val="Punk"><a href="javascript:void(0)">Punk</a></li>
                        </ul>
                    </li>
                    <li id="2" val="Classical"><a href="javascript:void(0)">Classical</a>
                        <ul>
                            <li id="21" val="Modern"><a href="javascript:void(0)">Modern</a></li>
                            <li id="22" val="Opera"><a href="javascript:void(0)">Opera</a></li>
                            <li id="23" val="Piano"><a href="javascript:void(0)">Piano</a></li>
                            <li id="24" val="Romantic"><a href="javascript:void(0)">Romantic</a></li>
                            <li id="25" val="Symphony"><a href="javascript:void(0)">Symphony</a></li>
                        </ul>
                    </li>
                    <li id="3" val="Electronic"><a href="javascript:void(0)">Electronic</a>
                        <ul>
                            <li id="31" val="Breakbeat"><a href="javascript:void(0)">Breakbeat</a></li>
                            <li id="32" val="Dance"><a href="javascript:void(0)">Dance</a></li>
                            <li id="33" val="Electro"><a href="javascript:void(0)">Electro</a></li>
                            <li id="34" val="House"><a href="javascript:void(0)">House</a></li>
                            <li id="35" val="Techno"><a href="javascript:void(0)">Techno</a></li>
                            <li id="36" val="Trance"><a href="javascript:void(0)">Trance</a></li>
                        </ul>
                    </li>
                    <li id="4" val="Metal"><a href="javascript:void(0)">Metal</a>
                        <ul>
                            <li id="41" val="Classic+Metal"><a href="javascript:void(0)">Classic Metal</a></li>
                            <li id="42" val="Heavy+Metal"><a href="javascript:void(0)">Heavy Metal</a></li>
                            <li id="43" val="Metalcore"><a href="javascript:void(0)">Metalcore</a></li>
                            <li id="44" val="Power+Metal"><a href="javascript:void(0)">Power Metal</a></li>
                        </ul>
                    </li>
                    <li id="5" val="Pop"><a href="javascript:void(0)">Pop</a>
                        <ul>
                            <li id="51" val="Dance+Pop"><a href="javascript:void(0)">Dance Pop</a></li>
                            <li id="52" val="Oldies"><a href="javascript:void(0)">Oldies</a></li>
                            <li id="53" val="Top+40"><a href="javascript:void(0)">Top 40</a></li>
                            <li id="54" val="World+Pop"><a href="javascript:void(0)">World Pop</a></li>
                        </ul>
                    </li>
                </ul>
                <div class="clear"></div>
            </div>
            <div class="stlist">__stations__</div>
            <div class="clear"></div>
            <div class="cred">Powered by <a href="http://www.script-tutorials.com/">Script Tutorials</a></div>

        </div>
    </body>
</html>

Сначала обратите внимание на то, как скрипт загружает библиотеку jQuery из Google. Это может быть очень полезно, если вы не хотите хранить этот файл непосредственно на вашем хосте. Наш заголовочный элемент содержит красивую строку поиска со встроенным проигрывателем jasl ( я использовал отличный проигрыватель FFMp3 Live Stream Player ), который позволяет нам воспроизводить аудиопотоки без каких-либо проблем.

Далее, с левой стороны (под заголовком) у нас есть список категорий и подкатегорий на основе UL-LI. Правая сторона будет содержать список самых последних станций, и, когда мы будем искать или выбирать категорию, правая сторона будет отфильтрована Ajaxy. А пока — он содержит ключ __stations__ (ключ шаблона), и мы заменим фактическое значение на PHP.

На нашем следующем файле шаблона, радиоплеер:

шаблоны / radio.html

<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="191" height="46" bgcolor="#FFFFFF">
<param name="movie" value="player/ffmp3-config.swf" />
<param name="flashvars" value="url=__stream__&lang=en&codec=mp3&volume=100&introurl=&autoplay=true&traking=false&jsevents=false&buffering=5&skin=player/def/skin.xml&title=__title__" />
<param name="wmode" value="opaque" />
<param name="allowscriptaccess" value="always" />
<param name="scale" value="noscale" />
<embed src="player/ffmp3-config.swf" flashvars="url=__stream__&lang=en&codec=mp3&volume=100&introurl=&autoplay=true&traking=false&jsevents=false&buffering=5&skin=player/def/skin.xml&title=__title__" width="191" scale="noscale" height="46" wmode="opaque" bgcolor="#FFFFFF" allowscriptaccess="always" type="application/x-shockwave-flash" />
</object>

Конечно, он содержит свои собственные ключи шаблона (__title__ и __stream__), которые мы будем использовать после.

Шаг 2. CSS

Вот наши файлы стилей:

CSS / main.css

Первый содержит стили нашей тестовой страницы (этот файл всегда доступен в нашем пакете)

CSS / radio.css

/* header area */
.header {
    height:62px;
}
.header input {
    background:#aaa url(../images/search.png) no-repeat 5px center;
    border:1px solid #888;
    border-radius:10px;
    float:right;
    margin:14px 10px 0 0;
    outline:none;
    padding-left:20px;
    width:200px;

    -webkit-transition: 0.5s;
    -moz-transition: 0.5s;
    -o-transition: 0.5s;
    transition: 0.5s;
}
.header input:focus {
    background-color:#eee;
    width:300px;
}
.header > span {
    display:block;
    float:left;
    line-height:40px;
    padding:7px;

    -webkit-transition: 0.5s;
    -moz-transition: 0.5s;
    -o-transition: 0.5s;
    transition: 0.5s;
}

/* stations list */
.stlist {
    float:right;
    margin-right:1%;
    width:71%;
}
.stlist ul {
    list-style:none outside none;
    margin:0;
    padding:0;
}
.stlist ul li {
    border-bottom:1px dotted #444;
    overflow:hidden;
    padding:10px;
}
.stlist ul li > a > img {
    border:1px solid #CCC;
    float:left;
    height:85px;
    margin-right:15px;
    padding:1px;
    width:85px;
}
.stlist ul li > div {
    float:right;
    margin-left:15px;
    margin-top:-5px;
}
.stlist ul li > p.label,.stlist ul li > p.track {
    font-size:11px;
    font-weight:700;
}
.stlist ul li > p.label {
    color:#888;
}
.stlist ul li > p.channel {
    font-size:14px;
    font-weight:700;
    margin-bottom:17px;
}

/* genres list */
.genres_par {
    border-right:1px solid #ccc;
    float:left;
    width:26%;
}
ul.genres,ul.genres ul {
    list-style-type:none;
    margin:0;
    padding:0;
}
ul.genres ul {
    display:none;
    overflow:hidden;
    padding:0 15px;
}
ul.genres ul li {
    margin:3px;
}
ul.genres a {
    color:#333;
    display:block;
    font-size:18px;
    padding:4px 0;
    text-align:center;
    text-decoration:none;
}
ul.genres ul a {
    font-size:12px;
    text-align:left;
}
ul.genres li {
    border-bottom:1px solid #CCC;
    margin:0;
}
ul.genres li ul li a {
    background:none repeat scroll 0 0 #5bb951;
    border-radius:2px;
    color:#FFF;
    font-size:12px;
    padding:6px;
}
ul.genres li ul li a:hover {
    background-color:#53854E;
}

Шаг 3. JS

JS / script.js

$(document).ready(function(){
    $('#search').blur(function() {
        if ('' == $('#search').val()) $('#search').val('Search');
    });
    $('#search').focus(function() {
        if ('Search' == $('#search').val()) $('#search').val('');
    });

    $('ul.genres li a').click( // category slider
        function() {
            var checkElement = $(this).next();
            if((checkElement.is('ul')) && (!checkElement.is(':visible'))) {
                $('.genres li ul').slideUp(150);
                $(this).next().slideToggle(150);
            }
        }
    );

    $('ul.genres ul li a').click( // get stations by category
        function() {
            $.ajax({
                type: 'GET',
                url: 'index.php',
                data: 'action=get_genre_stations&id=' + $(this).parent().attr('id') + '&name=' + $(this).parent().attr('val'),
                success: function(data){
                    $('.stlist').fadeOut(400, function () {
                        $('.stlist').html(data);
                        $('.stlist').fadeIn(400);
                    });
                }
            });
        }
    );
});
function play(id) { // play function
    $('#rplayer').load('index.php?action=play&id=' + id, function() {});
    return false;
}
function get_stations_by_keyword() { // get stations by keyword
    var keyword = $('#search').val().replace(/ /g,"+");
    $.ajax({
        type: 'GET',
        url: 'index.php',
        data: 'action=get_keyword_stations&key=' + keyword,
        success: function(data){
            $('.stlist').fadeOut(400, function () {
                $('.stlist').html(data);
                $('.stlist').fadeIn(400);
            });
        }
    });
}

Как видите — там нет ничего сложного. Всего несколько обработчиков событий и две новые функции (для воспроизведения радиостанций и поиска станций по ключевому слову).

Шаг 4. PHP

index.php

<?php

// set error reporting level
if (version_compare(phpversion(), '5.3.0', '>=') == 1)
  error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED);
else
  error_reporting(E_ALL & ~E_NOTICE);

$aStations = array(
    0 => array(
        'category' => 31,
        'name' => 'EuroDance',
        'desc' => 'The newest and best of Eurodance hits',
        'url' => 'http://www.di.fm/eurodance',
        'br' => 96,
        'stream' => 'http://scfire-mtc-aa06.stream.aol.com:80/stream/1024'
    ),
    1 => array (
        'category' => 34,
        'name' => 'House',
        'desc' => 'Silky sexy deep house music direct from New York city!',
        'url' => 'http://www.di.fm/house',
        'br' => 96,
        'stream' => 'http://scfire-ntc-aa04.stream.aol.com:80/stream/1007'
    ),
    2 => array (
        'category' => 13,
        'name' => 'Trance',
        'desc' => 'The hottest, freshest trance music from around the globe!',
        'url' => 'http://www.di.fm/trance',
        'br' => 96,
        'stream' => 'http://scfire-ntc-aa04.stream.aol.com:80/stream/1003'
    ),
    3 => array (
        'category' => 51,
        'name' => 'Electro House',
        'desc' => 'An eclectic mix of electro and dirty house',
        'url' => 'http://www.di.fm/electro',
        'br' => 96,
        'stream' => 'http://scfire-ntc-aa04.stream.aol.com:80/stream/1025'
    )
);

function searchByCat($iCat, $aStations) {
    $aRes = array();
    foreach ($aStations as $i => $aInfo) {
        if ($aInfo['category'] == $iCat) {
            $aRes[$i] = $aInfo;
        }
    }
    return $aRes;
}
function searchByKeyword($sKey, $aStations) {
    $aRes = array();
    foreach ($aStations as $i => $aInfo) {
        if (false !== strpos($aInfo['name'], $sKey) || false !== strpos($aInfo['desc'], $sKey)) {
            $aRes[$i] = $aInfo;
        }
    }
    return $aRes;
}

function parseStationList($aData) {
    $sStations = '';
    if (is_array($aData) && count($aData) > 0) {
        foreach ($aData as $i => $a) {
            $sStationId = $i;
            $sStationBr = (int)$a['br'];
            $sStationName = $a['name'];
            $sStationDesc = $a['desc'];
            $sStationUrl = $a['url'];

            $sThumb = 'media/'.($sStationId+1).'.png';
            $sStations .= <<<EOF
<li>
    <a href="{$sStationId}" onclick="return play('{$sStationId}'); return false;"><img alt="{$sStationName}" src="{$sThumb}" title="{$sStationName}"></a>
    <div class="i">
        <p>Bitrate: {$sStationBr}</p>
    </div>
    <p class="channel"><a href="{$sStationId}" onclick="return play('{$sStationId}'); return false;">{$sStationName}</a></p>
    <p class="track">{$sStationDesc}</p>
    <p class="label">{$sStationUrl}</p>
</li>
EOF;
        }
    }
    $sStations = ($sStations == '') ? '<li>Nothing found</li>' : $sStations;
    return '<ul>' . $sStations . '</ul>';
}

switch ($_GET['action']) {
    case 'play':
        $i = (int)$_GET['id'];

        $aInfo = $aStations[$i];
        $aVars = array (
            '__stream__' => $aInfo['stream'],
            '__title__' => $aInfo['name']
        );
        echo strtr(file_get_contents('templates/radio.html'), $aVars); exit;
        break;
    case 'get_genre_stations':
        $i = (int)$_GET['id'];

        $aSearch = searchByCat($i, $aStations);

        $sStations = parseStationList($aSearch);
        header('Content-Type: text/html; charset=utf-8');
        echo $sStations; exit;
        break;
    case 'get_keyword_stations':
        $sKey = $_GET['key'];

        $aSearch = searchByKeyword($sKey, $aStations);

        $sStations = parseStationList($aSearch);
        header('Content-Type: text/html; charset=utf-8');
        echo $sStations; exit;
        break;
}

$sLastStations = parseStationList($aStations);
echo strtr(file_get_contents('templates/main_page.html'), array('__stations__' => $sLastStations));

Сначала я подготовил список наших радиостанций (всего 4 станции). Затем две функции поиска: searchByCat и searchByKeyword. Далее, специальная функция ‘parseStationList’, которая преобразует массив с отфильтрованными станциями в его HTML-представление. Наконец, небольшой случай переключения для управления нашими внутренними командами ajax.

Вывод

Вы всегда можете улучшить наш сценарий и поделиться своими идеями. Буду рад видеть ваши благодарности и комментарии. Удачи!