В этой статье хотелось бы описать свой опыт по применению web speech api в браузере Google Chrome для реализации голосового поиска и автоматического воспроизведения видеороликов с канала Youtube. Для демонстрации данного функционала нам понадобиться сделать следующие шаги:
- Установить набор: Apache2, PHP5(пакет curl обязательно).
- Иметь в наличии мультимедиа центр Dune HD или установить XBMC и настроить его для работы в сети INTERNET.
- Получить Youtube API Key для выполнения поисковых запросов.
Как сделать все вышеперечисленное, здесь описывать не буду, так как на эти темы полно статей. Принцип реализации такой:
- Распознаем фразу с помощью скрипта, написанного на JavaScript — работать будет только в Google Chrome.
- Ищем ролики, соответствующие поисковому запросу.
- Получаем прямые ссылки на ролики.
- Создаем плейлист из ссылок и названий роликов.
- Отправляем плейлист для воспроизведения на устройство.
Топология сети: Internet приходит в wan порт Wi-Fi роутера, а к нему подключаются:
- устройство, с которого будем управлять (планшет, смартфон, ноутбук).
- компьютер с web сервером Apache (если управление будет производится с него, то первое устройство может отсутствовать).
- собственно сам мультимедиа центр (программный — XBMC, или аппаратный — Dune HD).
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ru" lang="ru"> <head> <title>Умный Дом</title> <script language="javascript" type="text/javascript"> /* Создание нового объекта XMLHttpRequest для общения с Web-сервером */ var xmlHttp = false; /*@cc_on @*/ /*@if (@_jscript_version >= 5) try { xmlHttp = new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) { try { xmlHttp = new ActiveXObject("Microsoft.XMLHTTP"); } catch (e2) { xmlHttp = false; } } @end @*/ if (!xmlHttp && typeof XMLHttpRequest != 'undefined') { xmlHttp = new XMLHttpRequest(); } </script> <style> * { font-family: Verdana, Arial, sans-serif; font-size: 20px; } a:link { color:#000; text-decoration: none; } a:visited { color:#000; } a:hover { color:#33F; } body { text-align: center; } .button { background: -webkit-linear-gradient(top,#008dfd 0,#0370ea 100%); border: 1px solid #076bd2; border-radius: 3px; color: #fff; display: none; font-size: 13px; font-weight: bold; line-height: 1.3; padding: 8px 25px; text-align: center; text-shadow: 1px 1px 1px #076bd2; letter-spacing: normal; } .final { color: black; padding-right: 3px; } .interim { color: gray; } .info { font-size: 14px; text-align: center; color: #777; display: none; } .sidebyside { display: inline-block; width: 45%; min-height: 40px; text-align: left; vertical-align: top; } #headline { font-size: 40px; font-weight: 300; } #info { font-size: 20px; text-align: center; color: #777; visibility: hidden; } #results { font-size: 14px; font-weight: bold; border: 1px solid #ddd; padding: 15px; text-align: left; min-height: 30px; width: 500px; margin: 0 auto; } #start_button { border: 0; padding: 0; background: url(images/mic.gif); width: 50px; height: 50px; cursor: pointer; vertical-align: top; } #info_speak_now, #info_no_speech, #info_no_microphone, #info_upgrade { display: none; } </style> <meta charset="UTF-8" /> </head> <body> <div id="messages"> <input type="button" id="start_button" onclick="startButton(event);" /> <!-- сообщения на разные случаи --> <p id="info_start">Кликни на микрофон чтобы начать раздавать команды.</p> <p id="info_speak_now">Командуй!</p> <p id="info_no_speech">Голос не обнаружен.</p> <p id="info_no_microphone">Микрофон не найден.</p> <p id="info_upgrade">Твой браузер не поддерживает Web Speech API.</p> </div> <div id="results"> <span id="final_span" class="final"></span> </div> <script> var start_button = document.getElementById('start_button'), recognizing = false, // флаг идет ли распознование final_transcript = ''; // проверяем поддержку speach api if (!('webkitSpeechRecognition' in window)) { start_button.style.display = "none"; showInfo("info_upgrade"); } else { /* инициализируем api */ /* создаем объект */ var recognition = new webkitSpeechRecognition(); /* базовые настройки объекта */ recognition.lang = 'ru'; // язык, который будет распозноваться. Значение - lang code recognition.continuous = true; // не хотим чтобы когда пользователь прикратил говорить, распознование закончилось /* метод вызывается когда начинается распознование */ recognition.onstart = function() { recognizing = true; showInfo('info_speak_now'); // меняем инфо текст start_button.style.background = 'url(images/mic-animate.gif)'; // меняем вид кнопки }; /* обработчик ошибок */ recognition.onerror = function(event) { if (event.error == 'no-speech') { start_button.style.background = 'url(images/mic.gif)'; showInfo('info_no_speech'); } if (event.error == 'audio-capture') { start_button.style.background = 'url(images/mic.gif)'; showInfo('info_no_microphone'); } }; /* метод вызывается когда распознование закончено */ recognition.onend = function() { recognizing = false; recognition.start(); start_button.style.background = 'url(images/mic.gif)'; showInfo('info_start'); }; /* метод вызывается после каждой сказанной фразы. Параметра event используем атрибуты: - resultIndex - нижний индекс в результирующем массиве - results - массив всех результатов в текущей сессии */ recognition.onresult = function(event) { /* обход результирующего массива */ for (var i = event.resultIndex; i < event.results.length; ++i) { /* если фраза финальная (уже откорректированная) сохраняем в конечный результат */ if (event.results[i].isFinal) { final_transcript += event.results[i][0].transcript.toLowerCase(); } } final_span.innerHTML = final_transcript; var newText2 = final_transcript.replace(/(^\s+|\s+$)/g,''); var url = "/voice_search.php?q=" + encodeURI(newText2); xmlHttp.open("GET", url, true); xmlHttp.send(null); final_transcript = ''; // очищаем рапознанный текст }; } /* показ нужного сообщения */ function showInfo(id) { var messages = document.querySelectorAll('p'); for(i=0; i<messages.length; i++) messages[i].style.display = 'none'; document.getElementById(id).style.display = 'block'; } /* обработчик клика по микрофону */ function startButton(event) { if (recognizing) { // если запись уже идет, тогда останавливаем recognition.stop(); document.getElementById('final_span').innerHTML = ''; return; } recognition.start(); } </script> </body> </html>
Для полноценной работы скрипта, нужно еще создать папку images и положить в неё картинки с микрофончиками, которые можно взять здесь и здесь.
Данный скрипт делает всего два действия — распознает фразу и отправляет её AJAX запросом PHP скрипту. Также необходимо обратить внимание на то, что кодировка во всех скриптах должна быть UTF-8 (если делаете в Windows, то UTF-8 без ВОМ).
<?php // send info into core function send_info($info) { echo $info; } function send_req($url) { $ch = curl_init(); curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 5.1; rv:7.0.1) Gecko/20100101 Firefox/7.0.1"); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($ch, CURLOPT_HEADER, false); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_REFERER, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); $out = curl_exec($ch); curl_close($ch); return $out; } function get_video_url($videoId) { $url = 'http://www.youtube.com/get_video_info?&video_id='.$videoId.'&asv=3&el=detailpage&hl=en_US'; $first_found = ""; $last_found = ""; $first_quality = ""; $last_quality = ""; //$video_quality = 'medium'; $video_quality = 'hd1080'; $doc=send_req($url); $x=explode("&",$doc); $t=array(); $g=array(); $h=array(); foreach($x as $r) { $c=explode("=",$r); $n=$c[0]; $v=$c[1]; $y=urldecode($v); $t[$n]=$v; } $links = explode(',',urldecode($t['url_encoded_fmt_stream_map'])); $dlinks = array(); foreach ($links as $link) { parse_str($link,$linkarr); $itag = $linkarr['itag']; $quality = $linkarr['quality']; if (in_array($itag, array('18', '22', '37', '38'))) { if(isset($linkarr['s'])) { $linkarr['signature'] = file_get_contents('http://dune-club.info/echo?message=' . $linkarr['s']); unset($linkarr['s']); $dlinks[$linkarr['itag']] = $linkarr['url'] . "&signature=" . $linkarr['signature']; } else { $dlinks[$linkarr['itag']] = $linkarr['url']; $playback_url = $dlinks[$linkarr['itag']]; if ($first_found === "") { $first_found = $playback_url; $first_quality = $quality; } $last_found = $playback_url; $last_quality = $quality; if (($quality === $video_quality) || (($quality !== 'medium') && ($video_quality === 'hdonly'))) { $playback_url=(urldecode($playback_url)); return $playback_url; } } } } if (($last_found !== "") && ($video_quality !== 'hdonly')) { if ($video_quality === 'hd1080') { $first_found=(urldecode($first_found)); return $first_found; } else { $last_found=(urldecode($last_found)); return $last_found; } } else { //hd_print("--> video: $id; no mp4-stream."); return false; } } if(isset($_GET['q']) == false or $_GET['q']=="" ) { $url = "https://www.googleapis.com/youtube/v3/search?part=snippet&q=голосовое%20управление%20телевизором&type=video&maxResults=10&key=Youtube_API_key"; } else { $url = "https://www.googleapis.com/youtube/v3/search?part=snippet&q=".urlencode($_GET['q'])."&type=video&maxResults=10&key=Youtube_API_key"; } $res = json_decode(send_req($url)); if(isset($res->items) == false or ($res->items)=="" ) { $info="похоже что-то пошло не так!"; send_info($info); } else { $res = $res->items; //print_r($res); $fp = fopen('play_list.m3u', 'w+t'); $start="#EXTM3U\r\n"; fwrite($fp, $start); foreach ($res as $searchResult) { $title=($searchResult->snippet->title); $videoId = ($searchResult->id->videoId) ; $clip_url = get_video_url($videoId); if(isset($clip_url) == false or $clip_url=="") { $info="клип не найден!"; send_info($info); } else { $clip="#EXTINF:-1,$title\r\n$clip_url\r\n"; fwrite($fp, $clip); } } fclose($fp); $info="плейлист создан"; send_info($info); //url для Dune HD $url="http://ip_addr_dune/cgi-bin/do?cmd=start_file_playback&media_url=http://server_ip/play_list.m3u"; //url для XBMC $url="http://логин:пароль@ip-адрес:8080/jsonrpc?request={"jsonrpc":"2.0","id":"1","method":"Player.Open","params":{"item":{"file":"http://server_ip/play_list.m3u"}}}"; $curl = curl_init(); curl_setopt($curl, CURLOPT_URL, $url); curl_setopt($curl, CURLOPT_RETURNTRANSFER,true); $out = curl_exec($curl); curl_close($curl); } ?>
В данном скрипте в самом конце нужно будет отредактировать $url под настройки вашего мультимедиа центра и удалить лишний, а также исправить текст server_ip на ip адрес вашего Apache сервера и вставить свой Youtube_API_Key. Что происходит здесь: из скрипта распознавания речи сюда приходит текст распознанной фразы, далее с помощью Youtube API v3 производится поиск видео роликов подходящих под поисковой запрос, получив ссылки на ролики, мы пропускаем их через цикл, в котором извлекаются полные пути до видеофайлов, которые и записываются в плейлист play_list.m3u. Данный скрипт не претендует на идеальный код, так как написан чисто в ознакомительных целях, поэтому всевозможные проверки здесь отсутствуют.
Вот и всё, теперь заходим на наш web сервер по его ip адресу. Можно заходить с любого устройства: планшет, смартфон, ноутбук, единственное, что я заметил — в последнее время на смартфонах с Android, скрипт распознавания речи при отсутствии её, отправляет фразу повторно, с чем это связанно пока не понятно, но раньше такого не было.
На основе данного материала можно сделать еще много интересных вещей, таких как голосовой поиск музыки в VK и управления 1-wire устройствами. В общем пробуйте, если что не получится, то спрашивайте с удовольствием отвечу на все ваши вопросы.
P.S.: Статья написана по материалам:
W3C Web Speech API
YouTube api v3
Скрипт получения прямых ссылок взят из приложения YouTube для Dune HD и немного доработан под свои нужды. Для тех, кто хочет просто попробовать управлять мультимедиа центрами, без написания скриптов — можно почитать здесь. Результат моей работы на YouTube
ссылка на оригинал статьи http://habrahabr.ru/post/270809/
Добавить комментарий