From 68ed5b8534cdb3254fced5bf32db0188c213d905 Mon Sep 17 00:00:00 2001 From: ske087 Date: Sat, 22 Nov 2025 10:04:30 +0200 Subject: [PATCH] updated player with playlist --- src/main.py | 144 +++++++++++++++++++++++++++++++++++++++++- src/signage_player.kv | 25 +++++++- 2 files changed, 165 insertions(+), 4 deletions(-) diff --git a/src/main.py b/src/main.py index 49aecd3..58b3856 100644 --- a/src/main.py +++ b/src/main.py @@ -149,6 +149,88 @@ class SettingsPopup(Popup): # Restart the control hide timer self.player.schedule_hide_controls() + def test_connection(self): + """Test connection to server with current credentials""" + # Update status label to show testing + self.ids.connection_status.text = 'Testing connection...' + self.ids.connection_status.color = (1, 0.7, 0, 1) # Orange + + # Run test in background thread to avoid blocking UI + def run_test(): + try: + from player_auth import PlayerAuth + import re + + # Get current values from inputs + server_ip = self.ids.server_input.text.strip() + screen_name = self.ids.screen_input.text.strip() + quickconnect = self.ids.quickconnect_input.text.strip() + port = self.player.config.get('port', '443') + + if not all([server_ip, screen_name, quickconnect]): + Clock.schedule_once(lambda dt: self.update_connection_status( + 'Error: Fill all fields', False + )) + return + + # Build server URL + if server_ip.startswith('http://') or server_ip.startswith('https://'): + server_url = server_ip + if not ':' in server_ip.replace('https://', '').replace('http://', ''): + if port and port != '443' and port != '80': + server_url = f"{server_ip}:{port}" + else: + protocol = "https" if port == "443" else "http" + server_url = f"{protocol}://{server_ip}:{port}" + + Logger.info(f"SettingsPopup: Testing connection to {server_url}") + + # Create temporary auth instance (don't save) + auth = PlayerAuth('/tmp/temp_auth_test.json') + + # Try to authenticate + success, error = auth.authenticate( + server_url=server_url, + hostname=screen_name, + quickconnect_code=quickconnect + ) + + # Clean up temp file + try: + import os + if os.path.exists('/tmp/temp_auth_test.json'): + os.remove('/tmp/temp_auth_test.json') + except: + pass + + # Update UI on main thread + if success: + player_name = auth.get_player_name() + Clock.schedule_once(lambda dt: self.update_connection_status( + f'✓ Connected: {player_name}', True + )) + else: + Clock.schedule_once(lambda dt: self.update_connection_status( + f'✗ Failed: {error}', False + )) + + except Exception as e: + Logger.error(f"SettingsPopup: Connection test error: {e}") + Clock.schedule_once(lambda dt: self.update_connection_status( + f'✗ Error: {str(e)}', False + )) + + # Run in thread + threading.Thread(target=run_test, daemon=True).start() + + def update_connection_status(self, message, success): + """Update connection status label""" + self.ids.connection_status.text = message + if success: + self.ids.connection_status.color = (0, 1, 0, 1) # Green + else: + self.ids.connection_status.color = (1, 0, 0, 1) # Red + def save_and_close(self): """Save configuration and close popup""" # Update config @@ -195,6 +277,7 @@ class SignagePlayer(Widget): self.playlist_version = None self.consecutive_errors = 0 # Track consecutive playback errors self.max_consecutive_errors = 10 # Maximum errors before stopping + self.intro_played = False # Track if intro has been played # Paths self.base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) self.config_dir = os.path.join(self.base_dir, 'config') @@ -229,15 +312,15 @@ class SignagePlayer(Widget): # Load configuration self.load_config() + # Play intro video first + self.play_intro_video() + # Start async server tasks (non-blocking) asyncio.ensure_future(self.async_playlist_update_loop()) Logger.info("SignagePlayer: Async server tasks started") # Start media playback Clock.schedule_interval(self.check_playlist_and_play, 30) # Check every 30 seconds - - # Initial playlist check - self.check_playlist_and_play(None) def load_config(self): """Load configuration from file""" @@ -364,8 +447,63 @@ class SignagePlayer(Widget): Logger.error(f"SignagePlayer: Error loading playlist: {e}") self.show_error(f"Failed to load playlist: {e}") + def play_intro_video(self): + """Play intro video on startup""" + intro_path = os.path.join(self.resources_path, 'intro1.mp4') + + if not os.path.exists(intro_path): + Logger.warning(f"SignagePlayer: Intro video not found at {intro_path}") + # Skip intro and load playlist + self.intro_played = True + self.check_playlist_and_play(None) + return + + try: + Logger.info("SignagePlayer: Playing intro video...") + self.ids.status_label.opacity = 0 # Hide status label + + # Create video widget for intro + intro_video = Video( + source=intro_path, + state='play', + options={'eos': 'stop'}, + allow_stretch=True, + keep_ratio=True, + size=self.size, + pos=(0, 0) + ) + + # Bind to video end event + def on_intro_end(instance, value): + if value == 'stop': + Logger.info("SignagePlayer: Intro video finished") + # Remove intro video + if intro_video in self.ids.content_area.children: + self.ids.content_area.remove_widget(intro_video) + + # Mark intro as played + self.intro_played = True + + # Start normal playlist + self.check_playlist_and_play(None) + + intro_video.bind(state=on_intro_end) + + # Add intro video to content area + self.ids.content_area.add_widget(intro_video) + + except Exception as e: + Logger.error(f"SignagePlayer: Error playing intro video: {e}") + # Skip intro and load playlist + self.intro_played = True + self.check_playlist_and_play(None) + def check_playlist_and_play(self, dt): """Check for playlist updates and ensure playback is running""" + # Don't start playlist until intro is done + if not self.intro_played: + return + if not self.playlist: self.load_playlist() diff --git a/src/signage_player.kv b/src/signage_player.kv index 81142fa..aecb273 100644 --- a/src/signage_player.kv +++ b/src/signage_player.kv @@ -283,7 +283,30 @@ hint_text: '1920x1080 or auto' Widget: - size_hint_y: 0.1 + size_hint_y: 0.05 + + # Test Connection Button + Button: + id: test_connection_btn + text: 'Test Server Connection' + size_hint_y: None + height: dp(50) + background_color: 0.2, 0.4, 0.8, 1 + on_press: root.test_connection() + + # Connection Status Label + Label: + id: connection_status + text: 'Click button to test connection' + size_hint_y: None + height: dp(40) + text_size: self.size + halign: 'center' + valign: 'middle' + color: 0.7, 0.7, 0.7, 1 + + Widget: + size_hint_y: 0.05 # Status information Label: