Como citado nos posts anteriores, ao ativar o AudioPlayer da Alexa, ela fornece alguns intents específicos para a obtenção do estado da execução. De modo geral, na skill da EximiaCo, utilizamos estes intents para duas tarefas principais: atualizar o estado de reprodução do episódio atual e para gerenciar o playback contínuo de episódios.
import ask_sdk_core.utils as ask_utils
from .player import Player
from ask_sdk_core.dispatch_components import AbstractRequestHandler
class PlaybackStartedHandler(AbstractRequestHandler):
def can_handle(self, handler_input):
return ask_utils.is_request_type("AudioPlayer.PlaybackStarted")(handler_input)
def handle(self, handler_input):
player = Player(handler_input)
player.handle_playback_started()
return handler_input.response_builder.response
class PlaybackNearlyFinishedHandler(AbstractRequestHandler):
def can_handle(self, handler_input):
return ask_utils.is_request_type("AudioPlayer.PlaybackNearlyFinished")(handler_input)
def handle(self, handler_input):
player = Player(handler_input)
player.handle_playback_nearly_finished()
return handler_input.response_builder.response
class PlaybackStoppedHandler(AbstractRequestHandler):
def can_handle(self, handler_input):
return ask_utils.is_request_type("AudioPlayer.PlaybackStopped")(handler_input)
def handle(self, handler_input):
player = Player(handler_input)
player.handle_playback_stopped()
return handler_input.response_builder.response
class PlaybackFinishedHandler(AbstractRequestHandler):
def can_handle(self, handler_input):
return ask_utils.is_request_type("AudioPlayer.PlaybackFinished")(handler_input)
def handle(self, handler_input):
player = Player(handler_input)
player.handle_playback_finished()
return handler_input.response_builder.response
Com o intent AudioPlayer.PlaybackStarted, simplesmente armazenamos o episódio em execução atual, para que, posteriormente, ele possa ser recuperado durante as possíveis modificações no estado do playback.
def handle_playback_started(self):
current_token = self.handler_input.request_envelope.request.token
self.state.set_token(current_token)
self.state.set_current_episode(current_token)
No intent AudioPlayer.PlaybackNearlyFinished, a Alexa está informando que o episódio está perto do final, dando a possiblidade de definir o próximo episódio a ser executado. Importante reforçar a utilização do token ao enfileirar o playback. Quando é iniciada a reprodução, é informado o token da execução atual e também o token esperado da execução anterior, para garantir a ordem da execução. Caso o token anterior informado não coincida com o existente no episódio anterior (ou o atual, caso não tenha finalizado a reprodução), a diretiva PlayDirective é ignorada.
def handle_playback_nearly_finished(self):
current_episode = self.state.get_current_episode()
if current_episode is None:
return
previous_episode = current_episode
if not self.state.get_repeat():
previous_episode = self.episodes_provider.get_previous(current_episode)
if previous_episode is None and self.state.get_loop():
previous_episode = self.episodes_provider.get_latest()
if previous_episode is None:
return
self.handler_input.response_builder.add_directive(
PlayDirective(
play_behavior=PlayBehavior.ENQUEUE,
audio_item=AudioItem(
stream=Stream(
token=previous_episode["pub"],
url=previous_episode["address"],
offset_in_milliseconds=0,
expected_previous_token=current_episode["pub"]),
metadata=None))
).set_should_end_session(True)
Quando paramos a execução do episódio com um comando como “Pare”, ou “Cancelar”, é invocado o intent AudioPlayer.PlaybackStopped. Neste intent, armazenamos o offset do episódio que está sendo reproduzido. Isto possibilita que a Alexa retome a reprodução do episódio, quando for solicitado que ela continue a reprodução.
def handle_playback_stopped(self):
millis = self.handler_input.request_envelope.request.offset_in_milliseconds
self.state.set_offset(millis)
Ao finalizar o episódio, a Alexa invoca o intent AudioPlayer.PlaybackFinished. Neste momento apenas zeramos o offset do playback. Isso faz com que, ao iniciar um novo episódio, a Alexa comece a reprodução do início, e não do offset supostamente armazenado no estado da execução.
def handle_playback_finished(self):
self.reset()
Nesta etapa, a skill está pronta. No próximo post da série, falaremos sobre o seu processo de publicação.
Para quem acompanha os drops da EximiaCo, ou para aqueles que gostariam de acompanhar, a nossa skill está disponível no Marketplace da Alexa. Gostaríamos muito de conhecer a sua opinião.