import os from pathlib import Path from .notifier import Notifier from .player_mpv import Player class SoloTool: def __init__(self, songPool: str, player=None): self._songPool = Path(songPool) self._player = Player() if player is None else player self._notifier = Notifier(self._player) self._songs = [] self._song = None self._keyPoints = [] self._keyPoint = None def _updateSong(self, index): previousSong = self._song self._song = index self._player.pause() self._player.setCurrentSong(self._songPool / self._songs[index]) self._notifier.notify(Notifier.CURRENT_SONG_EVENT, index) previousKp = self._keyPoint self._keyPoint = self.keyPoints[0] if len(self.keyPoints) > 0 else 0.0 if previousKp != self._keyPoint: self._notifier.notify(Notifier.CURRENT_KEY_POINT_EVENT, self._keyPoint) if previousSong is None or self._keyPoints[previousSong] != self._keyPoints[index]: self._notifier.notify(Notifier.KEY_POINT_LIST_EVENT, self.keyPoints) @staticmethod def _keyPointValid(kp: float) -> bool: return kp is not None and kp >= 0.0 and kp < 1.0 @property def songs(self) -> list[str]: return self._songs.copy() def addSong(self, fileName: str) -> None: path = self._songPool / fileName if not os.path.isfile(path): raise FileNotFoundError(path) if path in self._songs: return self._songs.append(fileName) self._keyPoints.append([]) self._notifier.notify(Notifier.SONG_LIST_EVENT, self.songs) if self.song is None: self.song = 0 @property def song(self) -> int: return self._song @song.setter def song(self, new: int) -> None: if new is not None \ and new >= 0 \ and new < len(self._songs) \ and new != self._song: self._updateSong(new) @property def keyPoints(self) -> list[float]: if self._song is None: return None return self._keyPoints[self._song].copy() @keyPoints.setter def keyPoints(self, new: list[float]) -> None: if new is not None and self._song is not None: sanitized = sorted(list(set([p for p in new if SoloTool._keyPointValid(p)]))) self._keyPoints[self._song] = sanitized.copy() self._notifier.notify(Notifier.KEY_POINT_LIST_EVENT, self.keyPoints) @property def keyPoint(self) -> float: return self._keyPoint @keyPoint.setter def keyPoint(self, new: float) -> None: if self._song is not None and SoloTool._keyPointValid(new) and new != self._keyPoint: self._keyPoint = new self._notifier.notify(Notifier.CURRENT_KEY_POINT_EVENT, new) def play(self): self._player.play() def pause(self): self._player.pause() @property def playing(self) -> bool: return self._player.isPlaying() def jump(self): self._player.setPlaybackPosition(self._keyPoint) @property def rate(self) -> float: return self._player.getPlaybackRate() @rate.setter def rate(self, new: float) -> None: if new is not None and new >= 0.0 and new != self._player.getPlaybackRate(): self._player.setPlaybackRate(new) self._notifier.notify(Notifier.PLAYBACK_RATE_EVENT, new) @property def volume(self) -> float: return self._player.getPlaybackVolume() @volume.setter def volume(self, new: float) -> None: if new is not None and new >= 0.0 and new != self._player.getPlaybackVolume(): self._player.setPlaybackVolume(new) self._notifier.notify(Notifier.PLAYBACK_VOLUME_EVENT, new) @property def position(self) -> float: return self._player.getPlaybackPosition() @position.setter def position(self, new: float) -> None: if new is not None and new != self._player.getPlaybackPosition(): self._player.setPlaybackPosition(min(max(0.0, new), 1.0)) def registerSongSelectionCallback(self, callback): self._notifier.registerCallback(Notifier.CURRENT_SONG_EVENT, callback) def registerSongListCallback(self, callback): self._notifier.registerCallback(Notifier.SONG_LIST_EVENT, callback) def registerKeyPointSelectionCallback(self, callback): self._notifier.registerCallback(Notifier.CURRENT_KEY_POINT_EVENT, callback) def registerKeyPointListCallback(self, callback): self._notifier.registerCallback(Notifier.KEY_POINT_LIST_EVENT, callback) def registerPlayingStateCallback(self, callback): self._notifier.registerCallback(Notifier.PLAYING_STATE_EVENT, callback) def registerVolumeCallback(self, callback): self._notifier.registerCallback(Notifier.PLAYBACK_VOLUME_EVENT, callback) def registerRateCallback(self, callback): self._notifier.registerCallback(Notifier.PLAYBACK_RATE_EVENT, callback)