class AudioPlayer(object): STATES = { 'IDLE', 'PLAYING', 'STOPPED', 'PAUSED', 'BUFFER_UNDERRUN', 'FINISHED' } def __init__(self, alexa): self.alexa = alexa self.token = '' self.state = 'IDLE' self.player = Player() self.player.add_callback('eos', self.PlaybackFinished) self.player.add_callback('error', self.PlaybackFailed) # { # "directive": { # "header": { # "namespace": "AudioPlayer", # "name": "Play", # "messageId": "{{STRING}}", # "dialogRequestId": "{{STRING}}" # }, # "payload": { # "playBehavior": "{{STRING}}", # "audioItem": { # "audioItemId": "{{STRING}}", # "stream": { # "url": "{{STRING}}", # "streamFormat": "AUDIO_MPEG" # "offsetInMilliseconds": {{LONG}}, # "expiryTime": "{{STRING}}", # "progressReport": { # "progressReportDelayInMilliseconds": {{LONG}}, # "progressReportIntervalInMilliseconds": {{LONG}} # }, # "token": "{{STRING}}", # "expectedPreviousToken": "{{STRING}}" # } # } # } # } # } def Play(self, directive): if self.alexa.SpeechSynthesizer.state == 'PLAYING': self.alexa.SpeechSynthesizer.wait() behavior = directive['payload']['playBehavior'] self.token = directive['payload']['audioItem']['stream']['token'] audio_url = get_audio_url( directive['payload']['audioItem']['stream']['url']) self.player.play(audio_url) self.PlaybackStarted() logger.info('audio player is playing') def PlaybackStarted(self): self.state = 'PLAYING' event = { "header": { "namespace": "AudioPlayer", "name": "PlaybackStarted", "messageId": uuid.uuid4().hex }, "payload": { "token": self.token, "offsetInMilliseconds": self.player.position } } self.alexa.send_event(event) def PlaybackNearlyFinished(self): event = { "header": { "namespace": "AudioPlayer", "name": "PlaybackNearlyFinished", "messageId": uuid.uuid4().hex }, "payload": { "token": self.token, "offsetInMilliseconds": self.player.position } } self.alexa.send_event(event) def ProgressReportDelayElapsed(self): pass def ProgressReportIntervalElapsed(self): pass def PlaybackStutterStarted(self): pass def PlaybackStutterFinished(self): pass def PlaybackFinished(self): self.state = 'FINISHED' logger.info('playback is finished') event = { "header": { "namespace": "AudioPlayer", "name": "PlaybackFinished", "messageId": uuid.uuid4().hex }, "payload": { "token": self.token, "offsetInMilliseconds": self.player.position } } self.alexa.send_event(event) def PlaybackFailed(self): self.state = 'STOPPED' # { # "directive": { # "header": { # "namespace": "AudioPlayer", # "name": "Stop", # "messageId": "{{STRING}}", # "dialogRequestId": "{{STRING}}" # }, # "payload": { # } # } # } def Stop(self, directive): if self.state == 'PLAYING' or self.state == 'PAUSED': self.player.stop() self.PlaybackStopped() logger.info('audio player is stoped') def PlaybackStopped(self): self.state = 'STOPPED' event = { "header": { "namespace": "AudioPlayer", "name": "PlaybackStopped", "messageId": uuid.uuid4().hex }, "payload": { "token": self.token, "offsetInMilliseconds": self.player.position } } self.alexa.send_event(event) def pause(self): self.player.pause() self.PlaybackPaused() logger.info('audio player is paused') def PlaybackPaused(self): self.state = 'PAUSED' event = { "header": { "namespace": "AudioPlayer", "name": "PlaybackPaused", "messageId": uuid.uuid4().hex }, "payload": { "token": self.token, "offsetInMilliseconds": self.player.position } } self.alexa.send_event(event) def resume(self): self.player.resume() self.PlaybackResumed() logger.info('audio player is resumed') def PlaybackResumed(self): self.state = 'PLAYING' event = { "header": { "namespace": "AudioPlayer", "name": "PlaybackResumed", "messageId": uuid.uuid4().hex }, "payload": { "token": self.token, "offsetInMilliseconds": self.player.position } } self.alexa.send_event(event) # { # "directive": { # "header": { # "namespace": "AudioPlayer", # "name": "ClearQueue", # "messageId": "{{STRING}}", # "dialogRequestId": "{{STRING}}" # }, # "payload": { # "clearBehavior": "{{STRING}}" # } # } # } def ClearQueue(self, directive): self.PlaybackQueueCleared() behavior = directive['payload']['clearBehavior'] if behavior == 'CLEAR_ALL': self.player.stop() elif behavior == 'CLEAR_ENQUEUED': pass def PlaybackQueueCleared(self): event = { "header": { "namespace": "AudioPlayer", "name": "PlaybackQueueCleared", "messageId": uuid.uuid4().hex }, "payload": {} } self.alexa.send_event(event) def StreamMetadataExtracted(self): pass @property def context(self): if self.state != 'PLAYING': offset = 0 else: offset = self.player.position return { "header": { "namespace": "AudioPlayer", "name": "PlaybackState" }, "payload": { "token": self.token, "offsetInMilliseconds": offset, "playerActivity": self.state } }
class Alerts(object): STATES = {'IDLE', 'FOREGROUND', 'BACKGROUND'} def __init__(self, alexa): self.alexa = alexa self.player = Player() self.player.add_callback('eos', self.stop) self.player.add_callback('error', self.stop) alarm = os.path.realpath( os.path.join(os.path.dirname(__file__), '../resources/alarm.mp3')) self.alarm_uri = 'file://{}'.format(alarm) self.all_alerts = {} self.active_alerts = {} self.state = 'IDLE' def stop(self): """ Stop all active alerts """ for token in self.active_alerts.keys(): self.AlertStopped(token) self.active_alerts = {} self.state = 'IDLE' def enter_background(self): if self.state == 'FOREGROUND': self.state = 'BACKGROUND' self.player.pause() def enter_foreground(self): if self.state == 'BACKGROUND': self.state = 'FOREGROUND' self.player.resume() def _start_alert(self, token): if token in self.all_alerts: self.AlertStarted(token) # TODO: repeat play alarm until user stops it or timeout self.player.play(self.alarm_uri) # { # "directive": { # "header": { # "namespace": "Alerts", # "name": "SetAlert", # "messageId": "{{STRING}}", # "dialogRequestId": "{{STRING}}" # }, # "payload": { # "token": "{{STRING}}", # "type": "{{STRING}}", # "scheduledTime": "2017-08-07T09:02:58+0000", # } # } # } def SetAlert(self, directive): payload = directive['payload'] token = payload['token'] scheduled_time = dateutil.parser.parse(payload['scheduledTime']) # Update the alert if token in self.all_alerts: pass self.all_alerts[token] = payload interval = scheduled_time - datetime.datetime.now( scheduled_time.tzinfo) Timer(interval.seconds, self._start_alert, (token, )).start() self.SetAlertSucceeded(token) def SetAlertSucceeded(self, token): event = { "header": { "namespace": "Alerts", "name": "SetAlertSucceeded", "messageId": uuid.uuid4().hex }, "payload": { "token": token } } self.alexa.send_event(event) def SetAlertFailed(self, token): event = { "header": { "namespace": "Alerts", "name": "SetAlertFailed", "messageId": uuid.uuid4().hex }, "payload": { "token": token } } self.alexa.send_event(event) # { # "directive": { # "header": { # "namespace": "Alerts", # "name": "DeleteAlert", # "messageId": "{{STRING}}", # "dialogRequestId": "{{STRING}}" # }, # "payload": { # "token": "{{STRING}}" # } # } # } def DeleteAlert(self, directive): token = directive['payload']['token'] if token in self.active_alerts: self.AlertStopped(token) if token in self.all_alerts: del self.all_alerts[token] self.DeleteAlertSucceeded(token) def DeleteAlertSucceeded(self, token): event = { "header": { "namespace": "Alerts", "name": "DeleteAlertSucceeded", "messageId": uuid.uuid4().hex }, "payload": { "token": token } } self.alexa.send_event(event) def DeleteAlertFailed(self, token): event = { "header": { "namespace": "Alerts", "name": "DeleteAlertFailed", "messageId": uuid.uuid4().hex }, "payload": { "token": token } } self.alexa.send_event(event) def AlertStarted(self, token): if self.state == 'IDLE': self.state = 'FOREGROUND' self.active_alerts[token] = self.all_alerts[token] event = { "header": { "namespace": "Alerts", "name": "AlertStarted", "messageId": uuid.uuid4().hex }, "payload": { "token": token } } self.alexa.send_event(event) def AlertStopped(self, token): if token in self.active_alerts: del self.active_alerts[token] if token in self.all_alerts: del self.all_alerts[token] if not self.active_alerts: self.state = 'IDLE' event = { "header": { "namespace": "Alerts", "name": "AlertStopped", "messageId": "{STRING}" }, "payload": { "token": token } } self.alexa.send_event(event) def AlertEnteredForeground(self, token): event = { "header": { "namespace": "Alerts", "name": "AlertEnteredForeground", "messageId": uuid.uuid4().hex }, "payload": { "token": token } } self.alexa.send_event(event) def AlertEnteredBackground(self, token): event = { "header": { "namespace": "Alerts", "name": "AlertEnteredBackground", "messageId": uuid.uuid4().hex }, "payload": { "token": token } } self.alexa.send_event(event) @property def context(self): return { "header": { "namespace": "Alerts", "name": "AlertsState" }, "payload": { "allAlerts": list(self.all_alerts.values()), "activeAlerts": list(self.active_alerts.values()) } }
class AudioPlayer(object): STATES = { 'IDLE', 'PLAYING', 'STOPPED', 'PAUSED', 'BUFFER_UNDERRUN', 'FINISHED' } def __init__(self, alexa): self.alexa = alexa self.token = '' self.state = 'IDLE' self.player = Player() self.player.add_callback('eos', self.PlaybackFinished) self.player.add_callback('error', self.PlaybackFailed) # { # "directive": { # "header": { # "namespace": "AudioPlayer", # "name": "Play", # "messageId": "{{STRING}}", # "dialogRequestId": "{{STRING}}" # }, # "payload": { # "playBehavior": "{{STRING}}", # "audioItem": { # "audioItemId": "{{STRING}}", # "stream": { # "url": "{{STRING}}", # "streamFormat": "AUDIO_MPEG" # "offsetInMilliseconds": {{LONG}}, # "expiryTime": "{{STRING}}", # "progressReport": { # "progressReportDelayInMilliseconds": {{LONG}}, # "progressReportIntervalInMilliseconds": {{LONG}} # }, # "token": "{{STRING}}", # "expectedPreviousToken": "{{STRING}}" # } # } # } # } # } def Play(self, directive): behavior = directive['payload']['playBehavior'] self.token = directive['payload']['audioItem']['stream']['token'] audio_url = directive['payload']['audioItem']['stream']['url'] if audio_url.startswith('cid:'): mp3_file = os.path.join(tempfile.gettempdir(), audio_url[4:] + '.mp3') if os.path.isfile(mp3_file): # os.system('mpv "{}"'.format(mp3_file)) # os.system('rm -rf "{}"'.format(mp3_file)) self.player.play('file://{}'.format(mp3_file)) self.PlaybackStarted() else: # os.system('mpv {}'.format(audio_url)) self.player.play(audio_url) self.PlaybackStarted() def PlaybackStarted(self): self.state = 'PLAYING' event = { "header": { "namespace": "AudioPlayer", "name": "PlaybackStarted", "messageId": uuid.uuid4().hex }, "payload": { "token": self.token, "offsetInMilliseconds": self.player.position } } self.alexa.send_event(event) def PlaybackNearlyFinished(self): event = { "header": { "namespace": "AudioPlayer", "name": "PlaybackNearlyFinished", "messageId": uuid.uuid4().hex }, "payload": { "token": self.token, "offsetInMilliseconds": self.player.position } } self.alexa.send_event(event) def ProgressReportDelayElapsed(self): pass def ProgressReportIntervalElapsed(self): pass def PlaybackStutterStarted(self): pass def PlaybackStutterFinished(self): pass def PlaybackFinished(self): self.state = 'FINISHED' event = { "header": { "namespace": "AudioPlayer", "name": "PlaybackFinished", "messageId": uuid.uuid4().hex }, "payload": { "token": self.token, "offsetInMilliseconds": self.player.position } } self.alexa.send_event(event) def PlaybackFailed(self): self.state = 'STOPPED' # { # "directive": { # "header": { # "namespace": "AudioPlayer", # "name": "Stop", # "messageId": "{{STRING}}", # "dialogRequestId": "{{STRING}}" # }, # "payload": { # } # } # } def Stop(self, directive): self.player.stop() self.PlaybackStopped() def PlaybackStopped(self): self.state = 'STOPPED' event = { "header": { "namespace": "AudioPlayer", "name": "PlaybackStopped", "messageId": uuid.uuid4().hex }, "payload": { "token": self.token, "offsetInMilliseconds": self.player.position } } self.alexa.send_event(event) def pause(self): self.player.pause() self.PlaybackPaused() def PlaybackPaused(self): self.state = 'PAUSED' event = { "header": { "namespace": "AudioPlayer", "name": "PlaybackPaused", "messageId": uuid.uuid4().hex }, "payload": { "token": self.token, "offsetInMilliseconds": self.player.position } } self.alexa.send_event(event) def resume(self): self.player.resume() self.PlaybackResumed() def PlaybackResumed(self): self.state = 'PLAYING' event = { "header": { "namespace": "AudioPlayer", "name": "PlaybackResumed", "messageId": uuid.uuid4().hex }, "payload": { "token": self.token, "offsetInMilliseconds": self.player.position } } self.alexa.send_event(event) # { # "directive": { # "header": { # "namespace": "AudioPlayer", # "name": "ClearQueue", # "messageId": "{{STRING}}", # "dialogRequestId": "{{STRING}}" # }, # "payload": { # "clearBehavior": "{{STRING}}" # } # } # } def ClearQueue(self, directive): self.PlaybackQueueCleared() behavior = directive['payload']['clearBehavior'] if behavior == 'CLEAR_ALL': self.player.stop() elif behavior == 'CLEAR_ENQUEUED': pass def PlaybackQueueCleared(self): event = { "header": { "namespace": "AudioPlayer", "name": "PlaybackQueueCleared", "messageId": uuid.uuid4().hex }, "payload": {} } self.alexa.send_event(event) def StreamMetadataExtracted(self): pass @property def context(self): if self.state != 'PLAYING': offset = 0 else: offset = self.player.position return { "header": { "namespace": "AudioPlayer", "name": "PlaybackState" }, "payload": { "token": self.token, "offsetInMilliseconds": offset, "playerActivity": self.state } }