set ytm to be on-demand, disable other displays for debugging

This commit is contained in:
ChuckBuilds
2025-05-25 14:53:21 -05:00
parent 7da676f636
commit 8f8587f856
3 changed files with 90 additions and 61 deletions

View File

@@ -63,7 +63,7 @@
}
},
"clock": {
"enabled": true,
"enabled": false,
"format": "%H:%M:%S",
"update_interval": 1
},
@@ -98,7 +98,7 @@
"headlines_per_rotation": 2
},
"calendar": {
"enabled": true,
"enabled": false,
"credentials_file": "credentials.json",
"token_file": "token.pickle",
"update_interval": 3600,
@@ -106,7 +106,7 @@
"calendars": ["birthdays"]
},
"nhl_scoreboard": {
"enabled": true,
"enabled": false,
"test_mode": false,
"update_interval_seconds": 300,
"live_update_interval": 15,
@@ -168,7 +168,7 @@
}
},
"ncaam_scoreboard": {
"enabled": true,
"enabled": false,
"test_mode": false,
"update_interval_seconds": 3600,
"live_update_interval": 15,
@@ -182,7 +182,7 @@
}
},
"ncaa_bb_scoreboard": {
"enabled": true,
"enabled": false,
"test_mode": false,
"update_interval_seconds": 3600,
"live_update_interval": 20,
@@ -202,7 +202,7 @@
"update_interval": 3600
},
"mlb": {
"enabled": true,
"enabled": false,
"test_mode": false,
"update_interval_seconds": 300,
"live_update_interval": 20,
@@ -220,10 +220,10 @@
"text_display": {
"enabled": true,
"text": "Subscribe to ChuckBuilds",
"font_path": "assets/fonts/5x7.bdf",
"font_size": 7,
"scroll": false,
"scroll_speed": 25,
"font_path": "assets/fonts/press-start-2p.ttf",
"font_size": 8,
"scroll": true,
"scroll_speed": 10,
"text_color": [255, 0, 0],
"background_color": [0, 0, 0]
},

View File

@@ -48,6 +48,7 @@ class MusicManager:
self.scroll_position_artist = 0
self.title_scroll_tick = 0
self.artist_scroll_tick = 0
self.is_music_display_active = False # New state variable
self._load_config() # Load config first
self._initialize_clients() # Initialize based on loaded config
@@ -115,11 +116,8 @@ class MusicManager:
if self.preferred_source in ["auto", "ytm"]:
try:
self.ytm = YTMClient(update_callback=self._handle_ytm_direct_update)
if not self.ytm.is_available():
logging.warning(f"YTM Companion server not reachable at {self.ytm.base_url}. YTM features disabled.")
self.ytm = None
else:
logging.info(f"YTM Companion server connected at {self.ytm.base_url}.")
# We no longer check is_available() or connect here. Connection is on-demand.
logging.info(f"YTMClient initialized. Connection will be managed on-demand. Configured URL: {self.ytm.base_url}")
except Exception as e:
logging.error(f"Failed to initialize YTM client: {e}")
self.ytm = None
@@ -127,11 +125,33 @@ class MusicManager:
logging.info("YTM client initialization skipped due to preferred_source setting.")
self.ytm = None
def activate_music_display(self):
logger.info("Music display activated.")
self.is_music_display_active = True
if self.ytm and self.preferred_source in ["auto", "ytm"]:
if not self.ytm.is_connected():
logger.info("Attempting to connect YTM client due to music display activation.")
# Pass a reasonable timeout for on-demand connection
if self.ytm.connect_client(timeout=10):
logger.info("YTM client connected successfully on display activation.")
else:
logger.warning("YTM client failed to connect on display activation.")
else:
logger.debug("YTM client already connected during music display activation.")
def deactivate_music_display(self):
logger.info("Music display deactivated.")
self.is_music_display_active = False
if self.ytm and self.ytm.is_connected():
logger.info("Disconnecting YTM client due to music display deactivation.")
self.ytm.disconnect_client()
def _handle_ytm_direct_update(self, ytm_data):
"""Handles a direct state update from YTMClient."""
logger.debug(f"MusicManager received direct YTM update: {ytm_data.get('track', {}).get('title') if ytm_data else 'No Data'}")
if not self.enabled:
if not self.enabled or not self.is_music_display_active: # Check if display is active
logger.debug("Skipping YTM direct update: Manager disabled or music display not active.")
return
# Only process if YTM is the preferred source, or if auto and Spotify isn't actively playing.
@@ -271,10 +291,11 @@ class MusicManager:
if should_poll_ytm_now:
# Re-check availability just before polling
if self.ytm.is_available():
if self.ytm and self.ytm.is_connected(): # Check if connected instead of is_available()
try:
ytm_track = self.ytm.get_current_track()
if ytm_track and not ytm_track.get('player', {}).get('isPaused'):
# Ensure ytm_track is not None before trying to access its player info
if ytm_track and ytm_track.get('player') and not ytm_track.get('player', {}).get('isPaused'):
# If YTM is preferred, it overrides Spotify even if Spotify was playing
if self.preferred_source == "ytm" or not is_playing:
polled_track_info = ytm_track
@@ -282,11 +303,11 @@ class MusicManager:
is_playing = True
logging.debug(f"Polling YTM: Active track - {ytm_track.get('track', {}).get('title')}")
else:
logging.debug("Polling YTM: No active track or player paused.")
logging.debug("Polling YTM: No active track or player paused (or track data missing player info).")
except Exception as e:
logging.error(f"Error polling YTM: {e}")
else:
logging.debug("Skipping YTM poll: Server not available.")
logging.debug("Skipping YTM poll: Client not initialized or not connected.")
# Consider setting self.ytm = None if it becomes unavailable repeatedly?
# --- Consolidate and Check for Changes ---
@@ -420,12 +441,16 @@ class MusicManager:
else:
logger.info("Music manager: Polling thread stopped.")
self.poll_thread = None # Clear the thread object
# Also ensure YTM client is disconnected when polling stops completely
self.deactivate_music_display() # This will also handle YTM disconnect if needed
# Method moved from DisplayController and renamed
def display(self, force_clear: bool = False):
if force_clear: # Removed self.force_clear as it's passed directly
self.display_manager.clear()
# self.force_clear = False # Not needed here
# If forcing clear, it implies the music display might be stopping or resetting.
self.deactivate_music_display() # Deactivate YTM if display is forced clear.
# Use self.current_track_info which is updated by _poll_music_data
display_info = self.current_track_info
@@ -437,6 +462,9 @@ class MusicManager:
logger.info("Music Screen (MusicManager): Nothing playing or info unavailable.")
self._last_nothing_playing_log_time = time.time()
if self.is_music_display_active: # If we were showing music and now we are not
self.deactivate_music_display()
self.display_manager.clear() # Clear before drawing "Nothing Playing"
text_width = self.display_manager.get_text_width("Nothing Playing", self.display_manager.regular_font)
x_pos = (self.display_manager.matrix.width - text_width) // 2
@@ -451,6 +479,11 @@ class MusicManager:
self.last_album_art_url = None # Also clear the URL
return
# If we've reached here, it means we are about to display actual music info.
# Ensure YTM is active if it's a potential source.
if not self.is_music_display_active:
self.activate_music_display()
# Ensure screen is cleared if not force_clear but needed (e.g. transition from "Nothing Playing")
# This might be handled by DisplayController's force_clear logic, but can be an internal check too.
# For now, assuming DisplayController manages the initial clear for a new mode.

View File

@@ -114,75 +114,71 @@ class YTMClient:
else:
logging.warning(f"YTM auth file not found at {YTM_AUTH_CONFIG_PATH}. Run the authentication script to generate it. YTM features will be disabled.")
def _ensure_connected(self, timeout=5):
def connect_client(self, timeout=10):
if not self.ytm_token:
# No token, so cannot authenticate or connect. Log this clearly.
# load_config already warns if token file or token itself is missing.
# logging.warning("No YTM token loaded. Cannot connect to Socket.IO. Run authentication script.")
logging.warning("No YTM token loaded. Cannot connect to Socket.IO. Run authentication script.")
self.is_connected = False
return False
if not self.is_connected:
logging.info(f"Attempting to connect to YTM Socket.IO server: {self.base_url} on namespace /api/v1/realtime")
auth_payload = {"token": self.ytm_token}
if self.is_connected:
logging.debug("YTM client already connected.")
return True
try:
self._connection_event.clear() # Clear event before attempting connection
self.sio.connect(
self.base_url,
transports=['websocket'],
wait_timeout=timeout,
namespaces=['/api/v1/realtime'],
auth=auth_payload
)
# self._connection_event.clear() # No longer clear here
# Use a slightly longer timeout for the event wait than the connect call itself
# to ensure the connect event has time to be processed.
event_wait_timeout = timeout + 5 # e.g., if connect timeout is 10s, wait 15s for the event
if not self._connection_event.wait(timeout=event_wait_timeout):
logging.warning(f"YTM Socket.IO connection event not received within {event_wait_timeout}s (connect timeout was {timeout}s).")
self.is_connected = False # Ensure is_connected is false on timeout
return False
return self.is_connected # This should be true if connect event fired and no timeout
except socketio.exceptions.ConnectionError as e:
logging.error(f"YTM Socket.IO connection error: {e}")
logging.info(f"Attempting to connect to YTM Socket.IO server: {self.base_url} on namespace /api/v1/realtime")
auth_payload = {"token": self.ytm_token}
try:
self._connection_event.clear()
self.sio.connect(
self.base_url,
transports=['websocket'],
wait_timeout=timeout,
namespaces=['/api/v1/realtime'],
auth=auth_payload
)
event_wait_timeout = timeout + 5
if not self._connection_event.wait(timeout=event_wait_timeout):
logging.warning(f"YTM Socket.IO connection event not received within {event_wait_timeout}s (connect timeout was {timeout}s).")
self.is_connected = False
return False
except Exception as e:
logging.error(f"Unexpected error during YTM Socket.IO connection: {e}")
self.is_connected = False
return False
return True # Already connected
logging.info(f"YTM Socket.IO connection successful: {self.is_connected}")
return self.is_connected
except socketio.exceptions.ConnectionError as e:
logging.error(f"YTM Socket.IO connection error: {e}")
self.is_connected = False
return False
except Exception as e:
logging.error(f"Unexpected error during YTM Socket.IO connection: {e}")
self.is_connected = False
return False
def is_available(self):
if not self.ytm_token: # Quick check: if no token, definitely not available.
if not self.ytm_token:
return False
if not self.is_connected:
return self._ensure_connected(timeout=10)
return True
return self.is_connected
def get_current_track(self):
if not self.is_available(): # is_available will attempt to connect if not connected and token exists
# logging.warning("YTM client not available, cannot get current track.") # is_available() or _ensure_connected() already logs issues.
if not self.is_connected:
return None
with self._data_lock:
if self.last_known_track_data:
return self.last_known_track_data
else:
# This is a normal state if no music is playing or just connected
# logging.debug("No track data received yet from YTM Companion Socket.IO.")
return None
def disconnect_client(self):
if self.is_connected:
self.sio.disconnect()
logging.info("YTM Socket.IO client disconnected.")
self.is_connected = False
else:
logging.debug("YTM Socket.IO client already disconnected or not connected.")
# Example Usage (for testing - needs to be adapted for Socket.IO async nature)
# if __name__ == '__main__':
# client = YTMClient()
# if client.is_available():
# if client.connect_client():
# print("YTM Server is available (Socket.IO).")
# try:
# for _ in range(10): # Poll for a few seconds