import os import threading import vlc from config import Config from time import sleep from log import DEBUG class Music: """ Manage the playing of music tracks """ def __init__(self): self.fading = False self.player = None self.track_path = None def fade(self): """ Fade the currently playing track. Return the current track path and position. The actual management of fading runs in its own thread so as not to hold up the UI during the fade. """ DEBUG("fade()") if not self.playing(): return (None, None) path = self.track_path position = self.player.get_position() thread = threading.Thread(target=self._fade) thread.start() return (path, position) def _fade(self): """ Implementation of fading the current track in a separate thread. """ DEBUG("_fade()") fade_time = Config.FADE_TIME / 1000 sleep_time = fade_time / Config.FADE_STEPS step_percent = int((100 / Config.FADE_STEPS) * -1) # Take a copy of current player to allow another track to be # started without interfering here p = self.player for i in range(100, 0, step_percent): p.audio_set_volume(i) sleep(sleep_time) p.pause() p.release() def get_playtime(self): "Return elapsed play time" return self.player.get_time() def play(self, path): """ Start playing the track at path. Raise AttributeError if path not found. """ DEBUG(f"play({path})") if not os.access(path, os.R_OK): raise AttributeError(f"Cannot access {path}") self.track_path = path self.player = vlc.MediaPlayer(path) self.player.audio_set_volume(100) self.player.play() def playing(self): "Return True if currently playing a track, else False" if self.player: return self.player.is_playing() else: return False