В этой статье хотелось бы описать свой опыт по применению web speech api в браузере Google Chrome для реализации голосового поиска и автоматического воспроизведения видеороликов с канала Youtube. Для демонстрации данного функционала нам понадобиться сделать следующие шаги:
Как сделать все вышеперечисленное, здесь описывать не буду, так как на эти темы полно статей. Принцип реализации такой:
Топология сети: Internet приходит в wan порт Wi-Fi роутера, а к нему подключаются:
Для полноценной работы скрипта, нужно еще создать папку images и положить в неё картинки с микрофончиками, которые можно взять здесь и здесь.
Данный скрипт делает всего два действия — распознает фразу и отправляет её AJAX запросом PHP скрипту. Также необходимо обратить внимание на то, что кодировка во всех скриптах должна быть UTF-8 (если делаете в Windows, то UTF-8 без ВОМ).
В данном скрипте в самом конце нужно будет отредактировать $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
- Установить набор: Apache2, PHP5(пакет curl обязательно).
- Иметь в наличии мультимедиа центр Dune HD или установить XBMC и настроить его для работы в сети INTERNET.
- Получить Youtube API Key для выполнения поисковых запросов.
Как сделать все вышеперечисленное, здесь описывать не буду, так как на эти темы полно статей. Принцип реализации такой:
- Распознаем фразу с помощью скрипта, написанного на JavaScript — работать будет только в Google Chrome.
- Ищем ролики, соответствующие поисковому запросу.
- Получаем прямые ссылки на ролики.
- Создаем плейлист из ссылок и названий роликов.
- Отправляем плейлист для воспроизведения на устройство.
Топология сети: Internet приходит в wan порт Wi-Fi роутера, а к нему подключаются:
- устройство, с которого будем управлять (планшет, смартфон, ноутбук).
- компьютер с web сервером Apache (если управление будет производится с него, то первое устройство может отсутствовать).
- собственно сам мультимедиа центр (программный — XBMC, или аппаратный — Dune HD).
Скрипт распознавания речи на JavaScript - index.html:
<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 - voice_search.php:
<?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=launch_media_url&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
Комментарии (5)
denv
13.11.2015 19:04Может быть кому интересно будет, perl + mojolicious = голосовой поиск сериалов на кино-дом + yandex speechkit, выдирает ссылку и запускает на XBMC. отрабатывает хорошо когда посторонних шумов нету. пробовал использовать pocketsphinx для выделения фразы «окей, дом» и запуска скрипта который уже идет через яндекс, так себе.
BOBS13
Идея интересная, я только и не особо понял XBMC — это программа? Или это какая-то аудио система со своим крутым ПО?
arbuzmaster
XBMC — мультимедиа центр, но только программный. То есть его можно установить практически на любую ОС. Новое название проекта xbmc — KODI. Это, что-то типа VLC только с большим функционалом. К стати VLC можно и здесь использовать, только нужно URL будет поменять, на тот который воспринимает VLC, там немного другие параметры.
BOBS13
Интересно, надо поробывать поискать его на мой телевизор — LG + Web OS)
arbuzmaster
На счет Web OS сказать ничего не могу — не знаю, но на Android точно есть, у меня на смартфоне стоит.