(function () { var currentRecognition; function shuffleArray(array) { var currentIndex = array.length, temporaryValue, randomIndex; // While there remain elements to shuffle... while (0 !== currentIndex) { // Pick a remaining element... randomIndex = Math.floor(Math.random() * currentIndex); currentIndex -= 1; // And swap it with the current element. temporaryValue = array[currentIndex]; array[currentIndex] = array[randomIndex]; array[randomIndex] = temporaryValue; } return array; } function getSampleCommands() { var deferred = DeferredBuilder.Deferred(); var commands = []; //commands.push('show my movies'); //commands.push('pull up my tv shows'); commands.push('play my latest episodes'); commands.push('play next up'); commands.push('shuffle my favorite songs'); commands.push('show my tv guide'); commands.push('pull up my recordings'); commands.push('control chromecast'); commands.push('control [device name]'); commands.push('turn on display mirroring'); commands.push('turn off display mirroring'); commands.push('toggle display mirroring'); deferred.resolveWith(null, [shuffleArray(commands)]); return deferred.promise(); } function processText(text) { var deferred = DeferredBuilder.Deferred(); processTextInternal(text, deferred); return deferred.promise(); } function parseContext(text, result) { text = text.toLowerCase(); var i, length; for (i = 0, length = result.removeWords.length; i < length; i++) { text = text.replace(result.removeWords[i], ''); } text = text.trim(); var removeAtStart = [ 'my' ]; for (i = 0, length = removeAtStart.length; i < length; i++) { if (text.indexOf(removeAtStart[i]) == 0) { text = text.substring(removeAtStart[i].length); } } result.what = text; text = text.trim(); var words = text.toLowerCase().split(' '); if (words.indexOf('favorite') != -1) { result.filters.push('favorite'); } if (text.indexOf('latest movies') != -1 || text.indexOf('latest films') != -1) { result.sortby = 'datecreated'; result.sortorder = 'Descending'; result.filters.push('unplayed'); result.itemType = 'Movie'; return; } if (text.indexOf('latest episodes') != -1) { result.sortby = 'datecreated'; result.sortorder = 'Descending'; result.filters.push('unplayed'); result.itemType = 'Episode'; return; } if (text.indexOf('next up') != -1) { result.category = 'nextup'; return; } if (text.indexOf('movies') != -1 || text.indexOf('films') != -1) { result.itemType = 'Movie'; return; } if (text.indexOf('shows') != -1 || text.indexOf('series') != -1) { result.itemType = 'Series'; return; } if (text.indexOf('songs') != -1) { result.itemType = 'Audio'; return; } } function parseText(text) { var result = { action: '', itemName: '', itemType: '', category: '', filters: [], removeWords: [], sortby: '', sortorder: 'Ascending', limit: null, userId: Dashboard.getCurrentUserId() }; var textLower = text.toLowerCase(); var words = text.toLowerCase().split(' '); var displayWords = [ 'show', 'pull up', 'display', 'go to', 'view' ]; if (displayWords.filter(function (w) { return textLower.indexOf(w) == 0; }).length) { if (words.indexOf('guide') != -1) { result.action = 'show'; result.category = 'tvguide'; } if (words.indexOf('recordings') != -1) { result.action = 'show'; result.category = 'recordings'; } result.removeWords = displayWords; return result; } var searchWords = [ 'search', 'search for', 'find', 'query' ]; if (searchWords.filter(function (w) { return textLower.indexOf(w) == 0; }).length) { // Search result.action = 'search'; result.removeWords = searchWords; return result; } var playWords = [ 'play', 'watch' ]; if (playWords.filter(function (w) { return textLower.indexOf(w) == 0; }).length) { // Play result.action = 'play'; result.removeWords = playWords; return result; } var controlWords = [ 'use', 'control' ]; if (controlWords.filter(function (w) { return textLower.indexOf(w) == 0; }).length) { // Play result.action = 'control'; result.removeWords = controlWords; return result; } var enableWords = [ 'enable', 'turn on' ]; if (enableWords.filter(function (w) { return textLower.indexOf(w) == 0; }).length) { // Play result.action = 'enable'; result.removeWords = enableWords; return result; } var disableWords = [ 'disable', 'turn off' ]; if (disableWords.filter(function (w) { return textLower.indexOf(w) == 0; }).length) { // Play result.action = 'disable'; result.removeWords = disableWords; return result; } var toggleWords = [ 'toggle' ]; if (toggleWords.filter(function (w) { return textLower.indexOf(w) == 0; }).length) { // Play result.action = 'toggle'; result.removeWords = toggleWords; return result; } if (words.indexOf('shuffle') != -1) { // Play result.action = 'shuffle'; result.removeWords.push('shuffle'); return result; } if (words.indexOf('record') != -1) { // Record result.action = 'record'; result.removeWords.push('record'); return result; } if (words.indexOf('guide') != -1) { result.action = 'show'; result.category = 'tvguide'; return result; } return result; } function processTextInternal(text, deferred) { var result = parseText(text); switch (result.action) { case 'show': parseContext(text, result); showCommand(result); break; case 'play': parseContext(text, result); playCommand(result); break; case 'shuffle': parseContext(text, result); playCommand(result, true); break; case 'search': parseContext(text, result); playCommand(result); break; case 'control': parseContext(text, result); controlCommand(result); break; case 'enable': parseContext(text, result); enableCommand(result); break; case 'disable': parseContext(text, result); disableCommand(result); break; case 'toggle': parseContext(text, result); toggleCommand(result); break; default: deferred.reject(); return; } deferred.resolve(); } function showCommand(result) { if (result.category == 'tvguide') { Dashboard.navigate('livetv.html?tab=1'); return; } if (result.category == 'recordings') { Dashboard.navigate('livetv.html?tab=3'); return; } } function enableCommand(result) { var what = result.what.toLowerCase(); if (what.indexOf('mirror') != -1) { MediaController.enableDisplayMirroring(true); } } function disableCommand(result) { var what = result.what.toLowerCase(); if (what.indexOf('mirror') != -1) { MediaController.enableDisplayMirroring(false); } } function toggleCommand(result) { var what = result.what.toLowerCase(); if (what.indexOf('mirror') != -1) { MediaController.toggleDisplayMirroring(); } } function controlCommand(result) { MediaController.trySetActiveDeviceName(result.what); } function playCommand(result, shuffle) { var query = { Limit: result.limit || 100, UserId: result.userId, ExcludeLocationTypes: "Virtual" }; if (result.category == 'nextup') { ApiClient.getNextUpEpisodes(query).done(function (queryResult) { playItems(queryResult.Items, shuffle); }); return; } if (shuffle) { result.sortby = result.sortby ? 'Random,' + result.sortby : 'Random'; } query.SortBy = result.sortby; query.SortOrder = result.sortorder; query.Recursive = true; if (result.filters.indexOf('unplayed') != -1) { query.IsPlayed = false; } if (result.filters.indexOf('played') != -1) { query.IsPlayed = true; } if (result.filters.indexOf('favorite') != -1) { query.Filters = 'IsFavorite'; } if (result.itemType) { query.IncludeItemTypes = result.itemType; } ApiClient.getItems(Dashboard.getCurrentUserId(), query).done(function (queryResult) { playItems(queryResult.Items, shuffle); }); } function playItems(items, shuffle) { if (shuffle) { items = shuffleArray(items); } items = items.map(function (i) { return i.Id; }); if (items.length) { MediaController.play({ ids: items }); } else { Dashboard.alert({ message: Globalize.translate('MessageNoItemsFound') }); } } function searchCommand(result) { } function renderSampleCommands(elem, commands) { commands.length = Math.min(commands.length, 4); commands = commands.map(function (c) { return '
' + c + '
'; }).join(''); $('.exampleCommands', elem).html(commands); } function showVoiceHelp() { var elem = $('.voiceInputHelp'); if (elem.length) { $('.unrecognizedCommand').hide(); $('.defaultVoiceHelp').show(); return; } var html = ''; var getCommandsPromise = getSampleCommands(); html += '
'; html += '
'; html += '
'; html += '
'; html += '

' + Globalize.translate('HeaderSaySomethingLike') + '

'; html += '
'; html += '
'; // defaultVoiceHelp html += '
'; html += ''; html += '' + Globalize.translate('ButtonCancel') + ''; // voiceHelpContent html += '
'; // voiceInputHelpInner html += '
'; // voiceInputHelp html += '
'; $(document.body).append(html); elem = $('.voiceInputHelp'); getCommandsPromise.done(function (commands) { renderSampleCommands(elem, commands); }); $('.btnCancel', elem).on('click', cancelListener); $('.btnRetry', elem).on('click', startListening); } function showUnrecognizedCommandHelp() { $('.unrecognizedCommand').show(); $('.defaultVoiceHelp').hide(); } function hideVoiceHelp() { $('.voiceInputHelp').remove(); } function cancelListener() { destroyCurrentRecognition(); hideVoiceHelp(); } function destroyCurrentRecognition() { var recognition = currentRecognition; if (recognition) { recognition.abort(); currentRecognition = null; } } function processTranscript(text) { $('.voiceInputText').html(text); if (text || AppInfo.isNativeApp) { $('.blockedMessage').hide(); } else { $('.blockedMessage').show(); } processText(text).done(hideVoiceHelp).fail(showUnrecognizedCommandHelp); } function startListening() { destroyCurrentRecognition(); Dashboard.importCss('voice/voice.css'); startListeningInternal(); } function startListeningInternal() { var recognition = new (window.SpeechRecognition || window.webkitSpeechRecognition)(); //recognition.continuous = true; //recognition.interimResults = true; recognition.onresult = function (event) { if (event.results.length > 0) { processTranscript(event.results[0][0].transcript || ''); } }; recognition.onerror = function () { processTranscript(''); }; recognition.onnomatch = function () { processTranscript(''); }; recognition.start(); currentRecognition = recognition; showVoiceHelp(); } window.VoiceInputManager = { isSupported: function () { return window.SpeechRecognition || window.webkitSpeechRecognition; }, startListening: startListening }; })();