1
0
Fork 0
mirror of https://github.com/jellyfin/jellyfin-web synced 2025-03-30 19:56:21 +00:00

use shared voice components

This commit is contained in:
Luke Pulverenti 2016-07-06 15:25:58 -04:00
parent aece94029c
commit 07da989bfd
28 changed files with 413 additions and 185 deletions

View file

@ -15,12 +15,12 @@
}, },
"devDependencies": {}, "devDependencies": {},
"ignore": [], "ignore": [],
"version": "1.4.66", "version": "1.4.68",
"_release": "1.4.66", "_release": "1.4.68",
"_resolution": { "_resolution": {
"type": "version", "type": "version",
"tag": "1.4.66", "tag": "1.4.68",
"commit": "16a14d612b193c7a375e2391aa9c2370add0e32d" "commit": "b14aefcc4a69f3dc2dfd530ddd0b99791719d259"
}, },
"_source": "https://github.com/MediaBrowser/emby-webcomponents.git", "_source": "https://github.com/MediaBrowser/emby-webcomponents.git",
"_target": "^1.2.0", "_target": "^1.2.0",

View file

@ -0,0 +1,242 @@
define(['playbackManager', 'focusManager', 'embyRouter'], function (playbackManager, focusManager, embyRouter) {
var lastInputTime = new Date().getTime();
function notify() {
lastInputTime = new Date().getTime();
}
function idleTime() {
return new Date().getTime() - lastInputTime;
}
function select(sourceElement) {
sourceElement.click();
}
var eventListenerCount = 0;
function on(scope, fn) {
eventListenerCount++;
scope.addEventListener('command', fn);
}
function off(scope, fn) {
if (eventListenerCount) {
eventListenerCount--;
}
scope.removeEventListener('command', fn);
}
var commandTimes = {};
function checkCommandTime(command) {
var last = commandTimes[command] || 0;
var now = new Date().getTime();
if ((now - last) < 1000) {
return false;
}
commandTimes[command] = now;
return true;
}
function handleCommand(name, options) {
notify();
var sourceElement = (options ? options.sourceElement : null);
if (sourceElement) {
sourceElement = focusManager.focusableParent(sourceElement);
}
sourceElement = sourceElement || document.activeElement || window;
if (eventListenerCount) {
var customEvent = new CustomEvent("command", {
detail: {
command: name
},
bubbles: true,
cancelable: true
});
var eventResult = sourceElement.dispatchEvent(customEvent);
if (!eventResult) {
// event cancelled
return;
}
}
switch (name) {
case 'up':
focusManager.moveUp(sourceElement);
break;
case 'down':
focusManager.moveDown(sourceElement);
break;
case 'left':
focusManager.moveLeft(sourceElement);
break;
case 'right':
focusManager.moveRight(sourceElement);
break;
case 'home':
embyRouter.goHome();
break;
case 'settings':
embyRouter.showSettings();
break;
case 'back':
embyRouter.back();
break;
case 'forward':
// TODO
break;
case 'select':
select(sourceElement);
break;
case 'pageup':
// TODO
break;
case 'pagedown':
// TODO
break;
case 'end':
// TODO
break;
case 'menu':
case 'info':
// TODO
break;
case 'next':
if (playbackManager.isPlayingVideo()) {
playbackManager.nextChapter();
} else if (playbackManager.isPlaying()) {
playbackManager.nextTrack();
}
break;
case 'previous':
if (playbackManager.isPlayingVideo()) {
playbackManager.previousChapter();
} else if (playbackManager.isPlaying()) {
playbackManager.previousTrack();
}
break;
case 'guide':
embyRouter.showGuide();
break;
case 'recordedtv':
embyRouter.showRecordedTV();
break;
case 'record':
// TODO
break;
case 'livetv':
embyRouter.showLiveTV();
break;
case 'mute':
playbackManager.mute();
break;
case 'unmute':
playbackManager.unMute();
break;
case 'togglemute':
playbackManager.toggleMute();
break;
case 'volumedown':
playbackManager.volumeDown();
break;
case 'volumeup':
playbackManager.volumeUp();
break;
case 'play':
playbackManager.unpause();
break;
case 'pause':
playbackManager.pause();
break;
case 'playpause':
playbackManager.playPause();
break;
case 'stop':
if (checkCommandTime('stop')) {
playbackManager.stop();
}
break;
case 'changezoom':
// TODO
break;
case 'changeaudiotrack':
// TODO
break;
case 'changesubtitletrack':
break;
case 'search':
embyRouter.showSearch();
break;
case 'favorites':
embyRouter.showFavorites();
break;
case 'fastforward':
playbackManager.fastForward();
break;
case 'rewind':
playbackManager.rewind();
break;
case 'togglefullscreen':
// TODO
break;
case 'disabledisplaymirror':
playbackManager.enableDisplayMirroring(false);
break;
case 'enabledisplaymirror':
playbackManager.enableDisplayMirroring(true);
break;
case 'toggledisplaymirror':
playbackManager.toggleDisplayMirroring();
break;
case 'movies':
// TODO
break;
case 'music':
// TODO
break;
case 'tv':
// TODO
break;
case 'latestepisodes':
// TODO
break;
case 'nowplaying':
// TODO
break;
case 'upcomingtv':
// TODO
break;
case 'nextup':
// TODO
break;
default:
break;
}
}
document.addEventListener('click', notify);
return {
trigger: handleCommand,
handle: handleCommand,
notify: notify,
idleTime: idleTime,
on: on,
off: off
};
});

View file

@ -27,6 +27,12 @@ define(['loading', 'viewManager', 'skinManager', 'pluginManager', 'backdrop', 'b
}, },
showLiveTV: function () { showLiveTV: function () {
skinManager.getCurrentSkin().showLiveTV(); skinManager.getCurrentSkin().showLiveTV();
},
showRecordedTV: function () {
skinManager.getCurrentSkin().showRecordedTV();
},
showFavorites: function () {
skinManager.getCurrentSkin().showFavorites();
} }
}; };

View file

@ -86,5 +86,11 @@
"ReplaceAllMetadata": "Replace all metadata", "ReplaceAllMetadata": "Replace all metadata",
"SearchForMissingMetadata": "Search for missing metadata", "SearchForMissingMetadata": "Search for missing metadata",
"LabelRefreshMode": "Refresh mode:", "LabelRefreshMode": "Refresh mode:",
"NoItemsFound": "No items found.",
"HeaderSaySomethingLike": "Say Something Like...",
"ButtonTryAgain": "Try Again",
"HeaderYouSaid": "You Said...",
"MessageWeDidntRecognizeCommand": "We're sorry, we didn't recognize that command.",
"MessageIfYouBlockedVoice": "If you denied voice access to the app you'll need to reconfigure before trying again.",
"RefreshDialogHelp": "Metadata is refreshed based on settings and internet services that are enabled in the Emby Server dashboard." "RefreshDialogHelp": "Metadata is refreshed based on settings and internet services that are enabled in the Emby Server dashboard."
} }

View file

@ -0,0 +1,10 @@
define(['playbackManager'], function (playbackManager) {
return function (result) {
result.success = true;
if (result.properties.devicename) {
playbackManager.trySetActiveDeviceName(result.properties.devicename);
}
return;
}
});

View file

@ -1,10 +1,10 @@
define([], function () { define(['inputManager'], function (inputManager) {
return function (result) { return function (result) {
result.success = true; result.success = true;
switch (result.item.deviceid) { switch (result.item.deviceid) {
case 'displaymirroring': case 'displaymirroring':
MediaController.enableDisplayMirroring(false); inputManager.trigger('disabledisplaymirror');
break; break;
default: default:
result.success = false; result.success = false;

View file

@ -1,10 +1,10 @@
define([], function () { define(['inputManager'], function (inputManager) {
return function (result) { return function (result) {
result.success = true; result.success = true;
switch (result.item.deviceid) { switch (result.item.deviceid) {
case 'displaymirroring': case 'displaymirroring':
MediaController.enableDisplayMirroring(true); inputManager.trigger('enabledisplaymirror');
break; break;
default: default:
result.success = false; result.success = false;

View file

@ -1,5 +1,4 @@
 define(['connectionManager', 'playbackManager', 'globalize'], function (connectionManager, playbackManager, globalize) {
define([], function () {
/// <summary> Play items. </summary> /// <summary> Play items. </summary>
/// <param name="items"> The items. </param> /// <param name="items"> The items. </param>
@ -16,13 +15,13 @@ define([], function () {
}); });
if (items.length) { if (items.length) {
MediaController.play({ playbackManager.play({
ids: items ids: items
}); });
} }
else { else {
require(['toast'], function (toast) { require(['toast'], function (toast) {
toast(Globalize.translate('MessageNoItemsFound')); toast(globalize.translate('sharedcomponents#NoItemsFound'));
}); });
} }
} }
@ -63,9 +62,10 @@ define([], function () {
query.IncludeItemTypes = result.item.itemType; query.IncludeItemTypes = result.item.itemType;
} }
var apiClient = connectionManager.currentApiClient();
if (result.item.sourceid === 'nextup') { if (result.item.sourceid === 'nextup') {
ApiClient.getNextUpEpisodes(query).then(function (queryResult) { apiClient.getNextUpEpisodes(query).then(function (queryResult) {
playItems(queryResult.Items, result.item.shuffle); playItems(queryResult.Items, result.item.shuffle);
@ -93,7 +93,7 @@ define([], function () {
} }
ApiClient.getItems(Dashboard.getCurrentUserId(), query).then(function (queryResult) { apiClient.getItems(apiClient.getCurrentUserId(), query).then(function (queryResult) {
playItems(queryResult.Items, result.item.shuffle); playItems(queryResult.Items, result.item.shuffle);
}); });

View file

@ -1,4 +1,4 @@
define([], function () { define(['inputManager'], function (inputManager) {
return function (result) { return function (result) {
switch (result.item.deviceid) { switch (result.item.deviceid) {

View file

@ -0,0 +1,98 @@
define(['inputManager', 'connectionManager', 'embyRouter'], function (inputManager, connectionManager, embyRouter) {
return function (result) {
result.success = true;
switch (result.item.sourceid) {
case 'music':
inputManager.trigger('music');
break;
case 'movies':
if (result.properties.movieName) {
//TODO: Find a way to display movie
var query = {
Limit: 1,
UserId: result.userId,
ExcludeLocationTypes: "Virtual",
NameStartsWith: result.item.itemType
};
if (result.item.itemType) {
query.IncludeItemTypes = result.item.itemType;
}
var apiClient = connectionManager.currentApiClient();
apiClient.getItems(apiClient.getCurrentUserId(), query).then(function (queryResult) {
if (queryResult.Items.length) {
embyRouter.showItem(queryResult.Items[0]);
}
});
} else {
inputManager.trigger('movies');
}
break;
case 'tvseries':
inputManager.trigger('tv');
break;
case 'livetv':
var act = result.item.menuid;
if (act) {
if (act.indexOf('livetv') != -1) {
inputManager.trigger('livetv');
} else if (act.indexOf('guide') != -1) {
inputManager.trigger('guide');
} else if (act.indexOf('channels') != -1) {
inputManager.trigger('livetv');
} else if (act.indexOf('recordings') != -1) {
inputManager.trigger('recordedtv');
} else if (act.indexOf('scheduled') != -1) {
inputManager.trigger('recordedtv');
} else if (act.indexOf('series') != -1) {
inputManager.trigger('recordedtv');
} else {
inputManager.trigger('livetv');
}
} else {
inputManager.trigger('livetv');
}
break;
case 'recordings':
inputManager.trigger('recordedtv');
break;
case 'latestepisodes':
inputManager.trigger('latestepisodes');
case 'home':
var act = result.item.menuid;
if (act) {
if (act.indexOf('home') != -1) {
inputManager.trigger('home');
}
else if (act.indexOf('nextup') != -1) {
inputManager.trigger('nextup');
}
else if (act.indexOf('favorites') != -1) {
inputManager.trigger('favorites');
} else if (act.indexOf('upcoming') != -1) {
inputManager.trigger('upcomingtv');
}
else if (act.indexOf('nowplaying') != -1) {
inputManager.trigger('nowplaying');
}
else {
inputManager.trigger('home');
}
} else {
inputManager.trigger('home');
}
case 'group':
break;
default:
result.success = false;
return;
}
}
});

View file

@ -1,10 +1,10 @@
define([], function () { define(['inputManager'], function (inputManager) {
return function (result) { return function (result) {
result.success = true; result.success = true;
switch (result.item.deviceid) { switch (result.item.deviceid) {
case 'displaymirroring': case 'displaymirroring':
MediaController.toggleDisplayMirroring(); inputManager.trigger('toggledisplaymirror');
break; break;
default: default:
result.success = false; result.success = false;

View file

@ -1,4 +1,4 @@
define(['dialogHelper', 'voice/voicereceiver', 'voice/voiceprocessor', 'globalize', 'emby-button', 'css!./voice.css', 'material-icons'], function (dialogHelper, voicereceiver, voiceprocessor, globalize) { define(['dialogHelper', './voicereceiver', './voiceprocessor', 'globalize', 'emby-button', 'css!./voice.css', 'material-icons', 'css!./../formdialog'], function (dialogHelper, voicereceiver, voiceprocessor, globalize) {
var lang = 'en-US'; var lang = 'en-US';
@ -126,7 +126,7 @@ define(['dialogHelper', 'voice/voicereceiver', 'voice/voiceprocessor', 'globaliz
html += '<div class="defaultVoiceHelp">'; html += '<div class="defaultVoiceHelp">';
html += '<h1 style="margin-bottom:1.25em;">' + globalize.translate('HeaderSaySomethingLike') + '</h1>'; html += '<h1 style="margin-bottom:1.25em;">' + globalize.translate('sharedcomponents#HeaderSaySomethingLike') + '</h1>';
html += '<div class="exampleCommands">'; html += '<div class="exampleCommands">';
html += '</div>'; html += '</div>';
@ -135,23 +135,23 @@ define(['dialogHelper', 'voice/voicereceiver', 'voice/voiceprocessor', 'globaliz
html += '</div>'; html += '</div>';
html += '<div class="unrecognizedCommand hide">'; html += '<div class="unrecognizedCommand hide">';
html += '<h1>' + globalize.translate('HeaderYouSaid') + '</h1>'; html += '<h1>' + globalize.translate('sharedcomponents#HeaderYouSaid') + '</h1>';
html += html +=
'<p class="exampleCommand voiceInputContainer"><i class="fa fa-quote-left"></i><span class="voiceInputText exampleCommandText"></span><i class="fa fa-quote-right"></i></p>'; '<p class="exampleCommand voiceInputContainer"><i class="fa fa-quote-left"></i><span class="voiceInputText exampleCommandText"></span><i class="fa fa-quote-right"></i></p>';
html += '<p>' + globalize.translate('MessageWeDidntRecognizeCommand') + '</p>'; html += '<p>' + globalize.translate('sharedcomponents#MessageWeDidntRecognizeCommand') + '</p>';
html += '<br/>'; html += '<br/>';
html += '<button is="emby-button" type="button" class="submit block btnRetry raised"><i class="md-icon">mic</i><span>' + html += '<button is="emby-button" type="button" class="submit block btnRetry raised"><i class="md-icon">mic</i><span>' +
globalize.translate('ButtonTryAgain') + globalize.translate('sharedcomponents#ButtonTryAgain') +
'</span></button>'; '</span></button>';
html += '<p class="blockedMessage hide">' + html += '<p class="blockedMessage hide">' +
globalize.translate('MessageIfYouBlockedVoice') + globalize.translate('sharedcomponents#MessageIfYouBlockedVoice') +
'<br/><br/></p>'; '<br/><br/></p>';
html += '</div>'; html += '</div>';
html += html +=
'<button is="emby-button" type="button" class="raised block btnCancelVoiceInput cancel"><i class="md-icon">close</i><span>' + globalize.translate('ButtonCancel') + '</span></button>'; '<button is="emby-button" type="button" class="raised block btnCancelVoiceInput cancel"><i class="md-icon">close</i><span>' + globalize.translate('sharedcomponents#ButtonCancel') + '</span></button>';
html += '</div>'; html += '</div>';
html += '</div>'; html += '</div>';

View file

@ -1,4 +1,4 @@
define(['voice/voicecommands.js', 'voice/grammarprocessor.js'], function (voicecommands, grammarprocessor) { define(['./voicecommands.js', './grammarprocessor.js', 'require'], function (voicecommands, grammarprocessor, require) {
var commandgroups; var commandgroups;
@ -11,21 +11,11 @@
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
var file = "grammar"; var file = "grammar";
//if (language && language.length > 0)
// file = language;
var xhr = new XMLHttpRequest(); require(['text!./grammar/' + file + '.json'], function (response) {
xhr.open('GET', "voice/grammar/" + file + ".json", true); commandgroups = JSON.parse(response);
xhr.onload = function (e) {
commandgroups = JSON.parse(this.response);
resolve(commandgroups); resolve(commandgroups);
} });
xhr.onerror = reject;
xhr.send();
}); });
} }
/// <summary> Process the transcript described by text. </summary> /// <summary> Process the transcript described by text. </summary>

View file

@ -39,6 +39,6 @@
"commit": "8715c83bf04a228de00ec662ed43eb6141e61b91" "commit": "8715c83bf04a228de00ec662ed43eb6141e61b91"
}, },
"_source": "git://github.com/Polymer/polymer.git", "_source": "git://github.com/Polymer/polymer.git",
"_target": "^1.0.0", "_target": "^1.1.0",
"_originalSource": "Polymer/polymer" "_originalSource": "Polymer/polymer"
} }

View file

@ -768,7 +768,7 @@ prevent = dy > dx;
prevent = dx > dy; prevent = dx > dy;
} }
if (prevent) { if (prevent) {
//ev.preventDefault(); ev.preventDefault();
} else { } else {
Gestures.prevent('track'); Gestures.prevent('track');
} }

View file

@ -75,6 +75,14 @@ define(['appStorage', 'browser'], function (appStorage, browser) {
return deviceName; return deviceName;
} }
function supportsVoiceInput() {
return window.SpeechRecognition ||
window.webkitSpeechRecognition ||
window.mozSpeechRecognition ||
window.oSpeechRecognition ||
window.msSpeechRecognition;
}
var appInfo; var appInfo;
var version = window.dashboardVersion || '3.0'; var version = window.dashboardVersion || '3.0';
@ -96,6 +104,12 @@ define(['appStorage', 'browser'], function (appStorage, browser) {
'sharing' 'sharing'
]; ];
features.push('externallinks');
if (supportsVoiceInput()) {
features.push('voiceinput');
}
return features.indexOf(command.toLowerCase()) != -1; return features.indexOf(command.toLowerCase()) != -1;
}, },
appInfo: function () { appInfo: function () {

View file

@ -140,9 +140,9 @@
} }
} }
require(['voice/voice'], function (voice) { require(['apphost'], function (apphost) {
if (voice.isSupported()) { if (apphost.supports('voiceinput')) {
header.querySelector('.headerVoiceButton').classList.remove('hide'); header.querySelector('.headerVoiceButton').classList.remove('hide');
} else { } else {
header.querySelector('.headerVoiceButton').classList.add('hide'); header.querySelector('.headerVoiceButton').classList.add('hide');
@ -164,8 +164,8 @@
} }
function showVoice() { function showVoice() {
require(['voice/voice'], function (voice) { require(['voiceDialog'], function (voiceDialog) {
voice.showDialog(); voiceDialog.showDialog();
}); });
} }

View file

@ -1752,6 +1752,7 @@ var AppInfo = {};
visibleinviewport: embyWebComponentsBowerPath + "/visibleinviewport", visibleinviewport: embyWebComponentsBowerPath + "/visibleinviewport",
browserdeviceprofile: embyWebComponentsBowerPath + "/browserdeviceprofile", browserdeviceprofile: embyWebComponentsBowerPath + "/browserdeviceprofile",
browser: embyWebComponentsBowerPath + "/browser", browser: embyWebComponentsBowerPath + "/browser",
inputManager: embyWebComponentsBowerPath + "/inputmanager",
qualityoptions: embyWebComponentsBowerPath + "/qualityoptions", qualityoptions: embyWebComponentsBowerPath + "/qualityoptions",
connectservice: apiClientBowerPath + '/connectservice', connectservice: apiClientBowerPath + '/connectservice',
hammer: bowerPath + "/hammerjs/hammer.min", hammer: bowerPath + "/hammerjs/hammer.min",
@ -1814,6 +1815,7 @@ var AppInfo = {};
define("fetchHelper", [embyWebComponentsBowerPath + "/fetchhelper"], returnFirstDependency); define("fetchHelper", [embyWebComponentsBowerPath + "/fetchhelper"], returnFirstDependency);
define("tvguide", [embyWebComponentsBowerPath + "/guide/guide", 'embyRouter'], returnFirstDependency); define("tvguide", [embyWebComponentsBowerPath + "/guide/guide", 'embyRouter'], returnFirstDependency);
define("voiceDialog", [embyWebComponentsBowerPath + "/voice/voicedialog"], returnFirstDependency);
define("viewManager", [embyWebComponentsBowerPath + "/viewmanager/viewmanager"], function (viewManager) { define("viewManager", [embyWebComponentsBowerPath + "/viewmanager/viewmanager"], function (viewManager) {
window.ViewManager = viewManager; window.ViewManager = viewManager;
@ -2006,16 +2008,6 @@ var AppInfo = {};
return Emby.Page; return Emby.Page;
}); });
// mock this for now. not used in this app
define("inputManager", [], function () {
return {
on: function () {
},
off: function () {
}
};
});
// mock this for now. not used in this app // mock this for now. not used in this app
define("playbackManager", [], function () { define("playbackManager", [], function () {
return { return {

View file

@ -1,10 +0,0 @@
define([], function () {
return function (result) {
result.success = true;
if (result.properties.devicename)
MediaController.trySetActiveDeviceName(result.properties.devicename);
return;
}
});

View file

@ -1,92 +0,0 @@
define([], function () {
return function (result) {
result.success = true;
switch (result.item.sourceid) {
case 'music':
Dashboard.navigate('music.html');
break;
case 'movies':
if (result.properties.movieName) {
//TODO: Find a way to display movie
var query = {
Limit: 1,
UserId: result.userId,
ExcludeLocationTypes: "Virtual"
};
if (result.item.itemType) {
query.IncludeItemTypes = result.item.itemType;
}
query.SearchTerm = result.properties.movieName;
ApiClient.getItems(Dashboard.getCurrentUserId(), query).then(function (queryResult) {
var s = queryResult[0];
});
}
else
Dashboard.navigate('movies.html');
break;
case 'tvseries':
Dashboard.navigate('tv.html');
break;
case 'livetv':
var act = result.item.menuid;
if (act) {
if (act.indexOf('livetv') != -1)
Dashboard.navigate('livetv.html?tab=0');
else if (act.indexOf('guide') != -1)
Dashboard.navigate('livetv.html?tab=1');
else if (act.indexOf('channels') != -1)
Dashboard.navigate('livetv.html?tab=2');
else if (act.indexOf('recordings') != -1)
Dashboard.navigate('livetv.html?tab=3');
else if (act.indexOf('scheduled') != -1)
Dashboard.navigate('livetv.html?tab=4');
else if (act.indexOf('series') != -1)
Dashboard.navigate('livetv.html?tab=5');
else
Dashboard.navigate('livetv.html?tab=0');
}
else
Dashboard.navigate('livetv.html?tab=0');
break;
case 'recordings':
Dashboard.navigate('livetv.html?tab=3');
break;
case 'latestepisodes':
Dashboard.navigate('tv.html?tab=1');
case 'home':
var act = result.item.menuid;
if (act) {
if (act.indexOf('home') != -1)
Dashboard.navigate('index.html');
else if (act.indexOf('nextup') != -1)
Dashboard.navigate('index.html?tab=2');
else if (act.indexOf('favorites') != -1)
Dashboard.navigate('index.html?tab=2');
else if (act.indexOf('upcoming') != -1)
Dashboard.navigate('index.html?tab=3');
else if (act.indexOf('nowplaying') != -1)
Dashboard.navigate('nowplaying.html');
else
Dashboard.navigate('index.html');
}
else
Dashboard.navigate('index.html');
case 'group':
break;
default:
result.success = false;
return;
}
}
});

View file

@ -1,28 +0,0 @@
define([], function () {
return {
isSupported: function () {
if (AppInfo.isNativeApp) {
// Crashes on some amazon devices
if (window.device && (device.platform || '').toLowerCase().indexOf('amazon') != -1) {
return false;
}
}
return window.SpeechRecognition ||
window.webkitSpeechRecognition ||
window.mozSpeechRecognition ||
window.oSpeechRecognition ||
window.msSpeechRecognition;
},
showDialog: function () {
require(['voice/voicedialog'], function (voicedialog) {
voicedialog.showDialog();
});
}
};
});