31807f37bf
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
348 lines
12 KiB
Bash
Executable File
348 lines
12 KiB
Bash
Executable File
#!/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 ""
|