feat: Refactor playlist handling in PlayerService and add comprehensive tests
This commit is contained in:
@@ -5,12 +5,12 @@ import threading
|
||||
import time
|
||||
from collections.abc import Callable, Coroutine
|
||||
from enum import Enum
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
import vlc # type: ignore[import-untyped]
|
||||
|
||||
from app.core.logging import get_logger
|
||||
from app.models.playlist import Playlist
|
||||
from app.models.sound import Sound
|
||||
from app.models.sound_played import SoundPlayed
|
||||
from app.repositories.playlist import PlaylistRepository
|
||||
@@ -316,39 +316,11 @@ class PlayerService:
|
||||
session = self.db_session_factory()
|
||||
try:
|
||||
playlist_repo = PlaylistRepository(session)
|
||||
|
||||
# Get main playlist (fallback for now)
|
||||
current_playlist = await playlist_repo.get_main_playlist()
|
||||
|
||||
if current_playlist and current_playlist.id:
|
||||
# Load playlist sounds
|
||||
sounds = await playlist_repo.get_playlist_sounds(current_playlist.id)
|
||||
|
||||
# Update state
|
||||
self.state.playlist_id = current_playlist.id
|
||||
self.state.playlist_name = current_playlist.name
|
||||
self.state.playlist_sounds = sounds
|
||||
self.state.playlist_length = len(sounds)
|
||||
self.state.playlist_duration = sum(
|
||||
sound.duration or 0 for sound in sounds
|
||||
)
|
||||
|
||||
# Reset current sound if playlist changed
|
||||
if self.state.current_sound_id and not any(
|
||||
s.id == self.state.current_sound_id for s in sounds
|
||||
):
|
||||
self.state.current_sound_id = None
|
||||
self.state.current_sound_index = None
|
||||
self.state.current_sound = None
|
||||
if self.state.status != PlayerStatus.STOPPED:
|
||||
await self._stop_playback()
|
||||
|
||||
# Set first track as current if no current track and playlist has sounds
|
||||
if not self.state.current_sound_id and sounds:
|
||||
self.state.current_sound_index = 0
|
||||
self.state.current_sound = sounds[0]
|
||||
self.state.current_sound_id = sounds[0].id
|
||||
|
||||
await self._handle_playlist_reload(current_playlist, sounds)
|
||||
logger.info(
|
||||
"Loaded playlist: %s (%s sounds)",
|
||||
current_playlist.name,
|
||||
@@ -361,6 +333,132 @@ class PlayerService:
|
||||
|
||||
await self._broadcast_state()
|
||||
|
||||
async def _handle_playlist_reload(
|
||||
self,
|
||||
current_playlist: Playlist,
|
||||
sounds: list[Sound],
|
||||
) -> None:
|
||||
"""Handle playlist reload logic with ID comparison."""
|
||||
# Store previous state for comparison
|
||||
previous_playlist_id = self.state.playlist_id
|
||||
previous_current_sound_id = self.state.current_sound_id
|
||||
previous_current_sound_index = self.state.current_sound_index
|
||||
|
||||
# Update basic playlist state
|
||||
self._update_playlist_state(current_playlist, sounds)
|
||||
|
||||
# Handle playlist changes based on ID comparison
|
||||
if (
|
||||
current_playlist.id is not None
|
||||
and previous_playlist_id != current_playlist.id
|
||||
):
|
||||
await self._handle_playlist_id_changed(
|
||||
previous_playlist_id, current_playlist.id, sounds,
|
||||
)
|
||||
elif previous_current_sound_id:
|
||||
await self._handle_same_playlist_track_check(
|
||||
previous_current_sound_id,
|
||||
previous_current_sound_index,
|
||||
sounds,
|
||||
)
|
||||
elif sounds:
|
||||
self._set_first_track_as_current(sounds)
|
||||
|
||||
async def _handle_playlist_id_changed(
|
||||
self,
|
||||
previous_id: int | None,
|
||||
current_id: int,
|
||||
sounds: list[Sound],
|
||||
) -> None:
|
||||
"""Handle when playlist ID changes - stop player and reset to first track."""
|
||||
logger.info(
|
||||
"Playlist changed from %s to %s - stopping player and resetting",
|
||||
previous_id,
|
||||
current_id,
|
||||
)
|
||||
|
||||
if self.state.status != PlayerStatus.STOPPED:
|
||||
await self._stop_playback()
|
||||
|
||||
if sounds:
|
||||
self._set_first_track_as_current(sounds)
|
||||
else:
|
||||
self._clear_current_track()
|
||||
|
||||
async def _handle_same_playlist_track_check(
|
||||
self,
|
||||
previous_sound_id: int,
|
||||
previous_index: int | None,
|
||||
sounds: list[Sound],
|
||||
) -> None:
|
||||
"""Handle track checking when playlist ID is the same."""
|
||||
# Find the current track in the new playlist
|
||||
new_index = self._find_sound_index(previous_sound_id, sounds)
|
||||
|
||||
if new_index is not None:
|
||||
# Track still exists - update index if it changed
|
||||
if new_index != previous_index:
|
||||
logger.info(
|
||||
"Current track %s moved from index %s to %s",
|
||||
previous_sound_id,
|
||||
previous_index,
|
||||
new_index,
|
||||
)
|
||||
# Always set the index and sound reference
|
||||
self.state.current_sound_index = new_index
|
||||
self.state.current_sound = sounds[new_index]
|
||||
else:
|
||||
# Current track no longer exists in playlist
|
||||
await self._handle_track_removed(previous_sound_id, sounds)
|
||||
|
||||
async def _handle_track_removed(
|
||||
self,
|
||||
previous_sound_id: int,
|
||||
sounds: list[Sound],
|
||||
) -> None:
|
||||
"""Handle when current track no longer exists in playlist."""
|
||||
logger.info(
|
||||
"Current track %s no longer exists in playlist - stopping and resetting",
|
||||
previous_sound_id,
|
||||
)
|
||||
|
||||
if self.state.status != PlayerStatus.STOPPED:
|
||||
await self._stop_playback()
|
||||
|
||||
if sounds:
|
||||
self._set_first_track_as_current(sounds)
|
||||
else:
|
||||
self._clear_current_track()
|
||||
|
||||
def _update_playlist_state(
|
||||
self, current_playlist: Playlist, sounds: list[Sound],
|
||||
) -> None:
|
||||
"""Update basic playlist state information."""
|
||||
self.state.playlist_id = current_playlist.id
|
||||
self.state.playlist_name = current_playlist.name
|
||||
self.state.playlist_sounds = sounds
|
||||
self.state.playlist_length = len(sounds)
|
||||
self.state.playlist_duration = sum(sound.duration or 0 for sound in sounds)
|
||||
|
||||
def _find_sound_index(self, sound_id: int, sounds: list[Sound]) -> int | None:
|
||||
"""Find the index of a sound in the sounds list."""
|
||||
for i, sound in enumerate(sounds):
|
||||
if sound.id == sound_id:
|
||||
return i
|
||||
return None
|
||||
|
||||
def _set_first_track_as_current(self, sounds: list[Sound]) -> None:
|
||||
"""Set the first track as the current track."""
|
||||
self.state.current_sound_index = 0
|
||||
self.state.current_sound = sounds[0]
|
||||
self.state.current_sound_id = sounds[0].id
|
||||
|
||||
def _clear_current_track(self) -> None:
|
||||
"""Clear the current track state."""
|
||||
self.state.current_sound_index = None
|
||||
self.state.current_sound = None
|
||||
self.state.current_sound_id = None
|
||||
|
||||
def get_state(self) -> dict[str, Any]:
|
||||
"""Get current player state."""
|
||||
return self.state.to_dict()
|
||||
|
||||
Reference in New Issue
Block a user