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": {},
"ignore": [],
"version": "1.4.66",
"_release": "1.4.66",
"version": "1.4.68",
"_release": "1.4.68",
"_resolution": {
"type": "version",
"tag": "1.4.66",
"commit": "16a14d612b193c7a375e2391aa9c2370add0e32d"
"tag": "1.4.68",
"commit": "b14aefcc4a69f3dc2dfd530ddd0b99791719d259"
},
"_source": "https://github.com/MediaBrowser/emby-webcomponents.git",
"_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 () {
skinManager.getCurrentSkin().showLiveTV();
},
showRecordedTV: function () {
skinManager.getCurrentSkin().showRecordedTV();
},
showFavorites: function () {
skinManager.getCurrentSkin().showFavorites();
}
};

View file

@ -86,5 +86,11 @@
"ReplaceAllMetadata": "Replace all metadata",
"SearchForMissingMetadata": "Search for missing metadata",
"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."
}

View file

@ -0,0 +1,177 @@
#Emby Voice commands
Emby voice commands use json and regular expression to find corresponding commands.
With this solution the translation to other languages will be simplified and is fully compatible with regular expression for many programming languages.
###Json template :
```json
[
{
"group": "general",
"name": "General commands",
"defaultValues": {
"sourceid": "",
"deviceid": "",
"itemName": "",
"itemType": "",
"shuffle": false,
"filters": [],
"sortBy": "",
"sortOrder": "",
"limit": 100,
"category": ""
},
"items": [
{
"actionid": "show",
"sourceid": "movies",
"menuid" : "home",
"groupid" : "movie",
"deviceid": "displaymirroring",
"command": "(?<action>play|Listen to)\\s?(?<determiner1>my|me)?\\s?(?<source> music)\\s?(?<ArtistName>.*)?\\s?(?<deviceaction>on device|to device)\\s?(?<Devicename>.*)",
"altcommand": "(?<action>play|Listen to)\\s?(?<determiner1>my|me)?\\s?(?<source> music)\\s?(?<ArtistName>.*)?",
"itemName": "",
"itemType": "movie",
"shuffle": false,
"filters": [ ],
"sortBy": "",
"sortOrder": "Ascending",
"limit": 100,
"category": "",
"commandtemplates": [
"Show Movie based commands",
"Show Music based commands",
"Show Picture based commands",
"Show TV series based commands",
"Show general commands"
]
}
]
}
]
```
###Json hierarchy
>+ Group
- defaultValues
- Items
+ commandtemplates
###Json Description :
**Group**
>**groupid** : (mandatory) id of the group commands
>**name** : (mandatory) name of the group
**Items and defaultValues (mandatory)**
**actionid** : (mandatory) Liste of actions available
>- show
- play
- shuffle
- search
- control
- enable
- disable
- toggle
**sourceid** : (optional) source commands available
>- music
- movies
- tvseries
- livetv
- recordings
- latestepisodes
- home
- group
**menuid** : (optional) menu commands available
>- Commands for live TV
- livetv
- guide
- channels
- recordings
- scheduled
- series
- group
>- Commands for home menus
- home
- nextup
- favorites
- upcoming
- nowplaying
**groupid** : (optional) name of the group
You can define a group name specified in the json file
**deviceid** : (optional) devices commands available
>- displaymirroring
**Emby filters** : (optional)
>- itemName
- itemType
- shuffle : default value = false
- filters
- sortBy
- sortOrder
- limit : default value = 100
- category
**commandtemplates** (mandatory)
array : list of text commands
**command** : (mandatory)
regular expression used to filter commands
**Exemple :**
command: "(?<action>play|Listen to)\\s?(?<determiner1>my|me)?\\s?(?<source> music)\\s?(?<ArtistName>.*)?\\s?(?<deviceaction>on device|to device)\\s?(?<Devicename>.*)",
altcommand: "(?<action>play|Listen to)\\s?(?<determiner1>my|me)?\\s?(?<source> music)\\s?(?<ArtistName>.*)?",
####Regular expression description
```json
(?<action> or ?<MovieName>, etc) - are for defining watts is captured
my|me - indicate each of those words/phrases can be used
\\s? - is for spaces
(?<MovieName>.*)? - the ? at the end of the closing brackets represent an optional value
```
In the example below theses phrases can the match for an action
- Show my movies
- Show movies
- Display movies
- Go to movies
- etc.
**altcommand** : (optional)
alternate regular expression used to filter commands if the property **command** does not match
####Regular expression description
```json
?<action> or ?<MovieName>, etc - are for defining watts is captured
my|me - indicate each of those words/phrases can be used
\s? - is for spaces
(?<MovieName>.*)? - the ? at the end of the closing brackets represent an optional value
```
####Additional properties used by regular expression
>**action** : Linked to actionid
>**source** : Linked to sourceid
>**menu** : Linked to menuid
>**group** : Linked to groupid
>**device** : Linked to deviceid
>**determiner1 or determiner2 etc** : used just to capture words
>**moviename**
>**devicename**
>**songname**
>**artistname**
>**albumname**
>**seriename**
>**seasonname**
>**picturename**
>**authorname**

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

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

View file

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

View file

@ -0,0 +1,106 @@
define(['connectionManager', 'playbackManager', 'globalize'], function (connectionManager, playbackManager, globalize) {
/// <summary> Play items. </summary>
/// <param name="items"> The items. </param>
/// <param name="shuffle"> The shuffle. </param>
/// <returns> . </returns>
function playItems(items, shuffle) {
if (shuffle) {
items = shuffleArray(items);
}
items = items.map(function (i) {
return i.Id;
});
if (items.length) {
playbackManager.play({
ids: items
});
}
else {
require(['toast'], function (toast) {
toast(globalize.translate('sharedcomponents#NoItemsFound'));
});
}
}
/// <summary> Shuffle array. </summary>
/// <param name="array"> The array. </param>
/// <returns> . </returns>
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;
}
return function (result) {
result.success = false;
var query = {
Limit: result.item.limit || 100,
UserId: result.userId,
ExcludeLocationTypes: "Virtual"
};
if (result.item.itemType) {
query.IncludeItemTypes = result.item.itemType;
}
var apiClient = connectionManager.currentApiClient();
if (result.item.sourceid === 'nextup') {
apiClient.getNextUpEpisodes(query).then(function (queryResult) {
playItems(queryResult.Items, result.item.shuffle);
});
result.success = true;
return;
}
if (result.item.shuffle) {
result.item.sortBy = result.sortBy ? 'Random,' + result.item.sortBy : 'Random';
}
query.SortBy = result.item.sortBy;
query.SortOrder = result.item.sortOrder;
query.Recursive = true;
if (result.item.filters.indexOf('unplayed') !== -1) {
query.IsPlayed = false;
}
if (result.item.filters.indexOf('played') !== -1) {
query.IsPlayed = true;
}
if (result.item.filters.indexOf('favorite') !== -1) {
query.Filters = 'IsFavorite';
}
apiClient.getItems(apiClient.getCurrentUserId(), query).then(function (queryResult) {
playItems(queryResult.Items, result.item.shuffle);
});
result.success = true;
return;
}
});

View file

@ -0,0 +1,11 @@
define(['inputManager'], function (inputManager) {
return function (result) {
switch (result.item.deviceid) {
default:
result.success = false;
return;
}
}
});

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

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

View file

@ -0,0 +1,347 @@
[
{
"groupid": "general",
"name": "General commands",
"items": [
{
"actionid": "show",
"sourceid": "group",
"groupid": "movie",
"command": "(?<action>show|display|view)\\s?(?<source>Movie)?\\s?(?<text>based commands)",
"commandtemplates": [ "Show Movie based commands" ]
},
{
"actionid": "show",
"sourceid": "group",
"groupid": "music",
"command": "(?<action>show|display|view)\\s?(?<source>Music)?\\s?(?<text>based commands)",
"commandtemplates": [ "Show Music based commands" ]
},
{
"actionid": "show",
"sourceid": "group",
"groupid": "picture",
"command": "(?<action>show|display|view)\\s?(?<source>Picture)?\\s?(?<text>based commands)",
"commandtemplates": [ "Show Picture based commands" ]
},
{
"actionid": "show",
"sourceid": "group",
"groupid": "tvseries",
"command": "(?<action>show|display|view)\\s?(?<source>TV series)?\\s?(?<text>based commands)",
"commandtemplates": [ "Show TV series based commands" ]
},
{
"actionid": "show",
"sourceid": "group",
"groupid": "home",
"command": "(?<action>show|display|view)\\s?(?<source>home page)?\\s?(?<text>based commands)",
"commandtemplates": [ "Show home page based commands" ]
},
{
"actionid": "show",
"sourceid": "group",
"groupid": "general",
"command": "(?<action>show|display|view)\\s?(?<source>general)?\\s?(?<text>group commands)",
"commandtemplates": [ "Show general group commands" ]
},
{
"actionid": "show",
"sourceid": "group",
"groupid": "devices",
"command": "(?<action>show|display|view)\\s?(?<source>devices)?\\s?(?<text>based commands)",
"commandtemplates": [ "Show devices based commands" ]
},
{
"actionid": "show",
"sourceid": "group",
"groupid": "livetv",
"command": "(?<action>show|display|view)\\s?(?<source>live tv)?\\s?(?<text>based commands)",
"commandtemplates": [ "Show Live TV based commands" ]
}
]
},
{
"groupid": "home",
"name": "Home commands",
"items": [
{
"actionid": "show",
"sourceid": "home",
"menuid": "home",
"command": "(?<action> show|display|view)\\s?(?<menu>home)",
"commandtemplates": [ "Show home" ]
},
{
"actionid": "show",
"sourceid": "home",
"menuid": "nextup",
"command": "(?<action> show|display|view)\\s?(?<menu>next up)",
"commandtemplates": [ "Show next up" ]
},
{
"actionid": "show",
"sourceid": "home",
"menuid": "favorites",
"command": "(?<action> show|display|view)\\s?(?<menu>favorites)",
"commandtemplates": [ "Show favorites" ]
},
{
"actionid": "show",
"sourceid": "home",
"menuid": "upcoming",
"command": "(?<action> show|display|view)\\s?(?<menu>upcoming)",
"commandtemplates": [ "Show upcoming" ]
},
{
"actionid": "show",
"sourceid": "home",
"menuid": "nowplaying",
"command": "(?<action> show|display|view)\\s?(?<menu>now playing)",
"commandtemplates": [ "Show now playing" ]
}
]
},
{
"groupid": "movie",
"name": "Movie based commands",
"defaultValues": {
"sourceid": "movies",
"itemType": "Movie",
"filters": [ ],
"sortBy": "",
"sortOrder": "Ascending"
},
"items": [
{
"actionid": "show",
"sourceid": "movies",
"command": "(?<action>show|display|go to|view)\\s?(?<determiner1>my|me)?\\s?(?<source>movies)\\s?(?<MovieName>.*)?",
"altcommand": "(?<action>show|display|go to|view)\\s?(?<determiner1>the)?\\s?(?<source>movie)\\s?(?<MovieName>.*)?",
"itemName": "",
"commandtemplates": [
"Show movies",
"Show my movies",
"Show the movie [movie name]"
]
},
{
"actionid": "play",
"sourceid": "movies",
"command": "(?<action>play|Listen to)\\s?(?<determiner1>my|me)?\\s?(?<source> music)\\s?(?<ArtistName>.*)?\\s?(?<deviceaction>on device|to device)\\s?(?<Devicename>.*)",
"altcommand": "(?<action>play|Listen to)\\s?(?<determiner1>my|me)?\\s?(?<source> music)\\s?(?<ArtistName>.*)?",
"itemName": "",
"itemType": "Audio",
"shuffle": false,
"filters": [ ],
"sortBy": "",
"sortOrder": "Ascending",
"commandtemplates": [
"Play music",
"Play my music",
"Listen to my music"
]
},
{
"actionid": "play",
"sourceid": "latestmovies",
"command": "(?<action>play)\\s?(?<determiner1>my|me)?\\s?(?<source>latest movies|latest films)\\s?(?<deviceaction>on device|to device)\\s?(?<Devicename>.*)",
"altcommand": "(?<action>play)\\s?(?<determiner1>my|me)?\\s?(?<source>latest movies|latest films)",
"filters": [ "unplayed" ],
"sortBy": "datecreated",
"sortOrder": "Descending",
"commandtemplates": [
"Play my latest movies",
"Play latest films"
]
}
]
},
{
"groupid": "music",
"name": "Music based commands",
"defaultValues": {
"itemName": "",
"itemType": "Audio",
"shuffle": false,
"filters": [ ],
"sortBy": "",
"sortOrder": "Ascending"
},
"items": [
{
"actionid": "show",
"sourceid": "music",
"command": "(?<action>show|display|go to|view)\\s?(?<determiner1>my|me)?\\s?(?<source>(music|songs))\\s?(?<SongName>.*)?",
"commandtemplates": [
"Show music",
"Show my music",
"Show my songs"
]
},
{
"actionid": "play",
"sourceid": "music",
"command": "(?<action>play|Listen to)\\s?(?<determiner1>my|me)?\\s?(?<source> music)\\s?(?<ArtistName>.*)?\\s?(?<deviceaction>on device|to device)\\s?(?<Devicename>.*)",
"altcommand": "(?<action>play|Listen to)\\s?(?<determiner1>my|me)?\\s?(?<source> music)\\s?(?<ArtistName>.*)?",
"itemName": "",
"commandtemplates": [
"Play music",
"Play my music",
"Listen to my music"
]
},
{
"actionid": "shuffle",
"sourceid": "music",
"command": "(?<action>shuffle)\\s?(?<determiner1>my|me)?\\s?(?<source> music|favorite songs)\\s?(?<ArtistName>.*)?\\s?(?<deviceaction>on device|to device)\\s?(?<Devicename>.*)",
"altcommand": "(?<action>shuffle)\\s?(?<determiner1>my|me)?\\s?(?<source> music|favorite songs)\\s?(?<ArtistName>.*)?",
"shuffle": true,
"commandtemplates": [
"Play music",
"Play my music",
"Listen to my music",
"shuffle my favorite songs"
]
}
]
},
{
"groupid": "tvseries",
"name": "TV series based commands",
"defaultValues": {
"itemType": "Series",
"filters": [ ],
"sortBy": "",
"sortOrder": "Ascending"
},
"items": [
{
"actionid": "show",
"sourceid": "tvseries",
"command": "(?<action>show|display|go to|view)\\s?(?<determiner1>my|me)?\\s?(?<source>(tv series))\\s?(?<SerieName>.*)?",
"commandtemplates": [
"Show tv series",
"Show my tv series"
]
},
{
"actionid": "play",
"sourceid": "nextup",
"command": "(?<action>play|Listen to)\\s?(?<source> next up)\\s?(?<deviceaction>on device|to device)\\s?(?<Devicename>.*)",
"altcommand": "(?<action>play|Listen to)\\s?(?<source> next up)",
"commandtemplates": [ "Play next up" ]
},
{
"actionid": "play",
"sourceid": "latestepisodes",
"command": "(?<action>play)\\s?(?<determiner1>my|me)?\\s?(?<source>latest episodes)\\s?(?<deviceaction>on device|to device)\\s?(?<Devicename>.*)",
"altcommand": "(?<action>play)\\s?(?<determiner1>my|me)?\\s?(?<source>latest episodes)",
"filters": [ "unplayed" ],
"sortBy": "datecreated",
"sortOrder": "Descending",
"commandtemplates": [ "Play my latest episodes" ]
}
]
},
{
"groupid": "livetv",
"name": "Live TV based commands",
"items": [
{
"actionid": "show",
"sourceid": "livetv",
"menuid": "suggestions",
"command": "(?<action>show|display|go to|view)\\s?(?<determiner1>my|me)?\\s?(?<source>Live tv|live tv suggestions)",
"commandtemplates": [ "Show live tv" ]
},
{
"actionid": "show",
"sourceid": "livetv",
"menuid": "guide",
"command": "(?<action>show|display|go to|view)\\s?(?<determiner1>my|me)?\\s?(?<source>Live tv guide)",
"commandtemplates": [ "Show live tv guide" ]
},
{
"actionid": "show",
"sourceid": "livetv",
"menuid": "channels",
"command": "(?<action>show|display|go to|view)\\s?(?<determiner1>my|me)?\\s?(?<source>Live tv channels)",
"commandtemplates": [ "Show live tv channels" ]
},
{
"actionid": "show",
"sourceid": "livetv",
"menuid": "recordings",
"command": "(?<action>show|display|go to|view)\\s?(?<determiner1>my|me)?\\s?(?<source>Live tv recordings)",
"commandtemplates": [ "Show live tv recordings" ]
},
{
"actionid": "show",
"sourceid": "livetv",
"menuid": "scheduled",
"command": "(?<action>show|display|go to|view)\\s?(?<determiner1>my|me)?\\s?(?<source>Live tv scheduled)",
"commandtemplates": [ "Show live tv scheduled" ]
},
{
"actionid": "show",
"sourceid": "livetv",
"menuid": "series",
"command": "(?<action>show|display|go to|view)\\s?(?<determiner1>my|me)?\\s?(?<source>Live tv series)",
"commandtemplates": [ "Show live tv series" ]
}
]
},
{
"groupid": "picture",
"name": "Picture based commands",
"defaultValues": {
"itemType": "Picture",
"shuffle": false,
"filters": [ ],
"sortBy": "",
"sortOrder": "Ascending"
},
"items": [
{
"actionid": "show",
"sourceid": "tvseries",
"command": "(?<action>show|display|go to|view)\\s?(?<determiner1>my|me)?\\s?(?<source>pictures)",
"commandtemplates": [
"Show pictures",
"Show my pictures"
]
}
]
},
{
"groupid": "devices",
"name": "Devices based commands",
"items": [
{
"actionid": "enable",
"deviceid": "displaymirroring",
"command": "(?<action> enable|turn on)\\s?(?<device>display mirroring)",
"commandtemplates": [ "Turn on display mirroring", "Enable display mirroring" ]
},
{
"actionid": "disable",
"deviceid": "displaymirroring",
"command": "(?<action> disable|turn off)\\s?(?<device>display mirroring)",
"commandtemplates": [ "Turn off display mirroring", "Disable display mirroring" ]
},
{
"actionid": "toggle",
"deviceid": "displaymirroring",
"command": "(?<action>toggle)\\s?(?<device>display mirroring)",
"commandtemplates": [ "Toggle display mirroring" ]
},
{
"actionid": "control",
"command": "(?<action>control)\\s?(?<Devicename>.*)",
"commandtemplates": [ "Control [Device Name]" ]
}
]
}
]

View file

@ -0,0 +1,347 @@
[
{
"groupid": "general",
"name": "General commands",
"items": [
{
"actionid": "show",
"sourceid": "group",
"groupid": "movie",
"command": "(?<action>show|display|view)\\s?(?<source>Movie)?\\s?(?<text>based commands)",
"commandtemplates": [ "Show movie commands" ]
},
{
"actionid": "show",
"sourceid": "group",
"groupid": "music",
"command": "(?<action>show|display|view)\\s?(?<source>Music)?\\s?(?<text>based commands)",
"commandtemplates": [ "Show music commands" ]
},
{
"actionid": "show",
"sourceid": "group",
"groupid": "picture",
"command": "(?<action>show|display|view)\\s?(?<source>Picture)?\\s?(?<text>based commands)",
"commandtemplates": [ "Show photo commands" ]
},
{
"actionid": "show",
"sourceid": "group",
"groupid": "tvseries",
"command": "(?<action>show|display|view)\\s?(?<source>TV series)?\\s?(?<text>based commands)",
"commandtemplates": [ "Show tv library commands" ]
},
{
"actionid": "show",
"sourceid": "group",
"groupid": "home",
"command": "(?<action>show|display|view)\\s?(?<source>home page)?\\s?(?<text>based commands)",
"commandtemplates": [ "Show home screen commands" ]
},
{
"actionid": "show",
"sourceid": "group",
"groupid": "general",
"command": "(?<action>show|display|view)\\s?(?<source>general)?\\s?(?<text>group commands)",
"commandtemplates": [ "Show general commands" ]
},
{
"actionid": "show",
"sourceid": "group",
"groupid": "devices",
"command": "(?<action>show|display|view)\\s?(?<source>devices)?\\s?(?<text>based commands)",
"commandtemplates": [ "Show device commands" ]
},
{
"actionid": "show",
"sourceid": "group",
"groupid": "livetv",
"command": "(?<action>show|display|view)\\s?(?<source>live tv)?\\s?(?<text>based commands)",
"commandtemplates": [ "Show live tv commands" ]
}
]
},
{
"groupid": "home",
"name": "Home commands",
"items": [
{
"actionid": "show",
"sourceid": "home",
"menuid": "home",
"command": "(?<action> show|display|view)\\s?(?<menu>home)",
"commandtemplates": [ "Show home" ]
},
{
"actionid": "show",
"sourceid": "home",
"menuid": "nextup",
"command": "(?<action> show|display|view)\\s?(?<menu>next up)",
"commandtemplates": [ "Show next up" ]
},
{
"actionid": "show",
"sourceid": "home",
"menuid": "favorites",
"command": "(?<action> show|display|view)\\s?(?<menu>favorites)",
"commandtemplates": [ "Show favorites" ]
},
{
"actionid": "show",
"sourceid": "home",
"menuid": "upcoming",
"command": "(?<action> show|display|view)\\s?(?<menu>upcoming)",
"commandtemplates": [ "Show upcoming" ]
},
{
"actionid": "show",
"sourceid": "home",
"menuid": "nowplaying",
"command": "(?<action> show|display|view)\\s?(?<menu>now playing)",
"commandtemplates": [ "Show now playing" ]
}
]
},
{
"groupid": "movie",
"name": "Movie based commands",
"defaultValues": {
"sourceid": "movies",
"itemType": "Movie",
"filters": [ ],
"sortBy": "",
"sortOrder": "Ascending"
},
"items": [
{
"actionid": "show",
"sourceid": "movies",
"command": "(?<action>show|display|go to|view)\\s?(?<determiner1>my|me)?\\s?(?<source>movies)\\s?(?<MovieName>.*)?",
"altcommand": "(?<action>show|display|go to|view)\\s?(?<determiner1>the)?\\s?(?<source>movie)\\s?(?<MovieName>.*)?",
"itemName": "",
"commandtemplates": [
"Show movies",
"Show my movies",
"Show the movie [movie name]"
]
},
{
"actionid": "play",
"sourceid": "movies",
"command": "(?<action>play|Listen to)\\s?(?<determiner1>my|me)?\\s?(?<source> music)\\s?(?<ArtistName>.*)?\\s?(?<deviceaction>on device|to device)\\s?(?<Devicename>.*)",
"altcommand": "(?<action>play|Listen to)\\s?(?<determiner1>my|me)?\\s?(?<source> music)\\s?(?<ArtistName>.*)?",
"itemName": "",
"itemType": "Audio",
"shuffle": false,
"filters": [ ],
"sortBy": "",
"sortOrder": "Ascending",
"commandtemplates": [
"Play music",
"Play my music",
"Listen to my music "
]
},
{
"actionid": "play",
"sourceid": "latestmovies",
"command": "(?<action>play)\\s?(?<determiner1>my|me)?\\s?(?<source>latest movies|latest films)\\s?(?<deviceaction>on device|to device)\\s?(?<Devicename>.*)",
"altcommand": "(?<action>play)\\s?(?<determiner1>my|me)?\\s?(?<source>latest movies|latest films)",
"filters": [ "unplayed" ],
"sortBy": "datecreated",
"sortOrder": "Descending",
"commandtemplates": [
"Play my latest movies",
"Play latest films"
]
}
]
},
{
"groupid": "music",
"name": "Music based commands",
"defaultValues": {
"itemName": "",
"itemType": "Audio",
"shuffle": false,
"filters": [ ],
"sortBy": "",
"sortOrder": "Ascending"
},
"items": [
{
"actionid": "show",
"sourceid": "music",
"command": "(?<action>show|display|go to|view)\\s?(?<determiner1>my|me)?\\s?(?<source>(music|songs))\\s?(?<SongName>.*)?",
"commandtemplates": [
"Show music",
"Show my music",
"Show my songs"
]
},
{
"actionid": "play",
"sourceid": "music",
"command": "(?<action>play|Listen to)\\s?(?<determiner1>my|me)?\\s?(?<source> music)\\s?(?<ArtistName>.*)?\\s?(?<deviceaction>on device|to device)\\s?(?<Devicename>.*)",
"altcommand": "(?<action>play|Listen to)\\s?(?<determiner1>my|me)?\\s?(?<source> music)\\s?(?<ArtistName>.*)?",
"itemName": "",
"commandtemplates": [
"Play music",
"Play my music",
"Listen to my music "
]
},
{
"actionid": "shuffle",
"sourceid": "music",
"command": "(?<action>shuffle)\\s?(?<determiner1>my|me)?\\s?(?<source> music|favorite songs)\\s?(?<ArtistName>.*)?\\s?(?<deviceaction>on device|to device)\\s?(?<Devicename>.*)",
"altcommand": "(?<action>shuffle)\\s?(?<determiner1>my|me)?\\s?(?<source> music|favorite songs)\\s?(?<ArtistName>.*)?",
"shuffle": true,
"commandtemplates": [
"Play music",
"Play my music",
"Listen to my music ",
"shuffle my favorite songs"
]
}
]
},
{
"groupid": "tvseries",
"name": "TV series based commands",
"defaultValues": {
"itemType": "Series",
"filters": [ ],
"sortBy": "",
"sortOrder": "Ascending"
},
"items": [
{
"actionid": "show",
"sourceid": "tvseries",
"command": "(?<action>show|display|go to|view)\\s?(?<determiner1>my|me)?\\s?(?<source>(tv series))\\s?(?<SerieName>.*)?",
"commandtemplates": [
"Show tv series",
"Show my tv series"
]
},
{
"actionid": "play",
"sourceid": "nextup",
"command": "(?<action>play|Listen to)\\s?(?<source> next up)\\s?(?<deviceaction>on device|to device)\\s?(?<Devicename>.*)",
"altcommand": "(?<action>play|Listen to)\\s?(?<source> next up)",
"commandtemplates": [ "Play next up" ]
},
{
"actionid": "play",
"sourceid": "latestepisodes",
"command": "(?<action>play)\\s?(?<determiner1>my|me)?\\s?(?<source>latest episodes)\\s?(?<deviceaction>on device|to device)\\s?(?<Devicename>.*)",
"altcommand": "(?<action>play)\\s?(?<determiner1>my|me)?\\s?(?<source>latest episodes)",
"filters": [ "unplayed" ],
"sortBy": "datecreated",
"sortOrder": "Descending",
"commandtemplates": [ "Play my latest episodes" ]
}
]
},
{
"groupid": "livetv",
"name": "Live TV based commands",
"items": [
{
"actionid": "show",
"sourceid": "livetv",
"menuid": "suggestions",
"command": "(?<action>show|display|go to|view)\\s?(?<determiner1>my|me)?\\s?(?<source>Live tv|live tv suggestions)",
"commandtemplates": [ "Show live tv" ]
},
{
"actionid": "show",
"sourceid": "livetv",
"menuid": "guide",
"command": "(?<action>show|display|go to|view)\\s?(?<determiner1>my|me)?\\s?(?<source>Live tv guide)",
"commandtemplates": [ "Show live tv guide" ]
},
{
"actionid": "show",
"sourceid": "livetv",
"menuid": "channels",
"command": "(?<action>show|display|go to|view)\\s?(?<determiner1>my|me)?\\s?(?<source>Live tv channels)",
"commandtemplates": [ "Show live tv channels" ]
},
{
"actionid": "show",
"sourceid": "livetv",
"menuid": "recordings",
"command": "(?<action>show|display|go to|view)\\s?(?<determiner1>my|me)?\\s?(?<source>Live tv recordings)",
"commandtemplates": [ "Show live tv recordings" ]
},
{
"actionid": "show",
"sourceid": "livetv",
"menuid": "scheduled",
"command": "(?<action>show|display|go to|view)\\s?(?<determiner1>my|me)?\\s?(?<source>Live tv scheduled)",
"commandtemplates": [ "Show live tv scheduled" ]
},
{
"actionid": "show",
"sourceid": "livetv",
"menuid": "series",
"command": "(?<action>show|display|go to|view)\\s?(?<determiner1>my|me)?\\s?(?<source>Live tv series)",
"commandtemplates": [ "Show live tv series" ]
}
]
},
{
"groupid": "picture",
"name": "Picture based commands",
"defaultValues": {
"itemType": "Picture",
"shuffle": false,
"filters": [ ],
"sortBy": "",
"sortOrder": "Ascending"
},
"items": [
{
"actionid": "show",
"sourceid": "tvseries",
"command": "(?<action>show|display|go to|view)\\s?(?<determiner1>my|me)?\\s?(?<source>pictures)",
"commandtemplates": [
"Show pictures",
"Show my pictures"
]
}
]
},
{
"groupid": "devices",
"name": "Devices based commands",
"items": [
{
"actionid": "enable",
"deviceid": "displaymirroring",
"command": "(?<action> enable|turn on)\\s?(?<device>display mirroring)",
"commandtemplates": [ "Turn on display mirroring", "Enable display mirroring" ]
},
{
"actionid": "disable",
"deviceid": "displaymirroring",
"command": "(?<action> disable|turn off)\\s?(?<device>display mirroring)",
"commandtemplates": [ "Turn off display mirroring", "Disable display mirroring" ]
},
{
"actionid": "toggle",
"deviceid": "displaymirroring",
"command": "(?<action>toggle)\\s?(?<device>display mirroring)",
"commandtemplates": [ "Toggle display mirroring" ]
},
{
"actionid": "control",
"command": "(?<action>control)\\s?(?<Devicename>.*)",
"commandtemplates": [ "Control [Device Name]" ]
}
]
}
]

View file

@ -0,0 +1,241 @@
// <date>09.10.2015</date>
// <summary>grammarprocessor class</summary>
define([], function () {
/// <summary> The named register exponent. </summary>
var NamedRegExp = function (pattern, string) {
pattern = pattern.toString();
var regexp = [];
var groupRX = /\(\?\<(.*?)\>\s?(.*?)\)/i;
while (groupRX.test(pattern)) {
var match = groupRX.exec(pattern);
regexp.push({
name: match[1].trim().toLowerCase(),
pattern: match[2].trim().toLowerCase(),
value: null,
title: ''
});
pattern = pattern.replace(groupRX, '(' + match[2].trim() + ')');
}
var finalMatch = (new RegExp(pattern, "i")).exec(string);
if (finalMatch) {
for (var i = 0, len = regexp.length; i < len; i++) {
if (finalMatch[(i + 1)] !== false) {
var mth = finalMatch[(i + 1)];
if (mth)
mth = mth.trim().toLowerCase();
regexp[i].value = mth;
}
}
}
else
regexp = null;
return regexp;
};
/// <summary> Processcommands. </summary>
/// <param name="text"> The text. </param>
/// <param name="commandgroups"> The command groups. </param>
/// <returns> . </returns>
function processcommands(text, commandgroups) {
var result = {
groupid: '',
name: '',
item: {
actionid: '',
groupid: '',
sourceid: '',
menuid: '',
deviceid: '',
itemName: '',
itemType: '',
shuffle: false,
filters: [],
sortBy: '',
sortOrder: '',
item: 100,
category: '',
usedefaultvalues: true
},
defaultValues: {
sourceid: '',
deviceid: '',
itemName: '',
itemType: '',
shuffle: false,
filters: [],
sortBy: '',
sortOrder: '',
limit: 100,
category: ''
},
properties: {
movieName: null,
devicename: null,
songName: null,
artistName: null,
albumName: null,
serieName: null,
seasonName: null,
pictureName: null,
authorname: null,
},
command: null,
text: text,
userId: Dashboard.getCurrentUserId(),
success: false
};
var isvalid = false;
commandgroups.map(function (group) {
if (isvalid)
return;
if (group.defaultValues && group.defaultValues.length > 0) {
group.defaultValues.map(function (item) {
});
}
if (group.items && group.items.length > 0) {
group.items.map(function (item) {
var regex = NamedRegExp(item.command, text);
if (!regex && item.altcommand)
regex = NamedRegExp(item.altcommand, text)
if (regex && regex.length > 0) {
//Group data
if (group.groupid) result.groupid = group.groupid;
if (group.name) result.name = group.name;
if (group.defaultValues) {
result.defaultValues.sourceid = group.defaultValues.sourceid || result.defaultValues.sourceid;
result.defaultValues.deviceid = group.defaultValues.deviceid || result.defaultValues.deviceid;
result.defaultValues.itemName = group.defaultValues.itemName || result.defaultValues.itemName;
result.defaultValues.itemType = group.defaultValues.itemType || result.defaultValues.itemType;
result.defaultValues.shuffle = group.defaultValues.shuffle || result.defaultValues.shuffle;
result.defaultValues.filters = group.defaultValues.filters || result.defaultValues.filters;
result.defaultValues.sortBy = group.defaultValues.sortBy || result.defaultValues.sortBy;
result.defaultValues.sortOrder = group.defaultValues.sortOrder || result.defaultValues.sortOrder;
result.defaultValues.limit = group.defaultValues.limit || result.defaultValues.limit;
result.defaultValues.category = group.defaultValues.category || result.defaultValues.category;
}
if (group.name) result.name = group.name;
//Item data
var usegroupDefault = checkItemProperty(item.usedefaultvalues, result.item.usedefaultvalues);
result.item.usedefaultvalues = usegroupDefault;
result.item.actionid = checkItemProperty(item.actionid, result.item.actionid);
result.item.groupid = checkItemProperty(item.groupid, result.item.groupid);
result.item.menuid = checkItemProperty(item.menuid, result.item.menuid);
result.item.sourceid = checkItemProperty(item.sourceid, result.item.sourceid, usegroupDefault, result.defaultValues.sourceid);
result.item.deviceid = checkItemProperty(item.deviceid, result.item.deviceid, usegroupDefault, result.defaultValues.deviceid);
result.item.itemName = checkItemProperty(item.itemName, result.item.itemName, usegroupDefault, result.defaultValues.itemName);
result.item.itemType = checkItemProperty(item.itemType, result.item.itemType, usegroupDefault, result.defaultValues.itemType);
result.item.shuffle = checkItemProperty(item.shuffle, result.item.shuffle, usegroupDefault, result.defaultValues.shuffle);
result.item.filters = checkItemProperty(item.filters, result.item.filters, usegroupDefault, result.defaultValues.filters);
result.item.sortBy = checkItemProperty(item.sortBy, result.item.sortBy, usegroupDefault, result.defaultValues.sortBy);
result.item.sortOrder = checkItemProperty(item.sortOrder, result.item.sortOrder, usegroupDefault, result.defaultValues.sortOrder);
result.item.limit = checkItemProperty(item.limit, result.item.limit, usegroupDefault, result.defaultValues.limit);
result.command = item;
regex.map(function (regresult) {
switch (regresult.name) {
case 'moviename':
result.properties.movieName = regresult.value;
break;
case 'devicename':
result.properties.devicename = regresult.value;
break;
case 'songname':
result.properties.songName = regresult.value;
break;
case 'artistname':
result.properties.artistName = regresult.value;
break;
case 'albumname':
result.properties.albumName = regresult.value;
break;
case 'seriename':
result.properties.serieName = regresult.value;
break;
case 'seasonname':
result.properties.seasonName = regresult.value;
break;
case 'picturename':
result.properties.pictureName = regresult.value;
break;
case 'authorname':
result.properties.authorname = regresult.value;
break;
}
if (result.text)
result.text = result.text.replace(regresult.value, '').trim();
});
isvalid = true;
}
if (isvalid)
return;
});
}
});
return result;
}
/// <summary> Gets RegEx property. </summary>
/// <param name="actions"> The actions. </param>
/// <param name="property"> The property. </param>
/// <returns> The RegEx property. </returns>
function getRegExProperty(actions, property) {
var idx = -1;
idx = actions.map(function (e) { return e.name; }).indexOf(name);
if (idx > -1)
return actions[idx];
else
return null;
}
/// <summary> Check item property. </summary>
/// <param name="property"> The property. </param>
/// <param name="itemDefaultValue"> The item default value. </param>
/// <param name="useGroupDefaultValue"> The use group default value. </param>
/// <param name="groupDefaultValue"> The group default value. </param>
/// <returns> . </returns>
function checkItemProperty(property, itemDefaultValue, useGroupDefaultValue, groupDefaultValue) {
if (property)
return property;
if (useGroupDefaultValue && groupDefaultValue)
return groupDefaultValue;
return itemDefaultValue;
}
return function (commandgroups, text) {
if (commandgroups) {
var result = processcommands(text, commandgroups);
console.log(text);
console.log(commandgroups);
}
return result;
}
});

View file

@ -0,0 +1,20 @@
.voiceHelpContent {
max-width: 700px;
margin: auto;
}
.exampleCommand {
margin: 1em 0;
}
.exampleCommandText {
padding-left: .25em;
}
.defaultVoiceHelp {
margin-bottom: 2em;
}
.voiceInputContainer {
margin: 1.5em 0;
}

View file

@ -0,0 +1,60 @@
// <date>09.10.2015</date>
// <summary>voicecommands class</summary>
define([], function () {
/// <summary> Process the command. </summary>
/// <param name="commandPath"> Full pathname of the command file. </param>
/// <param name="result"> The result. </param>
/// <returns> . </returns>
function processCommand(commandPath, result) {
return new Promise(function (resolve, reject) {
require([commandPath], function (command) {
command(result);
if (result.success) {
resolve(result);
}
reject();
});
});
}
return function (result) {
return new Promise(function (resolve, reject) {
switch (result.item.actionid) {
case 'show':
processCommand('voice/commands/showcommands.js', result).then(function (result) { resolve(result); });
break;
case 'play':
processCommand('voice/commands/playcommands.js', result).then(function (result) { resolve(result); });
break;
case 'shuffle':
processCommand('voice/commands/playcommands.js', result).then(function (result) { resolve(result); });
break;
case 'search':
processCommand('voice/commands/searchcommands.js', result).then(function (result) { resolve(result); });
break;
case 'control':
processCommand('voice/commands/controlcommands.js', result).then(function (result) { resolve(result); });
break;
case 'enable':
processCommand('voice/commands/enablecommands.js', result).then(function (result) { resolve(result); });
break;
case 'disable':
processCommand('voice/commands/disablecommands.js', result).then(function (result) { resolve(result); });
break;
case 'toggle':
processCommand('voice/commands/togglecommands.js', result).then(function (result) { resolve(result); });
break;
default:
reject();
return;
}
});
}
});

View file

@ -0,0 +1,272 @@
define(['dialogHelper', './voicereceiver', './voiceprocessor', 'globalize', 'emby-button', 'css!./voice.css', 'material-icons', 'css!./../formdialog'], function (dialogHelper, voicereceiver, voiceprocessor, globalize) {
var lang = 'en-US';
/// <summary> Shuffle array. </summary>
/// <param name="array"> The array. </param>
/// <returns> array </returns>
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;
}
/// <summary> Gets sample commands. </summary>
/// <returns> The sample commands. </returns>
function getSampleCommands(groupid) {
return voiceprocessor.getCommandGroups().then(function (commandGroups) {
groupid = typeof (groupid) !== 'undefined' ? groupid : '';
var commands = [];
commandGroups.map(function (group) {
if ((group.items && group.items.length > 0) && (groupid == group.groupid || groupid == '')) {
group.items.map(function (item) {
if (item.commandtemplates && item.commandtemplates.length > 0) {
item.commandtemplates.map(function (templates) {
commands.push(templates);
});
}
});
}
});
return shuffleArray(commands);
});
}
/// <summary> Gets command group. </summary>
/// <param name="groupid"> The groupid. </param>
/// <returns> The command group. </returns>
function getCommandGroup(groupid) {
return voicereceiver.getCommandGroups()
.then(function (commandgroups) {
if (commandgroups) {
var idx = -1;
idx = commandgroups.map(function (e) { return e.groupid; }).indexOf(groupid);
if (idx > -1)
return commandgroups[idx];
else
return null;
} else
return null;
});
}
/// <summary> Renders the sample commands. </summary>
/// <param name="elem"> The element. </param>
/// <param name="commands"> The commands. </param>
/// <returns> . </returns>
function renderSampleCommands(elem, commands) {
commands.length = Math.min(commands.length, 4);
commands = commands.map(function (c) {
return '<div class="exampleCommand"><span class="exampleCommandText">"' + c + '"</span></div>';
}).join('');
elem.querySelector('.exampleCommands').innerHTML = commands;
}
var currentDialog;
/// <summary> Shows the voice help. </summary>
/// <returns> . </returns>
function showVoiceHelp(groupid, title) {
console.log("Showing Voice Help", groupid, title);
var isNewDialog = false;
var dlg;
if (!currentDialog) {
isNewDialog = true;
dlg = dialogHelper.createDialog({
size: 'medium',
removeOnClose: true
});
dlg.classList.add('ui-body-b');
dlg.classList.add('background-theme-b');
var html = '';
html += '<div class="dialogHeader" style="margin:0 0 1em;">';
html += '<button is="paper-icon-button-light" class="btnCancel autoSize btnCancelVoiceInput" tabindex="-1"><i class="md-icon">arrow_back</i></button>';
html += '<div class="dialogHeaderTitle" id="voiceDialogGroupName">';
html += '</div>';
html += '</div>';
html += '<div>';
html += '<div class="dialogContent smoothScrollY">';
html += '<div class="dialogContentInner centeredContent">';
html += '<div class="voiceHelpContent">';
html += '<div class="defaultVoiceHelp">';
html += '<h1 style="margin-bottom:1.25em;">' + globalize.translate('sharedcomponents#HeaderSaySomethingLike') + '</h1>';
html += '<div class="exampleCommands">';
html += '</div>';
// defaultVoiceHelp
html += '</div>';
html += '<div class="unrecognizedCommand hide">';
html += '<h1>' + globalize.translate('sharedcomponents#HeaderYouSaid') + '</h1>';
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>';
html += '<p>' + globalize.translate('sharedcomponents#MessageWeDidntRecognizeCommand') + '</p>';
html += '<br/>';
html += '<button is="emby-button" type="button" class="submit block btnRetry raised"><i class="md-icon">mic</i><span>' +
globalize.translate('sharedcomponents#ButtonTryAgain') +
'</span></button>';
html += '<p class="blockedMessage hide">' +
globalize.translate('sharedcomponents#MessageIfYouBlockedVoice') +
'<br/><br/></p>';
html += '</div>';
html +=
'<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>';
dlg.innerHTML = html;
document.body.appendChild(dlg);
dialogHelper.open(dlg);
currentDialog = dlg;
dlg.addEventListener('close', function () {
currentDialog = null;
});
function onCancelClick() {
voicereceiver.cancel();
dialogHelper.close(dlg);
}
var closeButtons = dlg.querySelectorAll('.btnCancelVoiceInput');
for (var i = 0, length = closeButtons.length; i < length; i++) {
closeButtons[i].addEventListener('click', onCancelClick);
}
dlg.querySelector('.btnRetry').addEventListener('click', function () {
dlg.querySelector('.unrecognizedCommand').classList.add('hide');
dlg.querySelector('.defaultVoiceHelp').classList.remove('hide');
listen();
});
}
dlg = currentDialog;
if (groupid) {
getCommandGroup(groupid)
.then(
function (grp) {
dlg.querySelector('#voiceDialogGroupName').innerText = ' ' + grp.name;
});
getSampleCommands(groupid)
.then(function (commands) {
renderSampleCommands(currentDialog, commands);
listen();
})
.catch(function (e) { console.log("Error", e); });
} else if (isNewDialog) {
getSampleCommands()
.then(function (commands) {
renderSampleCommands(currentDialog, commands);
});
}
}
function processInput(input) {
return voiceprocessor.processTranscript(input);
}
/// <summary> Shows the unrecognized command help. </summary>
/// <returns> . </returns>
function showUnrecognizedCommandHelp(command) {
//speak("I don't understend this command");
if (command)
currentDialog.querySelector('.voiceInputText').innerText = command;
currentDialog.querySelector('.unrecognizedCommand').classList.remove('hide');
currentDialog.querySelector('.defaultVoiceHelp').classList.add('hide');
}
/// <summary> Shows the commands. </summary>
/// <param name="createUI"> The create user interface. </param>
/// <returns> . </returns>
function showCommands(result) {
//speak('Hello, what can I do for you?');
if (result)
showVoiceHelp(result.groupid, result.name);
else
showVoiceHelp();
}
function resetDialog() {
if (currentDialog) {
currentDialog.querySelector('.unrecognizedCommand').classList.add('hide');
currentDialog.querySelector('.defaultVoiceHelp').classList.remove('hide');
}
}
function showDialog() {
resetDialog();
showCommands();
listen();
}
function listen() {
voicereceiver.listenForCommand(lang || "en-US").then(processInput).then(function (data) {
closeDialog();
}, function (result) {
if (result.error == 'group') {
showVoiceHelp(result.item.groupid, result.groupName);
return;
}
showUnrecognizedCommandHelp(result.text || '');
});
}
function closeDialog() {
dialogHelper.close(currentDialog);
voicereceiver.cancel();
}
/// <summary> An enum constant representing the window. voice input manager option. </summary>
return {
showDialog: showDialog
};
});

View file

@ -0,0 +1,55 @@
define(['./voicecommands.js', './grammarprocessor.js', 'require'], function (voicecommands, grammarprocessor, require) {
var commandgroups;
function getCommandGroups() {
if (commandgroups) {
return Promise.resolve(commandgroups);
}
return new Promise(function (resolve, reject) {
var file = "grammar";
require(['text!./grammar/' + file + '.json'], function (response) {
commandgroups = JSON.parse(response);
resolve(commandgroups);
});
});
}
/// <summary> Process the transcript described by text. </summary>
/// <param name="text"> The text. </param>
/// <returns> . </returns>
function processTranscript(text) {
if (text) {
var processor = grammarprocessor(commandgroups, text);
if (processor && processor.command) {
console.log("Command from Grammar Processor", processor);
return voicecommands(processor)
.then(function (result) {
console.log("Result of executed command", result);
if (result.item.actionid === 'show' && result.item.sourceid === 'group') {
return Promise.reject({ error: "group", item: result.item, groupName: result.name });
} else {
return Promise.resolve({ item: result.item });
}
}, function () {
return Promise.reject({ error: "unrecognized-command", text: text });
});
} else {
return Promise.reject({ error: "unrecognized-command", text: text });
}
} else {
return Promise.reject({ error: "empty" });
}
}
/// <summary> An enum constant representing the window. voice input manager option. </summary>
return {
processTranscript: processTranscript,
getCommandGroups: getCommandGroups
};
});

View file

@ -0,0 +1,57 @@
define([], function () {
var currentRecognition = null;
/// <summary> Starts listening for voice commands </summary>
/// <returns> . </returns>
function listenForCommand(lang) {
return new Promise(function (resolve, reject) {
cancelListener();
var recognition = new (window.SpeechRecognition ||
window.webkitSpeechRecognition ||
window.mozSpeechRecognition ||
window.oSpeechRecognition ||
window.msSpeechRecognition)();
recognition.lang = lang;
recognition.onresult = function (event) {
console.log(event);
if (event.results.length > 0) {
var resultInput = event.results[0][0].transcript || '';
resolve(resultInput);
}
};
recognition.onerror = function () {
reject({ error: event.error, message: event.message });
};
recognition.onnomatch = function () {
reject({ error: "no-match" });
};
currentRecognition = recognition;
currentRecognition.start();
});
}
/// <summary> Cancel listener. </summary>
/// <returns> . </returns>
function cancelListener() {
if (currentRecognition) {
currentRecognition.abort();
currentRecognition = null;
}
}
/// <summary> An enum constant representing the window. voice input manager option. </summary>
return {
listenForCommand: listenForCommand,
cancel: cancelListener
};
});