Compare commits
5 Commits
f30f97683b
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| f7b889be11 | |||
| 39565c5761 | |||
| fb9f46a94f | |||
| 9d8eb0cf21 | |||
| 64e49886df |
@@ -1 +0,0 @@
|
|||||||
1782752485.750587
|
|
||||||
Executable
+11
@@ -0,0 +1,11 @@
|
|||||||
|
#\!/bin/bash
|
||||||
|
SCRIPT_DIR="/home/pi/kiwy-signage"
|
||||||
|
LOG_FILE="/home/pi/kiwy-signage/logs/player-background.log"
|
||||||
|
mkdir -p "."
|
||||||
|
echo "[2026-07-17 10:54:53] Starting player in background..." >> ""
|
||||||
|
cd "/home/pi/kiwy-signage" || exit 1
|
||||||
|
nohup bash start.sh >> "" 2>&1 &
|
||||||
|
PLAYER_PID=$\!
|
||||||
|
echo "[2026-07-17 10:54:53] Player started with PID: $PLAYER_PID" >> ""
|
||||||
|
echo "Player started in background (PID: $PLAYER_PID)"
|
||||||
|
echo "Logs: $LOG_FILE"
|
||||||
@@ -1,6 +1,3 @@
|
|||||||
#!/bin/bash
|
#\!/bin/bash
|
||||||
# Wait for desktop environment to be ready
|
|
||||||
sleep 15
|
sleep 15
|
||||||
|
cd /home/pi/kiwy-signage && bash start.sh
|
||||||
# Start the player
|
|
||||||
cd "/home/pi/Desktop/Kiwy-Signage" && bash start.sh
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"server_ip": "192.168.0.159",
|
"server_ip": "192.168.0.109",
|
||||||
"port": "8080",
|
"port": "8080",
|
||||||
"screen_name": "rpi-Receptie",
|
"screen_name": "Birou_IT",
|
||||||
"quickconnect_key": "8887779",
|
"quickconnect_key": "8887779",
|
||||||
"orientation": "Landscape",
|
"orientation": "Landscape",
|
||||||
"touch": "True",
|
"touch": "True",
|
||||||
|
|||||||
Regular → Executable
+583
-555
File diff suppressed because it is too large
Load Diff
Executable
+1056
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
After Width: | Height: | Size: 386 KiB |
@@ -1,23 +1,37 @@
|
|||||||
{
|
{
|
||||||
"count": 2,
|
"count": 4,
|
||||||
"player_id": 1,
|
"player_id": 1,
|
||||||
"player_name": "Receptie TV",
|
"player_name": "Test_player1",
|
||||||
"playlist": [
|
"playlist": [
|
||||||
{
|
{
|
||||||
"file_name": "sean-oulashin-KMn4VEeEPR8-unsplash.jpg",
|
"file_name": "sean-oulashin-KMn4VEeEPR8-unsplash.jpg",
|
||||||
"type": "image",
|
"type": "image",
|
||||||
"url": "media/sean-oulashin-KMn4VEeEPR8-unsplash.jpg",
|
"url": "media/sean-oulashin-KMn4VEeEPR8-unsplash.jpg",
|
||||||
"duration": 25,
|
"duration": 14,
|
||||||
|
"edit_on_player": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file_name": "weblink-1e4a4d6d885a",
|
||||||
|
"type": "weblink",
|
||||||
|
"url": "https://moto-adv.com/",
|
||||||
|
"duration": 30,
|
||||||
"edit_on_player": false
|
"edit_on_player": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"file_name": "robert-lukeman-zNN6ubHmruI-unsplash.jpg",
|
"file_name": "sample-30s.mp4",
|
||||||
"type": "image",
|
"type": "video",
|
||||||
"url": "media/robert-lukeman-zNN6ubHmruI-unsplash.jpg",
|
"url": "media/sample-30s.mp4",
|
||||||
"duration": 20,
|
"duration": 31,
|
||||||
"edit_on_player": false
|
"edit_on_player": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file_name": "edited_media/5/eye_e_v2.jpg",
|
||||||
|
"type": "image",
|
||||||
|
"url": "media/edited_media/5/eye_e_v2.jpg",
|
||||||
|
"duration": 50,
|
||||||
|
"edit_on_player": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"playlist_id": 1,
|
"playlist_id": 1,
|
||||||
"playlist_version": 15
|
"playlist_version": 14
|
||||||
}
|
}
|
||||||
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
+3
-4
@@ -1,7 +1,6 @@
|
|||||||
kivy>=2.3.0
|
kivy>=2.3.0
|
||||||
ffpyplayer
|
ffpyplayer
|
||||||
requests==2.32.4
|
requests>=2.32.0,<3.0.0
|
||||||
bcrypt==4.2.1
|
bcrypt>=4.2.0,<5.0.0
|
||||||
aiohttp==3.9.1
|
aiohttp>=3.9.0,<4.0.0
|
||||||
asyncio==3.4.3
|
|
||||||
evdev>=1.6.0
|
evdev>=1.6.0
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# Start Kivy Signage Player
|
# Start Kivy Signage Player
|
||||||
cd "$(dirname "$0")" && source .venv/bin/activate
|
|
||||||
cd "$(dirname "$0")/src"
|
cd "$(dirname "$0")/src"
|
||||||
python3 main.py
|
python3 main.py
|
||||||
Executable
+76
@@ -0,0 +1,76 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
MARKER_FILE="/etc/kiwy-signage/pi_boot_config_patched.marker"
|
||||||
|
|
||||||
|
is_pi() {
|
||||||
|
if [[ -f /proc/device-tree/model ]] && grep -qi "raspberry pi" /proc/device-tree/model; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if grep -qi "raspberry" /proc/cpuinfo 2>/dev/null; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
find_boot_config() {
|
||||||
|
if [[ -f /boot/firmware/config.txt ]]; then
|
||||||
|
echo "/boot/firmware/config.txt"
|
||||||
|
elif [[ -f /boot/config.txt ]]; then
|
||||||
|
echo "/boot/config.txt"
|
||||||
|
else
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
upsert_key() {
|
||||||
|
local key="$1"
|
||||||
|
local value="$2"
|
||||||
|
local file="$3"
|
||||||
|
|
||||||
|
if grep -Eq "^[#[:space:]]*${key}=" "$file"; then
|
||||||
|
sed -i -E "s|^[#[:space:]]*${key}=.*|${key}=${value}|" "$file"
|
||||||
|
else
|
||||||
|
echo "${key}=${value}" >> "$file"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ "${EUID}" -ne 0 ]]; then
|
||||||
|
echo "[pi-boot-patch] Please run as root."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! is_pi; then
|
||||||
|
echo "[pi-boot-patch] Not a Raspberry Pi. Skipping."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -f "$MARKER_FILE" ]]; then
|
||||||
|
echo "[pi-boot-patch] Patch already applied. Skipping."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
BOOT_CONFIG="$(find_boot_config)"
|
||||||
|
if [[ -z "$BOOT_CONFIG" ]]; then
|
||||||
|
echo "[pi-boot-patch] Boot config not found. Skipping."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[pi-boot-patch] Applying patch to ${BOOT_CONFIG}"
|
||||||
|
|
||||||
|
# Graphics and memory settings for smoother Chromium playback on Pi.
|
||||||
|
upsert_key "dtoverlay" "vc4-kms-v3d" "$BOOT_CONFIG"
|
||||||
|
upsert_key "gpu_mem" "256" "$BOOT_CONFIG"
|
||||||
|
upsert_key "disable_overscan" "1" "$BOOT_CONFIG"
|
||||||
|
|
||||||
|
# Keep HDMI and display active for kiosk use.
|
||||||
|
upsert_key "hdmi_blanking" "0" "$BOOT_CONFIG"
|
||||||
|
upsert_key "hdmi_ignore_cec_init" "1" "$BOOT_CONFIG"
|
||||||
|
upsert_key "hdmi_force_hotplug" "1" "$BOOT_CONFIG"
|
||||||
|
|
||||||
|
mkdir -p "$(dirname "$MARKER_FILE")"
|
||||||
|
date -u +"%Y-%m-%dT%H:%M:%SZ" > "$MARKER_FILE"
|
||||||
|
|
||||||
|
echo "[pi-boot-patch] Patch complete. Marker written to ${MARKER_FILE}"
|
||||||
+56
-27
@@ -1307,29 +1307,20 @@ class SignagePlayer(Widget):
|
|||||||
|
|
||||||
Logger.info(f"SignagePlayer: Playing item {self.current_index + 1}/{len(self.playlist)}: {file_name} ({duration}s)")
|
Logger.info(f"SignagePlayer: Playing item {self.current_index + 1}/{len(self.playlist)}: {file_name} ({duration}s)")
|
||||||
|
|
||||||
# ── Weblink → media transition (Wayland-safe) ──────────────────
|
# ── Weblink → media transition (desktop-flash safe) ──────────────
|
||||||
# On Wayland (Labwc) Window.raise_window() is a no-op, so we must
|
# For web→media transitions, render the next Kivy widget first,
|
||||||
# ensure Chromium's fullscreen window is fully gone BEFORE we try
|
# then close Chromium on the next frame. This prevents a brief
|
||||||
# to render Kivy content. We:
|
# desktop exposure while the compositor removes Chromium.
|
||||||
# 1. Stop watchdog + preload immediately
|
|
||||||
# 2. Terminate Chromium (non-blocking)
|
|
||||||
# 3. Hide the Kivy content_area so nothing stale is visible
|
|
||||||
# 4. Wait 200 ms for the compositor to remove Chromium's window
|
|
||||||
# 5. Show content_area + re-call play_current_media to render
|
|
||||||
if not _after_weblink:
|
if not _after_weblink:
|
||||||
proc = getattr(self, '_weblink_proc', None)
|
proc = getattr(self, '_weblink_proc', None)
|
||||||
if proc is not None:
|
if proc is not None:
|
||||||
|
next_is_weblink = media_item.get('type') == 'weblink'
|
||||||
|
if next_is_weblink:
|
||||||
|
# Weblink -> weblink: close current Chromium first.
|
||||||
self._stop_inactivity_watchdog()
|
self._stop_inactivity_watchdog()
|
||||||
self._kill_weblink_preload()
|
self._kill_weblink_preload()
|
||||||
self._weblink_proc = None
|
self._weblink_proc = None
|
||||||
|
|
||||||
# Hide content while Chromium is still closing
|
|
||||||
try:
|
|
||||||
self.ids.content_area.opacity = 0
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Terminate Chromium
|
|
||||||
if proc.poll() is None:
|
if proc.poll() is None:
|
||||||
try:
|
try:
|
||||||
proc.terminate()
|
proc.terminate()
|
||||||
@@ -1337,13 +1328,28 @@ class SignagePlayer(Widget):
|
|||||||
Logger.warning(f"SignagePlayer: weblink terminate: {exc}")
|
Logger.warning(f"SignagePlayer: weblink terminate: {exc}")
|
||||||
|
|
||||||
def _resume(dt):
|
def _resume(dt):
|
||||||
# Force-kill if still alive after the delay
|
|
||||||
if proc.poll() is None:
|
if proc.poll() is None:
|
||||||
try:
|
try:
|
||||||
proc.kill()
|
proc.kill()
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
# Restore content area and show Kivy window
|
try:
|
||||||
|
Window.show()
|
||||||
|
Window.raise_window()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
self.play_current_media(
|
||||||
|
force_reload=force_reload, _after_weblink=True
|
||||||
|
)
|
||||||
|
|
||||||
|
Clock.schedule_once(_resume, 0.2)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Weblink -> non-weblink: keep Chromium visible while we
|
||||||
|
# prepare next Kivy frame, then close Chromium deferred.
|
||||||
|
self._stop_inactivity_watchdog()
|
||||||
|
self._kill_weblink_preload()
|
||||||
|
self._weblink_proc = proc
|
||||||
try:
|
try:
|
||||||
self.ids.content_area.opacity = 1
|
self.ids.content_area.opacity = 1
|
||||||
except Exception:
|
except Exception:
|
||||||
@@ -1353,14 +1359,7 @@ class SignagePlayer(Widget):
|
|||||||
Window.raise_window()
|
Window.raise_window()
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
# Now render the actual media
|
Logger.debug("SignagePlayer: Deferred Chromium close after next frame render")
|
||||||
self.play_current_media(
|
|
||||||
force_reload=force_reload, _after_weblink=True
|
|
||||||
)
|
|
||||||
|
|
||||||
# 200 ms gives Labwc time to remove the fullscreen surface
|
|
||||||
Clock.schedule_once(_resume, 0.2)
|
|
||||||
return
|
|
||||||
# ────────────────────────────────────────────────────────────────
|
# ────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
# Handle web links before any file/path handling (no local file exists)
|
# Handle web links before any file/path handling (no local file exists)
|
||||||
@@ -1441,6 +1440,11 @@ class SignagePlayer(Widget):
|
|||||||
self.consecutive_errors = 0
|
self.consecutive_errors = 0
|
||||||
Logger.debug(f"SignagePlayer: Media started successfully")
|
Logger.debug(f"SignagePlayer: Media started successfully")
|
||||||
|
|
||||||
|
# If we arrived here from a weblink item, close Chromium after the
|
||||||
|
# next Kivy frame so the new widget is already visible underneath.
|
||||||
|
if media_item.get('type') != 'weblink' and getattr(self, '_weblink_proc', None) is not None:
|
||||||
|
self._kill_weblink_after_frame()
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
Logger.error(f"SignagePlayer: Error playing media: {e}")
|
Logger.error(f"SignagePlayer: Error playing media: {e}")
|
||||||
self.consecutive_errors += 1
|
self.consecutive_errors += 1
|
||||||
@@ -1602,9 +1606,12 @@ class SignagePlayer(Widget):
|
|||||||
self._skip_to_next_media()
|
self._skip_to_next_media()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
target_width, target_height = self._get_browser_target_size()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
Logger.info(f"SignagePlayer: Opening weblink in kiosk browser: {url}")
|
Logger.info(f"SignagePlayer: Opening weblink in kiosk browser: {url}")
|
||||||
Logger.info(f"SignagePlayer: Inactivity timeout set to {duration}s (touch resets the countdown)")
|
Logger.info(f"SignagePlayer: Inactivity timeout set to {duration}s (touch resets the countdown)")
|
||||||
|
Logger.info(f"SignagePlayer: Chromium target launch size: {target_width}x{target_height}")
|
||||||
# Kill the hidden pre-warm instance first; its work (binary in RAM,
|
# Kill the hidden pre-warm instance first; its work (binary in RAM,
|
||||||
# page in disk cache) makes the kiosk relaunch below near-instant.
|
# page in disk cache) makes the kiosk relaunch below near-instant.
|
||||||
self._kill_weblink_preload()
|
self._kill_weblink_preload()
|
||||||
@@ -1616,6 +1623,7 @@ class SignagePlayer(Widget):
|
|||||||
# --start-fullscreen is a normal maximised window that the
|
# --start-fullscreen is a normal maximised window that the
|
||||||
# compositor can un-stack without issues.
|
# compositor can un-stack without issues.
|
||||||
'--start-fullscreen',
|
'--start-fullscreen',
|
||||||
|
'--start-maximized',
|
||||||
'--app=' + url,
|
'--app=' + url,
|
||||||
'--noerrdialogs',
|
'--noerrdialogs',
|
||||||
'--disable-infobars',
|
'--disable-infobars',
|
||||||
@@ -1631,6 +1639,8 @@ class SignagePlayer(Widget):
|
|||||||
'--disable-background-networking',
|
'--disable-background-networking',
|
||||||
'--no-default-browser-check',
|
'--no-default-browser-check',
|
||||||
'--window-position=0,0',
|
'--window-position=0,0',
|
||||||
|
f'--window-size={target_width},{target_height}',
|
||||||
|
'--force-device-scale-factor=1',
|
||||||
])
|
])
|
||||||
|
|
||||||
# Unschedule any previous fixed timer — the watchdog thread takes
|
# Unschedule any previous fixed timer — the watchdog thread takes
|
||||||
@@ -1653,6 +1663,23 @@ class SignagePlayer(Widget):
|
|||||||
self._skip_to_next_media()
|
self._skip_to_next_media()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def _get_browser_target_size(self):
|
||||||
|
"""Return a stable browser launch size to avoid 800x600 first-paint flash."""
|
||||||
|
default_width, default_height = 1920, 1080
|
||||||
|
try:
|
||||||
|
width, height = Window.size
|
||||||
|
width = int(width)
|
||||||
|
height = int(height)
|
||||||
|
except Exception:
|
||||||
|
return default_width, default_height
|
||||||
|
|
||||||
|
# Chromium sometimes first-paints at 800x600 before fullscreen;
|
||||||
|
# force at least Full HD when reported dimensions are too small.
|
||||||
|
if width < 1280 or height < 720:
|
||||||
|
return default_width, default_height
|
||||||
|
|
||||||
|
return width, height
|
||||||
|
|
||||||
def _start_inactivity_watchdog(self, duration):
|
def _start_inactivity_watchdog(self, duration):
|
||||||
"""Start a background thread that monitors /dev/input/* for touch/key
|
"""Start a background thread that monitors /dev/input/* for touch/key
|
||||||
activity. The thread resets the idle counter on every event; when the
|
activity. The thread resets the idle counter on every event; when the
|
||||||
@@ -1893,6 +1920,8 @@ class SignagePlayer(Widget):
|
|||||||
if not browser:
|
if not browser:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
target_width, target_height = self._get_browser_target_size()
|
||||||
|
|
||||||
# Kill any stale preload first.
|
# Kill any stale preload first.
|
||||||
self._kill_weblink_preload()
|
self._kill_weblink_preload()
|
||||||
|
|
||||||
@@ -1903,7 +1932,7 @@ class SignagePlayer(Widget):
|
|||||||
'--app=' + url,
|
'--app=' + url,
|
||||||
# Place window completely outside the visible display area.
|
# Place window completely outside the visible display area.
|
||||||
'--window-position=-9999,-9999',
|
'--window-position=-9999,-9999',
|
||||||
'--window-size=1920,1080', # pre-render at full resolution
|
f'--window-size={target_width},{target_height}', # pre-render at target resolution
|
||||||
'--noerrdialogs',
|
'--noerrdialogs',
|
||||||
'--disable-infobars',
|
'--disable-infobars',
|
||||||
'--incognito',
|
'--incognito',
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"hostname": "rpi-Receptie",
|
"hostname": "Birou_IT",
|
||||||
"auth_code": "BrJrGzX_IT9oP_Lfke8Qgo4sDjMc49T1EhSnWmKKpDM",
|
"auth_code": "CZncd_2dlTZGieBEdqAbUTjf3qNyEUPDXr8jLVx7NLs",
|
||||||
"player_id": 1,
|
"player_id": 1,
|
||||||
"player_name": "Receptie TV",
|
"player_name": "Test_player1",
|
||||||
"playlist_id": 1,
|
"playlist_id": 1,
|
||||||
"orientation": "Landscape",
|
"orientation": "Landscape",
|
||||||
"authenticated": true,
|
"authenticated": true,
|
||||||
"server_url": "http://192.168.0.159:8080"
|
"server_url": "http://192.168.0.109:8080"
|
||||||
}
|
}
|
||||||
@@ -218,19 +218,6 @@ rm -f "$STOP_FLAG_FILE"
|
|||||||
# Change to the project directory
|
# Change to the project directory
|
||||||
cd "$SCRIPT_DIR"
|
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
|
# Check if configuration exists
|
||||||
if [ ! -f "config/app_config.json" ]; then
|
if [ ! -f "config/app_config.json" ]; then
|
||||||
log_message "⚠️ WARNING: Configuration file not found!"
|
log_message "⚠️ WARNING: Configuration file not found!"
|
||||||
@@ -250,6 +237,11 @@ while true; do
|
|||||||
# Clean old heartbeat
|
# Clean old heartbeat
|
||||||
rm -f "$HEARTBEAT_FILE"
|
rm -f "$HEARTBEAT_FILE"
|
||||||
|
|
||||||
|
# Activate virtual environment if it exists
|
||||||
|
if [ -f "$SCRIPT_DIR/.venv/bin/activate" ]; then
|
||||||
|
source "$SCRIPT_DIR/.venv/bin/activate"
|
||||||
|
fi
|
||||||
|
|
||||||
# Start the player
|
# Start the player
|
||||||
cd "$SCRIPT_DIR/src"
|
cd "$SCRIPT_DIR/src"
|
||||||
python3 main.py &
|
python3 main.py &
|
||||||
@@ -292,8 +284,8 @@ while true; do
|
|||||||
log_message "⏳ Waiting ${RESTART_DELAY}s before restart..."
|
log_message "⏳ Waiting ${RESTART_DELAY}s before restart..."
|
||||||
sleep $RESTART_DELAY
|
sleep $RESTART_DELAY
|
||||||
|
|
||||||
# Cleanup any zombie processes
|
# Ensure old player process is gone before restarting
|
||||||
pkill -9 -f "python3 main.py" 2>/dev/null
|
kill -9 $PLAYER_PID 2>/dev/null
|
||||||
|
|
||||||
done
|
done
|
||||||
|
|
||||||
@@ -301,6 +293,3 @@ log_message ""
|
|||||||
log_message "=========================================="
|
log_message "=========================================="
|
||||||
log_message "Watchdog stopped"
|
log_message "Watchdog stopped"
|
||||||
log_message "=========================================="
|
log_message "=========================================="
|
||||||
|
|
||||||
# Deactivate virtual environment (this line is never reached in watchdog mode)
|
|
||||||
deactivate
|
|
||||||
|
|||||||
Reference in New Issue
Block a user