From 235af0e4d1cf2e4760a6fce612bfc645759fa760 Mon Sep 17 00:00:00 2001 From: deathbybandaid Date: Tue, 14 Mar 2023 09:22:42 -0400 Subject: [PATCH] test --- DBRepair.sh | 1733 +++++++++++++++++++++++++++++++++++++++++++++++++ plex-token.sh | 4 + plex-tools.sh | 17 +- 3 files changed, 1747 insertions(+), 7 deletions(-) create mode 100644 DBRepair.sh diff --git a/DBRepair.sh b/DBRepair.sh new file mode 100644 index 0000000..1c9aff3 --- /dev/null +++ b/DBRepair.sh @@ -0,0 +1,1733 @@ +#!/bin/sh +######################################################################### +# Plex Media Server database check and repair utility script. # +# Maintainer: ChuckPa # +# Version: v1.0.0 # +# Date: 13-Mar-2023 # +######################################################################### + +# Version for display purposes +Version="v1.0.0" + +# Flag when temp files are to be retained +Retain=0 + +# Have the databases passed integrity checks +CheckedDB=0 + +# By default, we cannot start/stop PMS +HaveStartStop=0 +StartStopUser=0 +StartCommand="" +StopCommand="" + +# Keep track of how many times the user's hit enter with no command (implied EOF) +NullCommands=0 + +# Global variable - main database +CPPL=com.plexapp.plugins.library + +# Initial timestamp +TimeStamp="$(date "+%Y-%m-%d_%H.%M.%S")" + +# Initialize global runtime variables +CheckedDB=0 +Damaged=0 +Fail=0 +HaveStartStop=0 +HostType="" +LOG_TOOL="echo" +ShowMenu=1 + +# Universal output function +Output() { + if [ $Scripted -gt 0 ]; then + echo \[$(date "+%Y-%m-%d %H.%M.%S")\] "$@" + else + echo "$@" + fi + # $LOG_TOOL \[$(date "+%Y-%m-%d %H.%M.%S")\] "$@" +} + +# Write to Repair Tool log +WriteLog() { + + # Write given message into tool log file with TimeStamp + echo "$(date "+%Y-%m-%d %H.%M.%S") - $*" >> "$LOGFILE" + return 0 +} + +# Check given database file integrity +CheckDB() { + + # Confirm the DB exists + [ ! -f "$1" ] && Output "ERROR: $1 does not exist." && return 1 + + # Now check database for corruption + Result="$("$PLEX_SQLITE" "$1" "PRAGMA integrity_check(1)")" + if [ "$Result" = "ok" ]; then + return 0 + else + SQLerror="$(echo $Result | sed -e 's/.*code //')" + return 1 + fi + +} + +# Check all databases +CheckDatabases() { + + # Arg1 = calling function + # Arg2 = 'force' if present + + # Check each of the databases. If all pass, set the 'CheckedDB' flag + # Only force recheck if flag given + + # Check if not checked or forced + NeedCheck=0 + [ $CheckedDB -eq 0 ] && NeedCheck=1 + [ $CheckedDB -eq 1 ] && [ "$2" = "force" ] && NeedCheck=1 + + # Do we need to check + if [ $NeedCheck -eq 1 ]; then + + # Clear Damaged flag + Damaged=0 + CheckedDB=0 + + # Info + Output "Checking the PMS databases" + + # Check main DB + if CheckDB $CPPL.db ; then + Output "Check complete. PMS main database is OK." + WriteLog "$1"" - Check $CPPL.db - PASS" + else + Output "Check complete. PMS main database is damaged." + WriteLog "$1"" - Check $CPPL.db - FAIL ($SQLerror)" + Damaged=1 + fi + + # Check blobs DB + if CheckDB $CPPL.blobs.db ; then + Output "Check complete. PMS blobs database is OK." + WriteLog "$1"" - Check $CPPL.blobs.db - PASS" + + else + Output "Check complete. PMS blobs database is damaged." + WriteLog "$1"" - Check $CPPL.blobs.db - FAIL ($SQLerror)" + Damaged=1 + fi + + # Yes, we've now checked it + CheckedDB=1 + fi + + [ $Damaged -eq 0 ] && CheckedDB=1 + + # return status + return $Damaged +} + +# Return list of database backup dates for consideration in replace action +GetDates(){ + + Dates="" + Tempfile="/tmp/DBRepairTool.$$.tmp" + touch "$Tempfile" + + for i in $(find . -maxdepth 1 -name 'com.plexapp.plugins.library.db-????-??-??' | sort -r) + do + # echo Date - "${i//[^.]*db-/}" + Date="$(echo $i | sed -e 's/.*.db-//')" + + # Only add if companion blobs DB exists + [ -e $CPPL.blobs.db-$Date ] && echo $Date >> "$Tempfile" + + done + + # Reload dates in sorted order + Dates="$(sort -r <$Tempfile)" + + # Remove tempfile + rm -f "$Tempfile" + + # Give results + echo $Dates + return +} + +# Non-fatal SQLite error code check +SQLiteOK() { + + # Global error variable + SQLerror=0 + + # Quick exit- known OK + [ $1 -eq 0 ] && return 0 + + # Put list of acceptable error codes here + Codes="19 28" + + # By default assume the given code is an error + CodeError=1 + + for i in $Codes + do + if [ $i -eq $1 ]; then + CodeError=0 + SQLerror=$i + break + fi + done + return $CodeError +} + +# Perform a space check +# Store space available versus space needed in variables +# Return FAIL if needed GE available +# Arg 1, if provided, is multiplier +FreeSpaceAvailable() { + + Multiplier=3 + [ "$1" != "" ] && Multiplier=$1 + + # Available space where DB resides + SpaceAvailable=$(df -m "$AppSuppDir" | tail -1 | awk '{print $4}') + + # Get size of DB and blobs, Minimally needing sum of both + LibSize="$(stat $STATFMT $STATBYTES "$CPPL.db")" + BlobsSize="$(stat $STATFMT $STATBYTES "$CPPL.blobs.db")" + SpaceNeeded=$((LibSize + BlobsSize)) + + # Compute need (minimum $Multiplier existing; current, backup, temp and room to write new) + SpaceNeeded="$(expr $SpaceNeeded '*' $Multiplier)" + SpaceNeeded="$(expr $SpaceNeeded / 1000000)" + + # If need < available, all good + [ $SpaceNeeded -lt $SpaceAvailable ] && return 0 + + # Too close to call, fail + return 1 +} + +# Perform the actual copying for MakeBackup() +DoBackup() { + + if [ -e $2 ]; then + cp -p "$2" "$3" + Result=$? + if [ $Result -ne 0 ]; then + Output "Error $Result while backing up '$2'. Cannot continue." + WriteLog "$1 - MakeBackup $2 - FAIL" + + # Remove partial copied file and return + rm -f "$3" + return 1 + else + WriteLog "$1 - MakeBackup $2 - PASS" + return 0 + fi + fi +} + +# Make a backup of the current database files and tag with TimeStamp +MakeBackups() { + + Output "Backup current databases with '-BACKUP-$TimeStamp' timestamp." + + for i in "db" "db-wal" "db-shm" "blobs.db" "blobs.db-wal" "blobs.db-shm" + do + DoBackup "$1" "${CPPL}.${i}" "$DBTMP/${CPPL}.${i}-BACKUP-$TimeStamp" + Result=$? + done + + return $Result + +} + +ConfirmYesNo() { + + Answer="" + while [ "$Answer" = "" ] + do + printf "$1 (Y/N) ? " + read Input + + # EOF = No + [ "$Input" = "" ] && Answer=N ; [ "$Input" = "n" ] && Answer=N ; [ "$Input" = "N" ] && Answer=N + [ "$Input" = "y" ] && Answer=Y ; [ "$Input" = "Y" ] && Answer=Y + + # Unrecognized + if [ "$Answer" != "Y" ] && [ "$Answer" != "N" ]; then + printf "$Input" was not a valid reply. Please try again. + continue + fi + done + + if [ "$Answer" = "Y" ]; then + # Confirmed Yes + return 0 + else + return 1 + fi +} + +# Restore previously saved DB from given TimeStamp +RestoreSaved() { + + T="$1" + + for i in "db" "db-wal" "db-shm" "blobs.db" "blobs.db-wal" "blobs.db-shm" + do + [ -e "${CPPL}.${i}" ] && rm -f "${CPPL}.${i}" + [ -e "$DBTMP/${CPPL}.${i}-BACKUP-$T" ] && mv "$DBTMP/${CPPL}.${i}-BACKUP-$T" "${CPPL}.${i}" + done +} + +# Get the size of the given DB in MB +GetSize() { + + Size=$(stat $STATFMT $STATBYTES "$1") + Size=$(expr $Size / 1048576) + [ $Size -eq 0 ] && Size=1 + echo $Size +} + +# Determine which host we are running on and set variables +HostConfig() { + + # On all hosts except Mac + PIDOF="pidof" + STATFMT="-c" + STATBYTES="%s" + + # Synology (DSM 7) + if [ -d /var/packages/PlexMediaServer ] && \ + [ -d "/var/packages/PlexMediaServer/shares/PlexMediaServer/AppData/Plex Media Server" ]; then + + # Where is the software + PKGDIR="/var/packages/PlexMediaServer/target" + PLEX_SQLITE="$PKGDIR/Plex SQLite" + LOG_TOOL="logger" + + # Where is the data + AppSuppDir="/var/packages/PlexMediaServer/shares/PlexMediaServer/AppData" + DBDIR="$AppSuppDir/Plex Media Server/Plug-in Support/Databases" + PID_FILE="$AppSuppDir/Plex Media Server/plexmediaserver.pid" + LOGFILE="$DBDIR/DBRepair.log" + + # We are done + HostType="Synology (DSM 7)" + + # We do have start/stop as root + HaveStartStop=1 + StartCommand="/usr/syno/bin/synopkg start PlexMediaServer" + StopCommand="/usr/syno/bin/synopkg stop PlexMediaServer" + return 0 + + # Synology (DSM 6) + elif [ -d "/var/packages/Plex Media Server" ] && \ + [ -f "/usr/syno/sbin/synoshare" ]; then + + # Where is the software + PKGDIR="/var/packages/Plex Media Server/target" + PLEX_SQLITE="$PKGDIR/Plex SQLite" + LOG_TOOL="logger" + + # Get shared folder path + AppSuppDir="$(synoshare --get Plex | grep Path | awk -F\[ '{print $2}' | awk -F\] '{print $1}')" + + # Where is the data + AppSuppDir="$AppSuppDir/Library/Application Support" + if [ -d "$AppSuppDir/Plex Media Server" ]; then + + DBDIR="$AppSuppDir/Plex Media Server/Plug-in Support/Databases" + PID_FILE="$AppSuppDir/Plex Media Server/plexmediaserver.pid" + LOGFILE="$DBDIR/DBRepair.log" + + HostType="Synology (DSM 6)" + + # We do have start/stop as root + HaveStartStop=1 + StartCommand="/usr/syno/bin/synopkg start PlexMediaServer" + StopCommand="/usr/syno/bin/synopkg stop PlexMediaServer" + return 0 + fi + + + # QNAP (QTS & QuTS) + elif [ -f /etc/config/qpkg.conf ]; then + + # Where is the software + PKGDIR="$(getcfg -f /etc/config/qpkg.conf PlexMediaServer Install_path)" + PLEX_SQLITE="$PKGDIR/Plex SQLite" + LOG_TOOL="/sbin/log_tool -t 0 -a" + + # Where is the data + AppSuppDir="$PKGDIR/Library" + DBDIR="$AppSuppDir/Plex Media Server/Plug-in Support/Databases" + PID_FILE="$AppSuppDir/Plex Media Server/plexmediaserver.pid" + LOGFILE="$DBDIR/DBRepair.log" + + # Start/Stop + if [ -e /etc/init.d/plex.sh ]; then + HaveStartStop=1 + StartCommand="/etc/init.d/plex.sh start" + StopCommand="/etc/init.d/plex.sh stop" + fi + + HostType="QNAP" + return 0 + + # Standard configuration Linux host + elif [ -f /etc/os-release ] && \ + [ -d /usr/lib/plexmediaserver ] && \ + [ -d /var/lib/plexmediaserver ]; then + + # Where is the software + PKGDIR="/usr/lib/plexmediaserver" + PLEX_SQLITE="$PKGDIR/Plex SQLite" + LOG_TOOL="logger" + + # Where is the data + AppSuppDir="/var/lib/plexmediaserver/Library/Application Support" + # DBDIR="$AppSuppDir/Plex Media Server/Plug-in Support/Databases" + # PID_FILE="$AppSuppDir/Plex Media Server/plexmediaserver.pid" + + # Find the metadata dir if customized + if [ -e /etc/systemd/system/plexmediaserver.service.d ]; then + + # Glob up all 'conf files' found + NewSuppDir="$(cd /etc/systemd/system/plexmediaserver.service.d ; \ + cat override.conf local.conf *.conf 2>/dev/null | grep "APPLICATION_SUPPORT_DIR" | head -1)" + + if [ "$NewSuppDir" != "" ]; then + NewSuppDir="$(echo $NewSuppDir | sed -e 's/.*_DIR=//' | tr -d '"' | tr -d "'")" + + if [ -d "$NewSuppDir" ]; then + AppSuppDir="$NewSuppDir" + else + Output "Given application support directory override specified does not exist: '$NewSuppDir'. Ignoring." + fi + fi + fi + + DBDIR="$AppSuppDir/Plex Media Server/Plug-in Support/Databases" + PID_FILE="$AppSuppDir/Plex Media Server/plexmediaserver.pid" + LOGFILE="$DBDIR/DBRepair.log" + + HostType="$(grep ^PRETTY_NAME= /etc/os-release | sed -e 's/PRETTY_NAME=//' | sed -e 's/"//g')" + + HaveStartStop=1 + StartCommand="systemctl start plexmediaserver" + StopCommand="systemctl stop plexmediaserver" + return 0 + + # Netgear ReadyNAS + elif [ -e /etc/os-release ] && [ "$(cat /etc/os-release | grep ReadyNASOS)" != "" ]; then + + # Find PMS + if [ "$(echo /apps/plexmediaserver*)" != "/apps/plexmediaserver*" ]; then + + PKGDIR="$(echo /apps/plexmediaserver*)" + + # Where is the code + PLEX_SQLITE="$PKGDIR/Binaries/Plex SQLite" + AppSuppDir="$PKGDIR/MediaLibrary" + PID_FILE="$AppSuppDir/Plex Media Server/plexmediaserver.pid" + DBDIR="$AppSuppDir/Plex Media Server/Plug-in Support/Databases" + LOGFILE="$DBDIR/DBRepair.log" + LOG_TOOL="logger" + + HostType="Netgear ReadyNAS" + return 0 + fi + + # ASUSTOR + elif [ -f /etc/nas.conf ] && grep ASUSTOR /etc/nas.conf >/dev/null && \ + [ -d "/volume1/Plex/Library/Plex Media Server" ]; then + + # Where are things + PLEX_SQLITE="/volume1/.@plugins/AppCentral/plexmediaserver/Plex SQLite" + AppSuppDir="/volume1/Plex/Library" + PID_FILE="$AppSuppDir/Plex Media Server/plexmediaserver.pid" + DBDIR="$AppSuppDir/Plex Media Server/Plug-in Support/Databases" + LOGFILE="$DBDIR/DBRepair.log" + LOG_TOOL="logger" + + HostType="ASUSTOR" + return 0 + + # Containers: + # - Docker cgroup v1 & v2 + # - Podman (libpod) + elif [ "$(grep docker /proc/1/cgroup | wc -l)" -gt 0 ] || [ "$(grep 0::/ /proc/1/cgroup)" = "0::/" ] || + [ "$(grep libpod /proc/1/cgroup | wc -l)" -gt 0 ]; then + + # HOTIO Plex image structure is non-standard (contains symlink which breaks detection) + if [ -d "/app/usr/lib/plexmediaserver" ] && [ -d "/config/Plug-in Support" ]; then + PLEX_SQLITE="/app/usr/lib/plexmediaserver/Plex SQLite" + AppSuppDir="/config" + PID_FILE="$AppSuppDir/plexmediaserver.pid" + DBDIR="$AppSuppDir/Plug-in Support/Databases" + LOGFILE="$DBDIR/DBRepair.log" + LOG_TOOL="logger" + + HostType="HOTIO" + return 0 + + # Docker (All main image variants except binhex and hotio) + elif [ -d "/config/Library/Application Support" ]; then + + PLEX_SQLITE="/usr/lib/plexmediaserver/Plex SQLite" + AppSuppDir="/config/Library/Application Support" + PID_FILE="$AppSuppDir/Plex Media Server/plexmediaserver.pid" + DBDIR="$AppSuppDir/Plex Media Server/Plug-in Support/Databases" + LOGFILE="$DBDIR/DBRepair.log" + LOG_TOOL="logger" + + if [ -d "/var/run/service/svc-plex" ]; then + HaveStartStop=1 + StartCommand="s6-svc -u /var/run/service/svc-plex" + StopCommand="s6-svc -d /var/run/service/svc-plex" + fi + + if [ -d "/var/run/s6/services/plex" ]; then + HaveStartStop=1 + StartCommand="s6-svc -u /var/run/s6/services/plex" + StopCommand="s6-svc -d /var/run/s6/services/plex" + fi + + # lsio stop + if [ -d "/var/run/service/svc-plex" ]; then + HaveStartStop=1 + StartCommand="s6-svc -u /var/run/service/svc-plex" + StopCommand="s6-svc -d /var/run/service/svc-plex" + fi + + # HOTIO + if [ -d /run/service/plex ]; then + HaveStartStop=1 + StartCommand="s6-svc -u /run/service/plex" + StopCommand="s6-svc -d /run/service/plex" + fi + + HostType="Docker" + return 0 + + # BINHEX Plex image + elif [-f /usr/lib/python3.10/binhex.py ] && [ -d "/config/Plex Media Server" ]; then + + PLEX_SQLITE="/usr/lib/plexmediaserver/Plex SQLite" + AppSuppDir="/config" + PID_FILE="$AppSuppDir/Plex Media Server/plexmediaserver.pid" + DBDIR="$AppSuppDir/Plex Media Server/Plug-in Support/Databases" + LOGFILE="$DBDIR/DBRepair.log" + LOG_TOOL="logger" + + HostType="BINHEX" + return 0 + + fi + + + # Western Digital (OS5) + elif [ -f /etc/system.conf ] && [ -d /mnt/HD/HD_a2/Nas_Prog/plexmediaserver ] && \ + grep "Western Digital Corp" /etc/system.conf >/dev/null; then + + # Where things are + PLEX_SQLITE="/mnt/HD/HD_a2/Nas_Prog/plexmediaserver/binaries/Plex SQLite" + AppSuppDir="$(echo /mnt/HD/HD*/Nas_Prog/plex_conf)" + PID_FILE="$AppSuppDir/Plex Media Server/plexmediaserver.pid" + DBDIR="$AppSuppDir/Plex Media Server/Plug-in Support/Databases" + LOGFILE="$DBDIR/DBRepair.log" + LOG_TOOL="logger" + + HostType="Western Digital" + return 0 + + # Apple Mac + elif [ -d "/Applications/Plex Media Server.app" ] && \ + [ -d "$HOME/Library/Application Support/Plex Media Server" ]; then + + # Where is the software + PLEX_SQLITE="/Applications/Plex Media Server.app/Contents/MacOS/Plex SQLite" + AppSuppDir="$HOME/Library/Application Support" + DBDIR="$AppSuppDir/Plex Media Server/Plug-in Support/Databases" + PID_FILE="$DBDIR/dbtmp/plexmediaserver.pid" + LOGFILE="$DBDIR/DBRepair.log" + LOG_TOOL="logger" + + # MacOS uses pgrep and uses different stat options + PIDOF="pgrep" + STATFMT="-f" + STATBYTES="%z" + + # make the TMP directory in advance to store plexmediaserver.pid + mkdir -p "$DBDIR/dbtmp" + + # Remove stale PID file if it exists + [ -f "$PID_FILE" ] && rm "$PID_FILE" + + # If PMS is running create plexmediaserver.pid + PIDVALUE=$($PIDOF "Plex Media Server") + [ $PIDVALUE ] && echo $PIDVALUE > "$PID_FILE" + + HostType="Mac" + return 0 + fi + + # Unknown / currently unsupported host + return 1 +} + +# Simple function to set variables +SetLast() { + LastName="$1" + LastTimestamp="$2" + return 0 +} + +##### INDEX +DoIndex() { + + # Clear flag + Damaged=0 + Fail=0 + # Check databases before Indexing if not previously checked + if ! CheckDatabases "Reindex" ; then + Damaged=1 + CheckedDB=1 + Fail=1 + fi + + + # If damaged, exit + if [ $Damaged -eq 1 ]; then + Output "Databases are damaged. Reindex operation not available. Please repair or replace first." + return + fi + + # Databases are OK, Make a backup + Output "Backing up of databases" + MakeBackups "Reindex" + Result=$? + if [ $Result -eq 0 ]; then + WriteLog "Reindex - MakeBackup - PASS" + else + Output "Error making backups. Cannot continue." + WriteLog "Reindex - MakeBackup - FAIL ($Result)" + Fail=1 + return + fi + + # Databases are OK, Start reindexing + Output "Reindexing main database" + "$PLEX_SQLITE" $CPPL.db 'REINDEX;' + Result=$? + if SQLiteOK $Result; then + Output "Reindexing main database successful." + WriteLog "Reindex - Reindex: $CPPL.db - PASS" + else + Output "Reindexing main database failed. Error code $Result from Plex SQLite" + WriteLog "Reindex - Reindex: $CPPL.db - FAIL ($Result)" + Fail=1 + fi + + Output "Reindexing blobs database" + "$PLEX_SQLITE" $CPPL.blobs.db 'REINDEX;' + Result=$? + if SQLiteOK $Result; then + Output "Reindexing blobs database successful." + WriteLog "Reindex - Reindex: $CPPL.blobs.db - PASS" + else + Output "Reindexing blobs database failed. Error code $Result from Plex SQLite" + WriteLog "Reindex - Reindex: $CPPL.blobs.db - FAIL ($Result)" + Fail=1 + fi + + Output "Reindex complete." + + if [ $Fail -eq 0 ]; then + SetLast "Reindex" "$TimeStamp" + WriteLog "Reindex - PASS" + else + RestoreSaved "$TimeStamp" + WriteLog "Reindex - FAIL" + fi + + return $Fail + +} + +##### UNDO +DoUndo(){ + # Confirm there is something to undo + if [ "$LastTimestamp" != "" ]; then + + # Educate user + echo "" + echo "'Undo' restores the databases to the state prior to the last SUCCESSFUL action." + echo "If any action fails before it completes, that action is automatically undone for you." + echo "Be advised: Undo restores the databases to their state PRIOR TO the last action of 'Vacuum', 'Reindex', or 'Replace'" + echo "WARNING: Once Undo completes, there will be nothing more to Undo untl another successful action is completed" + echo "" + + if ConfirmYesNo "Undo '$LastName' performed at timestamp '$LastTimestamp' ? "; then + + Output "Undoing $LastName ($LastTimestamp)" + for j in "db" "db-wal" "db-shm" "blobs.db" "blobs.db-wal" "blobs.db-shm" + do + [ -e "$TMPDIR/$CPPL.$j-BACKUP-$LastTimestamp" ] && mv -f "$TMPDIR/$CPPL.$j-BACKUP-$LastTimestamp" $CPPL.$j + done + + Output "Undo complete." + WriteLog "Undo - Undo ${LastName}, TimeStamp $LastTimestamp" + SetLast "Undo" "" + fi + + else + Output "Nothing to undo." + WriteLog "Undo - Nothing to Undo." + fi + +} + +##### DoRepair +DoRepair() { + + + Damaged=0 + Fail=0 + + # Verify DBs are here + if [ ! -e $CPPL.db ]; then + Output "No main Plex database exists to repair. Exiting." + WriteLog "Repair - No main database - FAIL" + Fail=1 + return 1 + fi + + # Check size + Size=$(stat $STATFMT $STATBYTES $CPPL.db) + + # Exit if not valid + if [ $Size -lt 300000 ]; then + Output "Main database is too small/truncated, repair is not possible. Please try restoring a backup. " + WriteLog "Repair - Main databse too small - FAIL" + Fail=1 + return 1 + fi + + # Continue + Output "Exporting current databases using timestamp: $TimeStamp" + Fail=0 + + # Get the owning UID/GID before we proceed so we can restore + Owner="$(stat $STATFMT '%u:%g' $CPPL.db)" + + # Attempt to export main db to SQL file (Step 1) + Output "Exporting Main DB" + "$PLEX_SQLITE" $CPPL.db ".output '$TMPDIR/library.plexapp.sql-$TimeStamp'" .dump + Result=$? + if ! SQLiteOK $Result; then + + # Cannot dump file + Output "Error $Result from Plex SQLite while exporting $CPPL.db" + Output "Could not successfully export the main database to repair it. Please try restoring a backup." + WriteLog "Repair - Cannot recover main database to '$TMPDIR/library.plexapp.sql-$TimeStamp' - FAIL ($Result)" + Fail=1 + return 1 + fi + + # Attempt to export blobs db to SQL file + Output "Exporting Blobs DB" + "$PLEX_SQLITE" $CPPL.blobs.db ".output '$TMPDIR/blobs.plexapp.sql-$TimeStamp'" .dump + Result=$? + if ! SQLiteOK $Result; then + + # Cannot dump file + Output "Error $Result from Plex SQLite while exporting $CPPL.blobs.db" + Output "Could not successfully export the blobs database to repair it. Please try restoring a backup." + WriteLog "Repair - Cannot recover blobs database to '$TMPDIR/blobs.plexapp.sql-$TimeStamp' - FAIL ($Result)" + Fail=1 + return 1 + fi + + # Edit the .SQL files if all OK + if [ $Fail -eq 0 ]; then + + # Edit + sed -i -e 's/ROLLBACK;/COMMIT;/' "$TMPDIR/library.plexapp.sql-$TimeStamp" + sed -i -e 's/ROLLBACK;/COMMIT;/' "$TMPDIR/blobs.plexapp.sql-$TimeStamp" + fi + + # Inform user + Output "Successfully exported the main and blobs databases. Proceeding to import into new databases." + WriteLog "Repair - Export databases - PASS" + + # Library and blobs successfully exported, create new + Output "Importing Main DB." + "$PLEX_SQLITE" "$TMPDIR/$CPPL.db-REPAIR-$TimeStamp" < "$TMPDIR/library.plexapp.sql-$TimeStamp" + Result=$? + if ! SQLiteOK $Result; then + Output "Error $Result from Plex SQLite while importing from '$TMPDIR/library.plexapp.sql-$TimeStamp'" + WriteLog "Repair - Cannot import main database from '$TMPDIR/library.plexapp.sql-$TimeStamp' - FAIL ($Result)" + Output "Cannot continue." + Fail=1 + return 1 + fi + + Output "Importing Blobs DB." + "$PLEX_SQLITE" "$TMPDIR/$CPPL.blobs.db-REPAIR-$TimeStamp" < "$TMPDIR/blobs.plexapp.sql-$TimeStamp" + Result=$? + if ! SQLiteOK $Result ; then + Output "Error $Result from Plex SQLite while importing from '$TMPDIR/blobs.plexapp.sql-$TimeStamp'" + WriteLog "Repair - Cannot import blobs database from '$TMPDIR/blobs.plexapp.sql-$TimeStamp' - FAIL ($Result)" + Output "Cannot continue." + Fail=1 + return 1 + fi + + # Made it to here, now verify + Output "Successfully imported SQL data." + WriteLog "Repair - Import - PASS" + + # Verify databases are intact and pass testing + Output "Verifying databases integrity after importing." + + # Check main DB + if CheckDB "$TMPDIR/$CPPL.db-REPAIR-$TimeStamp" ; then + SizeStart=$(GetSize "$CPPL.db") + SizeFinish=$(GetSize "$TMPDIR/$CPPL.db-REPAIR-$TimeStamp") + Output "Verification complete. PMS main database is OK." + WriteLog "Repair - Verify main database - PASS (Size: ${SizeStart}MB/${SizeFinish}MB)." + else + Output "Verification complete. PMS main database import failed." + WriteLog "Repair - Verify main database - FAIL ($SQLerror)" + Fail=1 + fi + + # Check blobs DB + if CheckDB "$TMPDIR/$CPPL.blobs.db-REPAIR-$TimeStamp" ; then + SizeStart=$(GetSize "$CPPL.blobs.db") + SizeFinish=$(GetSize "$TMPDIR/$CPPL.blobs.db-REPAIR-$TimeStamp") + Output "Verification complete. PMS blobs database is OK." + WriteLog "Repair - Verify blobs database - PASS (Size: ${SizeStart}MB/${SizeFinish}MB)." + else + Output "Verification complete. PMS blobs database import failed." + WriteLog "Repair - Verify main database - FAIL ($SQLerror)" + Fail=1 + fi + + # If not failed, move files normally + if [ $Fail -eq 0 ]; then + + Output "Saving current databases with '-BACKUP-$TimeStamp'" + [ -e $CPPL.db ] && mv $CPPL.db "$TMPDIR/$CPPL.db-BACKUP-$TimeStamp" + [ -e $CPPL.blobs.db ] && mv $CPPL.blobs.db "$TMPDIR/$CPPL.blobs.db-BACKUP-$TimeStamp" + + Output "Making repaired databases active" + mv "$TMPDIR/$CPPL.db-REPAIR-$TimeStamp" $CPPL.db + mv "$TMPDIR/$CPPL.blobs.db-REPAIR-$TimeStamp" $CPPL.blobs.db + + Output "Repair complete. Please check your library settings and contents for completeness." + Output "Recommend: Scan Files and Refresh all metadata for each library section." + + # Ensure WAL and SHM are gone + [ -e $CPPL.blobs.db-wal ] && rm -f $CPPL.blobs.db-wal + [ -e $CPPL.blobs.db-shm ] && rm -f $CPPL.blobs.db-shm + [ -e $CPPL.db-wal ] && rm -f $CPPL.db-wal + [ -e $CPPL.db-shm ] && rm -f $CPPL.db-shm + + # Set ownership on new files + chown $Owner $CPPL.db $CPPL.blobs.db + + # We didn't fail, set CheckedDB status true (passed above checks) + CheckedDB=1 + + WriteLog "Repair - Move files - PASS" + WriteLog "Repair - PASS" + + SetLast "Repair" "$TimeStamp" + return 0 + else + + rm -f "$TMPDIR/$CPPL.db-REPAIR-$TimeStamp" + rm -f "$TMPDIR/$CPPL.blobs.db-REPAIR-$TimeStamp" + + Output "Repair has failed. No files changed" + WriteLog "Repair - $TimeStamp - FAIL" + CheckedDB=0 + Retain=1 + return 1 + fi +} + +##### DoReplace +DoReplace() { + + # If Databases already checked, confirm the user really wants to do this + Confirmed=0 + Fail=0 + if CheckDatabases "Replace"; then + if ConfirmYesNo "Are you sure you want to restore a previous database backup"; then + Confirmed=1 + fi + fi + + if [ $Damaged -eq 1 ] || [ $Confirmed -eq 1 ]; then + # Get list of dates to use + Dates="$(GetDates)" + + # If no backups, error and exit + if [ "$Dates" = "" ] && [ $Damaged -eq 1 ]; then + Output "Database is damaged and no backups avaiable." + Output "Only available option is Repair." + WriteLog "Replace - Scan for usable candidates - FAIL" + return 1 + fi + + Output "Checking for a usable backup." + Candidate="" + + # Make certain there is ample free space + if ! FreeSpaceAvailable ; then + Output "ERROR: Insufficient free space available on $AppSuppDir. Cannot continue + WriteLog "REPLACE - Insufficient free space available on $AppSuppDir. Aborted. + return 1 + fi + + Output "Database backups available are: $Dates" + for i in $Dates + do + + # Check candidate + if [ -e $CPPL.db-$i ] && \ + [ -e $CPPL.blobs.db-$i ] && \ + Output "Checking database $i" && \ + CheckDB $CPPL.db-$i && \ + CheckDB $CPPL.blobs.db-$i ; then + + Output "Found valid database backup date: $i" + Candidate=$i + + UseThis=0 + if ConfirmYesNo "Use backup '$Candidate' ?"; then + UseThis=1 + fi + + # OK, use this one + if [ $UseThis -eq 1 ]; then + + # Move database, wal, and shm (keep safe) with timestamp + Output "Saving current databases with timestamp: '-BACKUP-$TimeStamp'" + + for j in "db" "db-wal" "db-shm" "blobs.db" "blobs.db-wal" "blobs.db-shm" + do + [ -e $CPPL.$j ] && mv -f $CPPL.$j "$TMPDIR/$CPPL.$j-BACKUP-$TimeStamp" + done + WriteLog "Replace - Move Files - PASS" + + # Copy this backup into position as primary + Output "Copying backup database $Candidate to use as new database." + + cp -p $CPPL.db-$Candidate $CPPL.db-REPLACE-$TimeStamp + Result=$? + + if [ $Result -ne 0 ]; then + Output "Error $Result while copying $CPPL.db" + Output "Database file is incomplete. Please resolve manually." + WriteLog "Replace - Copy $CPPL.db-$Candidate - FAIL" + Fail=1 + else + WriteLog "Replace - Copy $CPPL.db-$i - PASS" + fi + + cp -p $CPPL.blobs.db-$Candidate $CPPL.blobs.db-REPLACE-$TimeStamp + Result=$? + + if [ $Result -ne 0 ]; then + Output "Error $Result while copying $CPPL.blobs.db" + Output "Database file is incomplete. Please resolve manually." + WriteLog "Replace - Copy $CPPL.blobs.db-$Candidate - FAIL" + Fail=1 + else + WriteLog "Replace - Copy $CPPL.blobs.db-$Candidate - PASS" + fi + + # If no failure copying, check and make active + if [ $Fail -eq 0 ]; then + # Final checks + Output "Copy complete. Performing final check" + + if CheckDB $CPPL.db-REPLACE-$TimeStamp && \ + CheckDB $CPPL.blobs.db-REPLACE-$TimeStamp ; then + + # Move into position as active + mv $CPPL.db-REPLACE-$TimeStamp $CPPL.db + mv $CPPL.blobs.db-REPLACE-$TimeStamp $CPPL.blobs.db + + # done + Output "Database recovery and verification complete." + WriteLog "Replace - Verify databases - PASS" + + else + + # DB did not verify after copy -- Something wrong + + rm -f $CPPL.db-$TimeStamp $CPPL.blobs.db-$TimeStamp + Output "Final check failed. Keeping existing databases" + WriteLog "Replace - Verify databases - FAIL" + WriteLog "Replace - Failed Databses - REMOVED" + fi + else + + Output "Could not copy backup databases. Out of disk space?" + Output "Restoring original databases" + + for k in "db" "db-wal" "db-shm" "blobs.db" "blobs.db-wal" "blobs.db-shm" + do + [ -e "$TMPDIR/$CPPL.$k-BACKUP-$TimeStamp" ] && mv -f "$TMPDIR/$CPPL.$k-BACKUP-$TimeStamp" $CPPL.$k + done + WriteLog "Replace - Verify databases - FAIL" + Fail=1 + fi + + # If successful, save + [ $Fail -eq 0 ] && SetLast "Replace" "$TimeStamp" + break + fi + fi + done + + # Error check if no Candidate found + if [ "$Candidate" = "" ]; then + Output "Error. No valid matching main and blobs database pairs. Cannot replace." + WriteLog "Replace - Select candidate - FAIL" + fi + fi +} + + +##### VACUUM +DoVacuum(){ + + # Clear flags + Fail=0 + Damaged=0 + + # Check databases before Indexing if not previously checked + if ! CheckDatabases "Vacuum " ; then + Damaged=1 + Fail=1 + fi + + # If damaged, exit + if [ $Damaged -eq 1 ]; then + Output "Databases are damaged. Vacuum operation not available. Please repair or replace first." + WriteLog "Vacuum - Databases damaged." + return 1 + fi + + # Make a backup + Output "Backing up databases" + if ! MakeBackups "Vacuum "; then + Output "Error making backups. Cannot continue." + WriteLog "Vacuum - MakeBackups - FAIL" + Fail=1 + return 1 + else + WriteLog "Vacuum - MakeBackups - PASS" + fi + + # Start vacuuming + Output "Vacuuming main database" + SizeStart=$(GetSize $CPPL.db) + + # Vacuum it + "$PLEX_SQLITE" $CPPL.db 'VACUUM;' + Result=$? + + if SQLiteOK $Result; then + SizeFinish=$(GetSize $CPPL.db) + Output "Vacuuming main database successful (Size: ${SizeStart}MB/${SizeFinish}MB)." + WriteLog "Vacuum - Vacuum main database - PASS (Size: ${SizeStart}MB/${SizeFinish}MB)." + else + Output "Vaccuming main database failed. Error code $Result from Plex SQLite" + WriteLog "Vacuum - Vacuum main database - FAIL ($Result)" + Fail=1 + fi + + Output "Vacuuming blobs database" + SizeStart=$(GetSize $CPPL.blobs.db) + + # Vacuum it + "$PLEX_SQLITE" $CPPL.blobs.db 'VACUUM;' + Result=$? + + if SQLiteOK $Result; then + SizeFinish=$(GetSize $CPPL.blobs.db) + Output "Vacuuming blobs database successful (Size: ${SizeStart}MB/${SizeFinish}MB)." + WriteLog "Vacuum - Vacuum blobs database - PASS (Size: ${SizeStart}MB/${SizeFinish}MB)." + else + Output "Vaccuming blobs database failed. Error code $Result from Plex SQLite" + WriteLog "Vacuum - Vacuum blobs database - FAIL ($Result)" + Fail=1 + fi + + if [ $Fail -eq 0 ]; then + Output "Vacuum complete." + WriteLog "Vacuum - PASS" + SetLast "Vacuum" "$TimeStamp" + else + Output "Vacuum failed." + WriteLog "Vacuum - FAIL" + RestoreSaved "$TimeStamp" + fi +} + +##### (import) Viewstate/Watch history from another DB and import +DoImport(){ + + if ! FreeSpaceAvailable; then + Output "Unable to make backups before importing. Insufficient free space available on $AppSuppDir." + WriteLog "Import - Insufficient free disk space for backups." + return 1 + fi + + printf "Pathname of database containing watch history to import: " + read Input + + # Did we get something? + [ "$Input" = "" ] && return 0 + + # Go see if it's a valid database + if [ ! -f "$Input" ]; then + Output "'$Input' does not exist." + return 1 + fi + + Output "" + WriteLog "Import - Attempting to import watch history from '$Input' " + + # Confirm our databases are intact + if ! CheckDatabases "Import "; then + Output "Error: PMS databases are damaged. Repair needed. Refusing to import." + WriteLog "Import - Verify main database - FAIL" + return 1 + fi + + # Check the given database + Output "Checking database '$Input'" + if ! CheckDB "$Input"; then + Output "Error: Given database '$Input' is damaged. Repair needed. Database not trusted. Refusing to import." + WriteLog "Import - Verify '$Input' - FAIL" + return 1 + fi + WriteLog "Import - Verify '$Input' - PASS" + Output "Check complete. '$Input' is OK." + + + # Make a backup + Output "Backing up PMS databases" + if ! MakeBackups "Import "; then + Output "Error making backups. Cannot continue." + WriteLog "Import - MakeBackups - FAIL" + Fail=1 + return 1 + fi + WriteLog "Import - MakeBackups - PASS" + + + # Export viewstate from DB + Output "Exporting Viewstate & Watch history" + echo ".dump metadata_item_settings metadata_item_views " | "$PLEX_SQLITE" "$Input" | grep -v TABLE | grep -v INDEX > "$TMPDIR/Viewstate.sql-$TimeStamp" + + # Make certain we got something usable + if [ $(wc -l "$TMPDIR/Viewstate.sql-$TimeStamp" | awk '{print $1}') -lt 1 ]; then + Output "No viewstates or history found to import." + WriteLog "Import - Nothing to import - FAIL" + return 1 + fi + + # Make a working copy to import into + Output "Preparing to import Viewstate and History data" + cp -p $CPPL.db "$TMPDIR/$CPPL.db-IMPORT-$TimeStamp" + Result=$? + + if [ $Result -ne 0 ]; then + Output "Error $Result while making a working copy of the PMS main database." + Output " File permissions? Disk full?" + WriteLog "Import - Prepare: Make working copy - FAIL" + return 1 + fi + + # Import viewstates into working copy (Ignore constraint errors during import) + printf 'Importing Viewstate & History data...' + "$PLEX_SQLITE" "$TMPDIR/$CPPL.db-IMPORT-$TimeStamp" < "$TMPDIR/Viewstate.sql-$TimeStamp" 2> /dev/null + +# # Purge duplicates (violations of unique constraint) +# cat < 1); +#EOF + + # Make certain the resultant DB is OK + Output " done." + Output "Checking database following import" + + if ! CheckDB "$TMPDIR/$CPPL.db-IMPORT-$TimeStamp" ; then + + # Import failed discard + Output "Error: Error code $Result during import. Import corrupted database." + Output " Discarding import attempt." + + rm -f "$TMPDIR/$CPPL.db-IMPORT-$TimeStamp" + + WriteLog "Import - Import: $Input - FAIL" + return 1 + fi + + # Import successful; switch to new DB + Output "PMS main database is OK. Making imported database active" + WriteLog "Import - Import: Making imported database active" + + # Move from tmp to active + mv "$TMPDIR/$CPPL.db-IMPORT-$TimeStamp" $CPPL.db + + # We were successful + Output "Viewstate import successful." + WriteLog "Import - Import: $Input - PASS" + + # We were successful + SetLast "Import" "$TimeStamp" + return 0 +} + +##### IsRunning (True if PMS is running) +IsRunning(){ + [ "$($PIDOF 'Plex Media Server')" != "" ] && return 0 + return 1 +} + +##### DoStart (Start PMS if able) +DoStart(){ + + if [ $HaveStartStop -eq 0 ]; then + Output "Start/Stop feature not available" + WriteLog "Start/Stop feature not available" + return 1 + else + + # Check if PMS running + if IsRunning; then + WriteLog "Start - PASS - PMS already runnning" + Output "Start not needed. PMS is running." + return 0 + fi + + Output "Starting PMS." + $StartCommand > /dev/null 2> /dev/null + Result=$? + + if [ $Result -eq 0 ]; then + WriteLog "Start - PASS" + Output "Started PMS" + else + WriteLog "Start - FAIL ($Result)" + Output "Could not start PMS. Error code: $Result" + fi + fi + return $Result +} + +##### DoStop (Stop PMS if able) +DoStop(){ + if [ $HaveStartStop -eq 0 ]; then + Output "Start/Stop feature not available" + WriteLog "Start/Stop feature not available" + return 1 + else + + Output "Stopping PMS." + $StopCommand > /dev/null 2> /dev/null + Result=$? + Count=10 + while [ $Result -eq 0 ] && IsRunning && [ $Count -gt 0 ] + do + sleep 1 + Count=$((Count - 1)) + done + + if [ $Result -eq 0 ]; then + WriteLog "Stop - PASS" + Output "Stopped PMS." + else + WriteLog "Stop - FAIL ($Result)" + Output "Could not stop PMS. Error code: $Result" + fi + fi + return $Result +} + +##### UpdateTimestamp +DoUpdateTimestamp() { + TimeStamp="$(date "+%Y-%m-%d_%H.%M.%S")" +} + +############################################################# +# Main utility begins here # +############################################################# + +# Initialize LastName LastTimestamp +SetLast "" "" + +# Are we scripted (command line args) +Scripted=0 +[ "$1" != "" ] && Scripted=1 + +# Identify this host +if ! HostConfig; then + Output 'Error: Unknown host. Current supported hosts are: QNAP, Syno, Netgear, Mac, ASUSTOR, WD (OS5), Linux wkstn/svr' + Output ' Current supported container images: Plexinc, LinuxServer, HotIO, & BINHEX' + exit 1 +fi + +# We might not be root but minimally make sure we have write access +if [ ! -w "$DBDIR" ]; then + echo ERROR: Cannot write to Databases directory. Insufficient privilege. + exit 2 +fi + +echo " " +# echo Detected Host: $HostType +WriteLog "============================================================" +WriteLog "Session start: Host is $HostType" + +# Make sure we have a logfile +touch "$LOGFILE" + +# Basic checks; PMS installed +if [ ! -f "$PLEX_SQLITE" ] ; then + Output "PMS is not installed. Cannot continue. Exiting." + WriteLog "PMS not installed." + exit 1 +fi + +# Set tmp dir so we don't use RAM when in DBDIR +DBTMP="./dbtmp" +mkdir -p "$DBDIR/$DBTMP" +export TMPDIR="$DBTMP" +export TMP="$DBTMP" + + +# If command line args then set flag +Scripted=0 +[ "$1" != "" ] && Scripted=1 + +# Can I write to the Databases directory ? +if [ ! -w "$DBDIR" ]; then + Output "ERROR: Cannot write to the Databases directory. Insufficient privilege or wrong UID. Exiting." + exit 1 +fi + +# Databases exist or Backups exist to restore from +if [ ! -f "$DBDIR/$CPPL.db" ] && \ + [ ! -f "$DBDIR/$CPPL.blobs.db" ] && \ + [ "$(echo com.plexapp.plugins.*-????-??-??)" = "com.plexapp.plugins.*-????-??-??" ]; then + + Output "Cannot locate databases. Cannot continue. Exiting." + WriteLog "Databases or backups not found." + exit 1 +fi + +# Work in the Databases directory +cd "$DBDIR" + +# Get the owning UID/GID before we proceed so we can restore +Owner="$(stat $STATFMT '%u:%g' $CPPL.db)" + +# Sanity check, We are either owner of the DB or root +if [ ! -w $CPPL.db ]; then + + Output "Do not have write permission to the Databases. Exiting." + WriteLog "No write permission to databases+. Exit." + exit 1 +fi + +# Run entire utility in a loop until all arguments used, EOF on input, or commanded to exit +while true +do + + echo " " + echo " " + echo " Plex Media Server Database Repair Utility ($HostType)" + echo " Version $Version" + echo " " + + + Choice=0; Exit=0; NullCommands=0 + + # Main menu loop + while [ $Choice -eq 0 ] + do + if [ $ShowMenu -eq 1 ] && [ $Scripted -eq 0 ]; then + + echo "" + echo "Select" + echo "" + [ $HaveStartStop -gt 0 ] && echo " 1 - 'stop' - Stop PMS" + [ $HaveStartStop -eq 0 ] && echo " 1 - 'stop' - (Not available. Stop manually)" + echo " 2 - 'automatic' - database check, repair/optimize, and reindex in one step." + echo " 3 - 'check' - Perform integrity check of database" + echo " 4 - 'vacuum' - Remove empty space from database" + echo " 5 - 'repair' - Repair/Optimize databases" + echo " 6 - 'reindex' - Rebuild database database indexes" + + [ $HaveStartStop -gt 0 ] && echo " 7 - 'start' - Start PMS" + [ $HaveStartStop -eq 0 ] && echo " 7 - 'start' - (Not available. Start manually)" + echo "" + echo " 8 - 'import' - Import watch history from another database independent of Plex. (risky)" + echo " 9 - 'replace' - Replace current databases with newest usable backup copy (interactive)" + echo " 10 - 'show' - Show logfile" + echo " 11 - 'status' - Report status of PMS (run-state and databases)" + echo " 12 - 'undo' - Undo last successful command" + echo "" + + echo " 99 - exit" + fi + if [ $Scripted -eq 0 ]; then + echo "" + printf "Enter command # -or- command name (4 char min) : " + fi + + # Watch for null command whether scripted or not. + if [ "$1" != "" ]; then + Input="$1" + # echo "$1" + shift + else + [ $Scripted -eq 0 ] && read Input + + # Handle EOF/forced exit + if [ "$Input" = "" ] ; then + if [ $NullCommands -gt 4 ]; then + Output "Unexpected EOF / End of command line options, Exiting" + Input="exit" && Exit=1 + else + NullCommands=$(($NullCommands + 1)) + [ $NullCommands -eq 4 ] && echo "WARNING: Next empty command exits as EOF. " + continue + fi + else + NullCommands=0 + fi + fi + + # Update timestamp + DoUpdateTimestamp + + # Validate command input + Command="$(echo $Input | tr '[A-Z]' '[a-z]' | awk '{print $1}')" + echo " " + + case "$Command" in + + # Stop PMS (if available this host) + 1|stop) + + DoStop + ;; + + + # Automatic of all common operations + 2|auto*) + + # Get current status + RunState=0 + + # Check if PMS running + if IsRunning; then + RunState=1 + WriteLog "Auto - FAIL - PMS runnning" + Output "Unable to run automatic sequence. PMS is running. Please stop PlexMediaServer." + continue + fi + + # Is there enough room to work + if ! FreeSpaceAvailable; then + WriteLog "Auto - FAIL - Insufficient free space on $AppSuppDir" + Output "Error: Unable to run automatic sequence. Insufficient free space available on $AppSuppDir" + Output " Space needed = $SpaceNeeded MB, Space available = $SpaveAvailable MB" + continue + fi + + # Start auto + Output "Automatic Check,Repair,Index started." + WriteLog "Auto - START" + + # Check the databases (forced) + Output "" + if CheckDatabases "Check " force ; then + WriteLog "Check - PASS" + CheckedDB=1 + else + WriteLog "Check - FAIL" + CheckedDB=0 + fi + + # Now Repair + Output "" + if ! DoRepair; then + + WriteLog "Repair - FAIL" + WriteLog "Auto - FAIL" + CheckedDB=0 + + Output "Repair failed. Automatic mode cannot continue. Please repair with individual commands" + continue + else + WriteLog "Repair - PASS" + CheckedDB=1 + fi + + # Now Index + DoUpdateTimestamp + Output "" + if ! DoIndex; then + WriteLog "Index - FAIL" + WriteLog "Auto - FAIL" + CheckedDB=0 + + Output "Index failed. Automatic mode cannot continue. Please repair with individual commands" + continue + else + WriteLog "Reindex - PASS" + fi + + # All good to here + WriteLog "Auto - COMPLETED" + Output "Automatic Check, Repair/optimize, & Index successful." + ;; + + + # Check databases + 3|chec*) + + # Check if PMS running + if IsRunning; then + WriteLog "Check - FAIL - PMS runnning" + Output "Unable to check databases. PMS is running." + continue + fi + + # CHECK DBs + if CheckDatabases "Check " force ; then + WriteLog "Check - PASS" + CheckedDB=1 + else + WriteLog "Check - FAIL" + CheckedDB=0 + fi + ;; + + + # Vacuum + 4|vacu*) + + # Check if PMS running + if IsRunning; then + WriteLog "Vacuum - FAIL - PMS runnning" + Output "Unable to vacuum databases. PMS is running." + continue + fi + + DoVacuum + continue + ;; + + # Repair (Same as optimize but assumes damaged so doesn't check) + 5|repa*) + + # Check if PMS running + if IsRunning; then + WriteLog "Repair - FAIL - PMS runnning" + Output "Unable to repair databases. PMS is running." + continue + fi + + # Is there enough room to work + if ! FreeSpaceAvailable; then + WriteLog "Import - FAIL - Insufficient free space on $AppSuppDir" + Output "Error: Unable to repair database. Insufficient free space available on $AppSuppDir" + continue + fi + + + DoRepair + ;; + + + # Index databases + 6|rein*|inde*) + + # Check if PMS running + if IsRunning; then + WriteLog "Index - FAIL - PMS runnning" + Output "Unable to index databases. PMS is running." + continue + fi + + # Is there enough room to work + if ! FreeSpaceAvailable; then + WriteLog "Index - FAIL - Insufficient free space on $AppSuppDir" + Output "Error: Unable to perform processing. Insufficient free space available on $AppSuppDir" + continue + fi + + # First check the databases + if CheckDatabases Check; then + WriteLog "Check - PASS" + CheckedDB=1 + + # Now index + if DoIndex ; then + WriteLog "Reindex - PASS" + else + WriteLog "Reindex - FAIL" + fi + else + WriteLog "Check - FAIL" + CheckedDB=0 + fi + ;; + + + # Start PMS (if available this host) + 7|star*) + + DoStart + ;; + + + # Menu on/off control + menu*) + + # Choices are ON,OFF,YES,NO + Option="$(echo $Input | tr '[A-Z]' '[a-z]' | awk '{print $2}')" + + [ "$Option" = "on" ] && ShowMenu=1 + [ "$Option" = "yes" ] && ShowMenu=1 + [ "$Option" = "off" ] && ShowMenu=0 && echo Menu off: Reenable with \'menu on\' command + [ "$Option" = "no" ] && ShowMenu=0 && echo menu off: Reenable with \'menu on\' command + ;; + + + # Import watch history + 8|impo*) + + DoImport + ;; + + + # Replace (from PMS backup) + 9|repl*) + + # Check if PMS running + if IsRunning; then + WriteLog "Replace - FAIL - PMS runnning" + Output "Unable to replace database from a backup copy. PMS is running." + continue + fi + + # Is there enough room to work + if ! FreeSpaceAvailable; then + WriteLog "Replace - FAIL - Insufficient free space on $AppSuppDir" + Output "Error: Unable to replace from backups. Insufficient free space available on $AppSuppDir" + continue + fi + + DoReplace + ;; + + + # Show loggfile + 10|show*) + + echo ================================================================================== + cat "$LOGFILE" + echo ================================================================================== + ;; + + + # Current status of Plex and databases + 11|stat*) + + Output "" + Output "Status report: $(date)" + if IsRunning ; then + Output " PMS is running." + else + Output " PMS is stopped." + fi + + [ $CheckedDB -eq 0 ] && Output " Databases are not checked, Status unknown." + [ $CheckedDB -eq 1 ] && [ $Damaged -eq 0 ] && Output " Databases are OK." + [ $CheckedDB -eq 1 ] && [ $Damaged -eq 1 ] && Output " Databases were checked and are damaged." + Output "" + ;; + + + # Undo + 12|undo*) + + DoUndo + ;; + + + # Quit/Exit + 99|exit|quit) + + # if cmd line mode, exit clean + if [ $Scripted -eq 1 ]; then + rm -rf $TMPDIR + WriteLog "Exit - Delete temp files." + + else + # Ask questions on interactive exit + if [ $Exit -eq 0 ]; then + # Ask if the user wants to remove the DBTMP directory and all backups thus far + if [ "$Input" = "exit" ] && ConfirmYesNo "Ok to remove temporary databases/workfiles for this session?" ; then + # There it goes + Output "Deleting all temporary work files." + WriteLog "Exit - Delete temp files." + rm -rf "$TMPDIR" + else + Output "Retaining all temporary work files." + WriteLog "Exit - Retain temp files." + fi + else + Output "Unexpected exit command. Keeping all temporary work files." + WriteLog "EOFExit - Retain temp files." + fi + fi + + WriteLog "Session end. $(date)" + WriteLog "============================================================" + exit 0 + ;; + + # Unknown command + *) + WriteLog "Unknown command: '$Input'" + Output "ERROR: Unknown command: '$Input'" + ;; + + esac + done +done +exit 0 diff --git a/plex-token.sh b/plex-token.sh index a9bf588..d4a5ac0 100644 --- a/plex-token.sh +++ b/plex-token.sh @@ -1 +1,5 @@ #!/bin/bash + +# ChuckPa already wrote a great function for getting host info +source ./DBRepair.sh 99 +echo "$AppSuppDir" diff --git a/plex-tools.sh b/plex-tools.sh index 1e3ceb0..668a359 100644 --- a/plex-tools.sh +++ b/plex-tools.sh @@ -2,11 +2,14 @@ echo "Welcome to plex-tools wrapper utility!" -is_command() { - local check_command="$1" - command -v "${check_command}" >/dev/null 2>&1 +script_delegation () +{ + if [ -f "$1" ] ; then + if [ "$1" == "token" ] + then + source ./plex-token.sh + fi + else + echo "'$1' is not a valid plex-tools command" + fi } - -if ! is_command plex-tools ; then - echo "plex-tools command could not be found" -fi