mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-04-10 13:02:59 +00:00
set ytm to be on-demand, disable other displays for debugging
This commit is contained in:
@@ -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]
|
||||
},
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user