"""One place that answers "what is the mouse doing right now?". Assembled on demand rather than accumulated from events. The join between what is playing and which figure is on the reader is the same one the MQTT player entity makes: read ``mouse.active_figure``, do not keep a second copy of it that can drift. """ from __future__ import annotations from datetime import date from musicmouse.app import App from musicmouse.services.web.schemas import ( BusOut, ClockStateOut, ConnectionOut, PlayerStateOut, TimerOut, ) from musicmouse.services.web.settings import to_percent __all__ = ["snapshot"] def snapshot(app: App) -> PlayerStateOut: player = app.player playlist = player.playlist album = app.album_for(playlist) index = player.track_index # The player only knows a file path, and a tagged file's name is rarely its title # ("01 - So ein schoener Tag.mp3"). The library read the real one at scan time. indexed = ( album.tracks[index] if album and 0 <= index < len(album.tracks) else None ) track = player.current_track return PlayerStateOut( playing=player.is_playing, album_id=album.id if album else None, album_title=album.title if album else (playlist.name if playlist else None), artist=album.artist if album else None, kind=album.kind if album else None, track_index=index, track_title=indexed.title if indexed else (track.title if track else None), track_count=len(playlist) if playlist else 0, position=player.position, # Likewise the duration: the player only has one once libVLC has opened the file. duration=(indexed.duration if indexed else 0.0) or player.duration, volume=to_percent(player.volume, app.config.general), active_figure=app.mouse.active_figure, connected=ConnectionOut( firmware=app.mouse.connected, mqtt=app.state.mqtt_connected, lirc=app.state.lirc_connected, ), clock=clock_state(app), ) def clock_state(app: App, today: date | None = None) -> ClockStateOut: state = app.state config = app.config.general.clock # Without a holiday calendar a weekday counts as a school day: a bus countdown on # a holiday is a smaller surprise than none on the first day back. today = today or date.today() school = state.school_day if state.school_day is not None else today.weekday() < 5 timer = state.parent_timer return ClockStateOut( school_day=state.school_day, school_day_reason=state.school_day_reason, bus=BusOut(time=config.bus_time, lead_minutes=config.bus_lead_minutes) if school else None, parent_timer=TimerOut( started_at=timer.started_at, ends_at=timer.ends_at, ringing=timer.ringing ) if timer else None, )