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
This commit is contained in:
43
New.txt
43
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 </home/pi/Desktop/Kiwy-Signage/config/resources/intro1.mp4>
|
||||
[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 "<BooleanProperty name=allow_stretch>" of object "<kivy.uix.image.AsyncImage object at 0x7fa5f79ef0>" has been set, it will be removed in a future version
|
||||
[WARNING] Deprecated property "<BooleanProperty name=keep_ratio>" of object "<kivy.uix.image.AsyncImage object at 0x7fa5f79ef0>" 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 $
|
||||
|
||||
|
||||
@@ -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}")
|
||||
|
||||
|
||||
76
src/main.py
76
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()
|
||||
|
||||
|
||||
22
start.sh
22
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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user