Permalink
Browse files

Added AudioPlayer support, #88, #92.

  • Loading branch information...
1 parent e2b8c6c commit cb4023f54c3cd4493fdb84c682154234d5027ef8 @fremail fremail committed with dblock Dec 26, 2016
Showing with 368 additions and 1 deletion.
  1. +1 −0 .gitignore
  2. +1 −0 CHANGELOG.md
  3. +59 −0 README.md
  4. +79 −1 index.js
  5. +52 −0 test/fixtures/intent_audioplayer.json
  6. +176 −0 test/test_alexa_app_audioplayer.js
View
@@ -46,6 +46,7 @@ Temporary Items
# IDE
.idea
+.vscode
# NPM
npm-debug.log
View
@@ -5,6 +5,7 @@
* [#76](https://github.com/matt-kruse/alexa-app/pull/76): `response.clear()` now sets the default `outputSpeech` to SSML - [@rickwargo](https://github.com/rickwargo).
* [#90](https://github.com/matt-kruse/alexa-app/pull/90): Added [Danger](http://danger.systems), PR linter - [@dblock](https://github.com/dblock).
* [#91](https://github.com/matt-kruse/alexa-app/pull/91): Fixed "Cannot read property 'new' of undefined" init error - [@fremail](https://github.com/fremail).
+* [#88](https://github.com/matt-kruse/alexa-app/pull/88), [#92](https://github.com/matt-kruse/alexa-app/pull/92): AudioPlayer functionality - [@wschaeferiii](https://github.com/wschaeferiii) and [@fremail](https://github.com/fremail).
* Your contribution here.
### 2.3.4 (May 23, 2016)
View
@@ -92,6 +92,17 @@ response.card(Object card)
// this internally sets the card response
response.linkAccount()
+// play audio stream (send AudioPlayer.Play directive) @see https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/custom-audioplayer-interface-reference#play-directive
+// skill supports stream(String url, String token, String expectedPreviousToken, Integer offsetInMilliseconds)
+response.audioPlayerPlayStream(String playBehavior, Object stream)
+
+// stop playing audio strem (send AudioPlayer.Stop directive)
+response.audioPlayerStop()
+
+// clear audio player queue (send AudioPlayer.ClearQueue directive)
+// clearBehavior is "CLEAR_ALL" by default
+response.audioPlayerClearQueue([ String clearBehavior ])
+
// tell Alexa whether the user's session is over; sessions end by default
// you can optionally pass a reprompt message
response.shouldEndSession(boolean end [, String reprompt] )
@@ -167,6 +178,54 @@ app.sessionEnded(function(request, response) {
});
```
+## AudioPlayer Event Request
+
+Define the handler for multiple events using multiple calls to `audioPlayer()`. You can define only one handler per event. Event handlers that don't return an immediate response (because they do some asynchronous operation) must return false.
+
+You can define handlers for the following events:
+
+* PlaybackStarted
+* PlaybackFinished
+* PlaybackStopped
+* PlaybackNearlyFinished
+* PlaybackFailed
+
+Read more about AudioPlayer request types in [AudioPlayer Interface Doc](https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/custom-audioplayer-interface-reference#audioplayer-requests).
+
+The following example will return `play` directive with a next audio on `AudioPlayer.PlaybackNearlyFinished` request.
+
+```javascript
+app.audioPlayer("PlaybackNearlyFinished", function(request, response) {
+ // immediate response
+ var stream = {
+ "url": "https://next-song-url",
+ "token": "some_token",
+ "expectedPreviousToken": "some_previous_token",
+ "offsetInMilliseconds": 0
+ };
+ response.audioPlayerPlayStream("ENQUEUE", stream);
+});
+```
+
+See an example of asynchronous response below.
+
+```javascript
+app.audioPlayer("PlaybackFinished", function(request, response) {
+ // async response
+ getNextSongFromDB(function(url, token) {
+ var stream = {
+ "url": url,
+ "token": token,
+ "expectedPreviousToken": "some_previous_token",
+ "offsetInMilliseconds": 0
+ };
+ response.audioPlayerPlayStream("ENQUEUE", stream);
+ response.send();
+ });
+ return false;
+});
+```
+
# Execute Code On Every Request
In addition to specific event handlers, you can define functions that will run on every request.
View
@@ -4,10 +4,12 @@ var SSML = require("./to-ssml");
var alexa = {};
alexa.response = function(session) {
+ var self = this;
this.resolved = false;
this.response = {
"version": "1.0",
"response": {
+ "directives": [],
"shouldEndSession": true
}
};
@@ -115,6 +117,36 @@ alexa.response = function(session) {
this.prepare = function() {
this.setSessionAttributes(this.sessionObject.getAttributes());
};
+ this.audioPlayerPlay = function(playBehavior, audioItem) {
+ var audioPlayerDirective = {
+ "type": "AudioPlayer.Play",
+ "playBehavior": playBehavior,
+ "audioItem": audioItem
+ };
+ self.response.response.directives.push(audioPlayerDirective);
+ return this;
+ };
+ this.audioPlayerPlayStream = function(playBehavior, stream) {
+ var audioItem = {
+ "stream": stream
+ };
+ return this.audioPlayerPlay(playBehavior, audioItem);
+ };
+ this.audioPlayerStop = function () {
+ var audioPlayerDirective = {
+ "type": "AudioPlayer.Stop"
+ };
+ self.response.response.directives.push(audioPlayerDirective);
+ return this;
+ };
+ this.audioPlayerClearQueue = function (clearBehavior) {
+ var audioPlayerDirective = {
+ "type": "AudioPlayer.ClearQueue",
+ "clearBehavior": clearBehavior || "CLEAR_ALL"
+ };
+ self.response.response.directives.push(audioPlayerDirective);
+ return this;
+ };
// legacy code below
// @deprecated
@@ -131,7 +163,6 @@ alexa.response = function(session) {
this.sessionObject.clear(key);
return this;
};
-
};
alexa.request = function(json) {
@@ -154,6 +185,34 @@ alexa.request = function(json) {
};
this.userId = this.data.context.System.user.userId;
this.applicationId = this.data.context.System.application.applicationId;
+ this.context = function () {
+ try {
+ return this.data.request.context = {
+ "System": {
+ "application": {
+ "applicationId": this.applicationId
+ },
+ "user": {
+ "userId": this.data.session.user.userId,
+ "accessToken": this.data.session.accessToken
+ },
+ "device": {
+ "supportedInterfaces": {
+ "AudioPlayer": {}
+ }
+ }
+ },
+ "AudioPlayer": {
+ "token": this.sessionId,
+ "offsetInMilliseconds": this.data.request.offsetInMilliseconds,
+ "playerActivity": this.data.request.playerActivity
+ }
+ };
+ } catch (e) {
+ console.error("missing context", e);
+ return null;
+ }
+ };
var session = new alexa.session(json.session);
this.hasSession = function() {
@@ -235,6 +294,8 @@ alexa.app = function(name, endpoint) {
this.messages = {
// When an intent was passed in that the application was not configured to handle
"NO_INTENT_FOUND": "Sorry, the application didn't know what to do with that intent",
+ // When an AudioPlayer event was passed in that the application was not configured to handle
+ "NO_AUDIO_PLAYER_EVENT_HANDLER_FOUND": "Sorry, the application didn't know what to do with that AudioPlayer event",
// When the app was used with 'open' or 'launch' but no launch handler was defined
"NO_LAUNCH_FUNCTION": "Try telling the application what to do instead of opening it",
// When a request type was not recognized
@@ -276,6 +337,13 @@ alexa.app = function(name, endpoint) {
self.intents[intentName].schema = schema;
}
};
+ this.audioPlayerEventHandlers = {};
+ this.audioPlayer = function(eventName, func) {
+ self.audioPlayerEventHandlers[eventName] = {
+ "name": eventName,
+ "function": func
+ };
+ };
this.launchFunc = null;
this.launch = function(func) {
self.launchFunc = func;
@@ -341,6 +409,16 @@ alexa.app = function(name, endpoint) {
response.send();
}
}
+ } else if (0 === requestType.indexOf("AudioPlayer.")) {
+ var event = requestType.slice(12);
+ var eventHandlerObject = self.audioPlayerEventHandlers[event];
+ if (typeof eventHandlerObject != "undefined" && typeof eventHandlerObject["function"] == "function") {
+ if (false !== eventHandlerObject["function"](request, response)) {
+ response.send();
+ }
+ } else {
+ throw "NO_AUDIO_PLAYER_EVENT_HANDLER_FOUND";
+ }
} else {
throw "INVALID_REQUEST_TYPE";
}
@@ -0,0 +1,52 @@
+{
+ "version": "1.0",
+ "session": {
+ "new": false,
+ "sessionId": "amzn1.echo-api.session.abeee1a7-aee0-41e6-8192-e6faaed9f5ef",
+ "application": {
+ "applicationId": "amzn1.echo-sdk-ams.app.000000-d0ed-0000-ad00-000000d00ebe"
+ },
+ "attributes": {},
+ "user": {
+ "userId": "amzn1.account.AM3B227HF3FAM1B261HK7FFM3A2"
+ }
+ },
+ "request": {
+ "type": "IntentRequest",
+ "requestId": "amzn1.echo-api.request.6919844a-733e-4e89-893a-fdcb77e2ef0d",
+ "timestamp": "2015-05-13T12:34:56Z",
+ "intent": {
+ "name": "audioPlayerIntent",
+ "slots": {}
+ },
+ "context": {
+ "application": {
+ "applicationId": "amzn1.echo-sdk-ams.app.000000-d0ed-0000-ad00-000000d00fuck"
+ },
+ "user": {
+ "userId": "amzn1.account.AM3B227HF3FAM1B261HK7FFM3A2",
+ "accessToken": "sample.accessToken"
+ },
+ "device": {
+ "supportedInterfaces": {
+ "AudioPlayer": {}
+ }
+ }
+ },
+ "AudioPlayer": {
+ "token": "amzn1.echo-api.session.abeee1a7-aee0-41e6-8192-e6faaed9f5ef",
+ "offsetInMilliseconds": 0,
+ "playerActivity": "PLAYING"
+ }
+ },
+ "context": {
+ "System": {
+ "user": {
+ "userId": "amzn1.account.AM3B227HF3FAM1B261HK7FFM3A2"
+ },
+ "application": {
+ "applicationId": "amzn1.echo-sdk-ams.app.000000-d0ed-0000-ad00-000000d00ebe"
+ }
+ }
+ }
+}
Oops, something went wrong.

0 comments on commit cb4023f

Please sign in to comment.