Files
Varaverk/Tools/emby_database_repair.sh
T

226 lines
8.6 KiB
Bash

#!/bin/bash
# -----------------------------------------------------------------------------------------------
# --------------------------------- Emby Database Repair ---------------------------------------
# -----------------------------------------------------------------------------------------------
# Stops Emby, runs SQLite integrity checks on all Emby databases, and restarts.
# Use when Emby reports database corruption, unexpected crashes, or playback state issues.
#
# Checks performed:
# integrity_check — full SQLite integrity verification per database file
# quick_check — faster check for common corruption patterns
#
# If corruption is found:
# Reports which database files are corrupted
# Does NOT automatically repair — corruption repair requires manual steps
# Provides guidance on next steps per database type
#
# Emby database files checked:
# library.db — media library metadata
# library.db-wal — write-ahead log (if exists)
# librarydb.db — legacy library database
# users.db — user accounts and settings
# authentication.db — API keys and sessions
# activity.db — activity log
#
# HOST1 repairs the shared production Emby (HOST1_EMBY_REPAIR_CONTAINER).
# HOST2 repairs its own personal Emby (HOST2_EMBY_REPAIR_CONTAINER).
# detect_hosts() selects the correct container at runtime.
# Config path is detected from Docker inspect — no hardcoded paths needed.
# Supports --dry-run to show what would be checked without stopping Emby.
# -----------------------------------------------------------------------------------------------
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/../Master.conf"
source "$SCRIPT_DIR/../common.sh"
parse_args "$@"
# -----------------------------------------------------------------------------------------------
# ━━━ $ICON_GEAR Setup ━━━
# -----------------------------------------------------------------------------------------------
echo ""
echo "━━━ $ICON_GEAR Setup ━━━"
if [[ "$EUID" -ne 0 ]]; then
error "Must be run as root"
exit 1
fi
success "Running as root"
acquire_lock
# Select correct Emby container based on which server is running this script
detect_hosts
if [[ "$LOCAL_SERVER_NAME" == "$HOST1" ]]; then
EMBY_CONTAINER="$HOST1_EMBY_REPAIR_CONTAINER"
else
EMBY_CONTAINER="$HOST2_EMBY_REPAIR_CONTAINER"
fi
info "Emby container: $LOCAL_SERVER_NAME$EMBY_CONTAINER"
if ! command -v sqlite3 >/dev/null 2>&1; then
error "sqlite3 not found — install sqlite package"
exit 1
fi
success "sqlite3 available"
[[ "$DRY_RUN" == true ]] && warn "DRY RUN — Emby will not be stopped"
# Detect Emby config path from Docker mount
EMBY_CONFIG_HOST=$(docker inspect "$EMBY_CONTAINER" 2>/dev/null | \
jq -r '.[] | .Mounts[] | select(.Destination == "/config") | .Source' 2>/dev/null)
if [[ -z "$EMBY_CONFIG_HOST" ]]; then
error "Could not detect Emby config path from Docker mounts"
error "Make sure $EMBY_CONTAINER is the correct container name in Master.conf"
exit 1
fi
success "Emby config path: $EMBY_CONFIG_HOST"
# -----------------------------------------------------------------------------------------------
# ━━━ $ICON_STOP Stop Emby ━━━
# -----------------------------------------------------------------------------------------------
echo ""
echo "━━━ $ICON_STOP Stop Emby ━━━"
EMBY_WAS_RUNNING=false
STATUS=$(docker inspect -f '{{.State.Running}}' "$EMBY_CONTAINER" 2>/dev/null)
if [[ "$STATUS" == "true" ]]; then
EMBY_WAS_RUNNING=true
warn "Stopping $EMBY_CONTAINER — active sessions will be interrupted"
if [[ "$DRY_RUN" == false ]]; then
docker stop "$EMBY_CONTAINER" >/dev/null 2>&1 && \
success "$ICON_STOPPED $EMBY_CONTAINER stopped" || \
{ error "Failed to stop $EMBY_CONTAINER"; exit 1; }
sleep 3 # brief wait for file handles to release
else
warn "DRY RUN — would stop $EMBY_CONTAINER"
fi
else
info "$EMBY_CONTAINER is not running — proceeding with checks"
fi
# -----------------------------------------------------------------------------------------------
# ━━━ $ICON_HEALTH Database Integrity Check ━━━
# -----------------------------------------------------------------------------------------------
echo ""
echo "━━━ $ICON_HEALTH Database Integrity Check ━━━"
START=$(date +%s)
# Emby database files to check
DB_FILES=(
"data/library.db"
"data/librarydb.db"
"data/users.db"
"data/authentication.db"
"data/activity.db"
)
PASS_DBS=()
FAIL_DBS=()
MISSING_DBS=()
for db_rel in "${DB_FILES[@]}"; do
db_path="${EMBY_CONFIG_HOST}/${db_rel}"
db_name=$(basename "$db_rel")
if [[ ! -f "$db_path" ]]; then
log "$db_name — not found, skipping"
MISSING_DBS+=("$db_name")
continue
fi
DB_SIZE=$(du -sh "$db_path" 2>/dev/null | cut -f1)
info "$ICON_HEALTH Checking $db_name ($DB_SIZE)..."
if [[ "$DRY_RUN" == true ]]; then
warn "DRY RUN — would check: $db_path"
continue
fi
# Run integrity check
RESULT=$(sqlite3 "$db_path" "PRAGMA integrity_check;" 2>/dev/null)
EXIT_CODE=$?
if [[ "$EXIT_CODE" -ne 0 ]]; then
error "$db_name — sqlite3 could not open database (may be locked or corrupt)"
FAIL_DBS+=("$db_name")
elif [[ "$RESULT" == "ok" ]]; then
success "$db_name — integrity check passed"
PASS_DBS+=("$db_name")
else
error "$db_name — integrity check FAILED"
echo "$RESULT" | head -10 | while IFS= read -r line; do
error " $line"
done
FAIL_DBS+=("$db_name")
fi
done
END=$(date +%s)
# -----------------------------------------------------------------------------------------------
# ━━━ $ICON_START Restart Emby ━━━
# -----------------------------------------------------------------------------------------------
echo ""
echo "━━━ $ICON_START Restart Emby ━━━"
if [[ "$EMBY_WAS_RUNNING" == true && "$DRY_RUN" == false ]]; then
docker start "$EMBY_CONTAINER" >/dev/null 2>&1 && \
success "$ICON_STARTED $EMBY_CONTAINER restarted" || \
error "Failed to restart $EMBY_CONTAINER — start it manually"
elif [[ "$DRY_RUN" == true && "$EMBY_WAS_RUNNING" == true ]]; then
warn "DRY RUN — would restart $EMBY_CONTAINER"
else
info "$EMBY_CONTAINER was not running — not restarting"
fi
# -----------------------------------------------------------------------------------------------
# ━━━ $ICON_SUMMARY Summary ━━━
# -----------------------------------------------------------------------------------------------
echo ""
echo "━━━━━ $ICON_SUMMARY EMBY DATABASE REPAIR SUMMARY ━━━━━"
echo "$ICON_HEALTH Container: $EMBY_CONTAINER"
echo "$ICON_HEALTH Config path: $EMBY_CONFIG_HOST"
echo "$ICON_TIME Duration: $(format_duration $((END - START)))"
echo ""
echo " $ICON_SUCCESS Passed: ${#PASS_DBS[@]} $ICON_ERROR Failed: ${#FAIL_DBS[@]} $ICON_INFO Missing: ${#MISSING_DBS[@]}"
echo ""
if [[ ${#PASS_DBS[@]} -gt 0 ]]; then
for db in "${PASS_DBS[@]}"; do echo " $ICON_SUCCESS $db"; done
fi
if [[ ${#FAIL_DBS[@]} -gt 0 ]]; then
for db in "${FAIL_DBS[@]}"; do echo " $ICON_ERROR $db"; done
fi
echo ""
if [[ "$DRY_RUN" == true ]]; then
echo "$ICON_WARN Status: DRY RUN — no checks performed"
elif [[ ${#FAIL_DBS[@]} -gt 0 ]]; then
echo "$ICON_ERROR Status: CORRUPTION FOUND"
echo ""
echo "$ICON_INFO Next steps for corrupted databases:"
echo " library.db — Stop Emby, delete library.db, restart"
echo " Emby will rebuild from media files (slow first start)"
echo " users.db — Stop Emby, restore from backup or delete"
echo " Deleting resets all user accounts"
echo " authentication.db — Stop Emby, delete, restart"
echo " API keys and sessions will be regenerated"
echo " activity.db — Stop Emby, delete, restart — activity log only"
echo ""
echo "$ICON_WARN Always take a backup before deleting any database file"
notify "Emby database corruption found on $(hostname) — failed: ${FAIL_DBS[*]} — manual intervention needed" "Emby DB Repair" "warning"
else
echo "$ICON_DONE Status: $ICON_SUCCESS ALL DATABASES HEALTHY"
notify "Emby database integrity check passed on $(hostname)${#PASS_DBS[@]} databases healthy" "Emby DB Repair" "normal"
fi
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"