Search code examples
node.jsalexa-skills-kit

Insert Intro Jingle at Amazon Alexa with node.js


I have created a skill for Amazon Alexa using node.js, which plays an MP3 stream. Now I have problems to play a jingle with a fixed URL before the stream starts. How do I have to proceed to realize this project?

Below is the most important part of the code of the simple player:

const LaunchRequestHandler = {
    canHandle(handlerInput) {
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'LaunchRequest'
            || (Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
                && Alexa.getIntentName(handlerInput.requestEnvelope) === 'PlayStationIntent');
    },
    handle(handlerInput) {
        const speakOutput = messages.welcome;

        return handlerInput.responseBuilder
            .speak(speakOutput)
            .addAudioPlayerPlayDirective("REPLACE_ALL", url, token(), 0)
            .getResponse();
    }
};

Solution

  • There are multiple options for implementing this:

    • SSML if the jingle is very short and comply to some encodings, you may include it in the speakOutput by using SSML and the audio tag.
    • M3U Instead of the including the URL of the stream directly in the AudioPlayerPlayDirective, you can include there the URL to an M3U, which then includes a playlist of the Jingle URL and the stream URL.
    • PlayBackNearlyFinished Intent Just sent as first play directive the url of the Jingle and add support for the PlayBackNearlyFinished Intent, which will be invoked by the AudioPlayer itself when playing Jingle is nearly finished and then send inside this intent an audio player play directive (without a speak) but with the URL of the stream. But be aware if that file gets nearly finished, the same PlayBackNearlyFinished Intent will get called, so you need to identify that it already has been called to avoid making an infinity loop. Best way would to use token attribute on both play commands with (first with "Jingle" and second with "Stream" or token()) so if PlayBackFinished Intent is called, check token in the request and only send the second play command, if token is "Jingle" and so identifying the Jingle has ended.

    The last option would change your code to something like:

    const LaunchRequestHandler = {
        canHandle(handlerInput) {
            return Alexa.getRequestType(handlerInput.requestEnvelope) === 'LaunchRequest'
                || (Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
                    && Alexa.getIntentName(handlerInput.requestEnvelope) === 'PlayStationIntent');
        },
        handle(handlerInput) {
            const speakOutput = messages.welcome;
    
            return handlerInput.responseBuilder
                .speak(speakOutput)
                .addAudioPlayerPlayDirective("REPLACE_ALL", url_jingle, "jingle", 0)
                .getResponse();
        }
    };
    
    const PlayBackFinishedHandler = {
        canHandle(handlerInput) {
            return Alexa.getRequestType(handlerInput.requestEnvelope) === 'AudioPlayer.PlaybackNearlyFinished';
        },
    
        handle(handlerInput) {
            if (handlerInput.requestEnvelope.request.token === 'jingle') {
                return handlerInput.responseBuilder
                    .addAudioPlayerPlayDirective("REPLACE_ALL", url, token(), 0)
                    .getResponse();
            }
        }
    };
    

    Additionally you would need to activate the AudioPlayer Interface and rebuild the model afterwards, so that those controls events will reach you.

    Audio Player Interface activation