Cleanup repo + harden offline install and watchdog

Repo hygiene:
- Remove dead scripts (.wait-for-display, .display-keepalive, .keep-screen-alive)
- Remove stale backup/scratch (install.sh.bak, New.txt, stale .pyc)
- Stop tracking machine-specific/generated state (config/app_config.json,
  playlists/server_playlist.json, src/player_auth.json, generated launchers)
- Prune duplicate wheel versions (one per package)
- Reorganize working_files -> dev/, dev/tests/, documentation/dev-notes/, scripts/
- Extend .gitignore; fix no-op rules in repo/.gitignore

Offline install (install.sh):
- Non-fatal system-dep step; per-package apt retry; .deb bundle install
- Skip pip self-upgrade offline; guard online fallbacks behind connectivity probe
- Dependency-free fetch_url (curl/wget/urllib); --no-reboot flag
- Fix corrupted literal backslash-bang escapes (shebangs, test -f, banner)
- Idempotent cron install (no more stacked @reboot lines)

Autostart / watchdog:
- start.sh: single-instance flock lock (.player_watchdog.lock) acquired only
  after a display is confirmed, preventing parallel watchdogs
- Set XDG_RUNTIME_DIR when missing; exit early if headless
- run_player.sh: activate .venv; stop_player.sh: clear lock file
This commit is contained in:
SKE087
2026-10-14 23:29:14 +03:00
parent f7b889be11
commit 31807f37bf
60 changed files with 633 additions and 1469 deletions
+347
View File
@@ -0,0 +1,347 @@
#!/bin/bash
#
# run_player-afterclone.sh
# ============================================================
# ONE-TIME post-clone setup for a cloned Raspberry Pi SD card.
#
# Problem it solves:
# The player identifies itself to DigiServer using the "screen_name"
# value in config/app_config.json (NOT the OS hostname). A cloned SD
# card carries the ORIGINAL card's screen_name + a cached auth token
# (src/player_auth.json). Two devices then fight over the same player
# session, so the cloned device never connects.
#
# What this script does:
# 1. Stops the running player/watchdog (so the token is not re-written)
# 2. Backs up the current identity/config files
# 3. Deletes the cached authentication token(s) -> forces re-auth
# 4. Sets a NEW unique screen_name and (optionally) server details
# 5. Leaves the player ready to boot and register as a BRAND-NEW player
#
# Usage:
# bash run_player-afterclone.sh [OPTIONS]
#
# Options:
# -n, --name NAME New unique screen/player name
# -s, --server IP Server IP / hostname
# -p, --port PORT Server port
# -k, --key KEY Quick-connect / auth key
# --https Enable HTTPS
# --no-https Disable HTTPS
# --verify-ssl Enable SSL certificate verification
# --no-verify-ssl Disable SSL certificate verification
# --set-hostname Also set the OS hostname to the new screen name
# -y, --yes Non-interactive; use args/defaults, no prompts
# --force Run even if this card was already set up
# -h, --help Show this help
#
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CONFIG_DIR="$SCRIPT_DIR/config"
CONFIG_FILE="$CONFIG_DIR/app_config.json"
VENV_DIR="$SCRIPT_DIR/.venv"
MARKER_FILE="$SCRIPT_DIR/.post-clone-setup.done"
BACKUP_ROOT="$SCRIPT_DIR/.post-clone-backups"
STAMP="$(date '+%Y%m%d-%H%M%S')"
# ---- Defaults / CLI state ----
NEW_NAME=""
NEW_SERVER=""
NEW_PORT=""
NEW_KEY=""
NEW_USE_HTTPS=""
NEW_VERIFY_SSL=""
SET_HOSTNAME=false
ASSUME_YES=false
FORCE=false
# ---- Pretty printing ----
info() { echo " $1"; }
ok() { echo " ✓ $1"; }
warn() { echo " ⚠️ $1"; }
err() { echo " ✗ $1"; }
usage() {
cat <<'EOF'
run_player-afterclone.sh - one-time post-clone setup for a cloned SD card.
Deletes the cached authentication token and configures a NEW unique
player identity so the card registers on the server as a new player.
Usage:
bash run_player-afterclone.sh [OPTIONS]
Options:
-n, --name NAME New unique screen/player name
-s, --server IP Server IP / hostname
-p, --port PORT Server port
-k, --key KEY Quick-connect / auth key
--https Enable HTTPS
--no-https Disable HTTPS
--verify-ssl Enable SSL certificate verification
--no-verify-ssl Disable SSL certificate verification
--set-hostname Also set the OS hostname to the new screen name
-y, --yes Non-interactive; use args/defaults, no prompts
--force Run even if this card was already set up
-h, --help Show this help
EOF
exit 0
}
# ---- Pick a python interpreter (prefer the venv, fall back to system) ----
pick_python() {
if [ -x "$VENV_DIR/bin/python3" ]; then
echo "$VENV_DIR/bin/python3"
elif command -v python3 >/dev/null 2>&1; then
echo "python3"
else
echo ""
fi
}
# ---- Parse arguments ----
while [ $# -gt 0 ]; do
case "$1" in
-n|--name) NEW_NAME="$2"; shift 2 ;;
-s|--server) NEW_SERVER="$2"; shift 2 ;;
-p|--port) NEW_PORT="$2"; shift 2 ;;
-k|--key) NEW_KEY="$2"; shift 2 ;;
--https) NEW_USE_HTTPS="true"; shift ;;
--no-https) NEW_USE_HTTPS="false"; shift ;;
--verify-ssl) NEW_VERIFY_SSL="true"; shift ;;
--no-verify-ssl) NEW_VERIFY_SSL="false"; shift ;;
--set-hostname) SET_HOSTNAME=true; shift ;;
-y|--yes) ASSUME_YES=true; shift ;;
--force) FORCE=true; shift ;;
-h|--help) usage ;;
*) err "Unknown option: $1"; echo ""; usage ;;
esac
done
echo "=========================================="
echo "Kiwy Signage - Post-Clone Setup"
echo "=========================================="
echo ""
# ---- Safety: only run once per card (unless --force) ----
if [ -f "$MARKER_FILE" ] && [ "$FORCE" != true ]; then
warn "This card looks already set up (marker: $(basename "$MARKER_FILE"))."
warn "Re-run with --force if you really want to reset it again."
echo ""
exit 1
fi
PYTHON_BIN="$(pick_python)"
if [ -z "$PYTHON_BIN" ]; then
err "python3 not found. Run install.sh first."
exit 1
fi
# ============================================================
# 1) Stop the running player + watchdog so nothing re-writes the token
# ============================================================
echo "▶ Stopping any running player/watchdog..."
pkill -f "bash.*start.sh" 2>/dev/null || true
pkill -f "python3 main.py" 2>/dev/null || true
sleep 2
pkill -9 -f "bash.*start.sh" 2>/dev/null || true
pkill -9 -f "python3 main.py" 2>/dev/null || true
ok "Player stopped"
echo ""
# ============================================================
# 2) Read current config (for defaults / display)
# ============================================================
CUR_NAME=""
CUR_SERVER=""
CUR_PORT=""
CUR_KEY=""
if [ -f "$CONFIG_FILE" ]; then
eval "$("$PYTHON_BIN" - "$CONFIG_FILE" <<'PYEOF'
import json, sys
try:
c = json.load(open(sys.argv[1]))
except Exception:
c = {}
def q(k):
v = c.get(k, "")
v = "" if v is None else str(v)
return v.replace("'", "'\\''")
print(f"CUR_NAME='{q('screen_name')}'")
print(f"CUR_SERVER='{q('server_ip')}'")
print(f"CUR_PORT='{q('port')}'")
print(f"CUR_KEY='{q('quickconnect_key')}'")
PYEOF
)"
fi
OS_HOSTNAME="$(hostname 2>/dev/null || echo '')"
# Sensible defaults: prefer an explicit new name, else the (changed) OS hostname,
# else the previous screen name.
DEF_NAME="$NEW_NAME"
[ -z "$DEF_NAME" ] && DEF_NAME="$OS_HOSTNAME"
[ -z "$DEF_NAME" ] && DEF_NAME="$CUR_NAME"
[ -z "$DEF_NAME" ] && DEF_NAME="player-$(date +%s | tail -c 5)"
# ============================================================
# 3) Prompt (unless -y / non-interactive) for the new identity
# ============================================================
prompt_value() {
# $1 = prompt text, $2 = default
local answer
if [ "$ASSUME_YES" = true ] || [ ! -t 0 ]; then
echo "$2"
return
fi
read -r -p " $1 [$2]: " answer
echo "${answer:-$2}"
}
echo "▶ New player identity"
info "Note: the server identifies this player by 'screen_name', not the OS hostname."
if [ -z "$NEW_NAME" ]; then
NEW_NAME="$(prompt_value "New unique screen/player name" "$DEF_NAME")"
fi
if [ -z "$NEW_SERVER" ]; then
NEW_SERVER="$(prompt_value "Server IP/hostname" "$CUR_SERVER")"
fi
if [ -z "$NEW_PORT" ]; then
NEW_PORT="$(prompt_value "Server port" "${CUR_PORT:-8080}")"
fi
if [ -z "$NEW_KEY" ]; then
NEW_KEY="$(prompt_value "Quick-connect key" "$CUR_KEY")"
fi
echo ""
# ============================================================
# 4) Back up current identity files
# ============================================================
BACKUP_DIR="$BACKUP_ROOT/$STAMP"
mkdir -p "$BACKUP_DIR"
echo "▶ Backing up current identity files to:"
info "$BACKUP_DIR"
[ -f "$CONFIG_FILE" ] && cp -p "$CONFIG_FILE" "$BACKUP_DIR/" 2>/dev/null || true
[ -f "$SCRIPT_DIR/src/player_auth.json" ] && cp -p "$SCRIPT_DIR/src/player_auth.json" "$BACKUP_DIR/" 2>/dev/null || true
ok "Backup complete"
echo ""
# ============================================================
# 5) Delete cached authentication tokens (force re-authentication)
# ============================================================
echo "▶ Removing cached authentication token(s)..."
REMOVED=0
while IFS= read -r token; do
[ -z "$token" ] && continue
rm -f "$token" && REMOVED=$((REMOVED + 1)) && info "deleted: ${token#$SCRIPT_DIR/}"
done < <(find "$SCRIPT_DIR" -name 'player_auth.json' -not -path '*/.venv/*' -not -path "$BACKUP_ROOT/*" 2>/dev/null)
rm -f /tmp/temp_auth_test.json 2>/dev/null || true
rm -f "$SCRIPT_DIR/.player_heartbeat" 2>/dev/null || true
rm -f "$SCRIPT_DIR/.player_stop_requested" 2>/dev/null || true
if [ "$REMOVED" -eq 0 ]; then
ok "No cached token found (already clean)"
else
ok "Removed $REMOVED token file(s)"
fi
echo ""
# ============================================================
# 6) Write the new config (screen_name + server details)
# ============================================================
echo "▶ Writing new player configuration..."
mkdir -p "$CONFIG_DIR"
CFG_PATH="$CONFIG_FILE" \
NEW_SCREEN_NAME="$NEW_NAME" \
NEW_SERVER_IP="$NEW_SERVER" \
NEW_PORT="$NEW_PORT" \
NEW_KEY="$NEW_KEY" \
NEW_USE_HTTPS="$NEW_USE_HTTPS" \
NEW_VERIFY_SSL="$NEW_VERIFY_SSL" \
"$PYTHON_BIN" <<'PYEOF'
import json, os
cfg_path = os.environ['CFG_PATH']
try:
with open(cfg_path) as f:
cfg = json.load(f)
except Exception:
cfg = {}
updates = {
'screen_name': os.environ.get('NEW_SCREEN_NAME', ''),
'server_ip': os.environ.get('NEW_SERVER_IP', ''),
'port': os.environ.get('NEW_PORT', ''),
'quickconnect_key': os.environ.get('NEW_KEY', ''),
}
for key, val in updates.items():
if val != '':
cfg[key] = val
if os.environ.get('NEW_USE_HTTPS', '') != '':
cfg['use_https'] = os.environ['NEW_USE_HTTPS'].lower() == 'true'
if os.environ.get('NEW_VERIFY_SSL', '') != '':
cfg['verify_ssl'] = os.environ['NEW_VERIFY_SSL'].lower() == 'true'
# Safe defaults for anything missing
cfg.setdefault('port', '8080')
cfg.setdefault('orientation', 'Landscape')
cfg.setdefault('touch', 'True')
cfg.setdefault('max_resolution', '1920x1080')
cfg.setdefault('edit_feature_enabled', True)
cfg.setdefault('use_https', False)
cfg.setdefault('verify_ssl', False)
with open(cfg_path, 'w') as f:
json.dump(cfg, f, indent=2)
f.write('\n')
print(json.dumps(cfg, indent=2))
PYEOF
ok "Configuration updated"
echo ""
# ============================================================
# 7) Optionally align the OS hostname with the screen name
# ============================================================
if [ "$SET_HOSTNAME" = true ] && [ -n "$NEW_NAME" ]; then
SAFE_HOST="$(echo "$NEW_NAME" | tr '[:upper:]' '[:lower:]' | tr -c 'a-z0-9-' '-' | sed 's/-\+$//')"
echo "▶ Setting OS hostname to '$SAFE_HOST' (player identity is still 'screen_name')..."
if command -v hostnamectl >/dev/null 2>&1; then
sudo hostnamectl set-hostname "$SAFE_HOST" 2>/dev/null \
&& ok "OS hostname set to $SAFE_HOST" \
|| warn "Could not set OS hostname (needs sudo)"
else
echo "$SAFE_HOST" | sudo tee /etc/hostname >/dev/null 2>&1 \
&& ok "OS hostname file updated" \
|| warn "Could not write /etc/hostname (needs sudo)"
fi
warn "A reboot is recommended for the hostname change to fully apply."
echo ""
fi
# ============================================================
# 8) Mark the card as set up + summary
# ============================================================
{
echo "Post-clone setup completed: $(date '+%Y-%m-%d %H:%M:%S')"
echo "screen_name: $NEW_NAME"
echo "server_ip: $NEW_SERVER"
echo "port: $NEW_PORT"
echo "backup: $BACKUP_DIR"
} > "$MARKER_FILE"
echo "=========================================="
echo "✓ Card is ready to connect to the server"
echo "=========================================="
echo ""
echo " Player name : $NEW_NAME"
echo " Server : $NEW_SERVER:$NEW_PORT"
echo " Backup : $BACKUP_DIR"
echo ""
echo " Next steps:"
echo " 1. (Optional) reboot: sudo reboot"
echo " 2. Start the player: ./start.sh"
echo ""
echo " The player will authenticate as a NEW player '$NEW_NAME'"
echo " and fetch its own playlist from the server."
echo ""