mirror of
https://github.com/ChuckPa/PlexDBRepair.git
synced 2025-11-06 03:08:55 -05:00
Merge pull request #194 from danrahn/master
[Windows] Check PMS state in more places
This commit is contained in:
commit
e2a3d1d04b
@ -3,7 +3,7 @@
|
|||||||
# #
|
# #
|
||||||
#########################################################################
|
#########################################################################
|
||||||
|
|
||||||
$PlexDBRepairVersion = 'v1.00.01'
|
$PlexDBRepairVersion = 'v1.00.02'
|
||||||
|
|
||||||
class PlexDBRepair {
|
class PlexDBRepair {
|
||||||
[PlexDBRepairOptions] $Options
|
[PlexDBRepairOptions] $Options
|
||||||
@ -14,10 +14,12 @@ class PlexDBRepair {
|
|||||||
[string] $Timestamp # Timestamp used for temporary database files
|
[string] $Timestamp # Timestamp used for temporary database files
|
||||||
[string] $LogFile # Path of our log file
|
[string] $LogFile # Path of our log file
|
||||||
[string] $Version # Current script version
|
[string] $Version # Current script version
|
||||||
|
[bool] $IsError # Whether we're currently in an error state
|
||||||
|
|
||||||
PlexDBRepair($Arguments, $Version) {
|
PlexDBRepair($Arguments, $Version) {
|
||||||
$this.Options = [PlexDBRepairOptions]::new()
|
$this.Options = [PlexDBRepairOptions]::new()
|
||||||
$this.Version = $Version
|
$this.Version = $Version
|
||||||
|
$this.IsError = $false
|
||||||
$Commands = $this.PreprocessArgs($Arguments)
|
$Commands = $this.PreprocessArgs($Arguments)
|
||||||
if ($null -eq $Commands) {
|
if ($null -eq $Commands) {
|
||||||
return
|
return
|
||||||
@ -187,7 +189,9 @@ class PlexDBRepair {
|
|||||||
|
|
||||||
switch -Regex ($Choice) {
|
switch -Regex ($Choice) {
|
||||||
"^(1|stop)$" { $this.DoStop() }
|
"^(1|stop)$" { $this.DoStop() }
|
||||||
"^(2|autom?a?t?i?c?)$" { $this.RunAutomaticDatabaseMaintenance() }
|
"^(2|autom?a?t?i?c?)$" {
|
||||||
|
$this.IsError = !$this.RunAutomaticDatabaseMaintenance()
|
||||||
|
}
|
||||||
"^(7|start?)$" { $this.StartPMS() }
|
"^(7|start?)$" { $this.StartPMS() }
|
||||||
"^(21|(prune?|remov?e?))$" { $this.PrunePhotoTranscoderCache() }
|
"^(21|(prune?|remov?e?))$" { $this.PrunePhotoTranscoderCache() }
|
||||||
"^(42|ignor?e?|honor?)$" {
|
"^(42|ignor?e?|honor?)$" {
|
||||||
@ -213,6 +217,14 @@ class PlexDBRepair {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# If our last DB operation failed, we don't want to automatically delete
|
||||||
|
# temporary files when in scripted mode.
|
||||||
|
if ($this.IsError -and $this.Options.Scripted) {
|
||||||
|
$this.Output("Exiting with errors. Keeping all temporary work files.")
|
||||||
|
$this.WriteLog("Exit - Retain temp files.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
$this.CleanDBTemp(!$this.Options.Scripted)
|
$this.CleanDBTemp(!$this.Options.Scripted)
|
||||||
$this.WriteEnd()
|
$this.WriteEnd()
|
||||||
return
|
return
|
||||||
@ -242,13 +254,12 @@ class PlexDBRepair {
|
|||||||
[void] DoStop() {
|
[void] DoStop() {
|
||||||
$this.WriteLog("Stop - START")
|
$this.WriteLog("Stop - START")
|
||||||
$PMS = $this.GetPMS()
|
$PMS = $this.GetPMS()
|
||||||
if ($PMS) {
|
if ($null -eq $PMS) {
|
||||||
$this.Output("Stopping PMS.")
|
|
||||||
} else {
|
|
||||||
$this.Output("PMS already stopped.")
|
$this.Output("PMS already stopped.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this.Output("Stopping PMS.")
|
||||||
|
|
||||||
# Plex doesn't respond to CloseMainWindow because it doesn't have a window,
|
# Plex doesn't respond to CloseMainWindow because it doesn't have a window,
|
||||||
# and Stop-Process does a forced exit of the process, so use taskkill to ask
|
# and Stop-Process does a forced exit of the process, so use taskkill to ask
|
||||||
@ -265,11 +276,12 @@ class PlexDBRepair {
|
|||||||
if ($PMS.HasExited) {
|
if ($PMS.HasExited) {
|
||||||
$this.WriteLog("Stop - PASS")
|
$this.WriteLog("Stop - PASS")
|
||||||
$this.Output("Stopped PMS.")
|
$this.Output("Stopped PMS.")
|
||||||
} else {
|
return
|
||||||
|
}
|
||||||
|
|
||||||
$this.OutputWarn("Could not stop PMS. PMS did not shutdown within 30 second limit.")
|
$this.OutputWarn("Could not stop PMS. PMS did not shutdown within 30 second limit.")
|
||||||
$this.WriteLog("Stop - FAIL (Timeout)")
|
$this.WriteLog("Stop - FAIL (Timeout)")
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
# Start Plex Media Server if it isn't already running
|
# Start Plex Media Server if it isn't already running
|
||||||
[void] StartPMS() {
|
[void] StartPMS() {
|
||||||
@ -293,14 +305,14 @@ class PlexDBRepair {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# All-in-one database utility - Repair/Check/Reindex
|
# All-in-one database utility - Repair/Check/Reindex
|
||||||
[void] RunAutomaticDatabaseMaintenance() {
|
[bool] RunAutomaticDatabaseMaintenance() {
|
||||||
$this.Output("Automatic Check,Repair,Index started.")
|
$this.Output("Automatic Check,Repair,Index started.")
|
||||||
$this.WriteLog("Auto - START")
|
$this.WriteLog("Auto - START")
|
||||||
|
|
||||||
if ($this.PMSRunning()) {
|
if ($this.PMSRunning()) {
|
||||||
$this.WriteLog("Auto - FAIL - PMS running")
|
$this.WriteLog("Auto - FAIL - PMS running")
|
||||||
$this.OutputWarn("Unable to run automatic sequence. PMS is running. Please stop PlexMediaServer.")
|
$this.OutputWarn("Unable to run automatic sequence. PMS is running. Please stop PlexMediaServer.")
|
||||||
return
|
return $false
|
||||||
}
|
}
|
||||||
|
|
||||||
# Create temporary backup directory
|
# Create temporary backup directory
|
||||||
@ -310,7 +322,7 @@ class PlexDBRepair {
|
|||||||
New-Item -Path $DBTemp -ItemType "directory" -ErrorVariable tempDirError *>$null
|
New-Item -Path $DBTemp -ItemType "directory" -ErrorVariable tempDirError *>$null
|
||||||
if ($TempDirError) {
|
if ($TempDirError) {
|
||||||
$this.ExitDBMaintenance("Unable to create temporary database directory", $false)
|
$this.ExitDBMaintenance("Unable to create temporary database directory", $false)
|
||||||
return
|
return $false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -320,10 +332,10 @@ class PlexDBRepair {
|
|||||||
$MainDBSQL = Join-Path $DBTemp -ChildPath "library.sql_$($this.TimeStamp)"
|
$MainDBSQL = Join-Path $DBTemp -ChildPath "library.sql_$($this.TimeStamp)"
|
||||||
if (!$this.FileExists($MainDB)) {
|
if (!$this.FileExists($MainDB)) {
|
||||||
$this.ExitDBMaintenance("Could not find $MainDBName in database directory", $false)
|
$this.ExitDBMaintenance("Could not find $MainDBName in database directory", $false)
|
||||||
return
|
return $false
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$this.ExportPlexDB($MainDB, $MainDBSQL)) { return }
|
if (!$this.ExportPlexDB($MainDB, $MainDBSQL)) { return $false }
|
||||||
|
|
||||||
$this.Output("Exporting Blobs DB")
|
$this.Output("Exporting Blobs DB")
|
||||||
$BlobsDBName = "com.plexapp.plugins.library.blobs.db"
|
$BlobsDBName = "com.plexapp.plugins.library.blobs.db"
|
||||||
@ -331,56 +343,86 @@ class PlexDBRepair {
|
|||||||
$BlobsDBSQL = Join-Path $DBTemp -ChildPath "blobs.sql_$($this.Timestamp)"
|
$BlobsDBSQL = Join-Path $DBTemp -ChildPath "blobs.sql_$($this.Timestamp)"
|
||||||
if (!$this.FileExists($BlobsDB)) {
|
if (!$this.FileExists($BlobsDB)) {
|
||||||
$this.ExitDBMaintenance("Could not find $BlobsDBName in database directory", $false)
|
$this.ExitDBMaintenance("Could not find $BlobsDBName in database directory", $false)
|
||||||
return
|
return $false
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$this.ExportPlexDB($BlobsDB, $BlobsDBSQL)) { return }
|
if (!$this.ExportPlexDB($BlobsDB, $BlobsDBSQL)) { return $false }
|
||||||
|
|
||||||
$this.Output("Successfully exported the main and blobs databases. Proceeding to import into new database.")
|
$this.Output("Successfully exported the main and blobs databases. Proceeding to import into new database.")
|
||||||
$this.WriteLog("Repair - Export databases - PASS")
|
$this.WriteLog("Repair - Export databases - PASS")
|
||||||
|
|
||||||
|
# Make sure Plex hasn't been started while we were exporting
|
||||||
|
if (!$this.CheckPMS("Auto ", "export")) { return $false }
|
||||||
|
|
||||||
$this.Output("Importing Main DB.")
|
$this.Output("Importing Main DB.")
|
||||||
$MainDBImport = Join-Path $this.PlexDBDir -ChildPath "${MainDBName}_$($this.Timestamp)"
|
$MainDBImport = Join-Path $this.PlexDBDir -ChildPath "${MainDBName}_$($this.Timestamp)"
|
||||||
if (!$this.ImportPlexDB($MainDBSQL, $MainDBImport)) { return }
|
if (!$this.ImportPlexDB($MainDBSQL, $MainDBImport)) { return $false }
|
||||||
|
|
||||||
$this.Output("Creating Blobs DB")
|
$this.Output("Importing Blobs DB.")
|
||||||
$BlobsDBImport = Join-Path $this.PlexDBDir -ChildPath "${BlobsDBName}_$($this.Timestamp)"
|
$BlobsDBImport = Join-Path $this.PlexDBDir -ChildPath "${BlobsDBName}_$($this.Timestamp)"
|
||||||
if (!$this.ImportPlexDB($BlobsDBSQL, $BlobsDBImport)) { return }
|
if (!$this.ImportPlexDB($BlobsDBSQL, $BlobsDBImport)) { return $false }
|
||||||
|
|
||||||
$this.Output("Successfully imported databases.")
|
$this.Output("Successfully imported databases.")
|
||||||
$this.WriteLog("Repair - Import - PASS")
|
$this.WriteLog("Repair - Import - PASS")
|
||||||
|
|
||||||
$this.Output("Verifying databases integrity after importing.")
|
$this.Output("Verifying databases integrity after importing.")
|
||||||
|
|
||||||
if (!$this.IntegrityCheck($MainDBImport, "Main")) { return }
|
if (!$this.IntegrityCheck($MainDBImport, "Main")) { return $false }
|
||||||
$this.Output("Verification complete. PMS main database is OK.")
|
$this.Output("Verification complete. PMS main database is OK.")
|
||||||
$this.WriteLog("Repair - Verify main database - PASS")
|
$this.WriteLog("Repair - Verify main database - PASS")
|
||||||
|
|
||||||
if (!$this.IntegrityCheck($BlobsDBImport, "Blobs")) { return }
|
if (!$this.IntegrityCheck($BlobsDBImport, "Blobs")) { return $false }
|
||||||
$this.Output("Verification complete. PMS blobs database is OK.")
|
$this.Output("Verification complete. PMS blobs database is OK.")
|
||||||
$this.WriteLog("Repair - Verify blobs database - PASS")
|
$this.WriteLog("Repair - Verify blobs database - PASS")
|
||||||
|
|
||||||
|
if (!$this.CheckPMS("Auto ", "import")) { return $false }
|
||||||
|
|
||||||
# Import complete, now reindex
|
# Import complete, now reindex
|
||||||
$this.WriteOutputLog("Reindexing Main DB")
|
$this.WriteOutputLog("Reindexing Main DB")
|
||||||
if (!$this.RunSQLCommand("""$MainDBImport"" ""REINDEX;""", "Failed to reindex Main DB")) { return }
|
if (!$this.RunSQLCommand("""$MainDBImport"" ""REINDEX;""", "Failed to reindex Main DB")) { return $false }
|
||||||
$this.WriteOutputLog("Reindexing Blobs DB")
|
$this.WriteOutputLog("Reindexing Blobs DB")
|
||||||
if (!$this.RunSQLCommand("""$BlobsDBImport"" ""REINDEX;""", "Failed to reindex Blobs DB")) { return }
|
if (!$this.RunSQLCommand("""$BlobsDBImport"" ""REINDEX;""", "Failed to reindex Blobs DB")) { return $false }
|
||||||
$this.WriteOutputLog("Reindexing complete.")
|
$this.WriteOutputLog("Reindexing complete.")
|
||||||
|
|
||||||
$this.WriteOutputLog("Moving current DBs to DBTMP and making new databases active")
|
$this.WriteOutputLog("Moving current DBs to DBTMP and making new databases active")
|
||||||
|
if (!$this.CheckPMS("Auto ", "new database copy")) { return $false }
|
||||||
|
|
||||||
$MoveError = $null
|
try {
|
||||||
Move-Item -Path $MainDB -Destination (Join-Path $DBTemp -ChildPath "${MainDBName}_$($this.TimeStamp)") -ErrorVariable moveError *>$null
|
$this.MoveDatabase($MainDB, (Join-Path $DBTemp -ChildPath "${MainDBName}_$($this.Timestamp)"), "move Main DB to DBTMP")
|
||||||
if ($MoveError) { $this.ExitDBMaintenance("Unable to move Main DB to DBTMP: $MoveError", $false); return }
|
$this.MoveDatabase($MainDBImport, $MainDB, "replace Main DB with rebuilt DB")
|
||||||
Move-Item -Path $MainDBImport -Destination $MainDB -ErrorVariable moveError *>$null
|
|
||||||
if ($MoveError) { $this.ExitDBMaintenance("Unable to replace Main DB with rebuilt DB: $MoveError", $false); return }
|
|
||||||
|
|
||||||
Move-Item -Path $BlobsDB -Destination (Join-Path $DBTemp -ChildPath "${BlobsDBName}_$($this.TimeStamp)") -ErrorVariable moveError *>$null
|
$this.MoveDatabase($BlobsDB, (Join-Path $DBTemp -ChildPath "${BlobsDBName}_$($this.Timestamp)"), "move Blobs DB to DBTMP")
|
||||||
if ($MoveError) { $this.ExitDBMaintenance("Unable to move Blobs DB to DBTMP: $MoveError", $false) }
|
$this.MoveDatabase($BlobsDBImport, $BlobsDB, "replace Blobs DB with rebuilt DB")
|
||||||
Move-Item -Path $BlobsDBImport -Destination $BlobsDB -ErrorVariable moveError *>$null
|
} catch {
|
||||||
if ($MoveError) { $this.ExitDBMaintenance("Unable to replace Blobs DB with rebuilt DB: $MoveError", $false); return }
|
$Error.Clear()
|
||||||
|
return $false
|
||||||
|
}
|
||||||
|
|
||||||
$this.ExitDBMaintenance("Database repair/rebuild/reindex completed.", $true)
|
$this.ExitDBMaintenance("Database repair/rebuild/reindex completed.", $true)
|
||||||
|
return $true
|
||||||
|
}
|
||||||
|
|
||||||
|
# Return whether we can continue DB repair (i.e. whether PMS is running) at the given stage in the process.
|
||||||
|
[bool] CheckPMS([string] $Stage, [string] $SubStage) {
|
||||||
|
if ($this.PMSRunning()) {
|
||||||
|
$SubMessage = if ($SubStage) { "during $SubStage" } else { "" }
|
||||||
|
$this.WriteLog("$Stage - FAIL - PMS running $SubMessage")
|
||||||
|
$this.OutputWarn("Unable to run $Stage. PMS is running. Please stop PlexMediaServer.")
|
||||||
|
return $false
|
||||||
|
}
|
||||||
|
|
||||||
|
return $true
|
||||||
|
}
|
||||||
|
|
||||||
|
# Try to move the source file to the destination. If it fails, attempt to find
|
||||||
|
# open file handles (requires handle.exe on PATH) and throw.
|
||||||
|
[void] MoveDatabase([string] $Source, [string] $Destination, [string] $FriendlyString) {
|
||||||
|
$MoveError = $null
|
||||||
|
Move-Item -Path $Source -Destination $Destination -ErrorVariable MoveError *>$null
|
||||||
|
if ($MoveError) {
|
||||||
|
$this.ExitDBMaintenance("Unable to $($FriendlyString): $MoveError", $false)
|
||||||
|
throw "Unable to move database"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Attempts to prune PhotoTranscoder images that are older than the specified date cutoff (30 days by default)
|
# Attempts to prune PhotoTranscoder images that are older than the specified date cutoff (30 days by default)
|
||||||
@ -639,6 +681,10 @@ class PlexDBRepair {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[bool] ExportPlexDB([string] $Source, [string] $Destination) {
|
||||||
|
return $this.RunSQLCommand("""$Source"" "".output '$Destination'"" .dump", "Failed to export '$Source' to '$Destination'")
|
||||||
|
}
|
||||||
|
|
||||||
# Run an SQL command.
|
# Run an SQL command.
|
||||||
# ErrorMessage is the message to output/write to the log on failure
|
# ErrorMessage is the message to output/write to the log on failure
|
||||||
[bool] RunSQLCommand([string] $Command, [string] $ErrorMessage) {
|
[bool] RunSQLCommand([string] $Command, [string] $ErrorMessage) {
|
||||||
@ -689,35 +735,15 @@ class PlexDBRepair {
|
|||||||
return $true
|
return $true
|
||||||
}
|
}
|
||||||
|
|
||||||
[bool] ExportPlexDB([string] $Source, [string] $Destination) {
|
|
||||||
$SqlError = $null
|
|
||||||
$Command = """$Source"" .dump | Set-Content ""$Destination"" -Encoding utf8"
|
|
||||||
|
|
||||||
try {
|
|
||||||
Invoke-Expression "& ""$($this.PlexSQL)"" $Command" -ev SqlError -EA Stop *>$null
|
|
||||||
} catch {
|
|
||||||
$Err = $Error -join "`n"
|
|
||||||
# Even if we ignore errors, we can't continue if the export failed.
|
|
||||||
$this.ExitDBMaintenance("Failed to export '$Source' to '$Destination': '$Err'", $false)
|
|
||||||
$Error.Clear()
|
|
||||||
return $false
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($SqlError) {
|
|
||||||
$Err = $SqlError -join "`n"
|
|
||||||
if ($this.Options.IgnoreErrors) {
|
|
||||||
$this.OutputWarn("Ignoring database errors during export: $Err")
|
|
||||||
} else {
|
|
||||||
$this.ExitDBMaintenance("Failed to export '$Source' to '$Destination': '$Err'", $false)
|
|
||||||
return $false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $true
|
|
||||||
}
|
|
||||||
|
|
||||||
# Import an exported .sql file into a new database
|
# Import an exported .sql file into a new database
|
||||||
[bool] ImportPlexDB($Source, $Destination) {
|
[bool] ImportPlexDB($Source, $Destination) {
|
||||||
|
# SQLite's .read can't handle files larger than 2GB on versions <3.45.0 (https://sqlite.org/forum/forumpost/9af57ba66fbb5349),
|
||||||
|
# and Plex SQLite is currently on 3.39.4 (as of PMS 1.41.6).
|
||||||
|
# If the source is smaller than 2GB we can .read it directly, otherwise do things in a more roundabout way.
|
||||||
|
if ($this.FileExists($Source) -and (Get-Item $Source).Length -lt 2GB) {
|
||||||
|
return $this.RunSQLCommand("""$Destination"" "".read '$Source'""", "Failed to import Plex database (importing '$Source' into '$Destination')")
|
||||||
|
}
|
||||||
|
|
||||||
$ImportError = $null
|
$ImportError = $null
|
||||||
$ExitCode = 0
|
$ExitCode = 0
|
||||||
$Err = $null
|
$Err = $null
|
||||||
@ -791,7 +817,7 @@ class PlexDBRepair {
|
|||||||
|
|
||||||
# Return whether PMS is running
|
# Return whether PMS is running
|
||||||
[bool] PMSRunning() {
|
[bool] PMSRunning() {
|
||||||
return !!$this.GetPMS()
|
return $null -ne $this.GetPMS()
|
||||||
}
|
}
|
||||||
|
|
||||||
# Retrieve the PMS process, if running
|
# Retrieve the PMS process, if running
|
||||||
|
|||||||
@ -4,6 +4,11 @@ Release notes for the Windows counterpart to DBRepair.sh (DBRepair-Windows.ps1)
|
|||||||
|
|
||||||
# Release Info
|
# Release Info
|
||||||
|
|
||||||
|
v1.00.02
|
||||||
|
- Check whether PMS is running at more points in the process.
|
||||||
|
- Don't remove temp files in scripted mode if the last operation failed.
|
||||||
|
- Better export process that improves upon the UTF-8 fix in v1.00.01.
|
||||||
|
|
||||||
v1.00.01
|
v1.00.01
|
||||||
- Bug Fix: Ensure UTF-8 characters get exported/imported properly.
|
- Bug Fix: Ensure UTF-8 characters get exported/imported properly.
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user