Exemplo n.º 1
0
        public async Task <SkillResponse> FunctionHandler(SkillRequest input, ILambdaContext context)
        {
            string accessKey     = Environment.GetEnvironmentVariable("ACCESS_KEY");
            string secretKey     = Environment.GetEnvironmentVariable("SECRET_KEY");
            string bucketStories = Environment.GetEnvironmentVariable("BUCKET_STORIES");

            StoriesS3Service storiesService = new StoriesS3Service(accessKey, secretKey, bucketStories);

            string           audioUrl;
            string           audioToken;
            SsmlOutputSpeech ssmlResponse = new SsmlOutputSpeech();

            if (input.GetRequestType() == typeof(LaunchRequest))
            {
                ssmlResponse.Ssml = "<speak>You can't open this skill.</speak>";
            }
            else if (input.GetRequestType() == typeof(IntentRequest))
            {
                IntentRequest intentRequest = (IntentRequest)input.Request;
                switch (intentRequest.Intent.Name)
                {
                case "TellRandomStory":
                    string randomStoryName = storiesService.GetRandomStoryName(await storiesService.ListStories());
                    audioUrl   = storiesService.GetStoryUrl(randomStoryName);
                    audioToken = "A story.";
                    return(ResponseBuilder.AudioPlayerPlay(PlayBehavior.ReplaceAll, audioUrl, audioToken));

                default:
                    ssmlResponse.Ssml = $"<speak>An error has occurred!</speak>";
                    break;
                }
            }
            return(ResponseBuilder.Tell(ssmlResponse));
        }
Exemplo n.º 2
0
        public SkillResponse FunctionHandler(SkillRequest input, ILambdaContext context)
        {
            context.Logger.LogLine("Request Type: " + input.GetRequestType().Name);

            if (input.GetRequestType() == typeof(LaunchRequest))
            {
                SkillResponse response = ResponseBuilder.AudioPlayerPlay(Alexa.NET.Response.Directive.PlayBehavior.ReplaceAll, "https://s3-eu-west-1.amazonaws.com/rtg-dispatcher/streaming-test/Dispatcher_Ready_Question.wav", "token");
                response.Response.OutputSpeech = new PlainTextOutputSpeech()
                {
                    Text = "Playing"
                };
                response.Response.ShouldEndSession = false;

                return(response);
            }
            else if (input.GetRequestType() == typeof(IntentRequest))
            {
                IntentRequest request = input.Request as IntentRequest;
                return(request.Intent.Name == "AMAZON.YesIntent" ? ResponseBuilder.AudioPlayerPlay(Alexa.NET.Response.Directive.PlayBehavior.ReplaceAll, "https://s3-eu-west-1.amazonaws.com/rtg-dispatcher/streaming-test/Caller_Birth.wav", "token") : ResponseBuilder.Empty());
            }

            return(ResponseBuilder.Empty());
        }
Exemplo n.º 3
0
 public void AddAudioPlayer(PlayBehavior behavior, string url, string enqueuedToken, string token, int offset)
 {
     RemoveDirective();
     Skill = ResponseBuilder.AudioPlayerPlay(behavior, url, enqueuedToken, token, offset);
     Skill.Response.ShouldEndSession = true;
 }
Exemplo n.º 4
0
 public void AddAudioPlayer(PlayBehavior behavior, string url, string token)
 {
     RemoveDirective();
     Skill = ResponseBuilder.AudioPlayerPlay(behavior, url, token);
     Skill.Response.ShouldEndSession = true;
 }
Exemplo n.º 5
0
        public static async Task <IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequest req,
            ILogger log)
        {
            string json = await req.ReadAsStringAsync();

            var     skillRequest = JsonConvert.DeserializeObject <SkillRequest>(json);
            var     requestType  = skillRequest.GetRequestType();
            Session session      = skillRequest.Session;

            SkillResponse response = null;

            if (requestType == typeof(LaunchRequest))
            {
                response = ResponseBuilder.Tell("Welcome to Gorilla Logic!");
                response.Response.ShouldEndSession = false;
            }
            else if (requestType == typeof(IntentRequest))
            {
                var intentRequest = skillRequest.Request as IntentRequest;

                switch (intentRequest.Intent.Name.ToLower())
                {
                case "gorillalocation":
                    response = ResponseBuilder.Tell("Gorilla Logic is located in Ruta N medellin Colombia oficina 2020");
                    var speech = new SsmlOutputSpeech();
                    //speech.Ssml = "<speak>Gorilla Logic is located in <lang xml:lang=\"es-ES\">Ruta Ene Medellin Colombia oficina 2020</lang></speak>";
                    response = ResponseBuilder.Tell(speech);
                    break;

                case "gorillamusic":
                case "amazon.resumeintent":
                    string audioUrl    = "{url}";
                    string audioToken  = "Gorillaz song 19 - 20000";
                    var    speechMusic = new SsmlOutputSpeech();
                    //speech.Ssml = $"<speak>{audioToken}<audio src=\"{audioUrl}\"/></speak>";
                    //response = ResponseBuilder.Tell(speechMusic);
                    response = ResponseBuilder.AudioPlayerPlay(PlayBehavior.ReplaceAll, audioUrl, audioToken, (int)skillRequest.Context.AudioPlayer.OffsetInMilliseconds);
                    break;

                case "gorillainvitation":
                    var speechInvitation = new SsmlOutputSpeech();
                    speechInvitation.Ssml = "<speak><voice name=\"Enrique\"><lang xml:lang=\"es-ES\">Estan todos invitados al meetup del 25 de julio donde yo alexa seré la protagonista. Los esperamos en Ruta N</lang></voice></speak>";
                    response = ResponseBuilder.Tell(speechInvitation);
                    break;

                case "gorillacalculation":
                    if (intentRequest.Intent.Slots.Count > 0)
                    {
                        if (intentRequest.Intent.Slots["year"] != null &&
                            intentRequest.Intent.Slots["year"].Value != null &&
                            intentRequest.Intent.Slots["date"] != null &&
                            intentRequest.Intent.Slots["date"].Value != null)
                        {
                            DateTime dateValue = DateTime.Parse(intentRequest.Intent.Slots["date"].Value.ToString());

                            dateValue = dateValue.AddYears(int.Parse(intentRequest.Intent.Slots["year"].Value.ToString()) - dateValue.Year);

                            int result = (DateTime.Now - dateValue).Days / 365;

                            response = ResponseBuilder.Tell($"you are {result} years old");
                            response.Response.ShouldEndSession = true;
                        }
                        else
                        {
                            response = ResponseBuilder.Ask("Please tell me, when were you born?", null);
                        }
                    }
                    else
                    {
                        response = ResponseBuilder.Ask("Please tell me, when were you born?", null);
                    }
                    break;

                case "amazon.pauseintent":
                    response = ResponseBuilder.AudioPlayerStop();
                    break;

                default:
                    break;
                }
            }
            else if (requestType == typeof(SessionEndedRequest))
            {
                response = ResponseBuilder.Tell("bye");
                response.Response.ShouldEndSession = true;
            }
            else if (requestType == typeof(AudioPlayerRequest))
            {
                // do some audio response stuff
                var audioRequest = skillRequest.Request as AudioPlayerRequest;

                //
                //if (audioRequest.AudioRequestType == AudioRequestType.PlaybackStopped)

                //
                //if (audioRequest.AudioRequestType == AudioRequestType.PlaybackNearlyFinished)
            }
            else
            {
                response = ResponseBuilder.Empty();
            }

            return(new OkObjectResult(response));
        }
Exemplo n.º 6
0
        public async Task <SkillResponse> FunctionHandler(SkillRequest input, ILambdaContext context)
        {
            var log = context.Logger;
            // I use the following lines to log and validate my input
            //      but this isn't a requirement for the skill
            //log.LogLine($"Skill Request Object...");
            //log.LogLine(JsonConvert.SerializeObject(input));

            SkillResponse returnResponse = new SkillResponse();
            var           audioItems     = AudioAssets.GetSampleAudioFiles();

            // initialize a connection to the database
            //  this also initialized the context for the DynamoDB helper
            var audioStateHelper = new AudioStateHelper();
            await audioStateHelper.VerifyTable();

            string userId = "";

            if (input.Session != null)
            {
                userId = input.Session.User.UserId;
            }
            else
            {
                userId = input.Context.System.User.UserId;
            }

            var lastState = await audioStateHelper.GetAudioState(userId);

            var currentState = new AudioState()
            {
                UserId = userId
            };

            currentState.State = lastState.State;

            // For an intent
            if (input.GetRequestType() == typeof(LaunchRequest))
            {
                log.LogLine($"Default LaunchRequest made");
                var output = new PlainTextOutputSpeech()
                {
                    Text = "Welcome to the Alexa audio sample. "
                           + "You can say, play the audio, to begin."
                };
                var reprompt = new Reprompt()
                {
                    OutputSpeech = new PlainTextOutputSpeech()
                    {
                        Text = "You can say, play the audio, to begin."
                    }
                };
                returnResponse = ResponseBuilder.Ask(output, reprompt);

                await audioStateHelper.SaveAudioState(currentState);
            }
            else if (input.GetRequestType() == typeof(IntentRequest))
            {
                var intentRequest = (IntentRequest)input.Request;
                var output        = new PlainTextOutputSpeech();
                var reprompt      = new Reprompt();
                log.LogLine($"Triggered " + intentRequest.Intent.Name);
                switch (intentRequest.Intent.Name)
                {
                case "PlayAudio":
                    currentState.State.Token     = audioItems.FirstOrDefault().Title;
                    currentState.State.State     = "PLAY_MODE";
                    currentState.State.Index     = 0;
                    currentState.State.playOrder = new List <int> {
                        0, 1, 2, 3, 4
                    };
                    returnResponse = ResponseBuilder.AudioPlayerPlay(
                        PlayBehavior.ReplaceAll,
                        audioItems[currentState.State.Index].Url,
                        currentState.State.Token);

                    break;

                case BuiltInIntent.Help:
                    output.Text           = "You can say, play the audio, to begin.";
                    reprompt.OutputSpeech = new PlainTextOutputSpeech()
                    {
                        Text = "You can say, play the audio, to begin."
                    };
                    returnResponse = ResponseBuilder.Ask(output, reprompt);
                    break;

                case BuiltInIntent.Cancel:
                    currentState.State.OffsetInMS = Convert.ToInt32(input.Context.AudioPlayer.OffsetInMilliseconds);
                    currentState.State.Token      = input.Context.AudioPlayer.Token;
                    currentState.State.State      = "PAUSE_MODE";
                    returnResponse = ResponseBuilder.AudioPlayerStop();
                    break;

                case BuiltInIntent.Next:
                    var thisFile = lastState.State.Token;
                    // get the last state, get the index, add 1
                    // or start from the beginning if you're doing a loop
                    currentState.State.Index++;
                    if (currentState.State.Index >= audioItems.Count)
                    {
                        currentState.State.Index = 0;
                    }
                    currentState.State.Token      = audioItems[currentState.State.Index].Title;
                    currentState.State.OffsetInMS = 0;
                    currentState.State.State      = "PLAY_MODE";
                    returnResponse = ResponseBuilder.AudioPlayerPlay(PlayBehavior.ReplaceAll,
                                                                     audioItems[currentState.State.Index].Url,
                                                                     currentState.State.Token);
                    break;

                case BuiltInIntent.Previous:
                    // get the last state, get the index, subtract 1
                    currentState.State.Index = currentState.State.Index - 1;
                    if (currentState.State.Index < 0)
                    {
                        currentState.State.Index = 0;
                    }

                    currentState.State.Token      = audioItems[currentState.State.Index].Title;
                    currentState.State.OffsetInMS = 0;
                    currentState.State.State      = "PLAY_MODE";
                    returnResponse = ResponseBuilder.AudioPlayerPlay(PlayBehavior.ReplaceAll,
                                                                     audioItems[currentState.State.Index].Url,
                                                                     currentState.State.Token);
                    break;

                case BuiltInIntent.Repeat:
                    // get the last state, get the index, start over at offset = 0
                    currentState.State.Token      = audioItems[currentState.State.Index].Title;
                    currentState.State.OffsetInMS = 0;
                    currentState.State.State      = "PLAY_MODE";
                    returnResponse = ResponseBuilder.AudioPlayerPlay(PlayBehavior.ReplaceAll,
                                                                     audioItems[currentState.State.Index].Url,
                                                                     currentState.State.Token,
                                                                     0);
                    break;

                case BuiltInIntent.StartOver:
                    // start everything from the beginning
                    currentState.State.Token      = audioItems[0].Title;
                    currentState.State.OffsetInMS = 0;
                    currentState.State.State      = "PLAY_MODE";
                    returnResponse = ResponseBuilder.AudioPlayerPlay(PlayBehavior.ReplaceAll,
                                                                     audioItems[0].Url,
                                                                     currentState.State.Token,
                                                                     0);
                    break;

                case BuiltInIntent.Stop:
                    currentState.State.OffsetInMS = Convert.ToInt32(input.Context.AudioPlayer.OffsetInMilliseconds);
                    currentState.State.Token      = input.Context.AudioPlayer.Token;
                    currentState.State.State      = "PAUSE_MODE";
                    returnResponse = ResponseBuilder.AudioPlayerStop();
                    break;

                case BuiltInIntent.Resume:
                    // Get the last state, start from the offest in milliseconds

                    returnResponse = ResponseBuilder.AudioPlayerPlay(PlayBehavior.ReplaceAll,
                                                                     audioItems[currentState.State.Index].Url,
                                                                     currentState.State.Token,
                                                                     currentState.State.OffsetInMS);
                    // If there was an enqueued item...
                    if (currentState.State.EnqueuedToken != null)
                    {
                        returnResponse.Response.Directives.Add(new AudioPlayerPlayDirective()
                        {
                            PlayBehavior = PlayBehavior.Enqueue,
                            AudioItem    = new Alexa.NET.Response.Directive.AudioItem()
                            {
                                Stream = new AudioItemStream()
                                {
                                    Url   = audioItems[currentState.State.Index + 1].Url,
                                    Token = audioItems[currentState.State.Index + 1].Title,
                                    ExpectedPreviousToken = currentState.State.Token,
                                    OffsetInMilliseconds  = 0
                                }
                            }
                        });
                    }

                    currentState.State.EnqueuedToken = audioItems[currentState.State.Index + 1].Title;
                    currentState.State.State         = "PLAY_MODE";
                    break;

                case BuiltInIntent.Pause:
                    currentState.State.OffsetInMS = Convert.ToInt32(input.Context.AudioPlayer.OffsetInMilliseconds);
                    currentState.State.Token      = input.Context.AudioPlayer.Token;
                    currentState.State.State      = "PAUSE_MODE";
                    returnResponse = ResponseBuilder.AudioPlayerStop();
                    break;

                default:
                    log.LogLine($"Unknown intent: " + intentRequest.Intent.Name);
                    output.Text           = "Welcome to Pocast Player";
                    reprompt.OutputSpeech = new PlainTextOutputSpeech()
                    {
                        Text = "This is your reprompt. Please do something."
                    };
                    returnResponse = ResponseBuilder.TellWithReprompt(output, reprompt);
                    break;
                }
            }
            else if (input.GetRequestType() == typeof(AudioPlayerRequest))
            {
                var audioRequest = input.Request as AudioPlayerRequest;

                if (audioRequest.AudioRequestType == AudioRequestType.PlaybackStarted)
                {
                    log.LogLine($"PlaybackStarted Triggered ");
                    // respond with Stop or ClearQueue
                    returnResponse = ResponseBuilder.AudioPlayerClearQueue(ClearBehavior.ClearEnqueued);
                }
                else if (audioRequest.AudioRequestType == AudioRequestType.PlaybackFinished)
                {
                    // Audio comes to an end on its own
                    log.LogLine($"PlaybackFinished Triggered ");
                    if (currentState.State.EnqueuedToken != null)
                    {
                        int itemIndex = audioItems.IndexOf(audioItems.Where(i => i.Title == currentState.State.EnqueuedToken).FirstOrDefault());
                        currentState.State.Token = audioItems[itemIndex].Title;
                        currentState.State.Index = itemIndex;
                        returnResponse           = ResponseBuilder.AudioPlayerPlay(PlayBehavior.ReplaceAll,
                                                                                   audioItems[itemIndex].Url,
                                                                                   currentState.State.Token);
                    }
                    else
                    {
                        // respond with Stop or ClearQueue
                        returnResponse = ResponseBuilder.AudioPlayerClearQueue(ClearBehavior.ClearEnqueued);
                    }
                }
                else if (audioRequest.AudioRequestType == AudioRequestType.PlaybackStopped)
                {
                    // This is when your audio is explicitly stopped
                    log.LogLine($"PlaybackStopped Triggered ");
                    currentState.State.State         = "PAUSE_MODE";
                    currentState.State.Token         = audioRequest.Token;
                    currentState.State.EnqueuedToken = audioRequest.EnqueuedToken;
                    currentState.State.OffsetInMS    = Convert.ToInt32(audioRequest.OffsetInMilliseconds);
                    log.LogLine($"Saving AudioState: " + currentState.State.Token + " at " + currentState.State.OffsetInMS.ToString() + "ms");
                    returnResponse = null;
                }
                else if (audioRequest.AudioRequestType == AudioRequestType.PlaybackNearlyFinished)
                {
                    log.LogLine($"PlaybackNearlyFinished Triggered ");

                    // we'll want to hand back the "next" item in the queue
                    //  First we check to see if there is an enqueued item and, if there is
                    //  we can respond with nothing
                    if (audioRequest.HasEnqueuedItem)
                    {
                        return(null);
                    }

                    // let's get the current token
                    var currentPlay = audioRequest.Token;
                    // get the index of that current item
                    int itemIndex = audioItems.IndexOf(audioItems.Where(i => i.Title == audioRequest.Token).FirstOrDefault());
                    if (itemIndex == -1)
                    {
                        log.LogLine($"Could not get the index of: " + audioRequest.Token);
                    }
                    itemIndex++;
                    if (itemIndex == audioItems.Count)
                    {
                        itemIndex = 0;
                    }

                    currentState.State.EnqueuedToken = audioItems[itemIndex].Title;
                    currentState.State.Token         = audioRequest.Token;
                    // if there is not, we send a play intent with "ENQUEUE"
                    returnResponse = ResponseBuilder.AudioPlayerPlay(
                        PlayBehavior.Enqueue,
                        audioItems[itemIndex].Url,
                        currentState.State.EnqueuedToken,
                        currentState.State.Token,
                        0);
                }
                else if (audioRequest.AudioRequestType == AudioRequestType.PlaybackFailed)
                {
                    log.LogLine($"PlaybackFailed Triggered");
                    // atm, we basically pretend nothing happened and play the first
                    //  file again on a failure
                    //  THIS IS A TERRIBLE SOLUTION
                    //  Figure out a better one for your skill
                    currentState.State.Token = audioItems.FirstOrDefault().Title;
                    currentState.State.Index = 0;
                    currentState.State.State = "PLAY_MODE";
                    returnResponse           = ResponseBuilder.AudioPlayerPlay(PlayBehavior.ReplaceAll, audioItems.FirstOrDefault().Url, currentState.State.Token);
                }
            }

            // I use the following code to validate and log my outputs for
            //      later investigation
            //log.LogLine($"Skill Response Object...");
            //string responseJson = "no response is given";
            //try
            //{
            //    responseJson = JsonConvert.SerializeObject(returnResponse);
            //}
            //catch
            //{
            //    log.LogLine(responseJson);
            //    return null;
            //}
            //log.LogLine(responseJson);

            // Save our state
            await audioStateHelper.SaveAudioState(currentState);

            // return our response
            return(returnResponse);
        }
Exemplo n.º 7
0
        public static async Task <IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequest req,
            ILogger log)
        {
            string json = await req.ReadAsStringAsync();

            var skillRequest = JsonConvert.DeserializeObject <SkillRequest>(json);

            bool isValid = await ValidateRequest(req, log, skillRequest);

            if (!isValid)
            {
                return(new BadRequestResult());
            }

            var requestType = skillRequest.GetRequestType();

            log.LogInformation("Request received, type: {0} id: {1}", requestType, skillRequest.Request.RequestId);

            SkillResponse response = null;

            if (requestType == typeof(LaunchRequest))
            {
                response = ResponseBuilder.Tell("Welcome to KEXP 90.3FM");
                response.Response.ShouldEndSession = false;
            }
            else if (requestType == typeof(IntentRequest))
            {
                var intentRequest = skillRequest.Request as IntentRequest;

                log.LogInformation("-> IntentRequest, name: {0}", intentRequest.Intent.Name);

                if (intentRequest.Intent.Name == "Play" || intentRequest.Intent.Name == "AMAZON.ResumeIntent" || intentRequest.Intent.Name == "AMAZON.StartOverIntent")
                {
                    //handle the intent
                    // https://live-aacplus-64.streamguys1.com/ -- NOTE should we go with higher bitrate?
                    response = ResponseBuilder.AudioPlayerPlay(Alexa.NET.Response.Directive.PlayBehavior.ReplaceAll, "https://live-aacplus-64.streamguys1.com/", "token");
                }
                else if (intentRequest.Intent.Name == "AMAZON.CancelIntent")
                {
                    response = ResponseBuilder.AudioPlayerStop();
                }
                else if (intentRequest.Intent.Name == "AMAZON.HelpIntent")
                {
                    response = ResponseBuilder.Tell("Say PLAY to play the KEXP live stream");

                    response.Response.ShouldEndSession = false;
                }
                else if (intentRequest.Intent.Name == "AMAZON.PauseIntent")
                {
                    response = ResponseBuilder.AudioPlayerStop();
                }
                else if (intentRequest.Intent.Name == "AMAZON.StopIntent")
                {
                    response = ResponseBuilder.AudioPlayerStop();
                }
                else if (intentRequest.Intent.Name == "AMAZON.NextIntent" || intentRequest.Intent.Name == "AMAZON.PreviousIntent")
                {
                    response = ResponseBuilder.Tell("Sorry, Next and Previous are not supported.");
                }
                else if (intentRequest.Intent.Name == "AMAZON.LoopOnIntent" || intentRequest.Intent.Name == "AMAZON.LoopOffIntent")
                {
                    response = ResponseBuilder.Tell("Sorry, looping is not supported.");
                }
                else if (intentRequest.Intent.Name == "AMAZON.ShuffleOnIntent" || intentRequest.Intent.Name == "AMAZON.ShuffleOffIntent")
                {
                    response = ResponseBuilder.Tell("Sorry, shuffle is not supported.");
                }
                else if (intentRequest.Intent.Name == "AMAZON.RepeatIntent")
                {
                    response = ResponseBuilder.Tell("Sorry, Repeat is not supported.");
                }
            }
            else if (requestType == typeof(AudioPlayerRequest))
            {
                // Don't do anything with these for now, but handle them gracefully.
                // More info: https://developer.amazon.com/en-US/docs/alexa/custom-skills/audioplayer-interface-reference.html
                var audioPlayerRequest = skillRequest.Request as AudioPlayerRequest;
                log.LogInformation("-> AudioPlayerRequest: {0}", audioPlayerRequest.AudioRequestType);
                response = ResponseBuilder.Empty();
            }
            else if (requestType == typeof(SessionEndedRequest))
            {
                log.LogInformation("-> Session ended");
                response = ResponseBuilder.Empty();
                response.Response.ShouldEndSession = true;
            }

            if (response == null)
            {
                log.LogError("*** Unhandled request:");
                log.LogError(json);
            }

            return(new OkObjectResult(response));
        }
Exemplo n.º 8
0
        public SkillResponse Play(AudioSessionAttributes attributes, bool isResetPlayOrder, string expectedPreviousToken = null)
        {
            _logger.LogLine($"AudioController.Play isResetPlayOrder={isResetPlayOrder}, expectedPreviousToken={expectedPreviousToken}");

            attributes.State = Constants.States.PlayMode;

            if (isResetPlayOrder)
            {
                _logger.LogLine("Resetting play order.");

                var currentItem = _feedItems[attributes.Index];

                attributes.PlayOrder = _feedItems
                                       .OrderByDescending(aItem => aItem.PublishDate)
                                       .Select(item => _feedItems.IndexOf(item)).ToList();

                if (!attributes.PlaybackFinished)
                {
                    attributes.Index = _feedItems.IndexOf(currentItem);
                }
            }

            if (attributes.PlaybackFinished)
            {
                _logger.LogLine("Playback is finished.");

                // Reset to top of the playlist when reached end.
                attributes.Index = 0;
                attributes.OffsetInMilliseconds = 0;
                attributes.PlaybackIndexChanged = true;
                attributes.PlaybackFinished     = false;
            }

            var token    = attributes.PlayOrder[attributes.Index];
            var feedItem = _feedItems[attributes.Index];
            var mp3Url   = feedItem.Mp3Url.Replace("http://", "https://");
            var artUrl   = feedItem.ArtUrl.Replace("http://", "https://");

            attributes.EnqueuedToken = -1;

            _logger.LogLine($"Building player response for {mp3Url}.");

            var response = ResponseBuilder.AudioPlayerPlay(
                expectedPreviousToken == null ? PlayBehavior.ReplaceAll : PlayBehavior.Enqueue,
                mp3Url,
                token.ToString(),
                expectedPreviousToken,
                (int)attributes.OffsetInMilliseconds);

            response.Response.ShouldEndSession = false;

            if (attributes.PlaybackIndexChanged)
            {
                _logger.LogLine("Playback index changed - adding card.");

                var cardTitle   = $"Playing {feedItem.Title}";
                var cardContent = feedItem.Description;

                response.Response.Card = new StandardCard
                {
                    Title   = cardTitle,
                    Content = cardContent,
                    Image   = new CardImage {
                        SmallImageUrl = artUrl, LargeImageUrl = artUrl
                    }
                };
            }

            return(response);
        }