Move to GitHub

This commit is contained in:
Ross Stewart
2022-08-05 16:37:56 +01:00
commit 58885be096
18 changed files with 2092 additions and 0 deletions

View File

@@ -0,0 +1,195 @@
import logging
from typing import Union
from ask_sdk_core.handler_input import HandlerInput
from ask_sdk_model import Response
from ask_sdk_model.ui import StandardCard
from ask_sdk_model.interfaces.audioplayer import (
PlayDirective, PlayBehavior, AudioItem, Stream, AudioItemMetadata,
StopDirective)
from ask_sdk_model.interfaces import display
from .track import Track
from .subsonic_api import SubsonicConnection
from .media_queue import MediaQueue
logger = logging.getLogger(__name__)
#
# Functions
#
def start_playback(mode: str, text: str, card_data: dict, track_details: Track, handler_input: HandlerInput) -> Response:
"""Function to play audio.
Begin playing audio when:
- Play Audio Intent is invoked.
- Resuming audio when stopped / paused.
- Next / Previous commands issues.
.. note ::
- https://developer.amazon.com/docs/custom-skills/audioplayer-interface-reference.html#play
- REPLACE_ALL: Immediately begin playback of the specified stream,
and replace current and enqueued streams.
:param str mode: play | continue - Play immediately or enqueue a track
:param str text: Text which should be spoken before playback starts
:param dict card_data: Data to display on a card
:param Track track_details: A Track object containing details of the track to use
:param HandlerInput handler_input: The Amazon Alexa HandlerInput object
:return: Amazon Alexa Response class
:rtype: Response
"""
if mode == 'play':
# Starting playback
logger.debug('In start_playback() - play mode')
if card_data:
# Cards are only supported if we are starting a new session
handler_input.response_builder.set_card(
StandardCard(
title=card_data['title'], text=card_data['text'],
# image=Image(
# small_image_url=card_data['small_image_url'],
# large_image_url=card_data['large_image_url'])
)
)
handler_input.response_builder.add_directive(
PlayDirective(
play_behavior=PlayBehavior.REPLACE_ALL,
audio_item=AudioItem(
stream=Stream(
token=track_details.id,
url=track_details.uri,
offset_in_milliseconds=track_details.offset,
expected_previous_token=None),
metadata=add_screen_background(card_data) if card_data else None
)
)
).set_should_end_session(True)
if text:
# Text is not supported if we are continuing an existing play list
handler_input.response_builder.speak(text)
logger.debug(f'Track ID: {track_details.id}')
logger.debug(f'Track Previous ID: {track_details.previous_id}')
logger.info(f'Playing track: {track_details.title} by: {track_details.artist}')
elif mode == 'continue':
# Continuing Playback
logger.debug('In start_playback() - continue mode')
handler_input.response_builder.add_directive(
PlayDirective(
play_behavior=PlayBehavior.ENQUEUE,
audio_item=AudioItem(
stream=Stream(
token=track_details.id,
url=track_details.uri,
# Offset is 0 to allow playing of the next track from the beginning
# if the Previous intent is used
offset_in_milliseconds=0,
expected_previous_token=track_details.previous_id),
metadata=None
)
)
).set_should_end_session(True)
logger.debug(f'Track ID: {track_details.id}')
logger.debug(f'Track Previous ID: {track_details.previous_id}')
logger.info(f'Enqueuing track: {track_details.title} by: {track_details.artist}')
return handler_input.response_builder.response
def stop(handler_input: HandlerInput) -> Response:
"""Stop playback
:param HandlerInput handler_input: The Amazon Alexa HandlerInput object
:return: Amazon Alexa Response class
:rtype: Response
"""
logger.debug('In stop()')
handler_input.response_builder.add_directive(StopDirective())
return handler_input.response_builder.response
def add_screen_background(card_data: dict) -> Union[AudioItemMetadata, None]:
"""Add background to card.
Cards are viewable on devices with screens and in the Alexa
app.
:param dict card_data: Dictionary containing card data
:return: An Amazon AudioItemMetadata object or None if card data is not present
:rtype: AudioItemMetadata | None
"""
logger.debug('In add_screen_background()')
if card_data:
metadata = AudioItemMetadata(
title=card_data['title'],
subtitle=card_data['text'],
art=display.Image(
content_description=card_data['title'],
sources=[
display.ImageInstance(
url='https://github.com/navidrome/navidrome/raw/master/resources/logo-192x192.png'
)
]
),
background_image=display.Image(
content_description=card_data['title'],
sources=[
display.ImageInstance(
url='https://github.com/navidrome/navidrome/raw/master/resources/logo-192x192.png'
)
]
)
)
return metadata
else:
return None
def enqueue_songs(api: SubsonicConnection, queue: MediaQueue, song_id_list: list) -> None:
"""Enqueue songs
Add Track objects to the queue deque
:param SubsonicConnection api: A SubsonicConnection object to allow access to the Navidrome API
:param MediaQueue queue: A MediaQueue object
:param list song_id_list: A list of song IDs to enqueue
:return: None
"""
for song_id in song_id_list:
song_details = api.get_song_details(song_id)
song_uri = api.get_song_uri(song_id)
# Create track object from song details
new_track = Track(song_details.get('song').get('id'),
song_details.get('song').get('title'),
song_details.get('song').get('artist'),
song_details.get('song').get('artistId'),
song_details.get('song').get('album'),
song_details.get('song').get('albumId'),
song_details.get('song').get('track'),
song_details.get('song').get('year'),
song_details.get('song').get('genre'),
song_details.get('song').get('duration'),
song_details.get('song').get('bitRate'),
song_uri,
0,
None)
# Add track object to queue
queue.add_track(new_track)