Move to GitHub
This commit is contained in:
214
skill/asknavidrome/media_queue.py
Normal file
214
skill/asknavidrome/media_queue.py
Normal file
@@ -0,0 +1,214 @@
|
||||
from collections import deque
|
||||
from copy import deepcopy
|
||||
import logging
|
||||
import random
|
||||
|
||||
from .track import Track
|
||||
|
||||
|
||||
class MediaQueue:
|
||||
""" The MediaQueue class
|
||||
|
||||
This class provides a queue based on a Python deque. This is used to store
|
||||
the tracks in the current play queue
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""
|
||||
:return: None
|
||||
"""
|
||||
|
||||
self.logger = logging.getLogger(__name__)
|
||||
"""Logger"""
|
||||
|
||||
self.queue: deque = deque()
|
||||
"""Deque containing tracks still to be played"""
|
||||
|
||||
self.history: deque = deque()
|
||||
"""Deque to hold tracks that have already been played"""
|
||||
|
||||
self.buffer: deque = deque()
|
||||
"""Deque to contain the list of tracks to be enqueued
|
||||
|
||||
This deque is created from self.queue when actions such as next or
|
||||
previous are performed. This is because Amazon can send the
|
||||
PlaybackNearlyFinished request early. Without self.buffer, this would
|
||||
change self.current_track causing us to lose the real position of the
|
||||
queue.
|
||||
"""
|
||||
|
||||
self.current_track: Track = Track()
|
||||
"""Property to hold the current track object"""
|
||||
|
||||
def add_track(self, track: Track) -> None:
|
||||
"""Add tracks to the queue
|
||||
|
||||
:param Track track: A Track object containing details of the track to be played
|
||||
:return: None
|
||||
"""
|
||||
|
||||
self.logger.debug('In add_track()')
|
||||
|
||||
if not self.queue:
|
||||
# This is the first track in the queue
|
||||
self.queue.append(track)
|
||||
else:
|
||||
# There are already tracks in the queue, ensure previous_id is set
|
||||
|
||||
# Get the last track from the deque
|
||||
prev_track = self.queue.pop()
|
||||
|
||||
# Set the previous_id attribute
|
||||
track.previous_id = prev_track.id
|
||||
|
||||
# Return the previous track to the deque
|
||||
self.queue.append(prev_track)
|
||||
|
||||
# Add the new track to the deque
|
||||
self.queue.append(track)
|
||||
|
||||
self.logger.debug(f'In add_track() - there are {len(self.queue)} tracks in the queue')
|
||||
|
||||
def shuffle(self) -> None:
|
||||
"""Shuffle the queue
|
||||
|
||||
Shuffles the queue and resets the previous track IDs required for the ENQUEUE PlayBehaviour
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
self.logger.debug('In shuffle()')
|
||||
|
||||
# Copy the original queue
|
||||
orig = self.queue
|
||||
new_queue = deque()
|
||||
|
||||
# Randomise the queue
|
||||
random.shuffle(orig)
|
||||
|
||||
track_id = None
|
||||
|
||||
for t in orig:
|
||||
if not new_queue:
|
||||
# This is the first track, get the ID and add it
|
||||
track_id = t.id
|
||||
new_queue.append(t)
|
||||
else:
|
||||
# Set the tracks previous_id
|
||||
t.previous_id = track_id
|
||||
|
||||
# Get the track ID to use as the next previous_id
|
||||
track_id = t.id
|
||||
|
||||
# Add the track to the queue
|
||||
new_queue.append(t)
|
||||
|
||||
# Replace the original queue with the new shuffled one
|
||||
self.queue = new_queue
|
||||
|
||||
def get_next_track(self) -> Track:
|
||||
"""Get the next track
|
||||
|
||||
Get the next track from self.queue and add it to the history deque
|
||||
|
||||
:return: The next track object
|
||||
:rtype: Track
|
||||
"""
|
||||
|
||||
self.logger.debug('In get_next_track()')
|
||||
|
||||
if self.current_track.id == '' or self.current_track.id is None:
|
||||
# This is the first track
|
||||
self.current_track = self.queue.popleft()
|
||||
else:
|
||||
# This is not the first track
|
||||
self.history.append(self.current_track)
|
||||
self.current_track = self.queue.popleft()
|
||||
|
||||
# Set the buffer to match the queue
|
||||
self.sync()
|
||||
|
||||
return self.current_track
|
||||
|
||||
def get_prevous_track(self) -> Track:
|
||||
"""Get the previous track
|
||||
|
||||
Get the last track added to the history deque and
|
||||
add it to the front of the play queue
|
||||
|
||||
:return: The previous track object
|
||||
:rtype: Track
|
||||
"""
|
||||
|
||||
self.logger.debug('In get_prevous_track()')
|
||||
|
||||
# Return the current track to the queue
|
||||
self.queue.appendleft(self.current_track)
|
||||
|
||||
# Set the new current track
|
||||
self.current_track = self.history.pop()
|
||||
|
||||
# Set the buffer to match the queue
|
||||
self.sync()
|
||||
|
||||
return self.current_track
|
||||
|
||||
def enqueue_next_track(self) -> Track:
|
||||
"""Get the next buffered track
|
||||
|
||||
Get the next track from the buffer without updating the current track
|
||||
attribute. This allows Amazon to send the PlaybackNearlyFinished
|
||||
request early to queue the next track while maintaining the playlist
|
||||
|
||||
:return: The next track to be played
|
||||
:rtype: Track
|
||||
"""
|
||||
|
||||
self.logger.debug('In enqueue_next_track()')
|
||||
|
||||
return self.buffer.popleft()
|
||||
|
||||
def clear(self) -> None:
|
||||
"""Clear queue, history and buffer deques
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
self.logger.debug('In clear()')
|
||||
self.queue.clear()
|
||||
self.history.clear()
|
||||
self.buffer.clear()
|
||||
|
||||
def get_queue_count(self) -> int:
|
||||
"""Get the number of tracks in the queue
|
||||
|
||||
:return: The number of tracks in the queue deque
|
||||
:rtype: int
|
||||
"""
|
||||
|
||||
self.logger.debug('In get_queue_count()')
|
||||
return len(self.queue)
|
||||
|
||||
def get_history_count(self) -> int:
|
||||
"""Get the number of tracks in the history deque
|
||||
|
||||
:return: The number of tracks in the history deque
|
||||
:rtype: int
|
||||
"""
|
||||
|
||||
self.logger.debug('In get_history_count()')
|
||||
return len(self.history)
|
||||
|
||||
def sync(self) -> None:
|
||||
"""Syncronise the buffer with the queue
|
||||
|
||||
Overwrite the buffer with the current queue.
|
||||
This is useful when pausing or stopping to ensure
|
||||
the resulting PlaybackNearlyFinished request gets
|
||||
the correct. In practice this will have already
|
||||
queued and there for missing from the current buffer
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
self.buffer = deepcopy(self.queue)
|
||||
Reference in New Issue
Block a user