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:
Executable
+347
@@ -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 ""
|
||||
Reference in New Issue
Block a user