"""Logic to play music from MusicProviders to supported players."""
from __future__ import annotations
+import asyncio
from typing import TYPE_CHECKING, Dict, Tuple
-from music_assistant.models.enums import EventType
+from music_assistant.models.enums import EventType, PlayerState
from music_assistant.models.errors import AlreadyRegisteredError
from music_assistant.models.event import MassEvent
from music_assistant.models.player import Player
from music_assistant.mass import MusicAssistant
-DB_TABLE = "queue_settings"
-
-
class PlayerController:
"""Controller holding all logic to play music from MusicProviders to supported players."""
async def setup(self) -> None:
"""Async initialize of module."""
- # nothing to setup (yet)
+ self.mass.create_task(self._poll_players())
async def cleanup(self) -> None:
"""Cleanup on exit."""
self.mass.signal_event(
MassEvent(EventType.PLAYER_ADDED, object_id=player.player_id, data=player)
)
+
+ async def _poll_players(self) -> None:
+ """Poll players every X interval."""
+ interval = 30
+ cur_tick = 0
+ while True:
+ for player in self.players:
+ if cur_tick == interval or player.state in (
+ PlayerState.PLAYING,
+ PlayerState.PAUSED,
+ ):
+ player.update_state()
+ if cur_tick == interval:
+ cur_tick = 0
+ else:
+ cur_tick += 1
+ await asyncio.sleep(1)
"""Toggle power on player."""
await self.power(not self.powered)
+ def on_update(self) -> None:
+ """Call when player is about to be updated in the player manager."""
+
def on_child_update(self, player_id: str, changed_keys: set) -> None:
"""Call when one of the child players of a playergroup updates."""
self.update_state(skip_forward=True)
if self.mass is None or self.mass.closed:
# guard
return
+ self.on_update()
# basic throttle: do not send state changed events if player did not change
cur_state = self.to_dict()
- changed_keys = get_changed_keys(self._prev_state, cur_state)
+ changed_keys = get_changed_keys(
+ self._prev_state, cur_state, ignore_keys=["elapsed_time"]
+ )
# always update the playerqueue
self.mass.players.get_player_queue(self.player_id).on_player_update()
return
self._prev_state = cur_state
- if changed_keys != {"elapsed_time"}:
- self.mass.signal_event(
- MassEvent(EventType.PLAYER_UPDATED, object_id=self.player_id, data=self)
- )
+ self.mass.signal_event(
+ MassEvent(EventType.PLAYER_UPDATED, object_id=self.player_id, data=self)
+ )
if skip_forward:
return
import os
import pathlib
import random
-from asyncio import Task, TimerHandle
+from asyncio import TimerHandle
from dataclasses import dataclass
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union
self._last_state = str
self._items: List[QueueItem] = []
self._save_task: TimerHandle = None
- self._update_task: Task = None
self._last_player_update: int = 0
self._last_stream_id: str = ""
self._snapshot: Optional[QueueSnapShot] = None
self.signal_next = False
self.mass.create_task(self.resume())
- # start poll/updater task if playback starts on player
- async def updater() -> None:
- """Update player queue every second while playing."""
- while True:
- await asyncio.sleep(1)
- self.update_state()
-
- if self.player.state == PlayerState.PLAYING and self.active:
- if not self._update_task or self._update_task.done():
- self._update_task = self.mass.create_task(updater)
- elif self._update_task:
- self._update_task.cancel()
- self._update_task = None
-
self.update_state()
def update_state(self) -> None: