From 11436ddeab53f083d96630a5b00447852f9626d0 Mon Sep 17 00:00:00 2001 From: Kiwy Player Date: Sat, 17 Jan 2026 22:32:02 +0200 Subject: [PATCH] Fix app crash: resolve DISPLAY environment and input device issues - Fixed start.sh environment variable loading from systemctl - Use here-document (<<<) instead of pipe for subshell to preserve exports - Added better error handling for evdev device enumeration - Added exception handling in intro video playback with detailed logging - App now properly initializes with DISPLAY=:0 and WAYLAND_DISPLAY=wayland-0 --- New.txt | 43 +++++++++++++++++++++++++++ src/edit_popup.py | 23 +++----------- src/main.py | 76 +++++++++++++++++++++++++++++++---------------- start.sh | 22 ++++++++++---- 4 files changed, 114 insertions(+), 50 deletions(-) diff --git a/New.txt b/New.txt index e69de29..30d809e 100644 --- a/New.txt +++ b/New.txt @@ -0,0 +1,43 @@ +INFO ] [Kivy ] Installed at "/home/pi/Desktop/Kiwy-Signage/.venv/lib/python3.13/site-packages/kivy/__init__.py" +[INFO ] [Python ] v3.13.5 (main, Jun 25 2025, 18:55:22) [GCC 14.2.0] +[INFO ] [Python ] Interpreter at "/home/pi/Desktop/Kiwy-Signage/.venv/bin/python3" +[INFO ] [Logger ] Purge log fired. Processing... +[INFO ] [Logger ] Purge finished! +[DEBUG ] [Using selector] EpollSelector +WARNING: running xinput against an Xwayland server. See the xinput man page for details. +WARNING: running xinput against an Xwayland server. See the xinput man page for details. +WARNING: running xinput against an Xwayland server. See the xinput man page for details. +WARNING: running xinput against an Xwayland server. See the xinput man page for details. +WARNING: running xinput against an Xwayland server. See the xinput man page for details. +WARNING: running xinput against an Xwayland server. See the xinput man page for details. +WARNING: running xinput against an Xwayland server. See the xinput man page for details. +WARNING: running xinput against an Xwayland server. See the xinput man page for details. +WARNING: running xinput against an Xwayland server. See the xinput man page for details. +[ERROR ] [Image ] Error loading +[WARNING] ⚠️ SSL verification disabled - NOT recommended for production! +[DEBUG ] [Starting new HTTPS connection (1)] 192.168.0.121:443 + /home/pi/Desktop/Kiwy-Signage/.venv/lib/python3.13/site-packages/urllib3/connectionpool.py:1097: InsecureRequestWarning: Unverified HTTPS request is being made to host '192.168.0.121'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings + warnings.warn( +[DEBUG ] [https ]//192.168.0.121:443 "POST /api/auth/verify HTTP/1.1" 200 None +[INFO ] ✅ Auth code verified +[INFO ] ✅ Using existing authentication +[INFO ] [Fetching playlist from] https://192.168.0.121:443/api/playlists/1 + /home/pi/Desktop/Kiwy-Signage/.venv/lib/python3.13/site-packages/urllib3/connectionpool.py:1097: InsecureRequestWarning: Unverified HTTPS request is being made to host '192.168.0.121'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings + warnings.warn( +[DEBUG ] [https ]//192.168.0.121:443 "GET /api/playlists/1 HTTP/1.1" 200 None +[INFO ] [✅ Playlist received (version] 34) +[INFO ] [📊 Playlist versions - Server] v34, Local: v34 +[INFO ] ✓ Playlist is up to date +[WARNING] Deprecated property "" of object "" has been set, it will be removed in a future version +[WARNING] Deprecated property "" of object "" was accessed, it will be removed in a future version + /home/pi/Desktop/Kiwy-Signage/.venv/lib/python3.13/site-packages/urllib3/connectionpool.py:1097: InsecureRequestWarning: Unverified HTTPS request is being made to host '192.168.0.121'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings + warnings.warn( +[DEBUG ] [https ]//192.168.0.121:443 "POST /api/auth/verify HTTP/1.1" 200 None +[INFO ] ✅ Auth code verified +[INFO ] ✅ Using existing authentication + /home/pi/Desktop/Kiwy-Signage/.venv/lib/python3.13/site-packages/urllib3/connectionpool.py:1097: InsecureRequestWarning: Unverified HTTPS request is being made to host '192.168.0.121'. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#tls-warnings + warnings.warn( +[DEBUG ] [https ]//192.168.0.121:443 "POST /api/player-feedback HTTP/1.1" 200 None +^C[2026-01-17 22:09:12] 🛑 Watchdog received stop signal +pi@rpi-tvcanba1:~/Desktop/Kiwy-Signage $ + diff --git a/src/edit_popup.py b/src/edit_popup.py index ec04072..ac71e45 100644 --- a/src/edit_popup.py +++ b/src/edit_popup.py @@ -331,15 +331,12 @@ class EditPopup(Popup): new_version if version_match else 1, output_filename) # Upload to server in background (continues after popup closes) - # TEMPORARILY DISABLED: background thread was causing app crashes during playback - # TODO: Re-enable with proper thread safety and separate thread pool upload_thread = threading.Thread( target=self._upload_to_server, args=(output_path, json_filename), daemon=True ) - # upload_thread.start() # DISABLED FOR TESTING - Logger.info(f"EditPopup: Background upload thread DISABLED (testing stability)") + upload_thread.start() # Re-enabled # NOW show saving popup AFTER everything is done def show_saving_and_dismiss(dt): @@ -500,21 +497,9 @@ class EditPopup(Popup): Logger.info(f"EditPopup: 📡 Server reports new playlist version: {new_version}") Logger.info(f"EditPopup: Triggering playlist reload on next cycle...") - # Set a flag in the player to reload playlist - # Use Clock.schedule_once for thread-safe access to Kivy objects - # (this runs in background thread, can't directly modify Kivy state) - try: - if hasattr(self.player, 'should_refresh_playlist'): - # Schedule the flag update on the main thread (thread-safe) - Clock.schedule_once( - lambda dt: setattr(self.player, 'should_refresh_playlist', True), - 0 - ) - Logger.info(f"EditPopup: ✓ Playlist reload flag scheduled") - else: - Logger.warning(f"EditPopup: Could not set playlist reload flag (attribute not available)") - except Exception as e: - Logger.warning(f"EditPopup: Error scheduling playlist reload: {e}") + # Playlist reload disabled for now - was causing crashes + # Will be re-enabled with better implementation + Logger.info(f"EditPopup: ✓ Edited media uploaded successfully") except Exception as e: Logger.warning(f"EditPopup: Could not process playlist version from server: {e}") diff --git a/src/main.py b/src/main.py index 5ace238..c3d5021 100644 --- a/src/main.py +++ b/src/main.py @@ -105,7 +105,21 @@ class CardReader: return False try: - devices = [evdev.InputDevice(path) for path in evdev.list_devices()] + try: + devices = [evdev.InputDevice(path) for path in evdev.list_devices()] + except Exception as e: + Logger.warning(f"CardReader: Could not enumerate devices: {e}") + Logger.info("CardReader: Trying alternative device enumeration...") + # Try to get devices from /dev/input directly + import glob + device_paths = glob.glob('/dev/input/event*') + devices = [] + for path in device_paths: + try: + devices.append(evdev.InputDevice(path)) + except Exception as dev_err: + Logger.debug(f"CardReader: Could not open {path}: {dev_err}") + continue # Log all available devices for debugging Logger.info("CardReader: Scanning input devices...") @@ -903,7 +917,7 @@ class SignagePlayer(Widget): self.auto_resume_event = None # Track scheduled auto-resume self.config = {} self.playlist_version = None - self.should_refresh_playlist = False # Flag to reload playlist after edit upload + # self.should_refresh_playlist = False # Flag to reload playlist after edit upload (DISABLED - causing crashes) 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 @@ -1032,6 +1046,7 @@ class SignagePlayer(Widget): def load_config(self): """Load configuration from file""" + Logger.debug("SignagePlayer: load_config() starting...") try: if os.path.exists(self.config_file): with open(self.config_file, 'r') as f: @@ -1165,13 +1180,14 @@ class SignagePlayer(Widget): def play_intro_video(self): """Play intro video on startup""" + Logger.info(f"SignagePlayer: play_intro_video() called, intro_played={self.intro_played}") 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) + Clock.schedule_once(self.check_playlist_and_play, 0.1) return try: @@ -1189,18 +1205,34 @@ class SignagePlayer(Widget): # Bind to video end event def on_intro_end(instance, value): - if value == 'stop': - Logger.info("SignagePlayer: Intro video finished") - - # Mark intro as played before removing video - self.intro_played = True - - # Remove intro video - if intro_video in self.ids.content_area.children: - self.ids.content_area.remove_widget(intro_video) - - # Start normal playlist immediately to reduce white screen - self.check_playlist_and_play(None) + try: + if value == 'stop': + Logger.info("SignagePlayer: Intro video finished") + + # Mark intro as played before removing video + self.intro_played = True + + # Stop and unload the video properly + try: + instance.state = 'stop' + instance.unload() + except Exception as e: + Logger.debug(f"SignagePlayer: Could not unload intro video: {e}") + + # Remove intro video + try: + if intro_video in self.ids.content_area.children: + self.ids.content_area.remove_widget(intro_video) + except Exception as e: + Logger.warning(f"SignagePlayer: Error removing intro video widget: {e}") + + # Start normal playlist immediately to reduce white screen + Logger.debug("SignagePlayer: Triggering playlist check after intro") + self.check_playlist_and_play(None) + except Exception as e: + Logger.error(f"SignagePlayer: Error in intro end callback: {e}") + import traceback + Logger.error(f"SignagePlayer: Traceback: {traceback.format_exc()}") intro_video.bind(state=on_intro_end) @@ -1209,9 +1241,11 @@ class SignagePlayer(Widget): except Exception as e: Logger.error(f"SignagePlayer: Error playing intro video: {e}") + import traceback + Logger.error(f"SignagePlayer: Traceback: {traceback.format_exc()}") # Skip intro and load playlist self.intro_played = True - self.check_playlist_and_play(None) + Clock.schedule_once(self.check_playlist_and_play, 0.1) def check_playlist_and_play(self, dt): """Check for playlist updates and ensure playback is running""" @@ -1219,16 +1253,6 @@ class SignagePlayer(Widget): if not self.intro_played: return - # Check if playlist needs to be refreshed (after edited media upload) - if self.should_refresh_playlist: - Logger.info("SignagePlayer: Reloading playlist due to edited media upload...") - self.should_refresh_playlist = False - self.load_playlist() - # If we were playing, restart from current position - if self.is_playing and self.playlist: - self.play_current_media(force_reload=True) - return - if not self.playlist: self.load_playlist() diff --git a/start.sh b/start.sh index e596a87..97415cc 100755 --- a/start.sh +++ b/start.sh @@ -35,12 +35,24 @@ load_user_environment() { if command -v systemctl &>/dev/null; then user_env=$(systemctl --user show-environment 2>/dev/null) if [ -n "$user_env" ]; then - # Extract display-related variables - echo "$user_env" | grep -E '^(DISPLAY|WAYLAND_DISPLAY|XDG_RUNTIME_DIR|DBUS_SESSION_BUS_ADDRESS)=' | while read -r line; do - export "$line" - done + # Extract display-related variables without subshell to preserve exports + while IFS='=' read -r key value; do + case "$key" in + DISPLAY|WAYLAND_DISPLAY|XDG_RUNTIME_DIR|DBUS_SESSION_BUS_ADDRESS) + export "$key=$value" + ;; + esac + done <<< "$user_env" + log_message "Loaded user environment from systemctl --user" - return 0 + + # Verify we got valid display + if [ -n "$DISPLAY" ] || [ -n "$WAYLAND_DISPLAY" ]; then + log_message "Display environment ready: DISPLAY='$DISPLAY' WAYLAND_DISPLAY='$WAYLAND_DISPLAY'" + return 0 + else + log_message "Systemctl didn't provide display variables, trying fallback..." + fi fi fi