Changeset - 1082f0725c32
[Not reviewed]
default
0 1 0
drewp@bigasterisk.com - 8 months ago 2024-05-28 22:34:03
drewp@bigasterisk.com
fix PlayerState semantics
1 file changed with 6 insertions and 4 deletions:
0 comments (0 inline, 0 general)
src/light9/ascoltami/player.py
Show inline comments
 
@@ -20,103 +20,105 @@ log = logging.getLogger()
 
GstFileUri = NewType('GstFileUri', str)
 

	
 

	
 
@dataclass
 
class PlayerState:
 
    song: GstFileUri | None = None
 
    duration: float = 0
 
    playing: bool = False  # time is advancing, and song time is now()-wallStartTime
 
    # wall time of song-time 0 (this is adjusted when you seek)
 
    wallStartTime: float = 0
 
    pausedSongTime: float | None = None  # if we're paused, this has the song time
 
    endOfSong: bool = False  # True if we're in the stopped state due to EOS
 

	
 

	
 
class Player:
 

	
 
    def __init__(self, autoStopOffset=4, onStateChange=lambda ps: None):
 
        """autoStopOffset is the number of seconds before the end of
 
        song before automatically stopping (which is really pausing).
 
        """
 
        self.autoStopOffset = autoStopOffset
 
        self.onStateChange = onStateChange
 
        self._playbin = self.pipeline = Gst.ElementFactory.make('playbin', None)
 

	
 
        self.playerState: PlayerState = PlayerState()
 

	
 
        self._lastWatchTime = 0
 
        self._autoStopTime = 0
 
        self._lastSetSongUri: GstFileUri | None = None
 

	
 
        # task.LoopingCall(self._watchTime).start(.050)
 
        asyncio.create_task(self._watchTime())
 

	
 
        #bus = self.pipeline.get_bus()
 
        # not working- see notes in pollForMessages
 
        #self._watchForMessages(bus)
 

	
 
    async def _watchTime(self):
 
        while True:
 
            now = time.time()
 
            try:
 
                self._pollForMessages()
 
                t = self.currentTime()
 
                # log.debug("watch %s < %s < %s", self._lastWatchTime, self._autoStopTime, t)
 
                if self._lastWatchTime < self._autoStopTime < t:
 
                    log.info("autostop")
 
                    self.pause()
 

	
 
                eos = t >= self.duration() - .1  #todo
 
                playing = self.isPlaying() and not eos
 
                ps = PlayerState(
 
                    song=self._getSongFileUri(),
 
                    duration=round(self.duration(), 2),
 
                    wallStartTime=round(now - t, 2) if self.isPlaying() else None,
 
                    playing=self.isPlaying(),
 
                    pausedSongTime=None if self.isPlaying() else t,
 
                    endOfSong=t >= self.duration() - .1,  #todo
 
                    wallStartTime=round(now - t, 2) if playing else None,
 
                    playing=playing,
 
                    pausedSongTime=None if playing else t,
 
                    endOfSong=eos,
 
                )
 

	
 
                if self.playerState != ps:
 
                    self.onStateChange(ps)
 
                    self.playerState = ps
 

	
 
                self._lastWatchTime = t
 
            except Exception:
 
                traceback.print_exc()
 
            await asyncio.sleep(0.5)
 

	
 
    def _watchForMessages(self, bus):
 
        """this would be nicer than pollForMessages but it's not working for
 
        me. It's like add_signal_watch isn't running."""
 
        bus.add_signal_watch()
 

	
 
        def onEos(*args):
 
            print("onEos", args)
 
            if self._onEOS is not None:
 
                self._onEOS(self._getSongFileUri())
 

	
 
        bus.connect('message::eos', onEos)
 

	
 
        def onStreamStatus(bus, message):
 
            print("streamstatus", bus, message)
 
            (statusType, _elem) = message.parse_stream_status()
 
            if statusType == Gst.StreamStatusType.ENTER:
 
                self.setupAutostop()
 

	
 
        bus.connect('message::stream-status', onStreamStatus)
 

	
 
    def _pollForMessages(self):
 
        """bus.add_signal_watch seems to be having no effect, but this works"""
 
        bus = self.pipeline.get_bus()
 
        mt = Gst.MessageType
 
        msg = bus.poll(
 
            mt.EOS | mt.STREAM_STATUS | mt.ERROR,  # | mt.ANY,
 
            0)
 
        if msg is not None:
 
            log.debug("bus message: %r %r", msg.src, msg.type)
 
            # i'm trying to catch here a case where the pulseaudio
 
            # output has an error, since that's otherwise kind of
 
            # mysterious to diagnose. I don't think this is exactly
 
            # working.
 
            if msg.type == mt.ERROR:
 
                log.error(repr(msg.parse_error()))
 
            if msg.type == mt.EOS:
 
                log.info("EOS msg: todo")
0 comments (0 inline, 0 general)