- Commented out forced pygame backend (causes issues with display initialization) - Added SDL_VIDEODRIVER and SDL_AUDIODRIVER fallback chains (wayland,x11,dummy) - Limited KIVY_INPUTPROVIDERS to wayland,x11 (avoids problematic input providers) - Reduced FFMPEG_THREADS from 4 to 2 (conserves Raspberry Pi resources) - Reduced LIBPLAYER_BUFFER from 2MB to 1MB (saves memory) - Fixed asyncio event loop deprecation warning (use try/except for get_running_loop) - Better exception handling for cursor hiding These changes fix the app crashing after 30 seconds due to graphics provider issues.
221 lines
7.1 KiB
Bash
Executable File
221 lines
7.1 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# Kivy Signage Player Startup Script with Watchdog
|
|
# This script monitors and auto-restarts the player if it crashes
|
|
|
|
# Get the directory where this script is located
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
|
# Configuration
|
|
MAX_RETRIES=999999 # Effectively unlimited retries
|
|
RESTART_DELAY=5 # Seconds to wait before restart
|
|
HEALTH_CHECK_INTERVAL=30 # Seconds between health checks
|
|
HEARTBEAT_FILE="$SCRIPT_DIR/.player_heartbeat"
|
|
STOP_FLAG_FILE="$SCRIPT_DIR/.player_stop_requested"
|
|
LOG_FILE="$SCRIPT_DIR/player_watchdog.log"
|
|
|
|
# Ensure log file is writable
|
|
if [ ! -w "$(dirname "$LOG_FILE")" ]; then
|
|
LOG_FILE="/tmp/kivy-player-watchdog.log"
|
|
fi
|
|
|
|
# Function to log messages (MUST be defined before use)
|
|
log_message() {
|
|
local msg="[$(date '+%Y-%m-%d %H:%M:%S')] $1"
|
|
echo "$msg"
|
|
# Try to write to log file, ignore errors if permission denied
|
|
echo "$msg" >> "$LOG_FILE" 2>/dev/null || true
|
|
}
|
|
|
|
# Load user's environment from systemd user session (most reliable method)
|
|
# This ensures we get the proper DISPLAY/WAYLAND_DISPLAY and session variables
|
|
load_user_environment() {
|
|
# Try to get environment from active user session via systemctl
|
|
local user_env
|
|
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
|
|
log_message "Loaded user environment from systemctl --user"
|
|
return 0
|
|
fi
|
|
fi
|
|
|
|
# Fallback: detect display manually
|
|
log_message "Falling back to manual display detection..."
|
|
|
|
if [ -z "$DISPLAY" ] && [ -z "$WAYLAND_DISPLAY" ]; then
|
|
# Try to detect Wayland display
|
|
if [ -S "/run/user/$(id -u)/wayland-0" ]; then
|
|
export WAYLAND_DISPLAY=wayland-0
|
|
log_message "Detected Wayland display: $WAYLAND_DISPLAY"
|
|
# Try to detect X11 display
|
|
elif [ -S "/tmp/.X11-unix/X0" ]; then
|
|
export DISPLAY=:0
|
|
log_message "Detected X11 display: $DISPLAY"
|
|
else
|
|
# Wait for display to come up (useful for systemd or delayed starts)
|
|
log_message "Waiting for display server to be ready (up to 30 seconds)..."
|
|
for i in {1..30}; do
|
|
sleep 1
|
|
if [ -S "/run/user/$(id -u)/wayland-0" ] 2>/dev/null; then
|
|
export WAYLAND_DISPLAY=wayland-0
|
|
log_message "Display server detected on attempt $i: Wayland"
|
|
return 0
|
|
elif [ -S "/tmp/.X11-unix/X0" ] 2>/dev/null; then
|
|
export DISPLAY=:0
|
|
log_message "Display server detected on attempt $i: X11"
|
|
return 0
|
|
fi
|
|
done
|
|
fi
|
|
fi
|
|
|
|
# Verify we have a display now
|
|
if [ -z "$DISPLAY" ] && [ -z "$WAYLAND_DISPLAY" ]; then
|
|
log_message "WARNING: No display server detected. This may cause graphics issues."
|
|
return 1
|
|
fi
|
|
|
|
log_message "Display environment ready: DISPLAY=$DISPLAY WAYLAND_DISPLAY=$WAYLAND_DISPLAY"
|
|
return 0
|
|
}
|
|
|
|
# Load the user environment early
|
|
load_user_environment
|
|
|
|
# Function to check if player is healthy
|
|
check_health() {
|
|
# Check if heartbeat file exists and is recent (within last 60 seconds)
|
|
if [ -f "$HEARTBEAT_FILE" ]; then
|
|
local last_update=$(stat -c %Y "$HEARTBEAT_FILE" 2>/dev/null || echo 0)
|
|
local current_time=$(date +%s)
|
|
local diff=$((current_time - last_update))
|
|
|
|
if [ $diff -lt 60 ]; then
|
|
return 0 # Healthy
|
|
else
|
|
log_message "⚠️ Player heartbeat stale (${diff}s old)"
|
|
return 1 # Unhealthy
|
|
fi
|
|
else
|
|
# If heartbeat file doesn't exist yet, assume player is starting
|
|
return 0
|
|
fi
|
|
}
|
|
|
|
# Cleanup function
|
|
cleanup() {
|
|
log_message "🛑 Watchdog received stop signal"
|
|
rm -f "$HEARTBEAT_FILE"
|
|
rm -f "$STOP_FLAG_FILE"
|
|
exit 0
|
|
}
|
|
|
|
# Trap signals for graceful shutdown
|
|
trap cleanup SIGINT SIGTERM
|
|
|
|
log_message "=========================================="
|
|
log_message "🚀 Kivy Signage Player Watchdog Started"
|
|
log_message "=========================================="
|
|
log_message "Project directory: $SCRIPT_DIR"
|
|
log_message "Max retries: Unlimited"
|
|
log_message "Restart delay: ${RESTART_DELAY}s"
|
|
log_message ""
|
|
|
|
# Remove old stop flag if exists (fresh start)
|
|
rm -f "$STOP_FLAG_FILE"
|
|
|
|
# Change to the project directory
|
|
cd "$SCRIPT_DIR"
|
|
|
|
# Check if virtual environment exists
|
|
if [ -d ".venv" ]; then
|
|
log_message "✓ Virtual environment found"
|
|
source .venv/bin/activate
|
|
else
|
|
log_message "⚠️ Creating virtual environment..."
|
|
python3 -m venv .venv
|
|
source .venv/bin/activate
|
|
log_message "📦 Installing dependencies..."
|
|
pip3 install -r requirements.txt
|
|
log_message "✓ Virtual environment ready"
|
|
fi
|
|
|
|
# Check if configuration exists
|
|
if [ ! -f "config/app_config.json" ]; then
|
|
log_message "⚠️ WARNING: Configuration file not found!"
|
|
log_message "Player may not function correctly without configuration"
|
|
fi
|
|
|
|
# Main watchdog loop
|
|
retry_count=0
|
|
while true; do
|
|
retry_count=$((retry_count + 1))
|
|
|
|
log_message ""
|
|
log_message "=========================================="
|
|
log_message "▶️ Starting player (attempt #${retry_count})"
|
|
log_message "=========================================="
|
|
|
|
# Clean old heartbeat
|
|
rm -f "$HEARTBEAT_FILE"
|
|
|
|
# Start the player
|
|
cd "$SCRIPT_DIR/src"
|
|
python3 main.py &
|
|
PLAYER_PID=$!
|
|
|
|
log_message "Player PID: $PLAYER_PID"
|
|
|
|
# Monitor the player
|
|
while true; do
|
|
sleep $HEALTH_CHECK_INTERVAL
|
|
|
|
# Check if process is still running
|
|
if ! kill -0 $PLAYER_PID 2>/dev/null; then
|
|
log_message "❌ Player process crashed or stopped (PID: $PLAYER_PID)"
|
|
break
|
|
fi
|
|
|
|
# Check health via heartbeat
|
|
if ! check_health; then
|
|
log_message "❌ Player health check failed - may be frozen"
|
|
kill $PLAYER_PID 2>/dev/null
|
|
sleep 2
|
|
kill -9 $PLAYER_PID 2>/dev/null
|
|
break
|
|
fi
|
|
|
|
# Player is healthy, continue monitoring
|
|
done
|
|
|
|
# Player stopped or crashed
|
|
# Check if user requested intentional exit
|
|
if [ -f "$STOP_FLAG_FILE" ]; then
|
|
log_message "✋ Stop flag detected - user requested exit via password"
|
|
log_message "Watchdog will NOT restart the player"
|
|
log_message "To restart, run ./start.sh again"
|
|
rm -f "$HEARTBEAT_FILE"
|
|
break
|
|
fi
|
|
|
|
log_message "⏳ Waiting ${RESTART_DELAY}s before restart..."
|
|
sleep $RESTART_DELAY
|
|
|
|
# Cleanup any zombie processes
|
|
pkill -9 -f "python3 main.py" 2>/dev/null
|
|
|
|
done
|
|
|
|
log_message ""
|
|
log_message "=========================================="
|
|
log_message "Watchdog stopped"
|
|
log_message "=========================================="
|
|
|
|
# Deactivate virtual environment (this line is never reached in watchdog mode)
|
|
deactivate
|