Update plex health scripts

This commit is contained in:
Rufus King 2026-07-28 21:07:42 -04:00
parent 2ae9363458
commit 293c07ed76
2 changed files with 44 additions and 367 deletions

View file

@ -1,365 +0,0 @@
#!/bin/bash
# =============================================================
# PLEX32 — Config Backup Script
# Creates tar archives of Plex and Tautulli configs
# Copies to NAS08 via NFS as single files
# Keeps last 3 backups, deletes older ones
# Sends HTML email report in KingDezigns style
# Location: /usr/scripts/plex32/plex32_backup.sh
# Schedule: Every 3 days at 2:00 AM (cron)
# =============================================================
# --- Config ---
HOSTNAME="plex32"
PLEX_SRC="/opt/plex/config"
TAUTULLI_SRC="/opt/tautulli/config"
NFS_DEST="/mnt/plex/config"
TAUTULLI_LOC="backups/tautulli"
PLEX_LOC="backups/plex"
TMP_DIR="/tmp/plex32-backup"
LOG_DIR="/var/log/plex32-backup"
LOG_FILE="$LOG_DIR/backup-$(date +%Y%m%d).log"
RETENTION_COUNT=3
COMPOSE_DIR="/home/rufusking/docker/plex"
DATESTAMP=$(date '+%Y-%m-%d')
TIMESTAMP=$(date '+%B %d, %Y at %I:%M %p')
PLEX_ARCHIVE="plex-config-${DATESTAMP}.tar.gz"
TAUTULLI_ARCHIVE="tautulli-config-${DATESTAMP}.tar.gz"
SMTP_SERVER="smtppro.zoho.com"
SMTP_PORT=465
SMTP_USER="rufus.king@kingdezigns.com"
SMTP_PASS="$(cat /etc/plex32-smtp-pass 2>/dev/null)"
EMAIL_TO="rufus.king@kingdezigns.com"
EMAIL_FROM="rufus.king@kingdezigns.com"
# --- Setup ---
mkdir -p "$LOG_DIR" "$TMP_DIR"
START_TIME=$(date +%s)
exec > >(tee -a "$LOG_FILE") 2>&1
echo "[$(date '+%Y-%m-%d %H:%M:%S')] PLEX32 backup started"
# --- Check NFS mount ---
if ! mountpoint -q "$NFS_DEST"; then
echo "[ERROR] NFS mount not available: $NFS_DEST"
exit 1
fi
# --- Stop containers ---
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Stopping Plex and Tautulli containers..."
cd "$COMPOSE_DIR" && docker compose down
STOP_STATUS=$?
if [ $STOP_STATUS -ne 0 ]; then
echo "[ERROR] Failed to stop containers cleanly"
fi
# --- Tar Plex config ---
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Creating Plex config archive..."
PLEX_START=$(date +%s)
tar -czf "$TMP_DIR/$PLEX_ARCHIVE" \
--exclude="config/Library/Application Support/Plex Media Server/Cache" \
--exclude="config/Library/Application Support/Plex Media Server/Logs" \
--exclude="config/Library/Application Support/Plex Media Server/Crash Reports" \
-C /opt/plex config 2>&1
PLEX_TAR_STATUS=$?
PLEX_END=$(date +%s)
PLEX_DURATION=$((PLEX_END - PLEX_START))
if [ $PLEX_TAR_STATUS -eq 0 ]; then
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Plex archive created in ${PLEX_DURATION}s"
else
echo "[ERROR] Plex tar failed with status $PLEX_TAR_STATUS"
fi
# --- Tar Tautulli config ---
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Creating Tautulli config archive..."
TAUTULLI_START=$(date +%s)
tar -czf "$TMP_DIR/$TAUTULLI_ARCHIVE" \
-C /opt/tautulli config 2>&1
TAUTULLI_TAR_STATUS=$?
TAUTULLI_END=$(date +%s)
TAUTULLI_DURATION=$((TAUTULLI_END - TAUTULLI_START))
if [ $TAUTULLI_TAR_STATUS -eq 0 ]; then
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Tautulli archive created in ${TAUTULLI_DURATION}s"
else
echo "[ERROR] Tautulli tar failed with status $TAUTULLI_TAR_STATUS"
fi
# --- Restart containers ---
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Restarting containers..."
cd "$COMPOSE_DIR" && docker compose up -d
START_STATUS=$?
if [ $START_STATUS -eq 0 ]; then
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Containers restarted successfully"
else
echo "[ERROR] Failed to restart containers — manual intervention required"
fi
# --- Copy Plex archive to NAS08 ---
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Copying Plex archive to NAS08..."
COPY_PLEX_START=$(date +%s)
cp "$TMP_DIR/$PLEX_ARCHIVE" "$NFS_DEST/$PLEX_LOC/"
PLEX_COPY_STATUS=$?
COPY_PLEX_END=$(date +%s)
PLEX_COPY_DURATION=$((COPY_PLEX_END - COPY_PLEX_START))
if [ $PLEX_COPY_STATUS -eq 0 ]; then
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Plex archive copied in ${PLEX_COPY_DURATION}s"
else
echo "[ERROR] Plex archive copy failed"
fi
# --- Copy Tautulli archive to NAS08 ---
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Copying Tautulli archive to NAS08..."
COPY_TAUTULLI_START=$(date +%s)
cp "$TMP_DIR/$TAUTULLI_ARCHIVE" "$NFS_DEST/$TAUTULLI_LOC/"
TAUTULLI_COPY_STATUS=$?
COPY_TAUTULLI_END=$(date +%s)
TAUTULLI_COPY_DURATION=$((COPY_TAUTULLI_END - COPY_TAUTULLI_START))
if [ $TAUTULLI_COPY_STATUS -eq 0 ]; then
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Tautulli archive copied in ${TAUTULLI_COPY_DURATION}s"
else
echo "[ERROR] Tautulli archive copy failed"
fi
# --- Rotate old backups — keep last 3 ---
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Rotating old backups..."
ls -t "$NFS_DEST/$PLEX_LOC"/plex-config-*.tar.gz 2>/dev/null | tail -n +$((RETENTION_COUNT + 1)) | xargs rm -f
ls -t "$NFS_DEST/$TAUTULLI_LOC"/tautulli-config-*.tar.gz 2>/dev/null | tail -n +$((RETENTION_COUNT + 1)) | xargs rm -f
# --- Cleanup temp files ---
rm -f "$TMP_DIR/$PLEX_ARCHIVE" "$TMP_DIR/$TAUTULLI_ARCHIVE"
# --- Sizes ---
PLEX_ARCHIVE_SIZE=$(ls -lh "$NFS_DEST/$PLEX_LOC/$PLEX_ARCHIVE" 2>/dev/null | awk '{print $5}')
TAUTULLI_ARCHIVE_SIZE=$(ls -lh "$NFS_DEST/$TAUTULLI_LOC/$TAUTULLI_ARCHIVE" 2>/dev/null | awk '{print $5}')
NFS_FREE=$(df -h "$NFS_DEST" 2>/dev/null | awk 'NR==2{print $4}')
PLEX_BACKUP_COUNT=$(ls "$NFS_DEST/$PLEX_LOC"/plex-config-*.tar.gz 2>/dev/null | wc -l)
TAUTULLI_BACKUP_COUNT=$(ls "$NFS_DEST/$TAUTULLI_LOC"/tautulli-config-*.tar.gz 2>/dev/null | wc -l)
# --- Total duration ---
END_TIME=$(date +%s)
TOTAL_DURATION=$((END_TIME - START_TIME))
TOTAL_MIN=$((TOTAL_DURATION / 60))
TOTAL_SEC=$((TOTAL_DURATION % 60))
# --- Overall status ---
PLEX_STATUS=$((PLEX_TAR_STATUS + PLEX_COPY_STATUS))
TAUTULLI_STATUS=$((TAUTULLI_TAR_STATUS + TAUTULLI_COPY_STATUS))
if [ $PLEX_STATUS -eq 0 ] && [ $TAUTULLI_STATUS -eq 0 ] && [ $START_STATUS -eq 0 ]; then
OVERALL_STATUS="SUCCESS"
STATUS_COLOR="#1a7f4b"
BANNER_ICON="✅"
BANNER_MSG="Backup completed successfully — Plex and Tautulli configs archived to NAS08"
BADGE_COLOR="#1a7f4b"
elif [ $PLEX_STATUS -ne 0 ] && [ $TAUTULLI_STATUS -ne 0 ]; then
OVERALL_STATUS="FAILED"
STATUS_COLOR="#b91c1c"
BANNER_ICON="🔴"
BANNER_MSG="Backup failed — both archive operations encountered errors"
BADGE_COLOR="#b91c1c"
else
OVERALL_STATUS="PARTIAL"
STATUS_COLOR="#b45309"
BANNER_ICON="⚠️"
BANNER_MSG="Backup partially completed — review individual results below"
BADGE_COLOR="#b45309"
fi
# --- Status labels ---
if [ $PLEX_STATUS -eq 0 ]; then
PLEX_LABEL="✅ SUCCESS"
PLEX_ROW_BG="#f0fdf4"
PLEX_TEXT_COLOR="#166534"
else
PLEX_LABEL="❌ FAILED"
PLEX_ROW_BG="#fff1f2"
PLEX_TEXT_COLOR="#b91c1c"
fi
if [ $TAUTULLI_STATUS -eq 0 ]; then
TAUTULLI_LABEL="✅ SUCCESS"
TAUTULLI_ROW_BG="#f0fdf4"
TAUTULLI_TEXT_COLOR="#166534"
else
TAUTULLI_LABEL="❌ FAILED"
TAUTULLI_ROW_BG="#fff1f2"
TAUTULLI_TEXT_COLOR="#b91c1c"
fi
if [ $START_STATUS -eq 0 ]; then
RESTART_LABEL="✅ RUNNING"
RESTART_COLOR="#166534"
else
RESTART_LABEL="❌ CHECK REQUIRED"
RESTART_COLOR="#b91c1c"
fi
# --- Send email ---
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Sending email report..."
python3 << PYEOF
import smtplib, ssl
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
smtp_server = "$SMTP_SERVER"
smtp_port = $SMTP_PORT
smtp_user = "$SMTP_USER"
smtp_pass = "$SMTP_PASS"
email_to = "$EMAIL_TO"
email_from = "$EMAIL_FROM"
subject = "PLEX32 Backup — $OVERALL_STATUS — $DATESTAMP"
html = """
<!DOCTYPE html>
<html>
<body style="margin:0;padding:20px;background:#f1f5f9;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;">
<table width="100%" cellspacing="0" cellpadding="0">
<tr><td align="center">
<table width="720" cellspacing="0" cellpadding="0" style="max-width:720px;width:100%;">
<!-- HEADER -->
<tr><td style="background:#0f172a;border-radius:10px 10px 0 0;padding:28px 32px;">
<table width="100%" cellspacing="0" cellpadding="0"><tr>
<td>
<div style="font-size:11px;color:#94a3b8;letter-spacing:1px;text-transform:uppercase;">Config Backup</div>
<div style="font-size:26px;font-weight:800;color:white;margin-top:4px;">🎬 plex32</div>
<div style="font-size:13px;color:#94a3b8;margin-top:4px;">$TIMESTAMP &nbsp;|&nbsp; VLAN 50 — Lab/Servers</div>
</td>
<td align="right">
<div style="background:$BADGE_COLOR;color:white;padding:10px 20px;border-radius:8px;font-size:14px;font-weight:700;">$OVERALL_STATUS</div>
</td>
</tr></table>
</td></tr>
<!-- STATUS BANNER -->
<tr><td style="background:$STATUS_COLOR;padding:12px 32px;">
<span style="color:white;font-size:14px;font-weight:600;">$BANNER_ICON &nbsp;$BANNER_MSG</span>
</td></tr>
<!-- BODY -->
<tr><td style="background:white;border:1px solid #e5e7eb;padding:28px 32px;">
<!-- STAT CARDS -->
<table width="100%" cellspacing="0" cellpadding="0">
<tr>
<td style="padding:4px;">
<div style="padding:12px;background:#f9fafb;border-radius:6px;text-align:center;">
<div style="font-size:11px;color:#9ca3af;text-transform:uppercase;letter-spacing:.5px;">Duration</div>
<div style="font-size:20px;font-weight:700;color:#111827;margin-top:2px;">${TOTAL_MIN}m ${TOTAL_SEC}s</div>
</div>
</td>
<td style="padding:4px;">
<div style="padding:12px;background:#f9fafb;border-radius:6px;text-align:center;">
<div style="font-size:11px;color:#9ca3af;text-transform:uppercase;letter-spacing:.5px;">Plex Archive</div>
<div style="font-size:20px;font-weight:700;color:#111827;margin-top:2px;">$PLEX_ARCHIVE_SIZE</div>
</div>
</td>
<td style="padding:4px;">
<div style="padding:12px;background:#f9fafb;border-radius:6px;text-align:center;">
<div style="font-size:11px;color:#9ca3af;text-transform:uppercase;letter-spacing:.5px;">Tautulli Archive</div>
<div style="font-size:20px;font-weight:700;color:#111827;margin-top:2px;">$TAUTULLI_ARCHIVE_SIZE</div>
</div>
</td>
<td style="padding:4px;">
<div style="padding:12px;background:#f9fafb;border-radius:6px;text-align:center;">
<div style="font-size:11px;color:#9ca3af;text-transform:uppercase;letter-spacing:.5px;">NAS08 Free</div>
<div style="font-size:20px;font-weight:700;color:#111827;margin-top:2px;">$NFS_FREE</div>
</div>
</td>
</tr>
</table>
<!-- RESULTS TABLE -->
<div style="margin-top:24px;background:white;border-radius:10px;border:1px solid #e5e7eb;overflow:hidden;">
<div style="font-size:13px;font-weight:700;color:#374151;padding:14px 18px;border-bottom:1px solid #e5e7eb;text-transform:uppercase;letter-spacing:.5px;">📋 Backup Results</div>
<table width="100%" cellspacing="0" cellpadding="0">
<tr style="background:#f9fafb;">
<th style="text-align:left;font-size:11px;color:#6b7280;padding:8px 18px;font-weight:600;text-transform:uppercase;letter-spacing:.5px;">Component</th>
<th style="text-align:left;font-size:11px;color:#6b7280;padding:8px 18px;font-weight:600;text-transform:uppercase;letter-spacing:.5px;">Archive</th>
<th style="text-align:left;font-size:11px;color:#6b7280;padding:8px 18px;font-weight:600;text-transform:uppercase;letter-spacing:.5px;">Copies Kept</th>
<th style="text-align:left;font-size:11px;color:#6b7280;padding:8px 18px;font-weight:600;text-transform:uppercase;letter-spacing:.5px;">Duration</th>
<th style="text-align:left;font-size:11px;color:#6b7280;padding:8px 18px;font-weight:600;text-transform:uppercase;letter-spacing:.5px;">Status</th>
</tr>
<tr style="background:$PLEX_ROW_BG;">
<td style="font-size:13px;font-weight:600;color:#111827;padding:10px 18px;border-top:1px solid #e5e7eb;">🎬 Plex Config</td>
<td style="font-size:12px;color:#374151;padding:10px 18px;border-top:1px solid #e5e7eb;font-family:monospace;">$PLEX_ARCHIVE</td>
<td style="font-size:13px;color:#374151;padding:10px 18px;border-top:1px solid #e5e7eb;">$PLEX_BACKUP_COUNT / $RETENTION_COUNT</td>
<td style="font-size:13px;color:#374151;padding:10px 18px;border-top:1px solid #e5e7eb;">${PLEX_DURATION}s tar + ${PLEX_COPY_DURATION}s copy</td>
<td style="font-size:13px;font-weight:700;padding:10px 18px;border-top:1px solid #e5e7eb;color:$PLEX_TEXT_COLOR;">$PLEX_LABEL</td>
</tr>
<tr style="background:$TAUTULLI_ROW_BG;">
<td style="font-size:13px;font-weight:600;color:#111827;padding:10px 18px;border-top:1px solid #e5e7eb;">📊 Tautulli Config</td>
<td style="font-size:12px;color:#374151;padding:10px 18px;border-top:1px solid #e5e7eb;font-family:monospace;">$TAUTULLI_ARCHIVE</td>
<td style="font-size:13px;color:#374151;padding:10px 18px;border-top:1px solid #e5e7eb;">$TAUTULLI_BACKUP_COUNT / $RETENTION_COUNT</td>
<td style="font-size:13px;color:#374151;padding:10px 18px;border-top:1px solid #e5e7eb;">${TAUTULLI_DURATION}s tar + ${TAUTULLI_COPY_DURATION}s copy</td>
<td style="font-size:13px;font-weight:700;padding:10px 18px;border-top:1px solid #e5e7eb;color:$TAUTULLI_TEXT_COLOR;">$TAUTULLI_LABEL</td>
</tr>
</table>
</div>
<!-- CONTAINER STATUS -->
<div style="margin-top:16px;padding:12px 14px;background:#f8fafc;border-radius:6px;border:1px solid #e5e7eb;">
<span style="font-size:12px;font-weight:600;color:#374151;text-transform:uppercase;letter-spacing:.5px;">Container Status After Backup</span>
<span style="margin-left:10px;font-size:13px;font-weight:700;color:$RESTART_COLOR;">$RESTART_LABEL</span>
</div>
<!-- DESTINATION INFO -->
<div style="margin-top:10px;padding:12px 14px;background:#f8fafc;border-radius:6px;border:1px solid #e5e7eb;">
<span style="font-size:12px;font-weight:600;color:#374151;text-transform:uppercase;letter-spacing:.5px;">Backup Destination</span>
<span style="margin-left:10px;font-size:13px;color:#374151;">NAS08 (192.168.150.35) via NFS — $NFS_FREE free</span>
</div>
<!-- RESTORE NOTE -->
<div style="margin-top:10px;padding:12px 14px;background:#fffbeb;border-radius:6px;border:1px solid #fac775;">
<span style="font-size:12px;font-weight:600;color:#633806;text-transform:uppercase;letter-spacing:.5px;">💡 To Restore</span>
<span style="margin-left:10px;font-size:13px;color:#633806;">tar -xzf $PLEX_ARCHIVE -C /opt/plex</span>
</div>
</td></tr>
<!-- FOOTER -->
<tr><td style="background:#0f172a;border-radius:0 0 10px 10px;padding:16px 32px;text-align:center;">
<span style="font-size:11px;color:#64748b;">Config Backup &nbsp;|&nbsp; plex32 &nbsp;|&nbsp; KingDezigns Infrastructure</span>
</td></tr>
</table>
</td></tr>
</table>
</body>
</html>
"""
msg = MIMEMultipart("alternative")
msg["Subject"] = subject
msg["From"] = email_from
msg["To"] = email_to
msg.attach(MIMEText(html, "html"))
ctx = ssl.create_default_context()
with smtplib.SMTP_SSL(smtp_server, smtp_port, context=ctx, timeout=15) as s:
s.login(smtp_user, smtp_pass)
s.sendmail(email_from, [email_to], msg.as_string())
print("Email sent successfully")
PYEOF
EMAIL_STATUS=$?
if [ $EMAIL_STATUS -eq 0 ]; then
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Email sent successfully"
else
echo "[ERROR] Email send failed"
fi
# --- Cleanup old logs ---
find "$LOG_DIR" -name "backup-*.log" -mtime +$RETENTION_DAYS -delete 2>/dev/null
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Backup complete — Status: $OVERALL_STATUS | Duration: ${TOTAL_MIN}m ${TOTAL_SEC}s"

View file

@ -71,6 +71,25 @@ fi
echo "[INFO] Plex status: $PLEX_STATUS_TEXT"
# =============================================================
# STEP 2B — VERIFY RUNNING CONTAINER (independent of pull result)
# =============================================================
echo "[INFO] Checking actual running Plex container..."
CONTAINER_STATUS=$(docker inspect -f '{{.State.Status}}' plex 2>/dev/null)
CONTAINER_STARTED=$(docker inspect -f '{{.State.StartedAt}}' plex 2>/dev/null | cut -d'.' -f1 | tr 'T' ' ')
CONTAINER_HEALTH=$(docker inspect -f '{{if .State.Health}}{{.State.Health.Status}}{{else}}n/a{{end}}' plex 2>/dev/null)
if [ "$CONTAINER_STATUS" = "running" ]; then
CONTAINER_OK=true
CONTAINER_STATUS_TEXT="Running since $CONTAINER_STARTED UTC"
else
CONTAINER_OK=false
CONTAINER_STATUS_TEXT="NOT running (status: ${CONTAINER_STATUS:-unknown})"
fi
echo "[INFO] Plex container status: $CONTAINER_STATUS_TEXT"
# =============================================================
# STEP 3 — CHECK FAIL2BAN VERSION
# =============================================================
@ -115,15 +134,25 @@ ANY_UPDATES=false
[ "$APT_FAILED" = true ] && ANY_FAILED=true
[ "$PLEX_FAILED" = true ] && ANY_FAILED=true
[ "$CONTAINER_OK" = false ] && ANY_FAILED=true
[ "$APT_UPDATES" -gt 0 ] && ANY_UPDATES=true
[ "$PLEX_UPDATE_AVAILABLE" = true ] && ANY_UPDATES=true
[ "$F2B_UPDATE_AVAILABLE" = true ] && ANY_UPDATES=true
[ "$DOCKER_UPDATE_AVAILABLE" = true ] && ANY_UPDATES=true
if [ "$ANY_FAILED" = true ]; then
# A failed registry pull (rate-limit, transient network blip, etc.) is not the
# same severity as the actual Plex container being down. If the pull check
# failed but the running container is confirmed healthy, badge it as a lower
# severity "registry check failed" rather than a full CHECK FAILED/critical state.
if [ "$CONTAINER_OK" = false ]; then
STATUS_COLOR="#b91c1c"
STATUS_BADGE="🔴 CONTAINER DOWN"
STATUS_BANNER_MSG="🔴 &nbsp;Plex container is NOT running — investigate immediately"
SUBJECT="🔴 PLEX32 — Plex Container Down — $DATE_SHORT"
elif [ "$ANY_FAILED" = true ]; then
STATUS_COLOR="#b45309"
STATUS_BADGE="⚠ CHECK FAILED"
STATUS_BANNER_MSG="⚠ &nbsp;One or more update checks failed — manual verification required"
STATUS_BANNER_MSG="⚠ &nbsp;Registry/update check failed, but Plex container itself is confirmed running — no action required unless this repeats daily"
SUBJECT="⚠ PLEX32 — Update Check Failed — $DATE_SHORT"
elif [ "$ANY_UPDATES" = true ]; then
STATUS_COLOR="#1d6fa8"
@ -229,6 +258,12 @@ PLEX_BADGE=$(make_badge "$PLEX_UPDATE_AVAILABLE" "$PLEX_FAILED")
F2B_BADGE=$(make_badge "$F2B_UPDATE_AVAILABLE" "false")
DOCKER_BADGE=$(make_badge "$DOCKER_UPDATE_AVAILABLE" "false")
if [ "$CONTAINER_OK" = true ]; then
CONTAINER_BADGE="<span style='background:#1a7f4b;color:white;padding:2px 8px;border-radius:4px;font-size:11px;font-weight:700;margin-left:8px;'>RUNNING</span>"
else
CONTAINER_BADGE="<span style='background:#b91c1c;color:white;padding:2px 8px;border-radius:4px;font-size:11px;font-weight:700;margin-left:8px;'>DOWN</span>"
fi
# =============================================================
# STEP 9 — BUILD & SEND EMAIL
# =============================================================
@ -249,9 +284,12 @@ subject = "$SUBJECT"
plex_status = "$PLEX_STATUS_TEXT"
f2b_status = "$F2B_STATUS_TEXT"
docker_status = "$DOCKER_STATUS_TEXT"
container_ok = "$CONTAINER_OK" == "true"
container_status = "$CONTAINER_STATUS_TEXT"
plex_badge = """$PLEX_BADGE"""
f2b_badge = """$F2B_BADGE"""
docker_badge = """$DOCKER_BADGE"""
container_badge = """$CONTAINER_BADGE"""
update_commands = """$UPDATE_COMMANDS_HTML"""
apt_packages_html = """$APT_PACKAGES_HTML"""
plex_error_html = """$PLEX_ERROR_HTML"""
@ -338,6 +376,10 @@ html = f"""<!DOCTYPE html>
<td style="padding:10px 14px;font-size:13px;color:#374151;font-weight:600;border-bottom:1px solid #f3f4f6;">🎬 Plex Media Server {plex_badge}</td>
<td style="padding:10px 14px;font-size:13px;color:#374151;border-bottom:1px solid #f3f4f6;">{plex_status}<br><span style="font-size:11px;color:#9ca3af;">Image: {plex_image_ver} | Built: {current_image_date}</span>{plex_error_html}</td>
</tr>
<tr>
<td style="padding:10px 14px;font-size:13px;color:#374151;font-weight:600;border-bottom:1px solid #f3f4f6;">✅ Plex Container (actual) {container_badge}</td>
<td style="padding:10px 14px;font-size:13px;color:#374151;border-bottom:1px solid #f3f4f6;">{container_status}<br><span style="font-size:11px;color:#9ca3af;">This is the real, running-right-now status — independent of the registry check above.</span></td>
</tr>
<tr>
<td style="padding:10px 14px;font-size:13px;color:#374151;font-weight:600;border-bottom:1px solid #f3f4f6;">🛡 Fail2Ban {f2b_badge}</td>
<td style="padding:10px 14px;font-size:13px;color:#374151;border-bottom:1px solid #f3f4f6;">{f2b_status}</td>