mirror of
https://github.com/ChuckPa/PlexDBRepair.git
synced 2025-11-06 03:08:55 -05:00
2404 lines
69 KiB
Bash
Executable File
2404 lines
69 KiB
Bash
Executable File
#!/bin/sh
|
|
#########################################################################
|
|
# Database Repair Utility for Plex Media Server. #
|
|
# Maintainer: ChuckPa #
|
|
# Version: v1.11.00 #
|
|
# Date: 23-May-2025 #
|
|
#########################################################################
|
|
|
|
# Version for display purposes
|
|
Version="v1.11.00"
|
|
|
|
# Have the databases passed integrity checks
|
|
CheckedDB=0
|
|
|
|
# By default, we cannot start/stop PMS
|
|
HaveStartStop=0
|
|
StartCommand=""
|
|
StopCommand=""
|
|
|
|
# By default, require root privilege
|
|
RootRequired=1
|
|
|
|
# By default, Errors are fatal.
|
|
IgnoreErrors=0
|
|
|
|
# By default, Duplicate view states not Removed
|
|
RemoveDuplicates=0
|
|
|
|
# Keep track of how many times the user's hit enter with no command (implied EOF)
|
|
NullCommands=0
|
|
|
|
# Default TMP dir for most hosts
|
|
TMPDIR="/tmp"
|
|
SYSTMP="/tmp"
|
|
|
|
# Global variable - main database
|
|
CPPL=com.plexapp.plugins.library
|
|
|
|
# Initial timestamp
|
|
TimeStamp="$(date "+%Y-%m-%d_%H.%M.%S")"
|
|
|
|
# Initialize global runtime variables
|
|
ManualConfig=0
|
|
CheckedDB=0
|
|
Damaged=0
|
|
DbPageSize=0
|
|
Fail=0
|
|
HaveStartStop=0
|
|
HostType=""
|
|
LOG_TOOL="echo"
|
|
ShowMenu=1
|
|
Exit=0
|
|
Scripted=0
|
|
HaveStartStop=0
|
|
|
|
# On all hosts except Mac
|
|
PIDOF="pidof"
|
|
STATFMT="-c"
|
|
STATBYTES="%s"
|
|
STATPERMS="%a"
|
|
|
|
# On all hosts except QNAP
|
|
DFFLAGS="-m"
|
|
|
|
# If LC_ALL is null, default to C
|
|
[ "$LC_ALL" = "" ] && export LC_ALL=C
|
|
|
|
# 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 $DFFLAGS "$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=$(($SpaceNeeded * $Multiplier))
|
|
SpaceNeeded=$(($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" != "Y" ] && [ "$Answer" != "N" ]
|
|
do
|
|
printf "%s (Y/N) ? " "$1"
|
|
read Input
|
|
|
|
# EOF = No
|
|
case "$Input" in
|
|
YES|YE|Y|yes|ye|y)
|
|
Answer=Y
|
|
;;
|
|
NO|N|no|n)
|
|
Answer=N
|
|
;;
|
|
*)
|
|
Answer=""
|
|
;;
|
|
esac
|
|
|
|
# Unrecognized
|
|
if [ "$Answer" != "Y" ] && [ "$Answer" != "N" ]; then
|
|
echo \"$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
|
|
}
|
|
|
|
# Return only the digits in the given version string
|
|
VersionDigits() {
|
|
local ver
|
|
ver=$(echo "$1" | tr -d [v\.] )
|
|
echo $ver
|
|
}
|
|
|
|
# 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
|
|
}
|
|
|
|
# Extract specified value from override file if it exists (Null if not)
|
|
GetOverride() {
|
|
|
|
Retval=""
|
|
|
|
# Don't know if we have pushd so do it long hand
|
|
CurrDir="$(pwd)"
|
|
|
|
# Find the metadata dir if customized
|
|
if [ -e /etc/systemd/system/plexmediaserver.service.d ]; then
|
|
|
|
# Get there
|
|
cd /etc/systemd/system/plexmediaserver.service.d
|
|
|
|
# Glob up all 'conf files' found
|
|
ConfFile="$(find override.conf local.conf *.conf 2>/dev/null | head -1)"
|
|
|
|
# If there is one, search it
|
|
if [ "$ConfFile" != "" ]; then
|
|
Retval="$(grep "$1" $ConfFile | head -1 | sed -e "s/.*${1}=//" | tr -d \" | tr -d \')"
|
|
fi
|
|
|
|
fi
|
|
|
|
# Go back to where we were
|
|
cd "$CurrDir"
|
|
|
|
# What did we find
|
|
echo "$Retval"
|
|
}
|
|
|
|
# Determine which host we are running on and set variables
|
|
HostConfig() {
|
|
|
|
# On all hosts except Mac
|
|
PIDOF="pidof"
|
|
STATFMT="-c"
|
|
STATBYTES="%s"
|
|
STATPERMS="%a"
|
|
|
|
# On all hosts except QNAP
|
|
DFFLAGS="-m"
|
|
|
|
# Manual Config
|
|
if [ $ManualConfig -eq 1 ]; then
|
|
|
|
CACHEDIR="$DBDIR/../../Cache/PhotoTranscoder"
|
|
LOGFILE="$DBDIR/DBRepair.log"
|
|
HostType="MANUAL"
|
|
return 0
|
|
fi
|
|
|
|
# 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"
|
|
CACHEDIR="$AppSuppDir/Plex Media Server/Cache/PhotoTranscoder"
|
|
PID_FILE="$AppSuppDir/Plex Media Server/plexmediaserver.pid"
|
|
LOGFILE="$DBDIR/DBRepair.log"
|
|
TMPDIR="$AppSuppDir/Plex Media Server/tmp"
|
|
SYSTMP="$TMPDIR"
|
|
|
|
# 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"
|
|
CACHEDIR="$AppSuppDir/Plex Media Server/Cache/PhotoTranscoder"
|
|
PID_FILE="$AppSuppDir/Plex Media Server/plexmediaserver.pid"
|
|
LOGFILE="$DBDIR/DBRepair.log"
|
|
TMPDIR="$AppSuppDir/Plex Media Server/tmp"
|
|
SYSTMP="$TMPDIR"
|
|
|
|
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"
|
|
CACHEDIR="$AppSuppDir/Plex Media Server/Cache/PhotoTranscoder"
|
|
PID_FILE="$AppSuppDir/Plex Media Server/plexmediaserver.pid"
|
|
LOGFILE="$DBDIR/DBRepair.log"
|
|
TMPDIR="$AppSuppDir/tmp"
|
|
SYSTMP="$TMPDIR"
|
|
|
|
# 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
|
|
|
|
# Use custom DFFLAGS (force POSIX mode)
|
|
DFFLAGS="-Pm"
|
|
|
|
HostType="QNAP"
|
|
return 0
|
|
|
|
# SNAP host (check before standard)
|
|
elif [ -d "/var/snap/plexmediaserver/common/Library/Application Support/Plex Media Server" ]; then
|
|
|
|
# Where things are
|
|
PLEX_SQLITE="/snap/plexmediaserver/current/Plex SQLite"
|
|
AppSuppDir="/var/snap/plexmediaserver/common/Library/Application Support"
|
|
CACHEDIR="$AppSuppDir/Plex Media Server/Cache/PhotoTranscoder"
|
|
PID_FILE="$AppSuppDir/plexmediaserver.pid"
|
|
DBDIR="$AppSuppDir/Plex Media Server/Plug-in Support/Databases"
|
|
LOGFILE="$DBDIR/DBRepair.log"
|
|
LOG_TOOL="logger"
|
|
TMPDIR="/var/snap/plexmediaserver/common/tmp"
|
|
SYSTMP="$TMPDIR"
|
|
|
|
HaveStartStop=1
|
|
StartCommand="snap start plexmediaserver"
|
|
StopCommand="snap stop plexmediaserver"
|
|
|
|
HostType="SNAP"
|
|
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
|
|
|
|
# Get custom AppSuppDir if specified
|
|
NewSuppDir="$(GetOverride PLEX_MEDIA_SERVER_APPLICATION_SUPPORT_DIR)"
|
|
|
|
if [ -d "$NewSuppDir" ]; then
|
|
AppSuppDir="$NewSuppDir"
|
|
else
|
|
Output "Given application support directory override specified does not exist: '$NewSuppDir'. Ignoring."
|
|
fi
|
|
fi
|
|
|
|
DBDIR="$AppSuppDir/Plex Media Server/Plug-in Support/Databases"
|
|
CACHEDIR="$AppSuppDir/Plex Media Server/Cache/PhotoTranscoder"
|
|
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"
|
|
TMPDIR="/tmp"
|
|
SYSTMP="$TMPDIR"
|
|
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"
|
|
CACHEDIR="$AppSuppDir/Plex Media Server/Cache/PhotoTranscoder"
|
|
LOGFILE="$DBDIR/DBRepair.log"
|
|
LOG_TOOL="logger"
|
|
TMPDIR="$PKGDIR/temp"
|
|
SYSTMP="$TMPDIR"
|
|
|
|
HaveStartStop=1
|
|
StartCommand="systemctl start fvapp-plexmediaserver"
|
|
StopCommand="systemctl stop fvapp-plexmediaserver"
|
|
|
|
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"
|
|
CACHEDIR="$AppSuppDir/Plex Media Server/Cache/PhotoTranscoder"
|
|
LOGFILE="$DBDIR/DBRepair.log"
|
|
LOG_TOOL="logger"
|
|
TMPDIR="/tmp"
|
|
SYSTMP="$TMPDIR"
|
|
|
|
HostType="ASUSTOR"
|
|
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"
|
|
CACHEDIR="$HOME/Library/Caches/PlexMediaServer/PhotoTranscoder"
|
|
LOGFILE="$DBDIR/DBRepair.log"
|
|
LOG_TOOL="logger"
|
|
TMPDIR="/tmp"
|
|
SYSTMP="$TMPDIR"
|
|
|
|
# MacOS uses pgrep and uses different stat options
|
|
PIDOF="pgrep"
|
|
STATFMT="-f"
|
|
STATBYTES="%z"
|
|
STATPERMS="%A"
|
|
|
|
# Root not required on MacOS. PMS runs as username.
|
|
RootRequired=0
|
|
|
|
# You can set haptic to 0 for silence.
|
|
HaveStartStop=1
|
|
StartCommand=startMacPMS
|
|
StopCommand=stopMacPMS
|
|
HapticOkay=1
|
|
|
|
HostType="Mac"
|
|
return 0
|
|
|
|
# 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"
|
|
CACHEDIR="$AppSuppDir/Plex Media Server/Cache/PhotoTranscoder"
|
|
LOGFILE="$DBDIR/DBRepair.log"
|
|
LOG_TOOL="logger"
|
|
TMPDIR="$(dirname $AppSuppDir)/plexmediaserver/tmp_transcoding"
|
|
SYSTMP="$TMPDIR"
|
|
HostType="Western Digital"
|
|
return 0
|
|
|
|
|
|
# - Docker cgroup v1 & v2
|
|
# - Podman (libpod)
|
|
# - Kubernetes (and TrueNAS platforms)
|
|
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 ] || [ "$(grep kube /proc/1/cgroup | wc -l)" -gt 0 ]; then
|
|
|
|
TMPDIR="/tmp"
|
|
SYSTMP="/tmp"
|
|
|
|
# HOTIO Plex image structure is non-standard (contains symlink which breaks detection)
|
|
if [ -n "$(grep -irslm 1 hotio /etc/s6-overlay/s6-rc.d)" ]; then
|
|
PLEX_SQLITE=$(find /app/bin/usr/lib/plexmediaserver /app/usr/lib/plexmediaserver /usr/lib/plexmediaserver -maxdepth 0 -type d -print -quit 2>/dev/null); PLEX_SQLITE="$PLEX_SQLITE/Plex SQLite"
|
|
AppSuppDir="/config"
|
|
PID_FILE="$AppSuppDir/plexmediaserver.pid"
|
|
DBDIR="$AppSuppDir/Plug-in Support/Databases"
|
|
CACHEDIR="$AppSuppDir/Plex Media Server/Cache/PhotoTranscoder"
|
|
LOGFILE="$DBDIR/DBRepair.log"
|
|
LOG_TOOL="logger"
|
|
|
|
if [ -d "/run/service/plex" ] || [ -d "/run/service/service-plex" ]; then
|
|
SERVICE_PATH=$([ -d "/run/service/plex" ] && echo "/run/service/plex" || [ -d "/run/service/service-plex" ] && echo "/run/service/service-plex")
|
|
HaveStartStop=1
|
|
StartCommand="s6-svc -u $SERVICE_PATH"
|
|
StopCommand="s6-svc -d $SERVICE_PATH"
|
|
fi
|
|
|
|
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"
|
|
CACHEDIR="$AppSuppDir/Plex Media Server/Cache/PhotoTranscoder"
|
|
|
|
LOGFILE="$DBDIR/DBRepair.log"
|
|
LOG_TOOL="logger"
|
|
|
|
# Miscellaneous start/stop methods
|
|
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 "/run/service/svc-plex" ]; then
|
|
HaveStartStop=1
|
|
StartCommand="s6-svc -u /run/service/svc-plex"
|
|
StopCommand="s6-svc -d /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
|
|
|
|
HostType="Docker"
|
|
return 0
|
|
|
|
# BINHEX Plex image
|
|
elif [ -e /etc/os-release ] && grep "IMAGE_ID=archlinux" /etc/os-release 1>/dev/null && \
|
|
[ -e /home/nobody/start.sh ] && grep PLEX_MEDIA /home/nobody/start.sh 1> /dev/null ; 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"
|
|
CACHEDIR="$AppSuppDir/Plex Media Server/Cache/PhotoTranscoder"
|
|
LOGFILE="$DBDIR/DBRepair.log"
|
|
LOG_TOOL="logger"
|
|
|
|
HostType="BINHEX"
|
|
return 0
|
|
|
|
fi
|
|
|
|
# Last chance to identify this host
|
|
elif [ -e /etc/os-release ]; then
|
|
|
|
# Arch Linux (must check for native Arch after binhex)
|
|
if [ "$(grep -E '=arch|="arch"' /etc/os-release)" != "" ] && \
|
|
[ -d /usr/lib/plexmediaserver ] && \
|
|
[ -d /var/lib/plex ]; then
|
|
|
|
# Where is the software
|
|
PKGDIR="/usr/lib/plexmediaserver"
|
|
PLEX_SQLITE="$PKGDIR/Plex SQLite"
|
|
LOG_TOOL="logger"
|
|
|
|
# Where is the data
|
|
AppSuppDir="/var/lib/plex"
|
|
|
|
# Find the metadata dir if customized
|
|
if [ -e /etc/systemd/system/plexmediaserver.service.d ]; then
|
|
|
|
# Get custom AppSuppDir if specified
|
|
NewSuppDir="$(GetOverride PLEX_MEDIA_SERVER_APPLICATION_SUPPORT_DIR)"
|
|
|
|
if [ "$NewSuppDir" != "" ]; then
|
|
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"
|
|
CACHEDIR="$AppSuppDir/Plex Media Server/Cache/PhotoTranscoder"
|
|
LOGFILE="$DBDIR/DBRepair.log"
|
|
LOG_TOOL="logger"
|
|
TMPDIR="/tmp"
|
|
SYSTMP="/tmp"
|
|
HostType="$(grep PRETTY_NAME /etc/os-release | sed -e 's/^.*="//' | tr -d \" )"
|
|
|
|
HaveStartStop=1
|
|
StartCommand="systemctl start plexmediaserver"
|
|
StopCommand="systemctl stop plexmediaserver"
|
|
return 0
|
|
fi
|
|
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
|
|
[ $IgnoreErrors -eq 1 ] && Fail=0
|
|
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=$?
|
|
[ $IgnoreErrors -eq 1 ] && Result=0
|
|
|
|
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=$?
|
|
[ $IgnoreErrors -eq 1 ] && Result=0
|
|
|
|
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=$?
|
|
[ $IgnoreErrors -eq 1 ] && Result=0
|
|
|
|
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
|
|
|
|
}
|
|
|
|
##### DoSetPageSize
|
|
DoSetPageSize() {
|
|
|
|
# If DBREPAIR_PAGESIZE variable exists, validate it.
|
|
[ "$DBREPAIR_PAGESIZE" = "" ] && return
|
|
|
|
# Is it a valid positive integer ?
|
|
if [ "$DBREPAIR_PAGESIZE" != "$(echo "$DBREPAIR_PAGESIZE" | sed 's/[^0-9]*//g')" ]; then
|
|
WriteLog "SetPageSize - ERROR: DBREPAIR_PAGESIZE is not a valid integer. Ignoring '$DBREPAIR_PAGESIZE'"
|
|
Output "ERROR: DBREPAIR_PAGESIZE is not a valid integer. Ignoring '$DBREPAIR_PAGESIZE'"
|
|
return
|
|
fi
|
|
|
|
# Make certain it's a multiple of 1024 and gt 0
|
|
DbPageSize=$DBREPAIR_PAGESIZE
|
|
[ $DbPageSize -le 0 ] && return
|
|
|
|
if [ $(expr $DbPageSize % 1024) -ne 0 ]; then
|
|
|
|
DbPageSize=$(expr $DBREPAIR_PAGESIZE + 1023)
|
|
DbPageSize=$(expr $DbPageSize / 1024)
|
|
DbPageSize=$(expr $DbPageSize \* 1024)
|
|
|
|
WriteLog "DoSetPageSize - ERROR: DBREPAIR_PAGESIZE ($DBREPAIR_PAGESIZE) not a multiple of 1024. New value = $DbPageSize."
|
|
Output "WARNING: DBREPAIR_PAGESIZE ($DBREPAIR_PAGESIZE) not a multiple of 1024. New value = $DbPageSize."
|
|
fi
|
|
|
|
# Must be compliant
|
|
if [ $DbPageSize -gt 65536 ]; then
|
|
Output "WARNING: DBREPAIR_PAGESIZE ($DbPageSize) too large. Reducing to 65536."
|
|
WriteLog "SetPageSize - DBREPAIR_PAGESIZE ($DbPageSize) too large. Reducing."
|
|
DbPageSize=65536
|
|
fi
|
|
|
|
# Confirm a valid power of two.
|
|
IsPowTwo=0
|
|
for i in 1024 2048 4096 8192 16384 32768 65536
|
|
do
|
|
[ $i -eq $DbPageSize ] && IsPowTwo=1 && break
|
|
done
|
|
|
|
if [ $IsPowTwo -eq 0 ] && [ $DbPageSize -lt 65536 ]; then
|
|
for i in 1024 2048 4096 8192 16384 32768 65536
|
|
do
|
|
if [ $i -gt $DbPageSize ]; then
|
|
Output "ERROR: DBREPAIR_SIZE ($DbPageSize) not a power of 2 between 1024 and 65536. Value selected = $i."
|
|
WriteLog "SetPageSize - DBREPAIR_PAGESIZE ($DbPageSize) not a power of 2. New value selected = $i"
|
|
DbPageSize=$i
|
|
IsPowTwo=1
|
|
fi
|
|
[ $IsPowTwo -eq 1 ] && break
|
|
done
|
|
fi
|
|
|
|
Output "Setting Plex SQLite page size ($DbPageSize)"
|
|
WriteLog "SetPageSize - Setting Plex SQLite page_size: $DbPageSize"
|
|
|
|
|
|
# Create DB with desired page size
|
|
"$PLEX_SQLITE" "$1" "PRAGMA page_size=${DbPageSize}; VACUUM;"
|
|
|
|
}
|
|
|
|
##### 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
|
|
|
|
# Temporary DB actions
|
|
Output "Performing DB cleanup tasks."
|
|
WriteLog "DB Cleanup tasks."
|
|
"$PLEX_SQLITE" $CPPL.db "DELETE from statistics_bandwidth where account_id is NULL;"
|
|
|
|
# Continue
|
|
Output "Exporting current databases using timestamp: $TimeStamp"
|
|
Fail=0
|
|
|
|
# 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=$?
|
|
[ $IgnoreErrors -eq 1 ] && Result=0
|
|
|
|
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=$?
|
|
[ $IgnoreErrors -eq 1 ] && Result=0
|
|
|
|
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."
|
|
WriteLog "Repair - Export databases - PASS"
|
|
|
|
# Library and blobs successfully exported, create new
|
|
Output "Start importing into new databases."
|
|
Output "Importing Main DB."
|
|
DoSetPageSize "$TMPDIR/$CPPL.db-REPAIR-$TimeStamp"
|
|
"$PLEX_SQLITE" "$TMPDIR/$CPPL.db-REPAIR-$TimeStamp" < "$TMPDIR/library.plexapp.sql-$TimeStamp"
|
|
Result=$?
|
|
[ $IgnoreErrors -eq 1 ] && Result=0
|
|
|
|
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."
|
|
DoSetPageSize "$TMPDIR/$CPPL.blobs.db-REPAIR-$TimeStamp"
|
|
"$PLEX_SQLITE" "$TMPDIR/$CPPL.blobs.db-REPAIR-$TimeStamp" < "$TMPDIR/blobs.plexapp.sql-$TimeStamp"
|
|
Result=$?
|
|
[ $IgnoreErrors -eq 1 ] && Result=0
|
|
|
|
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 databases."
|
|
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"
|
|
WriteLog "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
|
|
chmod $Perms $CPPL.db $CPPL.blobs.db
|
|
Result=$?
|
|
if [ $Result -ne 0 ]; then
|
|
Output "ERROR: Cannot set permissions on new databases. Error $Result"
|
|
Output " Please exit tool, keeping temp files, seek assistance."
|
|
Output " Use files: $TMPDIR/*-BACKUP-$TimeStamp"
|
|
WriteLog "Repair - Move files - FAIL"
|
|
Fail=1
|
|
return 1
|
|
fi
|
|
|
|
chown $Owner $CPPL.db $CPPL.blobs.db
|
|
Result=$?
|
|
if [ $Result -ne 0 ]; then
|
|
Output "ERROR: Cannot set ownership on new databases. Error $Result"
|
|
Output " Please exit tool, keeping temp files, seek assistance."
|
|
Output " Use files: $TMPDIR/*-BACKUP-$TimeStamp"
|
|
WriteLog "Repair - Move files - FAIL"
|
|
Fail=1
|
|
return 1
|
|
fi
|
|
|
|
# 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
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
##### Replace current DB with a valid backup DB (if available)
|
|
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"
|
|
Output " "
|
|
Count=0
|
|
Selection=0
|
|
Candidate=""
|
|
Valid=0
|
|
|
|
# Print the list of dates
|
|
for i in $Dates
|
|
do
|
|
Count=$((Count + 1))
|
|
echo " ${Count}) - $i"
|
|
done
|
|
|
|
# Get the selection
|
|
Selection=""
|
|
Output " "
|
|
echo -n "Select backup date by number or date name (blank = return to menu) "
|
|
read Selection
|
|
|
|
# If no selection - return
|
|
[ "$Selection" = "" ] && return
|
|
|
|
# Search for match
|
|
Candidate=""
|
|
Count=0
|
|
for i in $Dates; do
|
|
Count=$((Count + 1))
|
|
if [ "$Selection" = "$Count" ] || [ "$Selection" = "$i" ]; then
|
|
Candidate="$i"
|
|
fi
|
|
done
|
|
|
|
# If no match, return
|
|
if [ "$Candidate" = "" ]; then
|
|
Output "Error. No valid matching main and blobs database pairs. Cannot replace."
|
|
WriteLog "Replace - Select candidate - FAIL"
|
|
return
|
|
fi
|
|
|
|
# Check candidate
|
|
if [ -e $CPPL.db-$Candidate ] && \
|
|
[ -e $CPPL.blobs.db-$Candidate ] ; then
|
|
|
|
Fail=0
|
|
Output "Checking backup candidate $Candidate"
|
|
! CheckDB $CPPL.db-$Candidate && Fail=1
|
|
! CheckDB $CPPL.blobs.db-$Candidate && Fail=1
|
|
|
|
[ $Fail -eq 1 ] && Output "Backup from $Candidate is not usable. Try again" && return
|
|
|
|
Output "Database backup $Candidate is valid."
|
|
UseThis=0
|
|
Output " "
|
|
|
|
if ConfirmYesNo "Use backup dated: '$Candidate' ?"; then
|
|
UseThis=1
|
|
fi
|
|
else
|
|
# Did not pass checks
|
|
Output "One of the backup files is missing. Please make another selection"
|
|
Fail=1
|
|
return
|
|
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 from $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 " "
|
|
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"
|
|
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...'
|
|
DoSetPageSize "$TMPDIR/$CPPL.db-IMPORT-$TimeStamp"
|
|
"$PLEX_SQLITE" "$TMPDIR/$CPPL.db-IMPORT-$TimeStamp" < "$TMPDIR/Viewstate.sql-$TimeStamp" 2> /dev/null
|
|
|
|
# Remove duplicates (violations of unique constraint)
|
|
if [ $RemoveDuplicates -eq 1 ]; then
|
|
cat <<EOF | "$PLEX_SQLITE" "$TMPDIR/$CPPL.db-IMPORT-$TimeStamp"
|
|
DELETE FROM metadata_item_settings
|
|
WHERE rowid NOT IN
|
|
( SELECT MIN(rowid) FROM metadata_item_settings GROUP BY guid, account_id );
|
|
EOF
|
|
fi
|
|
|
|
# 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"
|
|
|
|
# Set owner and permissions
|
|
chown $Owner $CPPL.db
|
|
chmod $Perms $CPPL.db
|
|
|
|
# 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
|
|
|
|
if IsRunning; then
|
|
Output "Stopping PMS."
|
|
else
|
|
Output "PMS already stopped."
|
|
return 0
|
|
fi
|
|
|
|
$StopCommand > /dev/null 2> /dev/null
|
|
Result=$?
|
|
if [ $Result -ne 0 ]; then
|
|
Output "Cannot send stop command to PMS, error $Result. Please stop manually."
|
|
WriteLog "Cannot send stop command to PMS, error $Result. Please stop manually."
|
|
return 1
|
|
fi
|
|
|
|
Count=10
|
|
while IsRunning && [ $Count -gt 0 ]
|
|
do
|
|
sleep 3
|
|
Count=$((Count - 1))
|
|
done
|
|
|
|
if ! IsRunning; then
|
|
WriteLog "Stop - PASS"
|
|
Output "Stopped PMS."
|
|
return 0
|
|
else
|
|
WriteLog "Stop - FAIL (Timeout)"
|
|
Output "Could not stop PMS. PMS did not shutdown within 30 second limit."
|
|
fi
|
|
fi
|
|
return $Result
|
|
}
|
|
|
|
# Mac Helper Functions
|
|
startMacPMS() { TMPDIR=/tmp osascript -e 'tell app "Plex Media Server" to launch'; sleep 2; IsRunning && sayStarted; }
|
|
stopMacPMS() { osascript -e 'tell app "Plex Media Server" to quit'; sleep 2; IsRunning || sayStopped; }
|
|
sayStarted() { [ $HapticOkay -eq 1 ] && osascript -e 'say "started"'; }
|
|
sayStopped() { [ $HapticOkay -eq 1 ] && osascript -e 'say "Its stopped"'; }
|
|
|
|
# Do command line switches
|
|
#DoOptions() {
|
|
|
|
|
|
#}
|
|
|
|
# UpdateTimestamp
|
|
DoUpdateTimestamp() {
|
|
TimeStamp="$(date "+%Y-%m-%d_%H.%M.%S")"
|
|
}
|
|
|
|
# Get latest version from Github
|
|
GetLatestRelease() {
|
|
Response=$(curl -s "https://api.github.com/repos/ChuckPa/PlexDBRepair/tags")
|
|
if [ $? -eq 0 ]; then
|
|
LatestVersion="$(echo "$Response" | grep name | awk -F: '{print $2}' | sort -r | head -1 | tr -d \" | tr -d ' ' | tr -d ',')"
|
|
else
|
|
LatestVersion="$Version"
|
|
fi
|
|
|
|
}
|
|
|
|
# Download and update script
|
|
DownloadAndUpdate() {
|
|
Url="$1"
|
|
Filename="$2"
|
|
|
|
# Download the file and check if the download was successful
|
|
if curl -s "$Url" --output "${Filename}.tmp"; then
|
|
# Check if the file was written to and at least 50000 bytes
|
|
if [ -f "${Filename}.tmp" ]; then
|
|
if [ $(stat $STATFMT $STATBYTES "${Filename}.tmp") -gt 50000 ]; then
|
|
Output "Update downloaded successfully"
|
|
mv "$Filename" "${Filename}.bak"
|
|
mv "${Filename}.tmp" "$Filename"
|
|
chmod +x "$Filename"
|
|
return 0
|
|
else
|
|
Output "Error: Downloaded file incomplete."
|
|
rm -f "${Filename}.tmp"
|
|
fi
|
|
else
|
|
Output "Error: Unable to download update."
|
|
rm -f "${Filename}.tmp"
|
|
fi
|
|
else
|
|
Output "Error: Download failed."
|
|
rm -f "${Filename}.tmp"
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
# Prune old jpg files from the PhotoTranscoder directory (> 30 days -or- DBREPAIR_CACHEAGE days)
|
|
# and purge files from the TMP and Transcoder_Temp directories
|
|
DoPrunePhotoTranscoder() {
|
|
|
|
PruneIt=0
|
|
|
|
# Use default cache age of 30 days
|
|
CacheAge=30
|
|
|
|
# Does DBREPAIR_CACHEAGE exist and is it a valid positive integer ?
|
|
if [ "$DBREPAIR_CACHEAGE" != "" ]; then
|
|
if [ "$DBREPAIR_CACHEAGE" != "$(echo "$DBREPAIR_CACHEAGE" | sed 's/[^0-9]*//g')" ]; then
|
|
WriteLog "PrunePhotoTranscoder - ERROR: DBREPAIR_CACHEAGE is not a valid integer. Ignoring '$DBREPAIR_CACHEAGE'"
|
|
Output "ERROR: DBREPAIR_CACHEAGE is not a valid integer. Ignoring '$DBREPAIR_CACHEAGE'"
|
|
return
|
|
else
|
|
CacheAge=$DBREPAIR_CACHEAGE
|
|
fi
|
|
fi
|
|
|
|
# If scripted / command line options, clean automatically
|
|
if [ $Scripted -eq 1 ]; then
|
|
PruneIt=1
|
|
else
|
|
Output "Counting how many files can be removed."
|
|
FileCount=$(find "$CACHEDIR" \( -name \*.jpg -o -name \*.jpeg -o -name \*.png -o -name \*.ppm \) -mtime +${CacheAge} -print | wc -l)
|
|
FileCount=$(( $FileCount + $(find "/tmp" -name pms-\* -mtime +1 -print | grep -v systemd | grep -v Easy | wc -l)))
|
|
FileCount=$(( $FileCount + $(find "/tmp" \( -name \*.jpg -o -name \*.jpeg -o -name \*.png \) -mtime +1 | wc -l )))
|
|
|
|
# If nothing found, continue back to the menu
|
|
[ $FileCount -eq 0 ] && Output "No files found to prune." && return
|
|
|
|
# Ask if we should remove it
|
|
if ConfirmYesNo "OK to prune $FileCount files? "; then
|
|
PruneIt=1
|
|
fi
|
|
fi
|
|
|
|
# Prune old the jpgs/jpegs from Cache and clean up /tmp
|
|
if [ $PruneIt -eq 1 ]; then
|
|
Output "Pruning started."
|
|
WriteLog "Prune - START"
|
|
find "$CACHEDIR" \( -name \*.jpg -o -name \*.jpeg -o -name \*.png \) -mtime +${CacheAge} -delete
|
|
|
|
PurgeFiles="/tmp/PurgeList.$$"
|
|
find "$TMPDIR" /tmp -name pms-\* -mtime +1 -print | grep -v systemd | grep -v Easy -print > "$PurgeFiles" 2>> /dev/null
|
|
find "$TMPDIR" /tmp \( -name \*.jpg -o -name \*.jpeg -o -name \*.png \) -mtime +1 -print >> "$PurgeFiles" 2>> /dev/null
|
|
WriteLog "Prune - Removing $FileCount files."
|
|
while read Path
|
|
do
|
|
rm -rf "$Path" >> /dev/null 2>> /dev/null
|
|
done < "$PurgeFiles"
|
|
|
|
rm -f "$PurgeFiles"
|
|
|
|
Output "Pruning completed."
|
|
WriteLog "Prune - PASS."
|
|
fi
|
|
|
|
}
|
|
|
|
|
|
#############################################################
|
|
# Main utility begins here #
|
|
#############################################################
|
|
|
|
# Set Script Path
|
|
ScriptPath="$(readlink -f "$0")"
|
|
ScriptName="$(basename "$ScriptPath")"
|
|
ScriptWorkingDirectory="$(dirname "$ScriptPath")"
|
|
|
|
# Initialize LastName LastTimestamp
|
|
SetLast "" ""
|
|
|
|
# Process any given command line options in the ugliest manner possible :P~~
|
|
while [ "$(echo $1 | cut -c1)" = "-" ] && [ "$1" != "" ]
|
|
do
|
|
Opt="$(echo $1 | awk '{print $1}' | tr [A-Z] [a-z])"
|
|
[ "$Opt" = "-i" ] && shift
|
|
[ "$Opt" = "-f" ] && shift
|
|
[ "$Opt" = "-p" ] && shift
|
|
|
|
# Manual configuration options (running outside of container or unusual hosts)
|
|
if [ "$Opt" = "--sqlite" ]; then
|
|
|
|
# Is this the directory where Plex SQLite exists?
|
|
if [ -d "$2" ] && [ -f "$2/Plex SQLite" ]; then
|
|
PLEX_SQLITE="$2/Plex SQLite"
|
|
ManualConfig=1
|
|
|
|
# Or is it the direct path to Plex SQLite
|
|
elif echo "$2" | grep "Plex SQLite" > /dev/null && [ -f "$2" ] ; then
|
|
PLEX_SQLITE="$2"
|
|
|
|
else
|
|
Output "Given 'Plex SQLite' directory/path ('$2') is invalid. Aborting."
|
|
exit 2
|
|
fi
|
|
shift 2
|
|
fi
|
|
|
|
|
|
|
|
# Manual path to databases
|
|
if [ "$Opt" = "--databases" ]; then
|
|
|
|
# Manually specify path to where the databases reside and set all dependent dirs
|
|
if [ -d "$2" ] && [ -f "$2"/com.plexapp.plugins.library.db ]; then
|
|
DBDIR="$2"
|
|
AppSuppDir="$(dirname "$(dirname "$(dirname "$DBDIR")")")"
|
|
CACHEDIR="$AppSuppDir/Plex Media Server/Cache/PhotoTranscoder"
|
|
LOGFILE="$DBDIR/DBRepair.log"
|
|
ManualConfig=1
|
|
|
|
|
|
else
|
|
Output "Given Plex databases directory ('$2') is invalid. Aborting."
|
|
exit 2
|
|
fi
|
|
shift 2
|
|
fi
|
|
done
|
|
|
|
# Confirm completed manual config
|
|
if [ $ManualConfig -eq 1 ]; then
|
|
if [ "$DBDIR" = "" ] || [ "$PLEX_SQLITE" = "" ]; then
|
|
Output "Error: Both 'Plex SQLite' and Databases directory paths must be specified with Manual configuration."
|
|
WriteLog "Manual configuration incomplete. One of the required arguments was missing."
|
|
exit 2
|
|
fi
|
|
|
|
# Performing logging here
|
|
[ $IgnoreErrors -eq 1 ] && WriteLog "Opt: Database error checking ignored."
|
|
[ $RemoveDuplicates -eq 1 ] && WriteLog "Opt: Remove duplidate watch history viewstates."
|
|
WriteLog "Plex SQLite = '$PLEX_SQLITE'"
|
|
WriteLog "Databases = '$DBDIR'"
|
|
|
|
# Final configuration
|
|
HostType="User Defined"
|
|
fi
|
|
|
|
|
|
|
|
# Are we scripted (command line args)
|
|
Scripted=0
|
|
[ "$1" != "" ] && Scripted=1
|
|
|
|
# Identify this host
|
|
if [ $ManualConfig -eq 0 ] && ! HostConfig; then
|
|
Output 'Error: Unknown host. Current supported hosts are: QNAP, Syno, Netgear, Mac, ASUSTOR, WD (OS5), Linux wkstn/svr, SNAP'
|
|
Output ' Current supported container images: Plexinc, LinuxServer, HotIO, & BINHEX'
|
|
Output ' Manual host configuration is available in most use cases.'
|
|
Output ' '
|
|
Output 'Are you trying to run the tool from outside the container environment? Manual mode is available. Please see documentation.'
|
|
exit 1
|
|
fi
|
|
|
|
# If root required, confirm this script is running as root
|
|
if [ $RootRequired -eq 1 ] && [ $(id -u) -ne 0 ]; then
|
|
Output "ERROR: Tool running as username '$(whoami)'. '$HostType' requires 'root' user privilege."
|
|
Output " (e.g 'sudo -su root' or 'sudo bash')"
|
|
Output " Exiting."
|
|
exit 2
|
|
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"
|
|
|
|
# Now set as 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)"
|
|
Perms="$(stat $STATFMT $STATPERMS $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 " Database Repair Utility for Plex Media Server ($HostType)"
|
|
echo " Version $Version"
|
|
echo " "
|
|
|
|
# Print info if Manual
|
|
if [ $ManualConfig -eq 1 ]; then
|
|
WriteLog "SQLite path: '$PLEX_SQLITE'"
|
|
WriteLog "Database path: '$DBDIR'"
|
|
Output " PlexSQLite = '$PLEX_SQLITE'"
|
|
Output " Databases = '$DBDIR'"
|
|
fi
|
|
|
|
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' - Check, Repair/Optimize, and Reindex Database in one step."
|
|
echo " 3 - 'check' - Perform integrity check of database."
|
|
echo " 4 - 'vacuum' - Remove empty space from database without optimizing."
|
|
echo " 5 - 'repair' - Repair/Optimize databases."
|
|
echo " 6 - 'reindex' - Rebuild 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 " 21 - 'prune' - Remove old image files (jpeg,jpg,png) from PhotoTranscoder cache & all temp files left by PMS."
|
|
[ $IgnoreErrors -eq 0 ] && echo " 42 - 'ignore' - Ignore duplicate/constraint errors."
|
|
[ $IgnoreErrors -eq 1 ] && echo " 42 - 'honor' - Honor all database errors."
|
|
|
|
echo ""
|
|
echo " 88 - 'update' - Check for updates."
|
|
echo " 99 - 'quit' - Quit immediately. Keep all temporary files."
|
|
echo " 'exit' - Exit with cleanup options."
|
|
fi
|
|
|
|
if [ $Scripted -eq 0 ]; then
|
|
echo ""
|
|
printf "Enter command # -or- command name (4 char min) : "
|
|
|
|
# Read next command from user
|
|
read Input
|
|
|
|
# Handle EOF/forced exit
|
|
if [ "$Input" = "" ] ; then
|
|
if [ $NullCommands -gt 4 ]; then
|
|
Output "Unexpected EOF / End of command line options. Exiting. Keeping temp files. "
|
|
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
|
|
else
|
|
|
|
# Scripted
|
|
Input="$1"
|
|
|
|
# If end of line then force exit
|
|
if [ "$Input" = "" ]; then
|
|
Input="exit"
|
|
else
|
|
shift
|
|
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
|
|
;;
|
|
|
|
|
|
# Remove (prune) unused image files from PhotoTranscoder Cache > 30 days old.
|
|
# Also remove all PMS temp files left behind in TMP. (combine both into one action)
|
|
21|22|prun*|remo*|purg*)
|
|
|
|
# Check if PMS running
|
|
if IsRunning; then
|
|
WriteLog "Prune - FAIL - PMS runnning"
|
|
Output "Unable to prune PhotoTranscoder cache. PMS is running."
|
|
continue
|
|
fi
|
|
|
|
WriteLog "Prune - START"
|
|
DoPrunePhotoTranscoder
|
|
WriteLog "Prune - PASS"
|
|
;;
|
|
|
|
|
|
# Records count
|
|
30|coun*)
|
|
|
|
Temp="$DBDIR/DBRepair.tab1"
|
|
Temp2="$DBDIR/DBRepair.tab2"
|
|
|
|
# Ensure clean
|
|
rm -f "$Temp" "$Temp2"
|
|
|
|
# Get list of tables
|
|
Tables="$("$PLEX_SQLITE" "$DBDIR/com.plexapp.plugins.library.db" .tables | sed 's/ /\n/g')"
|
|
|
|
# Separate and sort tables
|
|
for i in $Tables
|
|
do
|
|
echo $i >> "$Temp"
|
|
done
|
|
sort < "$Temp" > "$Temp2"
|
|
|
|
Tables="$(cat "$Temp2")"
|
|
|
|
# Get counts
|
|
for Table in $Tables
|
|
do
|
|
Records=$("$PLEX_SQLITE" "$DBDIR/com.plexapp.plugins.library.db" "select count(*) from $Table;")
|
|
printf "%36s %-15d\n" $Table $Records
|
|
done
|
|
|
|
# Cleanup
|
|
rm -f "$Temp" "$Temp2"
|
|
;;
|
|
|
|
# Ignore/Honor errors
|
|
42|igno*|hono*)
|
|
|
|
IgnoreErrors=$((IgnoreErrors ^ 1))
|
|
[ $IgnoreErrors -eq 0 ] && Output "Honoring database errors." && WriteLog "Honoring database errors."
|
|
[ $IgnoreErrors -eq 1 ] && Output "Ignoring database errors." && WriteLog "Ignoring database errors."
|
|
;;
|
|
|
|
88|upda*)
|
|
|
|
DoUpdate=0
|
|
Output "Checking for update"
|
|
GetLatestRelease
|
|
if [ $(VersionDigits $LatestVersion) -gt $(VersionDigits $Version) ]; then
|
|
if [ $Scripted -eq 1 ]; then
|
|
DoUpdate=1
|
|
elif ConfirmYesNo "Download $LatestVersion and update?"; then
|
|
DoUpdate=1
|
|
fi
|
|
else
|
|
Output "No update available."
|
|
fi
|
|
|
|
# Check if script path is writable
|
|
if [ $DoUpdate -eq 1 ]; then
|
|
if [ -w "$ScriptWorkingDirectory" ]; then
|
|
Output "Updating from $Version to $LatestVersion"
|
|
DownloadAndUpdate "https://raw.githubusercontent.com/ChuckPa/PlexDBRepair/master/DBRepair.sh" "$ScriptWorkingDirectory/$ScriptName"
|
|
Result=$?
|
|
if [ $Result -eq 0 ]; then
|
|
chmod +x "$ScriptWorkingDirectory/$ScriptName"
|
|
Output "Restart to launch updated DBRepair.sh ($LatestVersion)"
|
|
WriteLog "Update - Updated to version $LatestVersion."
|
|
exit 0
|
|
else
|
|
Output "Unable to download and update. Error $Result."
|
|
fi
|
|
else
|
|
Output "Script path '${ScriptName}' is not writable."
|
|
fi
|
|
fi
|
|
|
|
[ $Scripted -eq 1 ] && [ $DoUpdate -eq 1 ] && exit 0
|
|
;;
|
|
|
|
# Quit
|
|
99|quit)
|
|
|
|
Output "Retaining all temporary work files."
|
|
WriteLog "Exit - Retain temp files."
|
|
exit 0
|
|
;;
|
|
|
|
# Orderly Exit
|
|
exit)
|
|
|
|
# If forced exit set, exit and retain
|
|
if [ $Exit -eq 1 ]; then
|
|
Output "Unexpected exit command. Keeping all temporary work files."
|
|
WriteLog "EOFExit - Retain temp files."
|
|
exit 1
|
|
fi
|
|
|
|
# If cmd line mode, exit clean without asking
|
|
if [ $Scripted -eq 1 ]; then
|
|
rm -rf $TMPDIR
|
|
WriteLog "Exit - Delete temp files."
|
|
|
|
else
|
|
# Ask questions on interactive exit
|
|
if 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
|
|
fi
|
|
|
|
WriteLog "Session end. $(date)"
|
|
WriteLog "============================================================"
|
|
exit 0
|
|
;;
|
|
|
|
# Unknown command
|
|
*)
|
|
WriteLog "Unknown command: '$Input'"
|
|
Output "ERROR: Unknown command: '$Input'"
|
|
;;
|
|
|
|
esac
|
|
done
|
|
done
|
|
exit 0
|