In this step, we will flesh out our podcast player and allow the user to pause and resume the audio file.
On Amazon Alexa
Currently, our podcast player can only start playing audio files, nothing else. It's time to change that. We will start with Amazon since they provide us with built-in intents, which we can use as a guideline later on.
The built-in intents for playback control are:
AMAZON.PauseIntent
AMAZON.ResumeIntent
AMAZON.CancelIntent
AMAZON.LoopOffIntent
AMAZON.LoopOnIntent
AMAZON.NextIntent
AMAZON.PreviousIntent
AMAZON.RepeatIntent
AMAZON.ShuffleOffIntent
AMAZON.ShuffleOnIntent
AMAZON.StartOverIntent
Two of these built-in intents, AMAZON.PauseIntent
and AMAZON.ResumeIntent
, are required, the other intents are technically not, but these intents can still be invoked by our user, so we should, as Amazon says, handle the situation gracefully and return a short response, explaining that the command is not supported, otherwise we will run into errors.
Updating the Jovo Language Model
Before we can work with the AudioPlayer interface on an Alexa device, we have to prepare one more thing. As I said two of these intents are required, because of that, an Alexa Skill, that has the AudioPlayer interface enabled, has to have both intents in its language model as well. Instead of using the Alexa Developer Portal to add those intents, we will use the Jovo Language Model, which allows us to maintain a single file, that will be used to create the platform specific language models.
We can find the language model file in the /models
folder of our Jovo project:
Here's its default state:
Right next to the other Alexa built-in intents inside the alexa
object, which is used to add platform-specific intents and input types, we will add the AMAZON.PauseIntent
and AMAZON.ResumeIntent
in the same format:
The content of each platform object will be used to extend the language model defined in the platform-independent part.
We can also change the invocation name to my podcast player
:
Now that our preparation are done, we can use the Jovo Language Model to create the platform specific files and deploy them to the Amazon Developer Console right after:
This was just a small sneak peek into the Jovo Language Model. We will use the it again in one the next steps, where we will go more in depth.
Updating the Logic
After we have updated our language model, we need to add the intents to the logic of the code:
Adding AMAZON.PauseIntent
Open up the app.js
file inside your src/
folder and add the AMAZON.PauseIntent
intent to our handler.
Every time the intent is called we want to pause the audio stream by sending out a stop directive (learn more in the official reference by Amazon):
To test it out we run our Jovo Webhook and say Alexa, start my podcast player to launch the app with our Amazon Echo. After the audio started playing, we just say Alexa, pause to invoke the AMAZON.PauseIntent
.
That's it. We have successfully paused the audio file.
Adding AMAZON.ResumeIntent
Alright, our user can now pause the audio, but now they also have to be able to resume where they left off. That's what the second required built-in AMAZON.ResumeIntent
is for.
As we learned at the beginning of the course, we can specify the point at which the audio stream should start playing using the offset
variable. To let the user resume, we simply save the offset at the time at which they stop the audio in a database, so we can retrieve the offset and use it in our ResumeIntent
to start the audio back at the correct spot.
For this, we have to do the following steps:
- An Introduction to Databases in Jovo
- Updating the PlaybackStopped Request Handler
- Saving and Retrieving the Current Audio File
An Introduction to Databases in Jovo
As the session is closed after the PauseIntent
, we have to find a way to persist data across sessions. This is where the Jovo Database Integrations can be helpful.
The default databse is File DB for local development and prototyping. Jovo will save the data inside a JSON
file, which we can find in our project's root folder under db/db.json
. To save and load data we use Jovo Framework's user class:
Later on, when you prepare to launch your application and host your app on e.g. AWS Lambda, you have switch to a different database, e.g. DynamoDB. Find all supported databases here.
Updating the PlaybackStopped Request Handler
The most convenient way to save the offset is with the help of the AlexaSkill.PlaybackStopped
request.
The request will be sent if:
- we stop the current stream and start a new one,
- we stop the audio stream altogether,
- the user pauses the current stream,
- the user makes any type of other voice requests, which will pause the audio for the moment. It will resume after the interaction has ended.
That JSON
request will contain the data we need:
We save the offset to our database using one of $audioPlayer
class's built-in methods:
Now we add the AMAZON.ResumeIntent
to our handler and retrieve the offset
from the database.
But, there's one more thing missing. How do we know, which audio file to stream? We have to not only save the offset
, but the current audio files URL as well.
Saving and Retrieving the Current Audio File
The first step is to save the current episode before we send out the first play directive:
Besides that, we have to remember that we enqueue the next song, which we have to save as the current episode after the first one finished playing. For that we save the audio file, which we enqueue, as the nextEpisode
and switch out currentEpisode
with nextEpisode
as soon as the audio stream finishes, which we get notified about with the PlaybackFinished
request:
Now we can finish implementing the AMAZON.ResumeIntent
. Simply retrieve the current episode from the database and send out a play directive using that and the offset
:
To test it out, simply pause the audio stream at some point and restart it with Alexa, resume.
Remaining Alexa built-in Intents
There are still quite many built-in intents remaining. For now we will simply tell the user that these are not implemented and revisit at least two of them at a later point:
On Google Assistant
Here's the deal: Implementing a resume and pause intent the same way won't work here. First of all, pausing an audio stream is handled by Google, so those requests don't reach our app's code. Resuming an audio stream at a certain point won't work either, because we can't specify the offset as we did with Alexa.
But, there's one thing we can do. Instead of starting with the very first audio file every time, we will automatically start playing the audio file the user last listened to.
To do that, we have to additionally save the current episode in the GoogleAction.Finished
intent:
Now we can check at our LAUNCH
intent if the request is from a new user, if that's the case we start with the first track, otherwise we stream the most recently listened one:
As you can see, for the Google Action we still need to find a way to use different title
parameters for different episodes. We will look into this in a later step.
Summary
Wow, that was quite a long step, but we can now successfully pause and resume episodes. Our handler should look like this:
Next Step
In the next step, we will build a system to store and retrieve the episodes of our podcast.