- Created .keep-screen-alive.sh wrapper script with multiple methods: * systemd-inhibit (primary - prevents OS-level sleep/suspend) * xset commands (prevents X11 screensaver) * Mouse movement (prevents idle timeout) - Added screen-keepalive.service systemd unit: * Runs xset s reset every 30 seconds * Auto-restarts on failure * Integrated with graphical session - Multiple layers of screen protection: * HDMI blanking disabled * CPU power saving disabled * System sleep/suspend disabled * X11 screensaver disabled * DPMS (Display Power Management) disabled * Display forced on periodically Screen will now remain active while player is running, preventing lockups or blank screens during playback.
448 lines
14 KiB
Bash
448 lines
14 KiB
Bash
#!/bin/bash
|
|
|
|
# Kivy Signage Player Installation Script
|
|
# Supports both online and offline installation
|
|
|
|
set -e
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
REPO_DIR="$SCRIPT_DIR/repo"
|
|
WHEELS_DIR="$REPO_DIR/python-wheels"
|
|
SYSTEM_DIR="$REPO_DIR/system-packages"
|
|
DEB_DIR="$SYSTEM_DIR/debs"
|
|
|
|
# Function to detect Raspberry Pi
|
|
detect_raspberry_pi() {
|
|
if grep -qi "raspberry" /proc/cpuinfo 2>/dev/null || [ -f /boot/config.txt ]; then
|
|
return 0 # Is Raspberry Pi
|
|
else
|
|
return 1 # Not Raspberry Pi
|
|
fi
|
|
}
|
|
|
|
# Function to setup autostart workflow
|
|
setup_autostart() {
|
|
echo ""
|
|
echo "=========================================="
|
|
echo "Setting up Autostart Workflow"
|
|
echo "=========================================="
|
|
|
|
# Determine the actual user (if running with sudo)
|
|
ACTUAL_USER="${SUDO_USER:-$(whoami)}"
|
|
ACTUAL_HOME=$(eval echo ~"$ACTUAL_USER")
|
|
|
|
AUTOSTART_DIR="$ACTUAL_HOME/.config/autostart"
|
|
SYSTEMD_DIR="$ACTUAL_HOME/.config/systemd/user"
|
|
LXDE_AUTOSTART="$ACTUAL_HOME/.config/lxsession/LXDE-pi/autostart"
|
|
|
|
# Method 1: XDG Autostart (works with most desktop environments)
|
|
echo "Creating XDG autostart entry..."
|
|
mkdir -p "$AUTOSTART_DIR"
|
|
|
|
cat > "$AUTOSTART_DIR/kivy-signage-player.desktop" << 'EOF'
|
|
[Desktop Entry]
|
|
Type=Application
|
|
Name=Kivy Signage Player
|
|
Comment=Digital Signage Player
|
|
Exec=bash -c "cd $SCRIPT_DIR && bash start.sh"
|
|
Icon=media-video-display
|
|
Categories=Utility;
|
|
NoDisplay=false
|
|
Terminal=true
|
|
StartupNotify=false
|
|
Hidden=false
|
|
EOF
|
|
|
|
# Replace $SCRIPT_DIR with actual path in the file
|
|
sed -i "s|\$SCRIPT_DIR|$SCRIPT_DIR|g" "$AUTOSTART_DIR/kivy-signage-player.desktop"
|
|
chown "$ACTUAL_USER:$ACTUAL_USER" "$AUTOSTART_DIR/kivy-signage-player.desktop"
|
|
chmod +x "$AUTOSTART_DIR/kivy-signage-player.desktop"
|
|
echo "✓ XDG autostart entry created for user: $ACTUAL_USER"
|
|
|
|
# Method 2: LXDE Autostart (for Raspberry Pi OS with LXDE)
|
|
if [ -f "$LXDE_AUTOSTART" ]; then
|
|
echo "Configuring LXDE autostart..."
|
|
if ! grep -q "kivy-signage-player" "$LXDE_AUTOSTART"; then
|
|
echo "" >> "$LXDE_AUTOSTART"
|
|
echo "# Kivy Signage Player autostart" >> "$LXDE_AUTOSTART"
|
|
echo "@bash -c 'cd $SCRIPT_DIR && bash start.sh'" >> "$LXDE_AUTOSTART"
|
|
chown "$ACTUAL_USER:$ACTUAL_USER" "$LXDE_AUTOSTART"
|
|
echo "✓ Added to LXDE autostart"
|
|
fi
|
|
fi
|
|
|
|
# Method 3: systemd user service (more reliable)
|
|
echo "Creating systemd user service..."
|
|
mkdir -p "$SYSTEMD_DIR"
|
|
|
|
cat > "$SYSTEMD_DIR/kivy-signage-player.service" << EOF
|
|
[Unit]
|
|
Description=Kivy Signage Player
|
|
After=graphical-session-started.target
|
|
PartOf=graphical-session.target
|
|
|
|
[Service]
|
|
Type=simple
|
|
ExecStart=/usr/bin/bash -c 'source $SCRIPT_DIR/.venv/bin/activate && cd $SCRIPT_DIR && bash start.sh'
|
|
Restart=on-failure
|
|
RestartSec=10
|
|
Environment="DISPLAY=:0"
|
|
Environment="XAUTHORITY=%h/.Xauthority"
|
|
StandardOutput=journal
|
|
StandardError=journal
|
|
|
|
[Install]
|
|
WantedBy=graphical-session.target
|
|
EOF
|
|
|
|
chown "$ACTUAL_USER:$ACTUAL_USER" "$SYSTEMD_DIR/kivy-signage-player.service"
|
|
chmod 644 "$SYSTEMD_DIR/kivy-signage-player.service"
|
|
|
|
# Reload and enable the service as the actual user
|
|
su - "$ACTUAL_USER" -c "systemctl --user daemon-reload" 2>/dev/null || true
|
|
su - "$ACTUAL_USER" -c "systemctl --user enable kivy-signage-player.service" 2>/dev/null || true
|
|
echo "✓ systemd user service created and enabled"
|
|
|
|
# Method 4: Cron job for fallback (starts at reboot)
|
|
echo "Setting up cron fallback..."
|
|
# Create a wrapper script that waits for desktop to be ready
|
|
CRON_WRAPPER="$SCRIPT_DIR/.start-player-cron.sh"
|
|
cat > "$CRON_WRAPPER" << 'EOF'
|
|
#!/bin/bash
|
|
# Wait for desktop environment to be ready
|
|
sleep 10
|
|
|
|
# Start the player
|
|
cd "$SCRIPT_DIR" && bash start.sh
|
|
EOF
|
|
sed -i "s|\$SCRIPT_DIR|$SCRIPT_DIR|g" "$CRON_WRAPPER"
|
|
chmod +x "$CRON_WRAPPER"
|
|
|
|
# Add to crontab for the actual user
|
|
if ! su - "$ACTUAL_USER" -c "crontab -l 2>/dev/null" | grep -q "kivy-signage-player"; then
|
|
su - "$ACTUAL_USER" -c "(crontab -l 2>/dev/null || true; echo '@reboot $CRON_WRAPPER') | crontab -" 2>/dev/null || true
|
|
echo "✓ Cron fallback configured"
|
|
fi
|
|
|
|
echo ""
|
|
echo "Autostart configuration methods:"
|
|
echo " 1. ✓ XDG Autostart Entry (~/.config/autostart/)"
|
|
echo " 2. ✓ systemd User Service (most reliable)"
|
|
if [ -f "$LXDE_AUTOSTART" ]; then
|
|
echo " 3. ✓ LXDE Autostart"
|
|
fi
|
|
echo " 4. ✓ Cron Fallback (@reboot)"
|
|
echo ""
|
|
}
|
|
|
|
# Function to disable power-saving mode on Raspberry Pi
|
|
setup_raspberry_pi_power_management() {
|
|
echo ""
|
|
echo "=========================================="
|
|
echo "Raspberry Pi Detected - Configuring Power Management"
|
|
echo "=========================================="
|
|
|
|
# Disable HDMI power-saving
|
|
echo "Disabling HDMI screen power-saving..."
|
|
if [ -f /boot/config.txt ]; then
|
|
# Check if hdmi_blanking is already configured
|
|
if grep -q "hdmi_blanking" /boot/config.txt; then
|
|
sudo sed -i 's/^hdmi_blanking=.*/hdmi_blanking=0/' /boot/config.txt
|
|
else
|
|
echo "hdmi_blanking=0" | sudo tee -a /boot/config.txt > /dev/null
|
|
fi
|
|
echo "✓ HDMI screen blanking disabled"
|
|
fi
|
|
|
|
# Disable screensaver and screen blanking in X11
|
|
echo "Configuring display blanking settings..."
|
|
|
|
# Create/update display configuration in home directory
|
|
XORG_DIR="/etc/X11/xorg.conf.d"
|
|
if [ -d "$XORG_DIR" ]; then
|
|
# Create display power management configuration
|
|
sudo tee "$XORG_DIR/99-no-blanking.conf" > /dev/null << 'EOF'
|
|
Section "ServerFlags"
|
|
Option "BlankTime" "0"
|
|
Option "StandbyTime" "0"
|
|
Option "SuspendTime" "0"
|
|
Option "OffTime" "0"
|
|
EndSection
|
|
|
|
Section "InputClass"
|
|
Identifier "Disable DPMS"
|
|
MatchClass "DPMS"
|
|
Option "DPMS" "false"
|
|
EndSection
|
|
EOF
|
|
echo "✓ X11 display blanking disabled"
|
|
fi
|
|
|
|
# Disable CPU power scaling (keep at max performance)
|
|
echo "Configuring CPU power management..."
|
|
|
|
# Create systemd service to keep CPU at max frequency
|
|
sudo tee /etc/systemd/system/disable-cpu-powersave.service > /dev/null << 'EOF'
|
|
[Unit]
|
|
Description=Disable CPU Power Saving for Signage Player
|
|
After=network.target
|
|
|
|
[Service]
|
|
Type=oneshot
|
|
ExecStart=/usr/bin/bash -c 'for cpu in /sys/devices/system/cpu/cpu[0-9]*; do echo performance > "$cpu/cpufreq/scaling_governor" 2>/dev/null; done'
|
|
RemainAfterExit=yes
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
EOF
|
|
|
|
# Enable and start the service
|
|
sudo systemctl daemon-reload
|
|
sudo systemctl enable disable-cpu-powersave.service
|
|
sudo systemctl start disable-cpu-powersave.service
|
|
echo "✓ CPU power saving disabled (performance mode enabled)"
|
|
|
|
# Disable sleep/suspend
|
|
echo "Disabling system sleep and suspend..."
|
|
sudo systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target 2>/dev/null || true
|
|
echo "✓ System sleep/suspend disabled"
|
|
|
|
# Disable screensaver via xset (if X11 is running)
|
|
if command -v xset &> /dev/null; then
|
|
# Create .xinitrc if it doesn't exist to disable screensaver
|
|
if [ ! -f ~/.xinitrc ]; then
|
|
cat >> ~/.xinitrc << 'EOF'
|
|
# Disable screen blanking and screensaver
|
|
xset s off
|
|
xset -dpms
|
|
xset s noblank
|
|
EOF
|
|
else
|
|
# Update existing .xinitrc if needed
|
|
if ! grep -q "xset s off" ~/.xinitrc; then
|
|
cat >> ~/.xinitrc << 'EOF'
|
|
|
|
# Disable screen blanking and screensaver
|
|
xset s off
|
|
xset -dpms
|
|
xset s noblank
|
|
EOF
|
|
fi
|
|
fi
|
|
echo "✓ X11 screensaver disabled in .xinitrc"
|
|
fi
|
|
|
|
# Create screen keep-alive wrapper script
|
|
echo "Creating screen keep-alive service..."
|
|
KEEPALIVE_SCRIPT="$SCRIPT_DIR/.keep-screen-alive.sh"
|
|
cat > "$KEEPALIVE_SCRIPT" << 'EOF'
|
|
#!/bin/bash
|
|
# Keep-screen-alive wrapper for player
|
|
# Prevents screen from locking/turning off while player is running
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
|
# Function to keep screen awake
|
|
keep_screen_awake() {
|
|
while true; do
|
|
# Move mouse slightly to prevent idle
|
|
if command -v xdotool &> /dev/null; then
|
|
xdotool mousemove_relative 1 1
|
|
xdotool mousemove_relative -1 -1
|
|
fi
|
|
|
|
# Disable DPMS and screensaver periodically
|
|
if command -v xset &> /dev/null; then
|
|
xset s reset
|
|
xset dpms force on
|
|
fi
|
|
|
|
sleep 30
|
|
done
|
|
}
|
|
|
|
# Function to inhibit systemd sleep (if available)
|
|
inhibit_sleep() {
|
|
if command -v systemd-inhibit &> /dev/null; then
|
|
# Run player under systemd inhibit to prevent sleep
|
|
systemd-inhibit --what=sleep --why="Signage player running" \
|
|
bash "$SCRIPT_DIR/start.sh"
|
|
return $?
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
# Try systemd inhibit first (most reliable)
|
|
if inhibit_sleep; then
|
|
exit 0
|
|
fi
|
|
|
|
# Fallback: Start keep-alive in background
|
|
keep_screen_awake &
|
|
KEEPALIVE_PID=$!
|
|
|
|
# Start the player
|
|
cd "$SCRIPT_DIR"
|
|
bash start.sh
|
|
PLAYER_EXIT=$?
|
|
|
|
# Kill keep-alive when player exits
|
|
kill $KEEPALIVE_PID 2>/dev/null || true
|
|
|
|
exit $PLAYER_EXIT
|
|
EOF
|
|
|
|
chmod +x "$KEEPALIVE_SCRIPT"
|
|
echo "✓ Screen keep-alive service created"
|
|
|
|
# Create systemd service to run keep-alive service
|
|
sudo tee /etc/systemd/system/screen-keepalive.service > /dev/null << EOF
|
|
[Unit]
|
|
Description=Keep Screen Awake During Signage Player
|
|
After=display-manager.service
|
|
PartOf=graphical-session.target
|
|
|
|
[Service]
|
|
Type=simple
|
|
ExecStart=/bin/bash -c 'while true; do xset s reset 2>/dev/null; sleep 30; done'
|
|
Restart=always
|
|
RestartSec=5
|
|
|
|
[Install]
|
|
WantedBy=graphical-session.target
|
|
EOF
|
|
|
|
sudo systemctl daemon-reload
|
|
sudo systemctl enable screen-keepalive.service
|
|
echo "✓ Systemd screen keep-alive service enabled"
|
|
|
|
echo ""
|
|
echo "⚠️ Note: Some changes require a system reboot to take effect."
|
|
echo "Please rerun this script after rebooting if power management issues persist."
|
|
echo ""
|
|
}
|
|
|
|
|
|
# Check for offline mode
|
|
OFFLINE_MODE=false
|
|
if [ "$1" == "--offline" ] || [ "$1" == "-o" ]; then
|
|
OFFLINE_MODE=true
|
|
echo "=========================================="
|
|
echo "Offline Installation Mode"
|
|
echo "=========================================="
|
|
elif [ -d "$WHEELS_DIR" ] && [ "$(ls -A $WHEELS_DIR 2>/dev/null)" ]; then
|
|
echo "=========================================="
|
|
echo "Offline packages detected - Using repo folder"
|
|
echo "=========================================="
|
|
OFFLINE_MODE=true
|
|
else
|
|
echo "=========================================="
|
|
echo "Online Installation Mode"
|
|
echo "=========================================="
|
|
fi
|
|
|
|
echo ""
|
|
echo "Installing Kivy Signage Player dependencies..."
|
|
echo ""
|
|
|
|
# Install system dependencies
|
|
echo "Step 1: Installing system dependencies..."
|
|
echo "--------------------"
|
|
|
|
if [ "$OFFLINE_MODE" = true ] && [ -d "$DEB_DIR" ] && [ "$(ls -A $DEB_DIR/*.deb 2>/dev/null)" ]; then
|
|
# Offline: Install from local .deb files
|
|
echo "Installing from offline .deb packages..."
|
|
cd "$DEB_DIR"
|
|
|
|
# Install all .deb files with dependencies
|
|
sudo dpkg -i *.deb 2>/dev/null || true
|
|
|
|
# Fix any broken dependencies
|
|
sudo apt-get install -f -y || true
|
|
|
|
echo "System packages installed from offline repository"
|
|
else
|
|
# Online: Use apt-get
|
|
echo "Updating package lists..."
|
|
sudo apt update
|
|
|
|
echo "Installing system packages..."
|
|
|
|
# Read packages from file if available, otherwise use default list
|
|
if [ -f "$SYSTEM_DIR/apt-packages.txt" ]; then
|
|
PACKAGES=$(grep -v '^#' "$SYSTEM_DIR/apt-packages.txt" | grep -v '^$' | tr '\n' ' ')
|
|
sudo apt install -y $PACKAGES
|
|
else
|
|
# Default package list
|
|
sudo apt install -y python3-pip python3-setuptools python3-dev
|
|
sudo apt install -y libsdl2-dev libsdl2-image-dev libsdl2-mixer-dev libsdl2-ttf-dev
|
|
sudo apt install -y libportmidi-dev libswscale-dev libavformat-dev libavcodec-dev
|
|
sudo apt install -y zlib1g-dev ffmpeg libavcodec-extra
|
|
sudo apt install -y gstreamer1.0-plugins-base gstreamer1.0-plugins-good
|
|
fi
|
|
|
|
echo "System packages installed successfully"
|
|
fi
|
|
|
|
echo ""
|
|
|
|
# Install Python dependencies
|
|
echo "Step 2: Installing Python dependencies..."
|
|
echo "--------------------"
|
|
|
|
if [ "$OFFLINE_MODE" = true ] && [ -d "$WHEELS_DIR" ] && [ "$(ls -A $WHEELS_DIR/*.whl 2>/dev/null)" ]; then
|
|
# Offline: Install from local wheels
|
|
echo "Installing from offline Python wheels..."
|
|
echo "Wheel files found: $(ls -1 $WHEELS_DIR/*.whl 2>/dev/null | wc -l)"
|
|
|
|
if pip3 install --break-system-packages --no-index --find-links="$WHEELS_DIR" -r requirements.txt 2>&1 | tee /tmp/pip_install.log; then
|
|
echo "Python packages installed from offline repository"
|
|
else
|
|
echo "Warning: Offline installation failed (possibly due to Python version mismatch)"
|
|
echo "Falling back to online installation..."
|
|
pip3 install --break-system-packages -r requirements.txt
|
|
echo "Python packages installed from PyPI"
|
|
fi
|
|
else
|
|
# Online: Use pip from PyPI
|
|
echo "Installing from PyPI..."
|
|
pip3 install --break-system-packages -r requirements.txt
|
|
|
|
echo "Python packages installed successfully"
|
|
fi
|
|
|
|
echo ""
|
|
echo "=========================================="
|
|
echo "Installation completed successfully!"
|
|
echo "=========================================="
|
|
echo ""
|
|
|
|
# Setup autostart workflow
|
|
setup_autostart
|
|
|
|
# Check if running on Raspberry Pi and setup power management
|
|
if detect_raspberry_pi; then
|
|
setup_raspberry_pi_power_management
|
|
fi
|
|
|
|
echo "To run the signage player:"
|
|
echo " cd src && python3 main.py"
|
|
echo ""
|
|
echo "Or use the run script:"
|
|
echo " bash run_player.sh"
|
|
echo ""
|
|
echo "Or start the watchdog (with auto-restart on crash):"
|
|
echo " bash start.sh"
|
|
echo ""
|
|
echo "Autostart has been configured. The player will start automatically when the desktop loads."
|
|
echo ""
|
|
|
|
# Check if config exists
|
|
if [ ! -d "$SCRIPT_DIR/config" ] || [ ! "$(ls -A $SCRIPT_DIR/config)" ]; then
|
|
echo "⚠️ Note: No configuration found."
|
|
echo "Please configure the player before running:"
|
|
echo " 1. Copy config/app_config.txt.example to config/app_config.txt"
|
|
echo " 2. Edit the configuration file with your server details"
|
|
echo ""
|
|
fi
|