mirror of
https://github.com/ChuckPa/PlexDBRepair.git
synced 2026-02-12 05:56:11 -06:00
Compare commits
137 Commits
4d8fa8058c
...
v1.14.00
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bfc440e1e1 | ||
|
|
1b458c7337 | ||
|
|
8e998598fd | ||
|
|
b62b606dc7 | ||
|
|
d6bf75db6d | ||
|
|
0701adf77a | ||
|
|
15b2dbabb3 | ||
|
|
cbd02cc6b6 | ||
|
|
43d636b371 | ||
|
|
85c88a59f3 | ||
|
|
f2060a0ede | ||
|
|
abc552fb12 | ||
|
|
aa76cbfefd | ||
|
|
d79b5e2b9d | ||
|
|
3bfb510ef7 | ||
|
|
247338278b | ||
|
|
dea8a805ce | ||
|
|
6d8bafbf99 | ||
|
|
e2acd24593 | ||
|
|
24b746db1b | ||
|
|
32d5f8f737 | ||
|
|
06c0f810dd | ||
|
|
ac6058dd48 | ||
|
|
019fd197f8 | ||
|
|
ebf170b1eb | ||
|
|
38cd4ded5c | ||
|
|
44f722166d | ||
|
|
2264ee89be | ||
|
|
93886de20f | ||
|
|
a3ba41b668 | ||
|
|
f96032e9c4 | ||
|
|
ea78ea663e | ||
|
|
f2a821c170 | ||
|
|
baf8398085 | ||
|
|
65f4e5d33d | ||
|
|
4872b82815 | ||
|
|
859f2bdbe1 | ||
|
|
0e1797a4c0 | ||
|
|
cf54baa5c4 | ||
|
|
b431e49455 | ||
|
|
c5c1f7b683 | ||
|
|
8c5f7606a7 | ||
|
|
3bb22f56cd | ||
|
|
481814ed2c | ||
|
|
d6f4dbf263 | ||
|
|
1c5c4b6fbc | ||
|
|
98c5274d86 | ||
|
|
1c1ed5a8c1 | ||
|
|
be74c6f85e | ||
|
|
865da1cd3c | ||
|
|
ca489c6c3f | ||
|
|
75481e41d3 | ||
|
|
3262ec7f7a | ||
|
|
8aed201782 | ||
|
|
21196a3cc3 | ||
|
|
a34511a219 | ||
|
|
e2a3d1d04b | ||
|
|
82f1575293 | ||
|
|
19b9b5bf6f | ||
|
|
dfebe80fa9 | ||
|
|
2fcd006ed4 | ||
|
|
d505486e95 | ||
|
|
ed72348a76 | ||
|
|
eae8c5a6da | ||
|
|
4df5421699 | ||
|
|
738dc8d7a0 | ||
|
|
48f7ff7f36 | ||
|
|
804c9014d1 | ||
|
|
984b4ff861 | ||
|
|
6aff3e4231 | ||
|
|
46c4b1ddcc | ||
|
|
1737631454 | ||
|
|
fe6c8d3ab6 | ||
|
|
f11d8de6be | ||
|
|
f4b987900a | ||
|
|
16d1807705 | ||
|
|
c74c6c601c | ||
|
|
013f55bd96 | ||
|
|
79f0153a96 | ||
|
|
c0000079d7 | ||
|
|
f869520ef8 | ||
|
|
91562060d1 | ||
|
|
4f5b5abdc8 | ||
|
|
b029ccebbb | ||
|
|
9c1008cf52 | ||
|
|
630d5b3b6e | ||
|
|
1a75cc9ede | ||
|
|
f2ec7d2ea9 | ||
|
|
361889230d | ||
|
|
f3175713a9 | ||
|
|
c9a9a1eb81 | ||
|
|
ec9edaf304 | ||
|
|
2e92ba88de | ||
|
|
57055172e0 | ||
|
|
029020ebc5 | ||
|
|
93c7b0fbe2 | ||
|
|
32c30e6826 | ||
|
|
907f233a06 | ||
|
|
d0084c96fb | ||
|
|
ec7e04dc35 | ||
|
|
1a3228e648 | ||
|
|
852fdfc0d5 | ||
|
|
ee8556d0f9 | ||
|
|
d8d2283c01 | ||
|
|
43ba32afa4 | ||
|
|
4d880bf788 | ||
|
|
d98dfa68bf | ||
|
|
c8162a487f | ||
|
|
89ea800bdb | ||
|
|
3ef48d099a | ||
|
|
26a8bf3e49 | ||
|
|
1ea8b86aea | ||
|
|
d96f091823 | ||
|
|
e02e5bc398 | ||
|
|
6ebb342973 | ||
|
|
a489a7f9e6 | ||
|
|
2a8a8e242b | ||
|
|
8863beeab2 | ||
|
|
d6d6c47928 | ||
|
|
38749aef78 | ||
|
|
6c7157cb5f | ||
|
|
fa760e3ddf | ||
|
|
80fb3a751c | ||
|
|
00bb897a92 | ||
|
|
fcf132f9e1 | ||
|
|
4f21080862 | ||
|
|
66ad17da60 | ||
|
|
4dcb686ab1 | ||
|
|
c5a4a88f82 | ||
|
|
f6008d76b7 | ||
|
|
ee335a69a5 | ||
|
|
2958fb5c26 | ||
|
|
8cbbc2b0b2 | ||
|
|
2d7e46dd6d | ||
|
|
db1edd13a4 | ||
|
|
47bd6e7c56 | ||
|
|
beeaa88315 |
@@ -10,25 +10,34 @@ To contribute to the project, please follow the following instructions:
|
||||
|
||||
### When submitting your work for review and merge:
|
||||
|
||||
1. Prior to opening a pull request,
|
||||
- Be certain to REBASE prior to opening the PR
|
||||
1. Create an issue in the DBRepair repo against the current release
|
||||
- Describe the deficiency to be addressed in sufficient detail
|
||||
|
||||
2. Complete your work in your branch
|
||||
- Be certain to REBASE prior to starting work.
|
||||
|
||||
3. Prior to opening a pull request,
|
||||
- Squash multiple commits into a single commit.
|
||||
- Clean up the commit message after squashing.
|
||||
- Use force-push if needed to unify all changes.
|
||||
- Use force-push if needed to unify all changes so there is one hash to reference.
|
||||
|
||||
2. Open the pull request against the 'contribute' branch.
|
||||
4. Open the pull request against the master branch
|
||||
|
||||
3. In the pull request, provide a description of what change(s) where made
|
||||
5. In the pull request, provide a description of what change(s) where made
|
||||
- As last text added, on a blank line,
|
||||
- Add the text: `Fixes:` followed by the URL of the open Issue
|
||||
eg: Fixes: https://ChuckPa/PlexDBRepair/issues/12 (if we were fixing issue 12)
|
||||
eg: Fixes: https://github.com/ChuckPa/DBRepair/issues/12 (if we were fixing issue 12)
|
||||
|
||||
- Adding the above text+http tag has the following impact:
|
||||
- The Issue which prompted the change is forever linked to the PR making documentation easy.
|
||||
- Adding the above text & URL has the following impact:
|
||||
-- The Issue which prompted the change is forever linked to the PR making documentation easy.
|
||||
-- In the event of unforseen issues, reverting the changes will be trivial and allow easier rework.
|
||||
|
||||
5. Be certain to request review with your pull request.
|
||||
6. Be certain to request review with your pull request.
|
||||
|
||||
6. Upon completion of review and testing, contributions will be merged into master.
|
||||
When the PR is approved and merged, Github will automatically mark the issue as closed and solved.
|
||||
7. Upon completion of review and testing, contributions will be merged into master.
|
||||
When the PR is approved and merged, Github will automatically mark the issue as closed and fixed.
|
||||
|
||||
8. The fix will be included in the next release or immediately in a new release if so warranted.
|
||||
|
||||
|
||||
### Policy
|
||||
@@ -36,5 +45,5 @@ To contribute to the project, please follow the following instructions:
|
||||
While questions, suggestions, enhancement requests, and bug fixes are welcome and almost always implemented,
|
||||
it's not possible to accept PRs without an accompanying issue documention which justifies the change.
|
||||
|
||||
|
||||
Thanks,
|
||||
Chuck
|
||||
|
||||
Binary file not shown.
1565
DBRepair.sh
1565
DBRepair.sh
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,9 @@
|
||||
Copyright (c) 2022, ChuckPa.
|
||||
Copyright (c) 2025, ChuckPa.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to use the Software for personal use only. The Software may not be modified, merged, published, distributed, sublicensed, and/or used in other form without the express written consent of the author.
|
||||
This software is the intellectual property of ChuckPa
|
||||
and used in conjunction with Plex Media Server (which is Copyright & Trademark of Plex, Inc.)
|
||||
|
||||
It may not be copied, modified, or redistributed in any way without expressly written permission of ChuckPa.
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
699
README.md
699
README.md
@@ -1,16 +1,12 @@
|
||||
# PlexDBRepair
|
||||
# DBRepair (for Plex Media Server)
|
||||
|
||||
[](https://github.com/ChuckPa/PlexDBRepair/issues)
|
||||
[](https://github.com/ChuckPa/PlexDBRepair/releases/latest)
|
||||
[](https://github.com/ChuckPa/PlexDBRepair/releases/latest)
|
||||
[](https://github.com/ChuckPa/PlexDBRepair/releases)
|
||||
[](https://github.com/ChuckPa/DBRepair/issues)
|
||||
[](https://github.com/ChuckPa/DBRepair/releases/latest)
|
||||
[](https://github.com/ChuckPa/DBRepair/releases/latest)
|
||||
[](https://github.com/ChuckPa/DBRepair/releases)
|
||||
[]('')
|
||||

|
||||
|
||||
# Introduction
|
||||
|
||||
DBRepair provides database repair and maintenance for the most common Plex Media Server database problems.
|
||||
It is a simple menu-driven utility with a command line backend.
|
||||
|
||||
DBRepair is run from a command line (terminal or ssh/putty session) which has sufficient privilege to read/write the databases (minimum).
|
||||
If sufficient privleges exist (root), and supported by the environment, the options to start and stop PMS are presented as well.
|
||||
@@ -22,6 +18,9 @@ If sufficient privleges exist (root), and supported by the environment, the opti
|
||||
2. Database is malformed / damaged / corrupted
|
||||
3. Database has bloated from media addition or changes
|
||||
4. Damaged indexes damaged
|
||||
5. Importing data from other databases (watch history)
|
||||
6. Cleaning up operational temp files which PMS has forgotten to clean up
|
||||
|
||||
|
||||
## Functions provided
|
||||
|
||||
@@ -30,11 +29,15 @@ If sufficient privleges exist (root), and supported by the environment, the opti
|
||||
|
||||
The following commands (or their number), listed in alphabetical order, are accepted as input.
|
||||
```
|
||||
AUTO(matic) - Automatically check, repair/optimize, and reindex the databases in one step.
|
||||
CHEC(k) - Check the main and blob databases integrity
|
||||
AUTO(matic) - Automatically check, repair/optimize, reindex, and FTS rebuild in one step.
|
||||
CHEC(k) - Check the main and blob databases integrity (includes FTS index check)
|
||||
DEFL(ate) - Deflate a bloated PMS database (faulty statistics data)
|
||||
EXIT - Exit the utility
|
||||
IGNOre/HONOr - Ignore/Honor constraint errors when IMPORTing additional data into DB.
|
||||
IMPO(rt) - Import viewstate / watch history from another database
|
||||
REIN(dex) - Rebuild the database indexes
|
||||
PRUN(e) - Prune (remove) old image files from transcoder cache diretory
|
||||
PURG(e) - Purge (delete) all temporary files left behind by PMS & the transcoder from the temp directory
|
||||
REIN(dex) - Rebuild the database indexes (includes FTS indexes)
|
||||
REPL(ace) - Replace the existing databases with a PMS-generated backup
|
||||
SHOW - Show the log file
|
||||
STAR(t) - Start PMS (not available on all platforms)
|
||||
@@ -49,27 +52,35 @@ If sufficient privleges exist (root), and supported by the environment, the opti
|
||||
For clarity, each command's name is 'quoted'.
|
||||
|
||||
```
|
||||
Plex Media Server Database Repair Utility (_host_configuration_name_)
|
||||
Version v1.0
|
||||
Database Repair Utility for Plex Media Server (_host_configuration_name_)
|
||||
Version v1.09.00
|
||||
|
||||
Select
|
||||
|
||||
1 - 'stop' PMS (if available)
|
||||
2 - 'automatic' database check, repair/optimize, and reindex in one step.
|
||||
3 - 'check' database
|
||||
4 - 'vacuum' database
|
||||
5 - 'repair' / 'optimize' database
|
||||
6 - 'reindex' database
|
||||
7 - 'start' PMS (if available)
|
||||
8 - 'import' viewstate (Watch history) from another PMS database
|
||||
9 - 'replace' current database with newest usable backup copy (interactive)
|
||||
10 - 'show' logfile
|
||||
11 - 'status' of PMS (Stop/Run and databases)
|
||||
12 - 'undo' - Undo last successful command
|
||||
1 - 'stop' - Stop PMS.
|
||||
2 - 'automatic' - Check, Repair/Optimize, Reindex, and FTS rebuild in one step.
|
||||
3 - 'check' - Perform integrity check of database and FTS indexes.
|
||||
4 - 'vacuum' - Remove empty space from database without optimizing.
|
||||
5 - 'repair' - Repair/Optimize databases.
|
||||
6 - 'reindex' - Rebuild database indexes.
|
||||
7 - 'start' - Start PMS
|
||||
|
||||
99 - exit
|
||||
8 - 'import' - Import watch history from another database independent of Plex. (risky).
|
||||
9 - 'replace' - Replace current databases with newest usable backup copy (interactive).
|
||||
10 - 'show' - Show logfile.
|
||||
11 - 'status' - Report status of PMS (run-state and databases).
|
||||
12 - 'undo' - Undo last successful command.
|
||||
|
||||
Enter command # -or- command name (4 char min) :
|
||||
21 - 'prune' - Prune (remove) old image files (jpeg,jpg,png) from PhotoTranscoder cache.
|
||||
22 - 'purge' - Purge (delete) all temporary files left behind by PMS & the transcoder.
|
||||
23 - 'deflate' - Deflate a bloated PMS main database.
|
||||
42 - 'ignore' - Ignore duplicate/constraint errors.
|
||||
|
||||
88 - 'update' - Check for updates.
|
||||
98 - 'quit' - Quit immediately. Keep all temporary files.
|
||||
99 'exit' - Exit with cleanup options.
|
||||
|
||||
Enter command # -or- command name (4 char min) :
|
||||
|
||||
|
||||
|
||||
@@ -83,14 +94,24 @@ If sufficient privleges exist (root), and supported by the environment, the opti
|
||||
- BINHEX
|
||||
- HOTIO
|
||||
- Podman (libgpod)
|
||||
4. Linux workstation & server
|
||||
5. Netgear (OS5 Linux-based systems)
|
||||
6. QNAP (QTS & QuTS)
|
||||
7. Synology (DSM 6 & DSM 7)
|
||||
8. Western Digital (OS5)
|
||||
4. FreeBSD (14+)
|
||||
5. Linux workstation & server
|
||||
6. MacOS
|
||||
7. Netgear (OS5 Linux-based systems)
|
||||
8. QNAP (QTS & QuTS)
|
||||
9. Synology (DSM 6 & DSM 7)
|
||||
10. Western Digital (OS5)
|
||||
```
|
||||
# Installation
|
||||
|
||||
### Downloading
|
||||
Download DBRepair.sh (if you want just the script)
|
||||
# This overwrites any existing version. Remove "-O DBRepair.sh" to not overwrite.
|
||||
```
|
||||
wget -O DBRepair.sh https://github.com/ChuckPa/PlexDBRepair/releases/latest/download/DBRepair.sh
|
||||
```
|
||||
### Moving the downloaded DBRepair.sh
|
||||
|
||||
Where to place the utility varies from host to host.
|
||||
Please use this table as a reference.
|
||||
|
||||
@@ -102,24 +123,45 @@ If sufficient privleges exist (root), and supported by the environment, the opti
|
||||
-------------------+---------------------+------------------------------------------
|
||||
Apple | Downloads | ~/Downloads
|
||||
Arch Linux | N/A | Anywhere
|
||||
ASUSTOR | Public | /volume1/Public
|
||||
binhex | N/A | Container root (adjacent /config)
|
||||
Docker | N/A | Container root (adjacent /config)
|
||||
ASUSTOR | Plex | /volume1/Plex
|
||||
Binhex | N/A | Container root (adjacent /config)
|
||||
Docker (Plex,LSIO) | N/A | Container root (adjacent /config)
|
||||
Hotio | N/A | Container root (adjacent /config)
|
||||
FreeBSD (14+) | N/A | Anywhere
|
||||
Kubernetes | N/A | Container root (adjacent /config)
|
||||
Linux (wkstn/svr) | N/A | Anywhere
|
||||
MacOS | N/A | Anywhere
|
||||
Netgear (ReadyNAS) | "your_choice" | "/data/your_choice"
|
||||
QNAP (QTS/QuTS) | Public | /share/Public
|
||||
SNAP | N/A | Anywhere
|
||||
Synology (DSM 6) | Plex | /volume1/Plex (change volume as required)
|
||||
Synology (DSM 7) | PlexMediaServer | /volume1/PlexMediaServer (change volume as required)
|
||||
Western Digital | Public | /mnt/HD/HD_a2/Public (Does not support 'MyCloudHome' series)
|
||||
```
|
||||
Plex,inc and LSIO docker images are included in "Docker" platform category independent of the actual host.
|
||||
|
||||
Additional hosts and docker images can easily be supported in almost all cases with appropriate path
|
||||
information. Please contact me as needed.
|
||||
|
||||
### Gettting a usable command line session
|
||||
1. Accessing a NAS
|
||||
Open a terminal/command line window on your computer.
|
||||
type: ssh admin-username@IP.addr.of.NAS
|
||||
|
||||
2. Linux
|
||||
Open a terminal session and elevate to the root (sudo) user
|
||||
|
||||
3. Windows -- for Windows PMS hosts
|
||||
Open a Command window
|
||||
Follow the instructions for the Windows version of DBRepair
|
||||
|
||||
|
||||
### General installation and usage instructions
|
||||
|
||||
1. Open your browser to https://github.com/ChuckPa/PlexDBRepair/releases/latest
|
||||
1. Open your browser to https://github.com/ChuckPa/DBRepair/releases/latest
|
||||
2. Download the source code (tar.gz or ZIP) file
|
||||
|
||||
3. Knowing the file name will always be of the form 'PlexDBRepair-X.Y.Z.tar.gz'
|
||||
3. Knowing the file name will always be of the form 'DBRepair-X.Y.Z.tar.gz'
|
||||
-- where X.Y.Z is the release number. Use the real values in place of X, Y, and Z.
|
||||
4. Place the tar.gz file in the appropriate directory on the system you'll use it.
|
||||
5. Open a command line session (usually Terminal or SSH)
|
||||
@@ -131,13 +173,15 @@ If sufficient privleges exist (root), and supported by the environment, the opti
|
||||
|
||||
|
||||
|
||||
### (In the examples below, substitute "x.y.z" with the actual version you download/use)
|
||||
|
||||
### EXAMPLE: To install & launch on Synology DSM 6 / DSM 7
|
||||
|
||||
|
||||
cd /volume1/Plex # use /volume1/PlexMediaServer on DSM 7
|
||||
sudo bash
|
||||
tar xf PlexDBRepair-x.y.z.tar.gz
|
||||
cd PlexDBRepair-x.y.z
|
||||
tar xf DBRepair-x.y.z.tar.gz
|
||||
cd DBRepair-x.y.z
|
||||
chmod +x DBRepair.sh
|
||||
./DBRepair.sh
|
||||
|
||||
@@ -148,8 +192,8 @@ If sufficient privleges exist (root), and supported by the environment, the opti
|
||||
sudo docker exec -it plex /bin/bash
|
||||
|
||||
# extract from downloaded version file name then cd into directory
|
||||
tar xf PlexDBRepair-1.0.0.tar.gz
|
||||
cd PlexDBRepair-1.0.0
|
||||
tar xf DBRepair-x.y.z.tar.gz
|
||||
cd DBRepair-x.y.z
|
||||
chmod +x DBRepair.sh
|
||||
./DBRepair.sh
|
||||
```
|
||||
@@ -157,8 +201,8 @@ If sufficient privleges exist (root), and supported by the environment, the opti
|
||||
```
|
||||
sudo bash
|
||||
cd /path/to/DBRepair.tar
|
||||
tar xf PlexDBRepair-1.0.0.tar.gz
|
||||
cd PlexDBRepair-1.0.0
|
||||
tar xf DBRepair-x.y.z.tar.gz
|
||||
cd DBRepair-x.y.z
|
||||
chmod +x DBRepair.sh
|
||||
./DBRepair.sh stop auto start exit
|
||||
```
|
||||
@@ -167,8 +211,8 @@ If sufficient privleges exist (root), and supported by the environment, the opti
|
||||
```
|
||||
osascript -e 'quit app "Plex Media Server"'
|
||||
cd ~/Downloads
|
||||
tar xvf PlexDBRepai PlexDBRepair-1.0.0.tar.gz
|
||||
cd PlexDBRepair-1.0.0
|
||||
tar xf DBRepair-x.y.z.tar.gz
|
||||
cd DBRepair-x.y.z
|
||||
|
||||
chmod +x DBRepair.sh
|
||||
./DBRepair.sh
|
||||
@@ -200,28 +244,38 @@ These examples
|
||||
|
||||
C. Database is malformed - No Backups
|
||||
1. (3) Check - Confirm either main or blobs database is damaged
|
||||
2. (5) Repair - Salavage as much as possible from the databases and rebuild them into a usable database.
|
||||
2. (5) Repair - Salvage as much as possible from the databases and rebuild them into a usable database.
|
||||
3. (6) Reindex - Generate new indexes so PMS doesn't need to at startup
|
||||
4. (99) Exit
|
||||
|
||||
C. Database sizes excessively large when compared to amount of media indexed (item count)
|
||||
1. (3) Check - Make certain both databases are fully intact (repair if needed)
|
||||
2. (4) Vacuum - Instruct SQLite to rebuild its tables and recover unused space.
|
||||
3. (6) Reindex - Rebuild Indexes.
|
||||
D. Database sizes excessively large/bloated when compared to amount of media indexed (item count)
|
||||
1. (23) Deflate - Correct the known problem with a database table and recover the wasted space.
|
||||
2. (2) Auto - Perform automated check, repair, and reindex of the deflated database
|
||||
4. (99) Exit
|
||||
|
||||
D. User interface has become 'sluggish' as more media was added
|
||||
E. User interface has become 'sluggish' as more media was added
|
||||
1. (3) Check - Confirm there is no database damage
|
||||
2. (5) Repair - You are not really repairing. You are rebuilding the DB in perfect sorted order.
|
||||
3. (6) Reindex - Rebuild Indexes.
|
||||
4. (99) Exit
|
||||
|
||||
E. Undo
|
||||
F. Undo
|
||||
Undo is a special case where you need the utility to backup ONE step.
|
||||
This is rarely needed. The only time you might want/need to backup one step is if Replace leaves you worse off
|
||||
than you were before. In this case, UNDO then Repair. Undo can only undo the single most-recent action.
|
||||
(Note: In a future release, you will be able to 'undo' every action taken until the DBs are in their original state)
|
||||
|
||||
G. HTTP 500 errors when adding to collections / FTS index corruption
|
||||
This occurs when standard integrity_check passes but FTS (Full-Text Search) indexes are corrupted.
|
||||
Symptoms: Adding items to collections fails, updating metadata fails, "database disk image is malformed"
|
||||
during UPDATE operations even though Check reports databases are OK.
|
||||
|
||||
1. (3) Check - Will show "FTS index damaged" message
|
||||
2. (6) Reindex - Rebuild indexes including FTS
|
||||
3. (99) Exit
|
||||
|
||||
Alternatively, use (2) Automatic which will detect and repair FTS issues automatically.
|
||||
|
||||
Special considerations:
|
||||
|
||||
1. As stated above, this utility requires PMS to be stopped in order to do what it does.
|
||||
@@ -246,18 +300,29 @@ Attention:
|
||||
-- This is for when DB operations keep getting worse and you don't know what to do.
|
||||
"99" is an old 'Get Smart' TV series reference where agent 99 would try to save agent 86 from harm.
|
||||
|
||||
"99" was originally going to be "Quit immediately save all files" but development feedback
|
||||
resulted in this configuration
|
||||
Community feedback has resulted in:
|
||||
|
||||
"Exit" is the preferred method to leave.
|
||||
|
||||
"Quit" was desired instead of "99" but there are those who didn't understand the difference or references.
|
||||
|
||||
If community feedback wants both "Quit. save temps" and "Exit, delete temps", behavior is easily changed.
|
||||
"99" or "Exit" - Preferred way to exit and cleanup temp databases
|
||||
"98" or "Quit" - Get out now without deleting the temp databases (Usually used only during unexpected failures)
|
||||
|
||||
Also please be aware the script understands interactive versus scripted mode.
|
||||
|
||||
|
||||
## Command line options
|
||||
|
||||
To avoid confusion and making the menu complicated, a few command line options have been added.
|
||||
|
||||
To use DBRepair when the container / host cannot be identified, --sqlite and --databases allow you
|
||||
to specify the pathnames from whichever context (namespace) DBRepair will be running in.
|
||||
|
||||
--sqlite Specify the path to "Plex SQLite" (diretory or full path)
|
||||
|
||||
--databases Specify the path to the directory which contains the PMS databases.
|
||||
|
||||
Both must be used together.
|
||||
|
||||
When operating with these options, DBRepair will indicate it's in Manual configuration mode.
|
||||
You may still use other command line commands (batch mode) or use it normally in interactive mode.
|
||||
|
||||
## Scripting support
|
||||
|
||||
@@ -274,6 +339,8 @@ Attention:
|
||||
|
||||
This executes: Stop PMS, Automatic (Check, Repair, Reindex), Start PMS, and Exit commands
|
||||
|
||||
Beginning with v1.06.00, the 'exit' command is optional when scripted.
|
||||
|
||||
|
||||
## Exiting
|
||||
|
||||
@@ -294,50 +361,63 @@ bash-4.4# ./DBRepair.sh
|
||||
|
||||
|
||||
|
||||
Plex Media Server Database Repair Utility (Synology (DSM 7))
|
||||
Version v1.0.0
|
||||
|
||||
Database Repair Utility for Plex Media Server (Ubuntu 20.04.6 LTS)
|
||||
Version v1.03.01
|
||||
|
||||
Select
|
||||
|
||||
1 - 'stop' - Stop PMS
|
||||
2 - 'automatic' - database check, repair/optimize, and reindex in one step.
|
||||
3 - 'check' - Perform integrity check of database
|
||||
4 - 'vacuum' - Remove empty space from database
|
||||
5 - 'repair' - Repair/Optimize databases
|
||||
6 - 'reindex' - Rebuild database database indexes
|
||||
1 - 'stop' - Stop PMS.
|
||||
2 - 'automatic' - Check, Repair/Optimize, and Reindex Database in one step.
|
||||
3 - 'check' - Perform integrity check of database.
|
||||
4 - 'vacuum' - Remove empty space from database without optimizing.
|
||||
5 - 'repair' - Repair/Optimize databases.
|
||||
6 - 'reindex' - Rebuild database indexes.
|
||||
7 - 'start' - Start PMS
|
||||
|
||||
8 - 'import' - Import watch history from another database independent of Plex. (risky)
|
||||
9 - 'replace' - Replace current databases with newest usable backup copy (interactive)
|
||||
10 - 'show' - Show logfile
|
||||
11 - 'status' - Report status of PMS (run-state and databases)
|
||||
12 - 'undo' - Undo last successful command
|
||||
8 - 'import' - Import watch history from another database independent of Plex. (risky).
|
||||
9 - 'replace' - Replace current databases with newest usable backup copy (interactive).
|
||||
10 - 'show' - Show logfile.
|
||||
11 - 'status' - Report status of PMS (run-state and databases).
|
||||
12 - 'undo' - Undo last successful command.
|
||||
|
||||
99 - exit
|
||||
21 - 'prune' - Remove old image files (jpeg,jpg,png) from PhotoTranscoder cache & all temp files left by PMS.
|
||||
22 - 'purge' - Remove unused temp files.
|
||||
23 - 'deflate' - Deflate a bloated PMS main database.
|
||||
42 - 'ignore' - Ignore duplicate/constraint errors.
|
||||
|
||||
Enter command # -or- command name (4 char min) : 1
|
||||
88 - 'update' - Check for updates.
|
||||
98 - 'quit' - Quit immediately. Keep all temporary files.
|
||||
99 - 'exit' - Exit with cleanup options.
|
||||
|
||||
Enter command # -or- command name (4 char min) : 1
|
||||
|
||||
Stopping PMS.
|
||||
Stopped PMS.
|
||||
|
||||
Select
|
||||
|
||||
1 - 'stop' - Stop PMS
|
||||
2 - 'automatic' - database check, repair/optimize, and reindex in one step.
|
||||
3 - 'check' - Perform integrity check of database
|
||||
4 - 'vacuum' - Remove empty space from database
|
||||
5 - 'repair' - Repair/Optimize databases
|
||||
6 - 'reindex' - Rebuild database database indexes
|
||||
1 - 'stop' - Stop PMS.
|
||||
2 - 'automatic' - Check, Repair/Optimize, and Reindex Database in one step.
|
||||
3 - 'check' - Perform integrity check of database.
|
||||
4 - 'vacuum' - Remove empty space from database without optimizing.
|
||||
5 - 'repair' - Repair/Optimize databases.
|
||||
6 - 'reindex' - Rebuild database indexes.
|
||||
7 - 'start' - Start PMS
|
||||
|
||||
8 - 'import' - Import watch history from another database independent of Plex. (risky)
|
||||
9 - 'replace' - Replace current databases with newest usable backup copy (interactive)
|
||||
10 - 'show' - Show logfile
|
||||
11 - 'status' - Report status of PMS (run-state and databases)
|
||||
12 - 'undo' - Undo last successful command
|
||||
8 - 'import' - Import watch history from another database independent of Plex. (risky).
|
||||
9 - 'replace' - Replace current databases with newest usable backup copy (interactive).
|
||||
10 - 'show' - Show logfile.
|
||||
11 - 'status' - Report status of PMS (run-state and databases).
|
||||
12 - 'undo' - Undo last successful command.
|
||||
|
||||
99 - exit
|
||||
21 - 'prune' - Remove old image files (jpeg,jpg,png) from PhotoTranscoder cache & all temp files left by PMS.
|
||||
22 - 'purge' - Remove unused temp files.
|
||||
23 - 'deflate' - Deflate a bloated PMS main database.
|
||||
42 - 'ignore' - Ignore duplicate/constraint errors.
|
||||
|
||||
88 - 'update' - Check for updates.
|
||||
98 - 'quit' - Quit immediately. Keep all temporary files.
|
||||
99 - 'exit' - Exit with cleanup options.
|
||||
|
||||
Enter command # -or- command name (4 char min) : auto
|
||||
|
||||
@@ -372,19 +452,28 @@ Automatic Check,Repair/optimize,Index successful.
|
||||
|
||||
Select
|
||||
|
||||
1 - 'stop' PMS
|
||||
2 - 'automatic' database check, repair/optimize, and reindex in one step.
|
||||
3 - 'check' database
|
||||
4 - 'vacuum' database
|
||||
5 - 'repair' / 'optimize' database
|
||||
6 - 'reindex' database
|
||||
7 - 'start' PMS
|
||||
8 - 'import' viewstate (Watch history) from another PMS database
|
||||
9 - 'replace' current database with newest usable backup copy (interactive)
|
||||
10 - 'show' logfile
|
||||
11 - 'status' of PMS (Stop/Run and databases)
|
||||
12 - 'undo' - Undo last successful command
|
||||
99 - exit
|
||||
1 - 'stop' - Stop PMS.
|
||||
2 - 'automatic' - Check, Repair/Optimize, and Reindex Database in one step.
|
||||
3 - 'check' - Perform integrity check of database.
|
||||
4 - 'vacuum' - Remove empty space from database without optimizing.
|
||||
5 - 'repair' - Repair/Optimize databases.
|
||||
6 - 'reindex' - Rebuild database indexes.
|
||||
7 - 'start' - Start PMS
|
||||
|
||||
8 - 'import' - Import watch history from another database independent of Plex. (risky).
|
||||
9 - 'replace' - Replace current databases with newest usable backup copy (interactive).
|
||||
10 - 'show' - Show logfile.
|
||||
11 - 'status' - Report status of PMS (run-state and databases).
|
||||
12 - 'undo' - Undo last successful command.
|
||||
|
||||
21 - 'prune' - Remove old image files (jpeg,jpg,png) from PhotoTranscoder cache & all temp files left by PMS.
|
||||
22 - 'purge' - Remove unused temp files.
|
||||
23 - 'deflate' - Deflate a bloated PMS main database.
|
||||
42 - 'ignore' - Ignore duplicate/constraint errors.
|
||||
|
||||
88 - 'update' - Check for updates.
|
||||
98 - 'quit' - Quit immediately. Keep all temporary files.
|
||||
99 - 'exit' - Exit with cleanup options.
|
||||
|
||||
Enter command # -or- command name (4 char min) : start
|
||||
|
||||
@@ -393,19 +482,28 @@ Started PMS
|
||||
|
||||
Select
|
||||
|
||||
1 - 'stop' PMS
|
||||
2 - 'automatic' database check, repair/optimize, and reindex in one step.
|
||||
3 - 'check' database
|
||||
4 - 'vacuum' database
|
||||
5 - 'repair' / 'optimize' database
|
||||
6 - 'reindex' database
|
||||
7 - 'start' PMS
|
||||
8 - 'import' viewstate (Watch history) from another PMS database
|
||||
9 - 'replace' current database with newest usable backup copy (interactive)
|
||||
10 - 'show' logfile
|
||||
11 - 'status' of PMS (Stop/Run and databases)
|
||||
12 - 'undo' - Undo last successful command
|
||||
99 - exit
|
||||
1 - 'stop' - Stop PMS.
|
||||
2 - 'automatic' - Check, Repair/Optimize, and Reindex Database in one step.
|
||||
3 - 'check' - Perform integrity check of database.
|
||||
4 - 'vacuum' - Remove empty space from database without optimizing.
|
||||
5 - 'repair' - Repair/Optimize databases.
|
||||
6 - 'reindex' - Rebuild database indexes.
|
||||
7 - 'start' - Start PMS
|
||||
|
||||
8 - 'import' - Import watch history from another database independent of Plex. (risky).
|
||||
9 - 'replace' - Replace current databases with newest usable backup copy (interactive).
|
||||
10 - 'show' - Show logfile.
|
||||
11 - 'status' - Report status of PMS (run-state and databases).
|
||||
12 - 'undo' - Undo last successful command.
|
||||
|
||||
21 - 'prune' - Remove old image files (jpeg,jpg,png) from PhotoTranscoder cache & all temp files left by PMS.
|
||||
22 - 'purge' - Remove unused temp files.
|
||||
23 - 'deflate' - Deflate a bloated PMS main database.
|
||||
42 - 'ignore' - Ignore duplicate/constraint errors.
|
||||
|
||||
88 - 'update' - Check for updates.
|
||||
98 - 'quit' - Quit immediately. Keep all temporary files.
|
||||
99 - 'exit' - Exit with cleanup options.
|
||||
|
||||
Enter command # -or- command name (4 char min) : stat
|
||||
|
||||
@@ -417,19 +515,28 @@ Status report: Sat Feb 25 04:38:50 PM EST 2023
|
||||
|
||||
Select
|
||||
|
||||
1 - 'stop' PMS
|
||||
2 - 'automatic' database check, repair/optimize, and reindex in one step.
|
||||
3 - 'check' database
|
||||
4 - 'vacuum' database
|
||||
5 - 'repair' / 'optimize' database
|
||||
6 - 'reindex' database
|
||||
7 - 'start' PMS
|
||||
8 - 'import' viewstate (Watch history) from another PMS database
|
||||
9 - 'replace' current database with newest usable backup copy (interactive)
|
||||
10 - 'show' logfile
|
||||
11 - 'status' of PMS (Stop/Run and databases)
|
||||
12 - 'undo' - Undo last successful command
|
||||
99 - exit
|
||||
1 - 'stop' - Stop PMS.
|
||||
2 - 'automatic' - Check, Repair/Optimize, and Reindex Database in one step.
|
||||
3 - 'check' - Perform integrity check of database.
|
||||
4 - 'vacuum' - Remove empty space from database without optimizing.
|
||||
5 - 'repair' - Repair/Optimize databases.
|
||||
6 - 'reindex' - Rebuild database indexes.
|
||||
7 - 'start' - Start PMS
|
||||
|
||||
8 - 'import' - Import watch history from another database independent of Plex. (risky).
|
||||
9 - 'replace' - Replace current databases with newest usable backup copy (interactive).
|
||||
10 - 'show' - Show logfile.
|
||||
11 - 'status' - Report status of PMS (run-state and databases).
|
||||
12 - 'undo' - Undo last successful command.
|
||||
|
||||
21 - 'prune' - Remove old image files (jpeg,jpg,png) from PhotoTranscoder cache & all temp files left by PMS.
|
||||
22 - 'purge' - Remove unused temp files.
|
||||
23 - 'deflate' - Deflate a bloated PMS main database.
|
||||
42 - 'ignore' - Ignore duplicate/constraint errors.
|
||||
|
||||
88 - 'update' - Check for updates.
|
||||
98 - 'quit' - Quit immediately. Keep all temporary files.
|
||||
99 - 'exit' - Exit with cleanup options.
|
||||
|
||||
Enter command # -or- command name (4 char min) : exit
|
||||
|
||||
@@ -447,8 +554,8 @@ root@lizum:/sata/plex/Plex Media Server/Plug-in Support/Databases# ./DBRepair.sh
|
||||
|
||||
|
||||
|
||||
Plex Media Server Database Repair Utility (Ubuntu 20.04.5 LTS)
|
||||
Version v1.0.0
|
||||
Database Repair Utility for Plex Media Server (Ubuntu 20.04.5 LTS)
|
||||
Version v1.03.01
|
||||
|
||||
|
||||
[2023-03-05 18.53.49] Stopping PMS.
|
||||
@@ -571,22 +678,61 @@ root@lizum:/sata/plex/Plex Media Server/Plug-in Support/Databases#
|
||||
|
||||
Checks the integrity of the Plex main and blobs databases.
|
||||
|
||||
Also performs FTS (Full-Text Search) index integrity checks. FTS indexes can become
|
||||
corrupted even when standard integrity checks pass, causing operations like adding
|
||||
items to collections to fail with "database disk image is malformed" errors.
|
||||
|
||||
If FTS corruption is detected, use 'reindex' (option 6) or 'automatic' (option 2)
|
||||
to rebuild the FTS indexes.
|
||||
|
||||
### Deflate
|
||||
|
||||
Repairs a known error in the PMS main database "statistics_bandwidth" table.
|
||||
After repairing it, it purges all the errant data from the table (reducing DB size)
|
||||
|
||||
This task can take a significant amount of time. It's frequently used when the DB size is an order of magnitude above what it should be (e.g. 31 GB vs 206 MB). Reductions from 134 GB to 210 MB have been realized.
|
||||
|
||||
### Exit
|
||||
|
||||
Exits the utility and removes all temporary database files created during processing.
|
||||
To save all intermediate databases, use the 'Quit' command.
|
||||
|
||||
### Ignore / Honor
|
||||
|
||||
Toggle the state (ON/OFF) of the IGNORE flag. When ON, Duplicates and UNIQUE constraint errors will be ignored.
|
||||
Caution is advised as other errors will be ignored during initial processing.
|
||||
|
||||
In ALL cases, DBRepair will never allow a bad database to be created.
|
||||
|
||||
### Import
|
||||
|
||||
Imports (raw) watch history from another PMS database without ability to check validity
|
||||
( This can have side effects of "negative watch count" being displayed. Caution is advised. )
|
||||
|
||||
### Prune (Remove)
|
||||
|
||||
Checks the PhotoTransoder cache directory for JPG, JPEG, and PNG files older than 30 days and removes them.
|
||||
Under normal operation, PMS manages this automatically.
|
||||
Under certain conditions, PMS will fail to prune them (which is run during Scheduled Maintenance)
|
||||
This command allows you to manually remove what PMS would do normally during that Scheduled Maintenance.
|
||||
|
||||
#### Warning: Initial pruning might take longer than expected.
|
||||
Execution time, using a Synology DS418 as benchmark, is approximately 100,000 image files per 2 minutes.
|
||||
|
||||
|
||||
|
||||
For now, Both forms "Prune" and "Remove" are accepted.
|
||||
|
||||
### Reindex
|
||||
|
||||
Rebuilds the database indexes after an import, repair, or replace operation.
|
||||
These indexes are used by PMS for searching (both internally and your typed searches)
|
||||
|
||||
Also checks FTS (Full-Text Search) index integrity and rebuilds if corruption is detected.
|
||||
FTS indexes can become corrupted even when standard integrity checks pass, causing
|
||||
"database disk image is malformed" errors during UPDATE operations (e.g., adding items
|
||||
to collections, updating metadata).
|
||||
|
||||
### Repair
|
||||
|
||||
Extracts/recovers all the usable data from the existing databases into text (SQL ascii) form.
|
||||
@@ -644,6 +790,10 @@ root@lizum:/sata/plex/Plex Media Server/Plug-in Support/Databases#
|
||||
After DB tasks are completed, and you've exited the container, restart it normally through
|
||||
your normal 'docker start' mechanism.
|
||||
|
||||
If your container (Image) supports start/stop , it will be shown in the menu for you to use.
|
||||
If not, you'll need to disable health checks before safely running this tool.
|
||||
|
||||
|
||||
### Undo
|
||||
|
||||
Undo allows you to "Undo" the last Import, Repair, Replace, or Vacuum command.
|
||||
@@ -655,4 +805,293 @@ root@lizum:/sata/plex/Plex Media Server/Plug-in Support/Databases#
|
||||
Instructs SQLite to remove the empty/deleted records and gaps from the databases.
|
||||
This is most beneficial after deleting whole library sections.
|
||||
|
||||
###
|
||||
For most users, the "automatic" command is the best method. It will regenerate the SQLite indexes
|
||||
as part of the process.
|
||||
|
||||
# Environment Variables
|
||||
|
||||
DBRepair now supports the use of environment variables to allow customization of some operations.
|
||||
|
||||
#### WARNING: Use of these variables may adverse impact PMS operation or performance. USE WITH CAUTION.
|
||||
|
||||
## DBREPAIR_CACHEAGE - Specify the maximum age for PhotoTrancoder Cache images to be retained
|
||||
|
||||
Default DBREPAIR_CACHEAGE is set at 30 days.
|
||||
|
||||
You may override this by setting DBREPAIR_CACHEAGE=N, where N is the number of days worth of cache image
|
||||
you wish to retain.
|
||||
|
||||
When using interactively, DBRepair will prompt you to confirm OK to remove and show you the cache age
|
||||
|
||||
Example: export DBREPAIR_CACHEAGE=20
|
||||
```
|
||||
Enter command # -or- command name (4 char min) : remove
|
||||
|
||||
Counting how many files are more than 20 days old.
|
||||
OK to prune 4497 files? (Y/N) ?
|
||||
```
|
||||
|
||||
## DBREPAIR_PAGESIZE - Allows setting the Plex SQLite 'page_size'.
|
||||
|
||||
Normal Linux (ext4, xfs) filesystems do not need this customization because the filesystem block size = 4096.
|
||||
ZFS users sometimes need to customize their datasets to improve I/O performance (HDDs vs SSDs).
|
||||
This capability allows them to compensate for some of those losses.
|
||||
|
||||
If present, sets the Plex SQLite 'page_size' for both Main and Blobs databases.
|
||||
If not present, the default page size for the host OS is used (typically 4096 to match the OS page size).
|
||||
|
||||
When in use, you will see the message: "Setting Plex SQLite page size ($DbPageSize)"
|
||||
This will be shown on the console output and reported in the logfile.
|
||||
|
||||
|
||||
### Constraints:
|
||||
|
||||
1. Must be a power of 2, with 1024 (2^10) as the Plex default. (per SQLite 3.12.0 documentation).
|
||||
Any invalid value provided will be rounded up to the next integral value (1024, 2048, 4096 ... 65536)
|
||||
This may or may not be the value you intended. Caution is advised.
|
||||
|
||||
2. May not exceed 65536 (per SQLite 3.12.0 documentation).
|
||||
Any value exceeding 65536 will be truncated to 65536.
|
||||
|
||||
### Validation
|
||||
|
||||
If the value of DBREPAIR_PAGESIZE is not compliant with requirements, a new value will be selected.
|
||||
|
||||
Typing errors will be rounded up (e.g 65535 vs 65536) to the next multiple of 1024 before validation
|
||||
is performed.
|
||||
|
||||
If the value is invalid, an error will be printed and recorded in the logfile. The next higher power
|
||||
of two will be selected.
|
||||
|
||||
If the value is too large, it will be reduced to the SQLite maximum of 65536.
|
||||
|
||||
### Management
|
||||
|
||||
If you attempt to optimize your database but find the resultant performance is not to your liking,
|
||||
you may try another value and run "automatic" again.
|
||||
|
||||
If you ultimately decide to run with the default values (4096),
|
||||
1. Remove the environment variable.
|
||||
2. Run DBRepair again using "automatic". Your databases will revert to the host OS's default.
|
||||
|
||||
### Usage: (QNAP example shown)
|
||||
|
||||
```
|
||||
# export DBREPAIR_PAGESIZE=65534
|
||||
# ./DBRepair.sh stop deflate auto start exit
|
||||
|
||||
|
||||
|
||||
Database Repair Utility for Plex Media Server (QNAP)
|
||||
Version v1.12.00
|
||||
|
||||
|
||||
[2025-10-21 16.54.00] PMS already stopped.
|
||||
|
||||
[2025-10-21 16.54.00] Check and Deflate started.
|
||||
[2025-10-21 16.54.00]
|
||||
[2025-10-21 16.54.00] Checking the PMS databases
|
||||
[2025-10-21 16.54.19] Check complete. PMS main database is OK.
|
||||
[2025-10-21 16.54.21] Check complete. PMS blobs database is OK.
|
||||
[2025-10-21 16.54.21]
|
||||
[2025-10-21 16.54.21] Backup current databases with '-BACKUP-2025-10-21_16.54.21' timestamp.
|
||||
[2025-10-21 16.56.29] Starting Deflate (Part 1 of 2 - Repair database table)
|
||||
[2025-10-21 16.56.29] Estimated completion is approx 6 minutes but is CPU & I/O speed dependent
|
||||
[2025-10-21 16.56.29]
|
||||
[2025-10-21 16.56.30] PMS main database successfully repaired.
|
||||
[2025-10-21 16.56.30] Starting Deflate (Part 2 of 2 - Reduce size)
|
||||
[2025-10-21 16.56.35] PMS main database size reduced.
|
||||
[2025-10-21 16.56.35] Verifying PMS main database.
|
||||
[2025-10-21 16.56.50] Verification complete. PMS main database is OK.
|
||||
[2025-10-21 16.56.50] PMS main database reduced from 31586 MB to 206 MB
|
||||
[2025-10-21 16.56.51] Saving current main database with '-BLOATED-2025-10-21_16.54.21'
|
||||
[2025-10-21 16.56.51] Making deflated database active
|
||||
[2025-10-21 16.56.51] PMS main database deflate completed.
|
||||
[2025-10-21 16.56.51] Deflate successful.
|
||||
[2025-10-21 16.56.51] Recommend running Auto next to complete optimization of new database.
|
||||
|
||||
[2025-10-21 16.56.51] Automatic Check,Repair,Index started.
|
||||
[2025-10-21 16.56.51]
|
||||
[2025-10-21 16.56.51] Checking the PMS databases
|
||||
[2025-10-21 16.57.04] Check complete. PMS main database is OK.
|
||||
[2025-10-21 16.57.05] Check complete. PMS blobs database is OK.
|
||||
[2025-10-21 16.57.05]
|
||||
[2025-10-21 16.57.05] Exporting current databases using timestamp: 2025-10-21_16.56.51
|
||||
[2025-10-21 16.57.05] Exporting Main DB
|
||||
[2025-10-21 16.57.24] Exporting Blobs DB
|
||||
[2025-10-21 16.59.18] Successfully exported the main and blobs databases.
|
||||
[2025-10-21 16.59.18] Start importing into new databases.
|
||||
[2025-10-21 16.59.18] Importing Main DB.
|
||||
[2025-10-21 16.59.18] Setting Plex SQLite page size (65536)
|
||||
[2025-10-21 17.00.19] Importing Blobs DB.
|
||||
[2025-10-21 17.00.19] Setting Plex SQLite page size (65536)
|
||||
[2025-10-21 17.00.32] Successfully imported databases.
|
||||
[2025-10-21 17.00.32] Verifying databases integrity after importing.
|
||||
[2025-10-21 17.01.59] Verification complete. PMS main database is OK.
|
||||
[2025-10-21 17.02.01] Verification complete. PMS blobs database is OK.
|
||||
[2025-10-21 17.02.01] Saving current databases with '-BACKUP-2025-10-21_16.56.51'
|
||||
[2025-10-21 17.02.01] Making repaired databases active
|
||||
[2025-10-21 17.02.01] Repair complete. Please check your library settings and contents for completeness.
|
||||
[2025-10-21 17.02.01] Recommend: Scan Files and Refresh all metadata for each library section.
|
||||
[2025-10-21 17.02.01]
|
||||
[2025-10-21 17.02.01] Backing up of databases
|
||||
[2025-10-21 17.02.01] Backup current databases with '-BACKUP-2025-10-21_17.02.01' timestamp.
|
||||
[2025-10-21 17.02.02] Reindexing main database
|
||||
[2025-10-21 17.02.13] Reindexing main database successful.
|
||||
[2025-10-21 17.02.13] Reindexing blobs database
|
||||
[2025-10-21 17.02.14] Reindexing blobs database successful.
|
||||
[2025-10-21 17.02.14] Reindex complete.
|
||||
[2025-10-21 17.02.14] Automatic Check, Repair/optimize, & Index successful.
|
||||
|
||||
[2025-10-21 17.02.14] Starting PMS.
|
||||
[2025-10-21 17.02.14] Started PMS
|
||||
|
||||
#
|
||||
```
|
||||
|
||||
# Special considerations - MANUAL CONFIGURATION
|
||||
|
||||
Manual configuration is enabled by supplying two command line arguments.
|
||||
These must precede all other options or commands on the command line.
|
||||
|
||||
--sqlite "Directory containing Plex SQLite" (OR) --sqlite "/path/to/Plex SQLite"
|
||||
|
||||
Scripted Example:
|
||||
|
||||
DBRepair.sh --sqlite /usr/lib/plexmediaserver --databases "/real/host/directory/...../Databases" auto prune
|
||||
-or-
|
||||
DBRepair.sh --sqlite "/tmp/plex/Plex SQLite" --databases "/real/host/directory/...../Databases" auto prune
|
||||
|
||||
Interactive Example:
|
||||
|
||||
DBRepair.sh --sqlite /usr/lib/plexmediaserver --databases /real/host/directory/...../Databases
|
||||
|
||||
## Manual Configuration -- Example of using with docker.
|
||||
|
||||
1. I find the SQLite executable
|
||||
```
|
||||
root@Jasper:/mnt/disk1/appdata# find /var/lib/docker -name \*SQLite\*
|
||||
/var/lib/docker/btrfs/subvolumes/4bb78fb70589d4d2ba56754f4d6bc0edd4cdaa8eab7986943767e09a66cefd19/usr/lib/plexmediaserver/Plex SQLite
|
||||
/var/lib/docker/btrfs/subvolumes/eae4fef243ca71fbf190957256705fdc493863ee1f08222a7df0b5004cc8afb6-init/usr/lib/plexmediaserver/Plex SQLite
|
||||
/var/lib/docker/btrfs/subvolumes/eae4fef243ca71fbf190957256705fdc493863ee1f08222a7df0b5004cc8afb6/usr/lib/plexmediaserver/Plex SQLite
|
||||
root@Jasper:/mnt/disk1/appdata#
|
||||
```
|
||||
|
||||
2. I get to where my container is
|
||||
```
|
||||
root@Jasper:~# cd /mnt/user
|
||||
root@Jasper:/mnt/user# ls
|
||||
Media/ appdata/ domains/ isos/ plex/ system/
|
||||
root@Jasper:/mnt/user# cd appdata
|
||||
root@Jasper:/mnt/user/appdata# ls
|
||||
PlexMediaServer/
|
||||
root@Jasper:/mnt/user/appdata# cd PlexMediaServer/
|
||||
```
|
||||
|
||||
3. Invoke DBRepair.sh with both --sqlite and --databases command line options specified (both are required when either is used)
|
||||
```
|
||||
root@Jasper:/mnt/user/appdata/PlexMediaServer# /tmp/DBRepair.sh --databases /mnt/user/appdata/PlexMediaServer/Library/Application\ Support/Plex\ Media\ Server/Plug-in\ Support/Databases/ --sqlite /var/lib/docker/btrfs/subvolumes/4bb78fb70589d4d2ba56754f4d6bc0edd4cdaa8eab7986943767e09a66cefd19/usr/lib/plexmediaserver/
|
||||
|
||||
|
||||
|
||||
Database Repair Utility for Plex Media Server (User Defined)
|
||||
Version v1.09.00
|
||||
|
||||
PlexSQLite = '/var/lib/docker/btrfs/subvolumes/4bb78fb70589d4d2ba56754f4d6bc0edd4cdaa8eab7986943767e09a66cefd19/usr/lib/plexmediaserver//Plex SQLite'
|
||||
Databases = '/mnt/user/appdata/PlexMediaServer/Library/Application Support/Plex Media Server/Plug-in Support/Databases/'
|
||||
|
||||
Select
|
||||
|
||||
1 - 'stop' - (Not available. Stop manually.)
|
||||
2 - 'automatic' - Check, Repair/Optimize, and Reindex Database in one step.
|
||||
3 - 'check' - Perform integrity check of database.
|
||||
4 - 'vacuum' - Remove empty space from database without optimizing.
|
||||
5 - 'repair' - Repair/Optimize databases.
|
||||
6 - 'reindex' - Rebuild database indexes.
|
||||
7 - 'start' - (Not available. Start manually)
|
||||
|
||||
8 - 'import' - Import watch history from another database independent of Plex. (risky).
|
||||
9 - 'replace' - Replace current databases with newest usable backup copy (interactive).
|
||||
10 - 'show' - Show logfile.
|
||||
11 - 'status' - Report status of PMS (run-state and databases).
|
||||
12 - 'undo' - Undo last successful command.
|
||||
|
||||
21 - 'prune' - Prune (remove) old image files (jpeg,jpg,png) from PhotoTranscoder cache older than specific age.
|
||||
22 - 'purge' - Purge (remove) all temporary files left by PMS & Transcoder in Temp Dir.'
|
||||
23 - 'deflate' - Deflate a bloated PMS main database.
|
||||
|
||||
|
||||
42 - 'ignore' - Ignore duplicate/constraint errors.
|
||||
|
||||
88 - 'update' - Check for updates.
|
||||
98 - 'quit' - Quit immediately. Keep all temporary files.
|
||||
99 - 'exit' - Exit with cleanup options.
|
||||
|
||||
Enter command # -or- command name (4 char min) :
|
||||
```
|
||||
|
||||
|
||||
|
||||
# Special considerations - Synology DSM 7
|
||||
|
||||
Using DBRepair on Synology DSM 7 systems with Task Scheduler requires special handling.
|
||||
DSM 7 has additional security (app-armor). Care must be taken to not violate this.
|
||||
|
||||
One exception must be implemented. Care must be used to implement.
|
||||
|
||||
### DSM 7 - Step 1 - Designate a DSM username which will run DBRepair
|
||||
- Creating a to-task username with a complex password is best practice
|
||||
- Create a Scheduled task, user-script:
|
||||
- Runs as root
|
||||
- Emails you the result
|
||||
- Is not scheduled
|
||||
- Is disabled in the Task Scheduler task list
|
||||
|
||||
Contents of the user-script are:
|
||||
```
|
||||
#!/bin/bash
|
||||
#
|
||||
# This script grants the given syno username (your username)
|
||||
# the ability to elevate to 'root' privilege for use with DBRepair.sh
|
||||
#
|
||||
# Set your desired Syno username here (no spaces in the username)
|
||||
MyUsername="chuck"
|
||||
|
||||
# Confirm username exists
|
||||
if [ "$(id "$MyUsername")" = "" ]; then
|
||||
echo ERROR: No such user \'$MyUsername\'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Remove old record
|
||||
sed -i s/^${MyUsername}.\*$// /etc/sudoers
|
||||
|
||||
# Add myself to sudoers
|
||||
echo "$MyUsername" 'ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers
|
||||
```
|
||||
### DSM 7 - Step 2 - Run DBRepair as the designated username
|
||||
|
||||
With the security now set, DBRepair can be invoked from Task Scheduler.
|
||||
|
||||
Download and place DBRepair.sh in the desired location (PlexMediaServer shared folder ok)
|
||||
Make certain it's executable.
|
||||
|
||||
Create Scheduled Task - User-Script to run DBRepair
|
||||
- Runs as the selected username
|
||||
- Emails you the result
|
||||
- Runs on the schedule you desire (Weekly after PMS scheduled tasks completed is optimal)
|
||||
```
|
||||
#!/bin/bash
|
||||
|
||||
# Go to the PlexMediaServer shared folder
|
||||
cd /var/packages/PlexMediaServer/shares/PlexMediaServer
|
||||
|
||||
# Run classic Stop PMS, Automatic optimization/repair, Start PMS, and exit sequence
|
||||
sudo ./DBRepair.sh stop auto start exit
|
||||
```
|
||||
|
||||
|
||||
|
||||
# Special Considerations - Windows
|
||||
|
||||
Windows support is available via DBRepair-Windows.ps1 and DBRepair-Windows.bat. See [README-Windows](Windows/README-Windows.md) for details.
|
||||
|
||||
264
ReleaseNotes
264
ReleaseNotes
@@ -1,13 +1,239 @@
|
||||
# PlexDBRepair
|
||||
# DBRepair
|
||||
|
||||
[](https://github.com/ChuckPa/PlexDBRepair/issues)
|
||||
[](https://github.com/ChuckPa/PlexDBRepair/releases/latest)
|
||||
[](https://github.com/ChuckPa/PlexDBRepair/releases/latest)
|
||||
[](https://github.com/ChuckPa/PlexDBRepair/releases)
|
||||
[](https://github.com/ChuckPa/DBRepair/issues)
|
||||
[](https://github.com/ChuckPa/DBRepair/releases/latest)
|
||||
[](https://github.com/ChuckPa/DBRepair/releases/latest)
|
||||
[](https://github.com/ChuckPa/DBRepair/releases)
|
||||
[]('')
|
||||

|
||||
|
||||
# Release Info:
|
||||
v1.14.00
|
||||
|
||||
1. Full-Text Search - Check and repair Full-Text Search (FTS) database indexes.
|
||||
Damaged indexes often manifests as "500" internal errors. (Collections)
|
||||
This release adds support for checking those indexes and rebuilding as needed.
|
||||
( Full credit & thanks to @maxfield-allison for this great work )
|
||||
|
||||
Do not be surprised if your Blobs DB has damaged indexes and needs repair.
|
||||
Even "Known Good" databases have shown FTS index problems.
|
||||
|
||||
v1.13.02
|
||||
|
||||
1. Autoincrement - Adjust statistics_bandwidth table per Plex engineering suggestion.
|
||||
|
||||
v1.13.01
|
||||
|
||||
1. Tag sort order - Github tags are not guaranteed to be numeric or in most-recent order.
|
||||
This update sorts version tags numerically and in reverse order to accommodate github shortcoming.
|
||||
|
||||
v1.13.00
|
||||
|
||||
1. Binhex containers - Add support for updated 'supervisord' start/stop control.
|
||||
Maintain support for legacy 'supervisor' start/stop control.
|
||||
|
||||
This will be removed in the next release
|
||||
Update your container before updating DBRepair.
|
||||
|
||||
v1.12.00
|
||||
|
||||
1. Deflate capability - Deflate a bloated PMS main database and retain bandwidth statistics.
|
||||
Recommended usage: "Deflate" followed by "Auto" to optimize the DB after reduction.
|
||||
- Method changed to reconstruction of bandwidth table versus selective copy.
|
||||
This results in correct operation of bandwidth statistics functionality
|
||||
as well as significantly improved performance.
|
||||
|
||||
2. ASUSTOR - Documentation error README.md corrected
|
||||
|
||||
|
||||
v1.11.09
|
||||
|
||||
1. HotIO Paths - Path defined for Cache dir (and PhotoTranscoder) incorrect. Fixed.
|
||||
|
||||
v1.11.08
|
||||
|
||||
1. Restart after update - Sometimes restart after update would reference the wrong path. Fixed.
|
||||
|
||||
v1.11.07
|
||||
|
||||
1. Add support FreeBSD - Add support for FreeBSD (14+)
|
||||
Thanks to @cdf-eagles for the work.
|
||||
|
||||
v1.11.06
|
||||
|
||||
1. Restart after update - Correct missing variable initialization.
|
||||
|
||||
v1.11.05
|
||||
|
||||
1. Remove menu option - Deflate menu option wasn't previously removed. Now removed.
|
||||
|
||||
v1.11.04
|
||||
|
||||
1. Binhex start/stop - Add support for stopping and starting PMS in Binhex containers.
|
||||
2. Restart after update - Add option to restart after live updating.
|
||||
3. Remove temp Deflate - Remove deflate function as of PMS 1.41.8 availability
|
||||
|
||||
v1.11.01 - v1.11.03 - Temporary versions to assist PMS database bloat issue.
|
||||
|
||||
v1.11.00
|
||||
|
||||
1. Rename Utility - Rename this tool to be compliant with Plex inc. Trademark Policy.
|
||||
Update all documentation
|
||||
Update Github repository
|
||||
|
||||
v1.10.06
|
||||
|
||||
1. Update tags - When updating, DBRepair would not format cleanly. This update corrects that formatting.
|
||||
It would often show the tags for multiple previous versions. This is undesirable.
|
||||
|
||||
v1.10.05
|
||||
|
||||
1. DB Cleanup - Temporary assist with PMS database cleanups
|
||||
2. Record Count report - Report the number of records in each main DB table. (Monitoring growth)
|
||||
|
||||
v1.10.03
|
||||
|
||||
1. LC_ALL - When LC_ALL="", set LC_ALL=C (MacOS now needs this)
|
||||
|
||||
v1.10.02
|
||||
|
||||
1. Refactor UPDATE - QNAP BusyBox no longer support POSIX grep. Refactored.
|
||||
|
||||
v1.10.01
|
||||
|
||||
1. Minor cleanup - Cleanup purge/prune handling after merging commands into one.
|
||||
|
||||
v1.10.00
|
||||
|
||||
1. Refactor REPLACE - Processing of DB Replace was awkward.
|
||||
List of available databases will now be printed in a menu
|
||||
After selecting the desired DB, the candidate will be checked.
|
||||
(Selection may be by list item # or date printed)
|
||||
If valid (DB and Blobs) replacement will proceed normally.
|
||||
|
||||
v1.09.00
|
||||
|
||||
1. Purge command - Add 'purge' command to clean up all temporary transcoder and image files.
|
||||
(usually found in /tmp)
|
||||
|
||||
v1.08.00
|
||||
|
||||
1. Linuxserver.io - Add support for new start/stop path.
|
||||
Silently support both previous and new path start/stop mechanisms.
|
||||
|
||||
v1.07.00
|
||||
|
||||
1. Mac start/stop - DBRepair now supports start/stop from the menu.
|
||||
Default configuration is for MacOS to tell you what it's doing (Haptic). Set to 0 for silence.
|
||||
|
||||
2. Code cleanup - Minor code cleanup / dead code removal.
|
||||
|
||||
v1.06.02
|
||||
|
||||
1. Bug fix - Fixed incorrect error handling when command line arguments not valid in manual configuration mode.
|
||||
Fixed incorrect reporting of what was wrong in manual configuration mode.
|
||||
|
||||
v1.06.01
|
||||
|
||||
1. Manual SQLite path - You many now also specify the full path to "Plex SQLite". ( example: "/tmp/downloads/Plex SQLite")
|
||||
DBRepair.sh will automatically figure out which form to use (path or directory)
|
||||
|
||||
--sqlite "/real/host/path/to/plexmediaserver/Directory"
|
||||
--sqlite "/real/host/path/to/plexmediaserver/directory/Plex SQLite"
|
||||
|
||||
v1.06.00
|
||||
|
||||
1. Manual configuration - You may now run DBRepair from outside container environments.
|
||||
This provides a method for running DBRepair when container healthcheck prevents it.
|
||||
|
||||
The directories to both:
|
||||
|
||||
a. Where Plex SQLite can be found: ( --sqlite "/real/host/path/to/plexmediaserver/Directory" )
|
||||
b. Where the databases can be found: ( --databases "/real/host/path/to/Plugin Support/Databases" )
|
||||
|
||||
If used, these options MUST precede all other command line options
|
||||
|
||||
WARNING: The host MUST provide all standard commands AND support "stat -c" option. Bash commands will NOT autoconfigure.
|
||||
|
||||
|
||||
2. "Exit" when scripted: The previously annoyance which required "exit" to be specified on the command line (scripted) has been fixed.
|
||||
"exit" is no longer required in this use.
|
||||
|
||||
"exit" or "quit" is still required in interactive use.
|
||||
|
||||
3. Windows: DBRepair-Windows now supports 32 bit PMS on 64 bit Windows base systems. (Mixed mode operation)
|
||||
|
||||
v1.05.02
|
||||
|
||||
1. HOTIO image paths HOTIO images have again changed PMS executable location. This update
|
||||
adds support for the lastest location.
|
||||
|
||||
v1.05.01
|
||||
|
||||
1. MacOS - Transcoder cache directory uses different location than default Plex.
|
||||
Make "CacheDir" variable.
|
||||
2. PPM files - Prune old PPM files just as other image files are pruned.
|
||||
|
||||
v1.05.00
|
||||
|
||||
1. Kubernetes support - Add support for Kubernetes (and also TrueNAS) containers.
|
||||
|
||||
v1.04.00
|
||||
|
||||
1. SNAP support - Add support for Plex Media Server SNAP package.
|
||||
|
||||
v1.03.00
|
||||
|
||||
1. New command: Purge - Removes old image files from the Trancoder cache.
|
||||
These are normally removed during scheduled maintenance.
|
||||
If scheduled maintenance doesn't complete all tasks, they won't get removed
|
||||
and will build up.
|
||||
|
||||
This command allows you to remove them.
|
||||
|
||||
The default age is 30 days. Files older than 30 days will be purged.
|
||||
|
||||
2. Introduces Environment variable support (customizing) DBRepair operation.
|
||||
|
||||
- DBREPAIR_PAGESIZE: DBREPAIR_PAGESIZE=N (where N is a multiple of 1024, less equal 65536)
|
||||
This sets the Plex SQLite database page_size during optimization operations.
|
||||
It's most beneficial on ZFS filesystems to allow tuning to the Dataset size.
|
||||
|
||||
- DBREPAIR_CACHEAGE: DBREPAIR_CACHEAGE=N (where N is the max age, in Days) to retain when purging
|
||||
older image files in the Cache/PhotoTranscoder directory.
|
||||
This variable allows you to set the purge age (greater than "N" days).
|
||||
|
||||
v1.02.01
|
||||
|
||||
- Minor fix to logging. One line of output at the console was not present in the logfile
|
||||
(this is important when diagnosing after connection-loss scenarios and DBRepair.sh was interrupted)
|
||||
|
||||
- Added documentation in README.md for using DBRepair with Synology DSM 7 Task Scheduler.
|
||||
(Maintains app-armor compliance)
|
||||
|
||||
v1.02.00
|
||||
|
||||
- Provide menu option to ignore/honor "Duplicate" and "UNIQUE Constraint" errors during DB Repair,
|
||||
and when importing data from other databases.
|
||||
This is equivalent to the '-i' command line option but now can be used interactively.
|
||||
|
||||
- Fixed. ConfirmYesNo would print error message when it shouldn't.
|
||||
- Fixed. Erroneous echo of your reply when responding to yes/no questions.
|
||||
|
||||
v1.01.04
|
||||
|
||||
- Missing IgnoreErrors (-i / -f) test when reimporting damaged DB with certain errors prevented repair
|
||||
in all cases.
|
||||
|
||||
v1.01.02
|
||||
|
||||
- Correct updating when scripted versus interactive.
|
||||
|
||||
v1.01.00
|
||||
|
||||
- Adds ability to self download and install latest version of DBRepair.sh
|
||||
- Slightly changes numbering style to accommodate future updates with new update capability.
|
||||
- Thanks to @causefx for his contributions to this release
|
||||
|
||||
v1.0.13
|
||||
|
||||
@@ -119,7 +345,7 @@ These type changes can only be performed by PMS.
|
||||
For clarity, each command's name is 'quoted'.
|
||||
|
||||
|
||||
Plex Media Server Database Repair Utility (_host_configuration_name_)
|
||||
Database Repair Utility for Plex Media Server (_host_configuration_name_)
|
||||
Version v1.0.0
|
||||
|
||||
Select
|
||||
@@ -184,10 +410,10 @@ These type changes can only be performed by PMS.
|
||||
|
||||
### General installation and usage instructions
|
||||
|
||||
1. Open your browser to https://github.com/ChuckPa/PlexDBRepair/releases/latest
|
||||
1. Open your browser to https://github.com/ChuckPa/DBRepair/releases/latest
|
||||
2. Download the source code (tar.gz or ZIP) file
|
||||
|
||||
3. Knowing the file name will always be of the form 'PlexDBRepair-X.Y.Z.tar.gz'
|
||||
3. Knowing the file name will always be of the form 'DBRepair-X.Y.Z.tar.gz'
|
||||
-- where X.Y.Z is the release number. Use the real values in place of X, Y, and Z.
|
||||
4. Place the tar.gz file in the appropriate directory on the system you'll use it.
|
||||
5. Open a command line session (usually Terminal or SSH)
|
||||
@@ -204,8 +430,8 @@ These type changes can only be performed by PMS.
|
||||
|
||||
cd /volume1/Plex
|
||||
sudo bash
|
||||
tar xf PlexDBRepair-x.y.z.tar.gz
|
||||
cd PlexDBRepair-x.y.z
|
||||
tar xf DBRepair-x.y.z.tar.gz
|
||||
cd DBRepair-x.y.z
|
||||
chmod +x DBRepair.sh
|
||||
./DBRepair.sh
|
||||
|
||||
@@ -217,8 +443,8 @@ These type changes can only be performed by PMS.
|
||||
sudo docker exec -it plex /bin/bash
|
||||
|
||||
# extract from downloaded version file name then cd into directory
|
||||
tar xf PlexDBRepair-1.0.0.tar.gz
|
||||
cd PlexDBRepair-1.0.0
|
||||
tar xf DBRepair-1.0.0.tar.gz
|
||||
cd DBRepair-1.0.0
|
||||
chmod +x DBRepair.sh
|
||||
./DBRepair.sh
|
||||
```
|
||||
@@ -226,8 +452,8 @@ These type changes can only be performed by PMS.
|
||||
```
|
||||
sudo bash
|
||||
cd /path/to/DBRepair.tar
|
||||
tar xf PlexDBRepair-1.0.0.tar.gz
|
||||
cd PlexDBRepair-1.0.0
|
||||
tar xf DBRepair-1.0.0.tar.gz
|
||||
cd DBRepair-1.0.0
|
||||
chmod +x DBRepair.sh
|
||||
./DBRepair.sh stop auto start exit
|
||||
```
|
||||
@@ -236,8 +462,8 @@ These type changes can only be performed by PMS.
|
||||
```
|
||||
osascript -e 'quit app "Plex Media Server"'
|
||||
cd ~/Downloads
|
||||
tar xvf PlexDBRepai PlexDBRepair-1.0.0.tar.gz
|
||||
cd PlexDBRepai PlexDBRepair-1.0.0
|
||||
tar xvf PlexDBRepai DBRepair-1.0.0.tar.gz
|
||||
cd PlexDBRepai DBRepair-1.0.0
|
||||
|
||||
chmod +x DBRepair.sh
|
||||
./DBRepair.sh
|
||||
@@ -342,7 +568,7 @@ bash-4.4# ./DBRepair.sh
|
||||
|
||||
|
||||
|
||||
Plex Media Server Database Repair Utility (Synology (DSM 7))
|
||||
Database Repair Utility for Plex Media Server (Synology (DSM 7))
|
||||
Version v1.0.0
|
||||
|
||||
|
||||
@@ -495,7 +721,7 @@ root@lizum:/sata/plex/Plex Media Server/Plug-in Support/Databases# ./DBRepair.sh
|
||||
|
||||
|
||||
|
||||
Plex Media Server Database Repair Utility (Ubuntu 20.04.5 LTS)
|
||||
Database Repair Utility for Plex Media Server (Ubuntu 20.04.5 LTS)
|
||||
Version v1.0.0
|
||||
|
||||
|
||||
@@ -689,4 +915,4 @@ root@lizum:/sata/plex/Plex Media Server/Plug-in Support/Databases#
|
||||
Instructs SQLite to remove the empty/deleted records and gaps from the databases.
|
||||
This is most beneficial after deleting whole library sections.
|
||||
|
||||
###
|
||||
###
|
||||
@@ -1,14 +1,21 @@
|
||||
@echo off
|
||||
REM PlexDBRepair.bat - Database maintenance / rebuild tool for Windows.
|
||||
REM DBRepair.bat - Database maintenance / rebuild tool for Windows.
|
||||
REM
|
||||
REM This tool currently works as a "full shot" service.
|
||||
REM - everything is done without need to interact.
|
||||
REM
|
||||
REM -- WARNNING -- WARNING -- WARNING
|
||||
REM
|
||||
REM 1. This is stable working software but not "Released" software. Development will continue.
|
||||
REM 2. You must ensure variable PlexData points to your databases. (there is no automatic detection at this time)
|
||||
REM
|
||||
REM This is stable working software but not "Released" software. Development will continue.
|
||||
|
||||
setlocal enabledelayedexpansion
|
||||
|
||||
echo.
|
||||
echo NOTE: This script is being replaced with the PowerShell script DBRepair-Windows.ps1,
|
||||
echo which aims to better emulate DBRepair.sh (more options, interactive mode, etc).
|
||||
echo Consider moving over to the new script.
|
||||
echo.
|
||||
|
||||
REM ### Create Timestamp
|
||||
set Hour=%time:~0,2%
|
||||
set Min=%time:~3,2%
|
||||
@@ -20,33 +27,79 @@ set Hour=%Hour: =%
|
||||
REM ## Set TimeStamp ##
|
||||
set TimeStamp=%Hour%-%Min%-%Sec%
|
||||
|
||||
REM These assume PMS is in the default location
|
||||
set "PlexData=%LOCALAPPDATA%\Plex Media Server\Plug-in Support\Databases"
|
||||
set "PlexSQL=%PROGRAMFILES%\Plex\Plex Media Server\Plex SQLite"
|
||||
REM Find PMS database location
|
||||
for /F "tokens=2* skip=2" %%a in ('REG.EXE QUERY "HKCU\Software\Plex, Inc.\Plex Media Server" /v "LocalAppDataPath" 2^> nul') do set "PlexData=%%b\Plex Media Server\Plug-in Support\Databases"
|
||||
if not exist "%PlexData%" (
|
||||
if exist "%LOCALAPPDATA%\Plex Media Server\Plug-in Support\Databases" (
|
||||
set "PlexData=%LOCALAPPDATA%\Plex Media Server\Plug-in Support\Databases"
|
||||
) else (
|
||||
echo Could not determine Plex database path.
|
||||
echo Normally %LOCALAPPDATA%\Plex Media Server\Plug-in Support\Databases
|
||||
echo.
|
||||
goto :EOF
|
||||
)
|
||||
)
|
||||
|
||||
REM Find PMS installation location.
|
||||
for /F "tokens=2* skip=2" %%a in ('REG.EXE QUERY "HKCU\Software\Plex, Inc.\Plex Media Server" /v "InstallFolder" 2^> nul') do set "PlexSQL=%%b\Plex SQLite.exe"
|
||||
|
||||
if not exist "%PlexSQL%" (
|
||||
REM InstallFolder might be set under HKLM, not HKCU
|
||||
for /F "tokens=2* skip=2" %%a in ('REG.EXE QUERY "HKLM\Software\Plex, Inc.\Plex Media Server" /v "InstallFolder" 2^> nul') do set "PlexSQL=%%b\Plex SQLite.exe"
|
||||
)
|
||||
|
||||
REM If InstallFolder wasn't set, or the resulting file doesn't exist, iterate through the
|
||||
REM PROGRAMFILES variables looking for it. If we still can't find it, ask the user to provide it.
|
||||
if not exist "%PlexSQL%" (
|
||||
if exist "%PROGRAMFILES%\Plex\Plex Media Server\Plex SQLite.exe" (
|
||||
set "PlexSQL=%PROGRAMFILES%\Plex\Plex Media Server\Plex SQLite.exe"
|
||||
) else (
|
||||
if exist "%PROGRAMFILES(X86)%\Plex\Plex Media Server\Plex SQLite.exe" (
|
||||
echo NOTE: 32-bit version of PMS detected on a 64-bit version of Windows. Updating to the 64-bit release of PMS is recommended.
|
||||
set "PlexSQL=%PROGRAMFILES(X86)%\Plex\Plex Media Server\Plex SQLite.exe"
|
||||
) else (
|
||||
echo Could not determine SQLite path. Please provide it below
|
||||
echo Normally %PROGRAMFILES%\Plex\Plex Media Server\Plex SQLite.exe
|
||||
echo.
|
||||
REM Last ditch effort, ask the user for the full path to Plex SQLite.exe
|
||||
set /p "PlexSQL=Path to Plex SQLite.exe: "
|
||||
if not exist "!PlexSQL!" (
|
||||
echo "!PlexSQL!" could not be found. Cannot continue.
|
||||
goto :EOF
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
REM Set temporary file locations
|
||||
set "DBtmp=%PlexData%\dbtmp"
|
||||
set "TmpFile=%DBtmp%\results.tmp"
|
||||
|
||||
|
||||
REM Time now.
|
||||
echo %time% -- ====== Session begins. (%date%) ======
|
||||
echo %time% -- ====== Session begins. (%date%) ====== >> "%PlexData%\PlexDBRepair.log"
|
||||
echo %time% -- ====== Session begins. (%date%) ====== >> "%PlexData%\DBRepair.log"
|
||||
|
||||
REM Make certain Plex is NOT running.
|
||||
tasklist | find /I "Plex Media Server.exe" >NUL
|
||||
if %ERRORLEVEL%==0 (
|
||||
echo %time% -- Plex is running. Please stop Plex Media Server and try again.
|
||||
echo %time% -- Plex is running. Please stop Plex Media Server and try again. >> "%PlexData%\PlexDBRepair.log"
|
||||
echo %time% -- Plex is running. Please stop Plex Media Server and try again. >> "%PlexData%\DBRepair.log"
|
||||
exit /B 1
|
||||
)
|
||||
|
||||
|
||||
cd "%PlexData%"
|
||||
|
||||
mkdir "%PlexData%\dbtmp" 2>NUL
|
||||
md "%PlexData%\dbtmp" 2>NUL
|
||||
del "%TmpFile%" 2>NUL
|
||||
|
||||
echo %time% -- Performing DB cleanup tasks
|
||||
echo %time% -- Performing DB cleanup tasks >> "%PlexData%\DBRepair.log"
|
||||
"%PlexSQL%" "%PlexData%\com.plexapp.plugins.library.db" "DELETE FROM statistics_bandwidth WHERE account_id IS NULL;"
|
||||
|
||||
echo %time% -- Exporting Main DB
|
||||
echo %time% -- Exporting Main DB >> "%PlexData%\PlexDBRepair.log"
|
||||
echo %time% -- Exporting Main DB >> "%PlexData%\DBRepair.log"
|
||||
echo .dump | "%PlexSQL%" "%PlexData%\com.plexapp.plugins.library.db" > "%DBtmp%\library.sql_%TimeStamp%"
|
||||
if not %ERRORLEVEL%==0 (
|
||||
echo %time% -- ERROR: Cannot export Main DB. Aborting.
|
||||
@@ -54,7 +107,7 @@ if not %ERRORLEVEL%==0 (
|
||||
)
|
||||
|
||||
echo %time% -- Exporting Blobs DB
|
||||
echo %time% -- Exporting Blobs DB >> "%PlexData%\PlexDBRepair.log"
|
||||
echo %time% -- Exporting Blobs DB >> "%PlexData%\DBRepair.log"
|
||||
echo .dump | "%PlexSQL%" "%PlexData%\com.plexapp.plugins.library.blobs.db" > "%DBtmp%\blobs.sql_%TimeStamp%"
|
||||
if not %ERRORLEVEL%==0 (
|
||||
echo %time% -- ERROR: Cannot export Blobs DB. Aborting.
|
||||
@@ -62,75 +115,75 @@ if not %ERRORLEVEL%==0 (
|
||||
|
||||
REM Now create new databases from SQL statements
|
||||
echo %time% -- Exporting Complete.
|
||||
echo %time% -- Exporting Complete. >> "%PlexData%\PlexDBRepair.log"
|
||||
echo %time% -- Exporting Complete. >> "%PlexData%\DBRepair.log"
|
||||
|
||||
echo %time% -- Creating Main DB
|
||||
echo %time% -- Creating Main DB >> "%PlexData%\PlexDBRepair.log"
|
||||
echo %time% -- Creating Main DB >> "%PlexData%\DBRepair.log"
|
||||
"%PlexSQL%" "%PlexData%\com.plexapp.plugins.library.db_%TimeStamp%" < "%DBtmp%\library.sql_%TimeStamp%"
|
||||
if not %ERRORLEVEL%==0 (
|
||||
echo %time% -- ERROR: Cannot create Main DB. Aborting.
|
||||
echo %time% -- ERROR: Cannot create Main DB. Aborting. >> "%PlexData%\PlexDBRepair.log"
|
||||
echo %time% -- ERROR: Cannot create Main DB. Aborting. >> "%PlexData%\DBRepair.log"
|
||||
exit /b 3
|
||||
)
|
||||
|
||||
echo %time% -- Verifying Main DB
|
||||
echo %time% -- Verifying Main DB >> "%PlexData%\PlexDBRepair.log"
|
||||
echo %time% -- Verifying Main DB >> "%PlexData%\DBRepair.log"
|
||||
"%PlexSQL%" "%PlexData%\com.plexapp.plugins.library.db_%TimeStamp%" "PRAGMA integrity_check(1)" >"%TmpFile%"
|
||||
set /p Result= < "%TmpFile%"
|
||||
del "%TmpFile%"
|
||||
|
||||
echo %time% -- Main DB verification check is: %Result%
|
||||
echo %time% -- Main DB verification check is: %Result% >> "%PlexData%\PlexDBRepair.log"
|
||||
echo %time% -- Main DB verification check is: %Result% >> "%PlexData%\DBRepair.log"
|
||||
if not "%Result%" == "ok" (
|
||||
echo %time% -- ERROR: Main DB verificaion failed. Exiting.
|
||||
echo %time% -- ERROR: Main DB verificaion failed. Exiting. >> "%PlexData%\PlexDBRepair.log"
|
||||
echo %time% -- ERROR: Main DB verification failed. Exiting.
|
||||
echo %time% -- ERROR: Main DB verification failed. Exiting. >> "%PlexData%\DBRepair.log"
|
||||
exit /B 4
|
||||
)
|
||||
echo %time% -- Main DB verification successful.
|
||||
echo %time% -- Main DB verification successful. >> "%PlexData%\PlexDBRepair.log"
|
||||
echo %time% -- Main DB verification successful. >> "%PlexData%\DBRepair.log"
|
||||
|
||||
|
||||
echo %time% -- Creating Blobs DB
|
||||
echo %time% -- Creating Blobs DB >> "%PlexData%\PlexDBRepair.log"
|
||||
echo %time% -- Creating Blobs DB >> "%PlexData%\DBRepair.log"
|
||||
"%PlexSQL%" "%PlexData%\com.plexapp.plugins.library.blobs.db_%TimeStamp%" < "%DBtmp%\blobs.sql_%TimeStamp%"
|
||||
if not %ERRORLEVEL%==0 (
|
||||
echo %time% -- ERROR: Cannot create Blobs DB. Aborting.
|
||||
echo %time% -- ERROR: Cannot create Blobs DB. Aborting. >> "%PlexData%\PlexDBRepair.log"
|
||||
echo %time% -- ERROR: Cannot create Blobs DB. Aborting. >> "%PlexData%\DBRepair.log"
|
||||
exit /b 5
|
||||
)
|
||||
|
||||
echo %time% -- Verifying Blobs DB
|
||||
echo %time% -- Verifying Blobs DB >> "%PlexData%\PlexDBRepair.log"
|
||||
echo %time% -- Verifying Blobs DB >> "%PlexData%\DBRepair.log"
|
||||
"%PlexSQL%" "%PlexData%\com.plexapp.plugins.library.blobs.db_%TimeStamp%" "PRAGMA integrity_check(1)" > "%TmpFile%"
|
||||
set /p Result= < "%TmpFile%"
|
||||
del "%TmpFile%"
|
||||
|
||||
echo %time% -- Blobs DB verification check is: %Result%
|
||||
echo %time% -- Blobs DB verification check is: %Result% >> "%PlexData%\PlexDBRepair.log"
|
||||
echo %time% -- Blobs DB verification check is: %Result% >> "%PlexData%\DBRepair.log"
|
||||
if not "%Result%" == "ok" (
|
||||
echo %time% -- ERROR: Blobs DB verificaion failed. Exiting.
|
||||
echo %time% -- ERROR: Blobs DB verificaion failed. Exiting. >> "%PlexData%\PlexDBRepair.log"
|
||||
echo %time% -- ERROR: Blobs DB verification failed. Exiting.
|
||||
echo %time% -- ERROR: Blobs DB verification failed. Exiting. >> "%PlexData%\DBRepair.log"
|
||||
exit /B 6
|
||||
)
|
||||
echo %time% -- Blobs DB verification successful.
|
||||
echo %time% -- Blobs DB verification successful. >> "%PlexData%\PlexDBRepair.log"
|
||||
echo %time% -- Blobs DB verification successful. >> "%PlexData%\DBRepair.log"
|
||||
echo %time% -- Import and verification complete.
|
||||
echo %time% -- Import and verification complete. >> "%PlexData%\PlexDBRepair.log"
|
||||
echo %time% -- Import and verification complete. >> "%PlexData%\DBRepair.log"
|
||||
|
||||
REM Import complete, now reindex
|
||||
echo %time% -- Reindexing Main DB
|
||||
echo %time% -- Reindexing Main DB >> "%PlexData%\PlexDBRepair.log"
|
||||
echo %time% -- Reindexing Main DB >> "%PlexData%\DBRepair.log"
|
||||
"%PlexSQL%" "%PlexData%\com.plexapp.plugins.library.db_%TimeStamp%" "REINDEX;"
|
||||
|
||||
echo %time% -- Reindexing Blobs DB
|
||||
echo %time% -- Reindexing Blobs DB >> "%PlexData%\PlexDBRepair.log"
|
||||
echo %time% -- Reindexing Blobs DB >> "%PlexData%\DBRepair.log"
|
||||
"%PlexSQL%" "%PlexData%\com.plexapp.plugins.library.blobs.db_%TimeStamp%" "REINDEX;"
|
||||
|
||||
REM Index complete, make active
|
||||
echo %time% -- Reindexing complete.
|
||||
echo %time% -- Reindexing complete. >> "%PlexData%\PlexDBRepair.log"
|
||||
echo %time% -- Reindexing complete. >> "%PlexData%\DBRepair.log"
|
||||
echo %time% -- Moving current DBs to DBTMP and making new databases active
|
||||
echo %time% -- Moving current DBs to DBTMP and making new databases active >> "%PlexData%\PlexDBRepair.log"
|
||||
echo %time% -- Moving current DBs to DBTMP and making new databases active >> "%PlexData%\DBRepair.log"
|
||||
|
||||
move "%PlexData%\com.plexapp.plugins.library.db" "%PlexData%\dbtmp\com.plexapp.plugins.library.db_%TimeStamp%"
|
||||
move "%PlexData%\com.plexapp.plugins.library.db_%TimeStamp%" "%PlexData%\com.plexapp.plugins.library.db"
|
||||
@@ -139,9 +192,9 @@ move "%PlexData%\com.plexapp.plugins.library.blobs.db" "%PlexData%\d
|
||||
move "%PlexData%\com.plexapp.plugins.library.blobs.db_%TimeStamp%" "%PlexData%\com.plexapp.plugins.library.blobs.db"
|
||||
|
||||
echo %time% -- Database repair/rebuild/reindex completed.
|
||||
echo %time% -- Database repair/rebuild/reindex completed. >> "%PlexData%\PlexDBRepair.log"
|
||||
echo %time% -- Database repair/rebuild/reindex completed. >> "%PlexData%\DBRepair.log"
|
||||
echo %time% -- ====== Session completed. ======
|
||||
echo %time% -- ====== Session completed. ====== >> "%PlexData%\PlexDBRepair.log"
|
||||
echo %time% -- ====== Session completed. ====== >> "%PlexData%\DBRepair.log"
|
||||
|
||||
exit /b
|
||||
|
||||
@@ -152,5 +205,5 @@ REM Output - Write text to the console and the log file
|
||||
:Output
|
||||
|
||||
echo %time% %~1
|
||||
echo %time% %~1 >> "%PlexData%\PlexDBRepair.log"
|
||||
echo %time% %~1 >> "%PlexData%\DBRepair.log"
|
||||
exit /B
|
||||
904
Windows/DBRepair-Windows.ps1
Normal file
904
Windows/DBRepair-Windows.ps1
Normal file
@@ -0,0 +1,904 @@
|
||||
#########################################################################
|
||||
# Database check and repair utility script for Plex Media Server #
|
||||
# #
|
||||
#########################################################################
|
||||
|
||||
$DBRepairVersion = 'v1.01.02'
|
||||
|
||||
class DBRepair {
|
||||
[DBRepairOptions] $Options
|
||||
|
||||
[string] $PlexDBDir # Path to Plex's Databases directory
|
||||
[string] $PlexCache # Path to the PhotoTranscoder directory
|
||||
[string] $PlexSQL # Path to 'Plex SQLite.exe'
|
||||
[string] $Timestamp # Timestamp used for temporary database files
|
||||
[string] $LogFile # Path of our log file
|
||||
[string] $Version # Current script version
|
||||
[string] $Stage # Current stage of the script (e.g. "Auto", "Prune", etc.)
|
||||
[bool] $IsError # Whether we're currently in an error state
|
||||
[string] $MainDB = "com.plexapp.plugins.library.db"
|
||||
[string] $BlobsDB = "com.plexapp.plugins.library.blobs.db"
|
||||
|
||||
DBRepair($Arguments, $Version) {
|
||||
$this.Options = [DBRepairOptions]::new()
|
||||
$this.Version = $Version
|
||||
$this.IsError = $false
|
||||
$Commands = $this.PreprocessArgs($Arguments)
|
||||
if ($null -eq $Commands) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!$this.Init()) {
|
||||
Write-Host "Unable to initialize script, cannot continue."
|
||||
return
|
||||
}
|
||||
|
||||
$this.PrintHeader($true)
|
||||
$this.MainLoop($Commands)
|
||||
}
|
||||
|
||||
[void] PrintHeader([boolean] $WriteToLog) {
|
||||
$OS = [System.Environment]::OSVersion.Version
|
||||
if ($WriteToLog) {
|
||||
$this.WriteLog("============================================================")
|
||||
$this.WriteLog("Session start: Host is Windows $($OS.Major) (Build $($OS.Build))")
|
||||
}
|
||||
|
||||
Write-Host "`n"
|
||||
Write-Host " Database Repair Utility for Plex Media Server (Windows $($OS.Major), Build $($OS.Build))"
|
||||
Write-Host " Version $($this.Version) "
|
||||
Write-Host
|
||||
}
|
||||
|
||||
[void] PrintHelp() {
|
||||
# -Help doesn't write to the log, since our log file path isn't set.
|
||||
$this.PrintHeader($false)
|
||||
Write-Host "When run without arguments, starts an interactive session that displays available options"
|
||||
Write-Host "and lets you select the operations you want to perform. Or, to run tasks automatically,"
|
||||
Write-Host "provide them directly to the script, e.g. '.\DBRepair-Windows.ps1 Stop Prune Start Exit'"
|
||||
Write-Host
|
||||
$this.PrintOptions("Main Options")
|
||||
Write-Host
|
||||
Write-Host "Extra Options - These can only be specified once (last one wins)"
|
||||
Write-Host
|
||||
Write-Host " -CacheAge [int] - The date cutoff for pruned images. Defaults to pruning images over 30"
|
||||
Write-Host " days old."
|
||||
Write-Host
|
||||
}
|
||||
|
||||
[void] PrintMenu() {
|
||||
$this.PrintOptions("Select")
|
||||
}
|
||||
|
||||
[void] PrintOptions([string]$Header) {
|
||||
# NOTE: While Windows only supports a subset of DBRepair.sh's features, keep the command
|
||||
# numbers the same as we attempt to reach feature parity
|
||||
Write-Host
|
||||
Write-Host $Header
|
||||
Write-Host
|
||||
Write-Host " 1 - 'stop' - Stop PMS."
|
||||
Write-Host " 2 - 'automatic' - Check, Repair/Optimize, and Reindex Database in one step."
|
||||
Write-Host
|
||||
Write-Host " 7 - 'start' - Start PMS"
|
||||
Write-Host
|
||||
Write-Host " 21 - 'prune' - Prune (remove) old image files (jpeg,jpg,png) from PhotoTranscoder cache."
|
||||
if ($this.Options.IgnoreErrors) {
|
||||
Write-Host " 42 - 'honor' - Honor all database errors."
|
||||
} else {
|
||||
Write-Host " 42 - 'ignore' - Ignore duplicate/constraint errors."
|
||||
}
|
||||
Write-Host
|
||||
Write-Host " 98 - 'quit' - Quit immediately. Keep all temporary files."
|
||||
Write-Host " 99 'exit' - Exit with cleanup options."
|
||||
Write-Host
|
||||
Write-Host " 'menu x' - Show this menu in interactive mode, where x is on/off/yes/no"
|
||||
}
|
||||
|
||||
# Do initial parsing of arguments that aren't part of the loop, returning
|
||||
# the list of arguments that _should_ be processed in the loop.
|
||||
#
|
||||
# E.g. given "Stop Prune CacheAge 20 Start", this function will set the CacheAge
|
||||
# to 20, and return "Stop Prune Start"
|
||||
[System.Collections.ArrayList] PreprocessArgs([string[]] $Arguments) {
|
||||
$FinalArgs = [System.Collections.ArrayList]::new()
|
||||
for ($i = 0; $i -lt $Arguments.Count; ++$i) {
|
||||
switch -Regex ($Arguments[$i]) {
|
||||
'^-?(H(elp)?|\?)$' {
|
||||
if ($Arguments.Count -gt 1) {
|
||||
Write-Warning "Found -Help, ignoring extra arguments"
|
||||
}
|
||||
|
||||
$this.PrintHelp()
|
||||
return $null
|
||||
}
|
||||
'^-?CacheAge$' {
|
||||
if ($i -eq $Arguments.Count - 1) {
|
||||
Write-Warning "Found -CacheAge argument, but no value. Using default of 30 days"
|
||||
Break
|
||||
}
|
||||
|
||||
++$i
|
||||
$Age = $Arguments[$i]
|
||||
if (!($Age -match "^\d+$")) {
|
||||
Write-Warning "Invalid -CacheAge value '$Age'. Using default of 30 days"
|
||||
Break
|
||||
}
|
||||
|
||||
$this.Options.CacheAge = [int]$Age
|
||||
}
|
||||
|
||||
Default { $FinalArgs.Add($_) }
|
||||
}
|
||||
}
|
||||
|
||||
return $FinalArgs
|
||||
}
|
||||
|
||||
# Setup variables required for this utility to work.
|
||||
[bool] Init() {
|
||||
$this.Timestamp = Get-Date -Format HH-mm-ss
|
||||
|
||||
$AppData = $this.GetAppDataDir()
|
||||
$Success = $this.GetPlexDBDir($AppData) -and $this.GetPlexSQL() -and $this.GetPhotoTranscoderDir($AppData)
|
||||
if ($Success) {
|
||||
$this.LogFile = Join-Path $this.PlexDBDir -ChildPath "DBRepair.log"
|
||||
}
|
||||
|
||||
return $Success
|
||||
}
|
||||
|
||||
# Core routine that loops over all provided commands and executes them in order.
|
||||
[void] MainLoop([System.Collections.ArrayList] $Arguments) {
|
||||
$this.Options.Scripted = $Arguments.Count -ne 0
|
||||
$i = 0
|
||||
$Argc = $Arguments.Count
|
||||
$NullInput = 0
|
||||
$EOFExit = $false
|
||||
while ($true) {
|
||||
$Choice = $null
|
||||
if ($this.Options.Scripted) {
|
||||
if ($i -eq $Argc) {
|
||||
$Choice = "exit"
|
||||
} else {
|
||||
$Choice = $Arguments[$i++]
|
||||
}
|
||||
} else {
|
||||
if ($this.Options.ShowMenu) {
|
||||
$this.PrintMenu()
|
||||
}
|
||||
|
||||
Write-Host
|
||||
$Choice = Read-Host "Enter command # -or- command name (4 char min)"
|
||||
if ($Choice -eq "") {
|
||||
++$NullInput
|
||||
if ($NullInput -eq 5) {
|
||||
$this.Output("Unexpected EOF / End of command line options. Exiting. Keeping temp files. ")
|
||||
$Choice = "exit"
|
||||
$EOFExit = $true
|
||||
} else {
|
||||
if ($NullInput -eq 4) {
|
||||
Write-Warning "Next empty command exits as EOF. "
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
$NullInput = 0
|
||||
}
|
||||
}
|
||||
|
||||
# Update timestamp
|
||||
$this.Timestamp = Get-Date -Format 'yyyy-MM-dd_HH.mm.ss'
|
||||
|
||||
switch -Regex ($Choice) {
|
||||
"^(1|stop)$" { $this.DoStop() }
|
||||
"^(2|autom?a?t?i?c?)$" {
|
||||
$this.SetStage("Auto")
|
||||
$this.IsError = !$this.RunAutomaticDatabaseMaintenance()
|
||||
}
|
||||
"^(7|start?)$" { $this.StartPMS() }
|
||||
"^(21|(prune?|remov?e?))$" {
|
||||
$this.SetStage("Prune")
|
||||
$this.PrunePhotoTranscoderCache()
|
||||
}
|
||||
"^(42|ignor?e?|honor?)$" {
|
||||
if (($this.Options.IgnoreErrors -and ($Choice[0] -eq 'i')) -or (!$this.Options.IgnoreErrors -and ($Choice[0] -eq 'h'))) {
|
||||
Write-Host "Honor/Ignore setting unchanged."
|
||||
Break
|
||||
}
|
||||
|
||||
$this.Options.IgnoreErrors = !$this.Options.IgnoreErrors
|
||||
$msg = if ($this.Options.IgnoreErrors) { "Ignoring database errors." } else { "Honoring database errors." }
|
||||
$this.WriteOutputLog($msg)
|
||||
}
|
||||
"^(98|quit)$" {
|
||||
$this.Output("Retaining all temporary work files.")
|
||||
$this.WriteLog("Exit - Retain temp files.")
|
||||
$this.WriteEnd()
|
||||
return
|
||||
}
|
||||
"^(99|exit)$" {
|
||||
if ($EOFExit) {
|
||||
$this.Output("Unexpected exit command. Keeping all temporary work files.")
|
||||
$this.WriteLog("EOFExit - Retain temp files.")
|
||||
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.WriteEnd()
|
||||
return
|
||||
}
|
||||
"^menu\b" {
|
||||
$Match = $Choice -match "^menu\s+(on|off|yes|no)"
|
||||
if (!$Match) {
|
||||
$this.OutputWarn("Invalid 'menu' format. Expected 'menu on/off/yes/no', got '$Choice'")
|
||||
Break
|
||||
}
|
||||
|
||||
$TurnOn = ($Matches.1 -eq 'on') -or ($Matches.1 -eq 'yes');
|
||||
$this.Options.ShowMenu = $TurnOn
|
||||
if (!$TurnOn) {
|
||||
Write-Host "Menu off: Reenable with 'menu on' command"
|
||||
}
|
||||
}
|
||||
Default {
|
||||
$this.OutputWarn("Unknown Command: '$Choice'")
|
||||
$this.WriteLog("Unknown command: '$Choice'")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Attempt to stop Plex Media Server if it's running
|
||||
[void] DoStop() {
|
||||
$this.WriteLog("Stop - START")
|
||||
$PMS = $this.GetPMS()
|
||||
if ($null -eq $PMS) {
|
||||
$this.Output("PMS already stopped.")
|
||||
return
|
||||
}
|
||||
|
||||
$this.Output("Stopping PMS.")
|
||||
|
||||
# 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
|
||||
# PMS to close nicely, and bail if that doesn't work.
|
||||
$ErrorText = $null
|
||||
Invoke-Expression "taskkill /im ""Plex Media Server.exe""" 2>$null -ErrorVariable ErrorText
|
||||
if ($ErrorText) {
|
||||
$this.WriteOutputLogWarn("Failed to send terminate signal to PMS, please stop manually.")
|
||||
$this.WriteOutputLogWarn($ErrorText -join "`n")
|
||||
return
|
||||
}
|
||||
|
||||
$PMS.WaitForExit(30000) *>$null # Wait at most 30 seconds for PMS to close. If it still hasn't by then, bail.
|
||||
if ($PMS.HasExited) {
|
||||
$this.WriteLog("Stop - PASS")
|
||||
$this.Output("Stopped PMS.")
|
||||
return
|
||||
}
|
||||
|
||||
$this.OutputWarn("Could not stop PMS. PMS did not shutdown within 30 second limit.")
|
||||
$this.WriteLog("Stop - FAIL (Timeout)")
|
||||
}
|
||||
|
||||
# Start Plex Media Server if it isn't already running
|
||||
[void] StartPMS() {
|
||||
$this.WriteLog("Start - START")
|
||||
if ($this.PMSRunning()) {
|
||||
$this.Output("Start not needed. PMS is running.")
|
||||
$this.WriteLog("Start - PASS - PMS is already running")
|
||||
return
|
||||
}
|
||||
|
||||
$PMS = Join-Path (Split-Path -Parent $this.PlexSQL) -ChildPath "Plex Media Server.exe"
|
||||
try {
|
||||
Start-Process $PMS -EA Stop
|
||||
$this.Output("Started PMS")
|
||||
$this.WriteLog("Start - PASS")
|
||||
} catch {
|
||||
$Err = $Error -join "`n"
|
||||
$this.OutputWarn("Could not start PMS: $Err")
|
||||
$Error.Clear()
|
||||
}
|
||||
}
|
||||
|
||||
# All-in-one database utility - Repair/Check/Reindex
|
||||
[bool] RunAutomaticDatabaseMaintenance() {
|
||||
$this.Output("Automatic Check,Repair,Index started.")
|
||||
$this.WriteLog($this.StageLog("START"))
|
||||
|
||||
if ($this.PMSRunning()) {
|
||||
$this.WriteLog($this.StageLog("FAIL - PMS running"))
|
||||
$this.OutputWarn("Unable to run automatic sequence. PMS is running. Please stop PlexMediaServer.")
|
||||
return $false
|
||||
}
|
||||
|
||||
# Create temporary backup directory
|
||||
$DBTemp = Join-Path $this.PlexDBDir -ChildPath "dbtmp"
|
||||
if (!$this.DirExists($DBTemp)) {
|
||||
$TempDirError = $null
|
||||
New-Item -Path $DBTemp -ItemType "directory" -ErrorVariable tempDirError *>$null
|
||||
if ($TempDirError) {
|
||||
$this.ExitDBMaintenance("Unable to create temporary database directory", $false)
|
||||
return $false
|
||||
}
|
||||
}
|
||||
|
||||
$this.Output("Exporting Main DB")
|
||||
$MainDBPath = Join-Path $this.PlexDBDir -ChildPath $this.MainDB
|
||||
$MainDBSQL = Join-Path $DBTemp -ChildPath "library.sql_$($this.TimeStamp)"
|
||||
if (!$this.FileExists($MainDBPath)) {
|
||||
$this.ExitDBMaintenance("Could not find $($this.MainDB) in database directory", $false)
|
||||
return $false
|
||||
}
|
||||
|
||||
if (!$this.ExportPlexDB($MainDBPath, $MainDBSQL)) { return $false }
|
||||
|
||||
$this.Output("Exporting Blobs DB")
|
||||
$BlobsDBPath = Join-Path $this.PlexDBDir -ChildPath $this.BlobsDB
|
||||
$BlobsDBSQL = Join-Path $DBTemp -ChildPath "blobs.sql_$($this.Timestamp)"
|
||||
if (!$this.FileExists($BlobsDBPath)) {
|
||||
$this.ExitDBMaintenance("Could not find $($this.BlobsDB) in database directory", $false)
|
||||
return $false
|
||||
}
|
||||
|
||||
if (!$this.ExportPlexDB($BlobsDBPath, $BlobsDBSQL)) { return $false }
|
||||
|
||||
$this.Output("Successfully exported the main and blobs databases. Proceeding to import into new database.")
|
||||
$this.WriteLog("Repair - Export databases - PASS")
|
||||
|
||||
# Make sure Plex hasn't been started while we were exporting
|
||||
if (!$this.CheckPMS("export")) { return $false }
|
||||
|
||||
$this.Output("Importing Main DB.")
|
||||
$MainDBImport = Join-Path $this.PlexDBDir -ChildPath "$($this.MainDB)_$($this.Timestamp)"
|
||||
if (!$this.ImportPlexDB($MainDBSQL, $MainDBImport)) { return $false }
|
||||
|
||||
$this.Output("Importing Blobs DB.")
|
||||
$BlobsDBImport = Join-Path $this.PlexDBDir -ChildPath "$($this.BlobsDB)_$($this.Timestamp)"
|
||||
if (!$this.ImportPlexDB($BlobsDBSQL, $BlobsDBImport)) { return $false }
|
||||
|
||||
$this.Output("Successfully imported databases.")
|
||||
$this.WriteLog("Repair - Import - PASS")
|
||||
|
||||
$this.Output("Verifying databases integrity after importing.")
|
||||
|
||||
if (!$this.IntegrityCheck($MainDBImport, "Main")) { return $false }
|
||||
$this.Output("Verification complete. PMS main database is OK.")
|
||||
$this.WriteLog("Repair - Verify main database - PASS")
|
||||
|
||||
if (!$this.IntegrityCheck($BlobsDBImport, "Blobs")) { return $false }
|
||||
$this.Output("Verification complete. PMS blobs database is OK.")
|
||||
$this.WriteLog("Repair - Verify blobs database - PASS")
|
||||
|
||||
if (!$this.CheckPMS("import")) { return $false }
|
||||
|
||||
# Import complete, now reindex
|
||||
$this.WriteOutputLog("Reindexing Main DB")
|
||||
if (!$this.RunSQLCommand("""$MainDBImport"" ""REINDEX;""", "Failed to reindex Main DB")) { return $false }
|
||||
$this.WriteOutputLog("Reindexing Blobs DB")
|
||||
if (!$this.RunSQLCommand("""$BlobsDBImport"" ""REINDEX;""", "Failed to reindex Blobs DB")) { return $false }
|
||||
$this.WriteOutputLog("Reindexing complete.")
|
||||
|
||||
$this.WriteOutputLog("Moving current DBs to DBTMP and making new databases active")
|
||||
if (!$this.CheckPMS("new database copy")) { return $false }
|
||||
|
||||
try {
|
||||
$this.MoveDatabase($MainDBPath, (Join-Path $DBTemp -ChildPath "$($this.MainDB)_$($this.Timestamp)"), "move Main DB to DBTMP")
|
||||
$this.MoveDatabase($MainDBImport, $MainDBPath, "replace Main DB with rebuilt DB")
|
||||
|
||||
$this.MoveDatabase($BlobsDBPath, (Join-Path $DBTemp -ChildPath "$($this.BlobsDB)_$($this.Timestamp)"), "move Blobs DB to DBTMP")
|
||||
$this.MoveDatabase($BlobsDBImport, $BlobsDBPath, "replace Blobs DB with rebuilt DB")
|
||||
} catch {
|
||||
$Error.Clear()
|
||||
return $false
|
||||
}
|
||||
|
||||
$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] $SubStage) {
|
||||
if ($this.PMSRunning()) {
|
||||
$SubMessage = if ($SubStage) { "during $SubStage" } else { "" }
|
||||
$this.WriteLog($this.StageLog("FAIL - PMS running $SubMessage"))
|
||||
$this.OutputWarn("Unable to run $($this.Stage.TrimEnd()). 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)
|
||||
[void] PrunePhotoTranscoderCache() {
|
||||
$this.WriteLog($this.StageLog("START"))
|
||||
if ($this.PMSRunning()) {
|
||||
$this.OutputWarn("Unable to prune Phototranscoder cache. PMS is running.")
|
||||
$this.WriteLog($this.StageLog("FAIL - PMS running"))
|
||||
return
|
||||
}
|
||||
|
||||
$Cutoff = $this.Options.CacheAge
|
||||
$ShouldPrune = $this.Options.Scripted
|
||||
if (!$ShouldPrune) {
|
||||
$this.Output("Counting how many files are more than $Cutoff days old")
|
||||
$CacheResult = $this.CheckPhotoTranscoderCache($true)
|
||||
$Prunable = $CacheResult.PrunableFiles
|
||||
$SpaceSaved = $CacheResult.SpaceSavings
|
||||
|
||||
if ($Prunable -eq 0) {
|
||||
$this.Output("No files found to prune.")
|
||||
$this.WriteLog($this.StageLog("PASS (no files found to prune)"))
|
||||
return
|
||||
}
|
||||
|
||||
$ShouldPrune = $this.GetYesNo("OK to prune $Prunable files ($SpaceSaved)")
|
||||
}
|
||||
|
||||
if ($ShouldPrune) {
|
||||
$this.Output("Pruning started.")
|
||||
$PruneResult = $this.CheckPhotoTranscoderCache($false)
|
||||
$Pruned = $PruneResult.PrunableFiles
|
||||
$Total = $PruneResult.TotalFiles
|
||||
$Saved = $PruneResult.SpaceSavings
|
||||
$this.WriteOutputLog($this.StageLog("Removed $Pruned files over $Cutoff days old ($Saved), out of $Total total files"))
|
||||
$this.Output("Pruning completed.")
|
||||
} else {
|
||||
$this.WriteOutputLog($this.StageLog("Prune cancelled by user"))
|
||||
}
|
||||
|
||||
$this.WriteLog($this.StageLog("PASS"))
|
||||
}
|
||||
|
||||
# Traverses PhotoTranscoder cache to find and delete files older than the specified max age.
|
||||
# If $DryRun is $true, don't remove items, just gather statistics.
|
||||
[CleanCacheResult] CheckPhotoTranscoderCache([bool] $DryRun) {
|
||||
$Cutoff = (Get-Date).AddDays(-$this.Options.CacheAge);
|
||||
$AllFiles = 0;
|
||||
$OldFiles = 0;
|
||||
$FreedBytes = 0;
|
||||
Get-ChildItem -Path $this.PlexCache -Recurse -File |
|
||||
Where-Object { $_.extension -in '.jpg','.jpeg','.png','.ppm' } |
|
||||
ForEach-Object {
|
||||
$AllFiles++;
|
||||
if ($_.LastWriteTime -lt $Cutoff) {
|
||||
$OldFiles++;
|
||||
$FreedBytes += $_.Length;
|
||||
if (!$DryRun) {
|
||||
Remove-Item $_.FullName;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return [CleanCacheResult]::new($AllFiles, $OldFiles, $FreedBytes)
|
||||
}
|
||||
|
||||
### Helpers ###
|
||||
|
||||
### Logging Helpers ###
|
||||
|
||||
[string] Now() { return Get-Date -Format 'yyyy-MM-dd HH.mm.ss' }
|
||||
|
||||
# Write the given text to the console
|
||||
[void] Output([string] $Text) {
|
||||
if ($this.Options.Scripted) {
|
||||
Write-Host "$($this.Now()) $Text"
|
||||
} else {
|
||||
Write-Host $Text
|
||||
}
|
||||
}
|
||||
|
||||
# Write the given text as a warning in the console
|
||||
[void] OutputWarn([string] $Text) {
|
||||
if ($this.Options.Scripted) {
|
||||
Write-Warning "$($this.Now()) $Text"
|
||||
} else {
|
||||
Write-Warning $Text
|
||||
}
|
||||
}
|
||||
|
||||
# Write the given text to the log file
|
||||
[void] WriteLog([string] $Text) {
|
||||
Add-Content -Path $this.LogFile -Value "$($this.Now()) -- $($Text)"
|
||||
}
|
||||
|
||||
# Write the given text to the log file and console
|
||||
[void] WriteOutputLog([string] $Text) {
|
||||
$this.WriteLog($Text)
|
||||
$this.Output($Text)
|
||||
}
|
||||
|
||||
# Write the given text to the log file and as warning text in the console
|
||||
[void] WriteOutputLogWarn([string] $Text) {
|
||||
$this.WriteLog($Text)
|
||||
$this.OutputWarn($Text)
|
||||
}
|
||||
|
||||
# Write out the end of the session
|
||||
[void] WriteEnd() {
|
||||
$this.WriteLog("Session end. $(Get-Date)")
|
||||
$this.WriteLog("============================================================")
|
||||
}
|
||||
|
||||
# Set the current stage (with the right amount of padding)
|
||||
[void] SetStage([string] $Stage) {
|
||||
$this.Stage = $Stage + (" " * [math]::Max(0, 8 - $Stage.Length))
|
||||
}
|
||||
|
||||
# Prepend the current stage to the given text
|
||||
[string] StageLog([string] $text) {
|
||||
return "$($this.Stage) - $text"
|
||||
}
|
||||
|
||||
### File Helpers ###
|
||||
|
||||
# Check whether the given directory exists (and is a directory)
|
||||
[bool] DirExists([string] $Dir) {
|
||||
if ($Dir) {
|
||||
return Test-Path $Dir -PathType Container
|
||||
}
|
||||
|
||||
return $false
|
||||
}
|
||||
|
||||
# Check whether the given file exists (and is a file)
|
||||
[bool] FileExists([string] $File) {
|
||||
if ($File) {
|
||||
return Test-Path $File -PathType Leaf
|
||||
}
|
||||
|
||||
return $false
|
||||
}
|
||||
|
||||
### Setup Helpers ###
|
||||
|
||||
# Retrieve Plex's data directory, exiting the script on failure
|
||||
[string] GetAppDataDir() {
|
||||
$PMSRegistry = $this.GetHKCU()
|
||||
$PlexAppData = $PMSRegistry.LocalAppDataPath
|
||||
if ($PlexAppData) {
|
||||
$PlexAppData = Join-Path -Path $PlexAppData -ChildPath "Plex Media Server"
|
||||
}
|
||||
|
||||
if ($this.DirExists($PlexAppData)) {
|
||||
return $PlexAppData
|
||||
}
|
||||
|
||||
$PlexAppData = "$env:LOCALAPPDATA\Plex Media Server"
|
||||
if ($this.DirExists($PlexAppData)) {
|
||||
return $PlexAppData
|
||||
}
|
||||
|
||||
Write-Host "Could not determine Plex data directory, cannot continue"
|
||||
Write-Host "Normally $env:LOCALAPPDATA\Plex Media Server"
|
||||
exit
|
||||
}
|
||||
|
||||
# Retrieve PMS settings under HKEY_CURRENT_USER, exiting the script on failure
|
||||
[PSCustomObject] GetHKCU() {
|
||||
try {
|
||||
return (Get-ItemProperty -path 'HKCU:\Software\Plex, Inc.\Plex Media Server' -EA Stop)
|
||||
} catch {
|
||||
Write-Warning "Could not find Plex registry settings (HKCU\Software\Plex, Inc.\Plex Media Server). Are you sure Plex is installed on this machine?"
|
||||
exit
|
||||
}
|
||||
}
|
||||
|
||||
# Set the Plex database directory, returning whether we found the directory
|
||||
[bool] GetPlexDBDir([string] $AppData) {
|
||||
$DBDir = Join-Path -Path $AppData -ChildPath "Plug-in Support\Databases"
|
||||
if ($this.DirExists($DBDir)) {
|
||||
$this.PlexDBDir = $DBDir;
|
||||
return $true;
|
||||
}
|
||||
|
||||
Write-Host "Could not find Databases folder, cannot continue."
|
||||
Write-Host "Normally $DBDir"
|
||||
return $false
|
||||
}
|
||||
|
||||
# Set the path to Plex's PhotoTranscoder cache, returning whether we found the directory.
|
||||
[bool] GetPhotoTranscoderDir([string] $AppData) {
|
||||
$CacheDir = Join-Path -Path $AppData -ChildPath "Cache\PhotoTranscoder"
|
||||
if ($this.DirExists($CacheDir)) {
|
||||
$this.PlexCache = $CacheDir
|
||||
return $true
|
||||
}
|
||||
|
||||
Write-Host "Could not find PhotoTranscoder path, cannot prune."
|
||||
Write-Host "Normally $CacheDir"
|
||||
return $false
|
||||
}
|
||||
|
||||
# Find the path to Plex SQLite.exe, falling back to user input if necessary.
|
||||
[bool] GetPlexSQL() {
|
||||
$PMSRegistry = $this.GetHKCU()
|
||||
$InstallDir = $PMSRegistry.InstallFolder
|
||||
if (!$InstallDir) {
|
||||
# Install location might also be in HKLM
|
||||
$InstallDir = (Get-ItemProperty -path 'HKLM:\SOFTWARE\Plex, Inc.\Plex Media Server' -EA Ignore).InstallFolder
|
||||
if (!$InstallDir) {
|
||||
# Final registry attempt - WOW6432Node
|
||||
$InstallDir = (Get-ItemProperty -path 'HKLM:\SOFTWARE\WOW6432Node\Plex, Inc.\Plex Media Server' -EA Ignore).InstallFolder
|
||||
}
|
||||
}
|
||||
|
||||
$SQL = if ($InstallDir) { Join-Path -Path $InstallDir -ChildPath "Plex SQLite.exe" } else { $null }
|
||||
if ($this.FileExists($SQL)) {
|
||||
$this.PlexSQL = $SQL
|
||||
return $true
|
||||
}
|
||||
|
||||
# Still couldn't find install directory. Try standard PROGRAMFILES variables
|
||||
$SQL = "$env:PROGRAMFILES\Plex\Plex Media Server\Plex SQ Lite.exe"
|
||||
if ($this.FileExists($SQL)) {
|
||||
$this.PlexSQL = $SQL
|
||||
return $true
|
||||
}
|
||||
|
||||
if (${env:PROGRAMFILES(X86)}) {
|
||||
$SQL = "${env:PROGRAMFILES(X86)}\Plex Plex Media Server\Plex SQLite.exe"
|
||||
if ($this.FileExists($SQL)) {
|
||||
Write-Host "Note: 32-bit version of PMS detected on a 64-bit version of Windows. Using the 64-bit release of PMS is recommended."
|
||||
$this.PlexSQL = $SQL
|
||||
return $true
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "Could not determine Plex SQLite location. Please provide it below"
|
||||
Write-Host "Normally $env:PORGRAMFILES\Plex\Plex Media Server\Plex SQLite.exe"
|
||||
$First = $true
|
||||
while (!$this.FileExists($SQL)) {
|
||||
if (!$First) {
|
||||
Write-Host "ERROR: '$SQL' could not be found"
|
||||
}
|
||||
|
||||
$First = $false
|
||||
$SQL = Read-Host -Prompt "Path to Plex SQLite.exe (Ctrl+C to cancel): "
|
||||
}
|
||||
|
||||
$this.PlexSQL = $SQL
|
||||
return $true
|
||||
}
|
||||
|
||||
### Database Helpers ###
|
||||
|
||||
# Writes to output/log when we're done with database maintenance (on success or failure)
|
||||
[void] ExitDBMaintenance([string] $Message, [boolean] $Success) {
|
||||
if ($Success) {
|
||||
$this.Output("Automatic Check,Repair,Index succeeded.")
|
||||
$this.WriteLog($this.StageLog("PASS"))
|
||||
} else {
|
||||
$this.OutputWarn("Database maintenance failed - $Message")
|
||||
$this.WriteLog($this.StageLog("$Message, cannot continue."))
|
||||
$this.WriteLog($this.StageLog("FAIL"))
|
||||
}
|
||||
}
|
||||
|
||||
[bool] ExportPlexDB([string] $Source, [string] $Destination) {
|
||||
return $this.RunSQLCommand("""$Source"" "".output '$Destination'"" .dump", "Failed to export '$Source' to '$Destination'")
|
||||
}
|
||||
|
||||
# Run an SQL command.
|
||||
# ErrorMessage is the message to output/write to the log on failure
|
||||
[bool] RunSQLCommand([string] $Command, [string] $ErrorMessage) {
|
||||
return $this.RunSQLCommandCore($Command, $ErrorMessage, $null)
|
||||
}
|
||||
|
||||
# Run an SQL command and retrieve the output of said command
|
||||
# ErrorMessage is the message to output/write to the log on failure
|
||||
[bool] GetSQLCommandResult([string] $Command, [string] $ErrorMessage, [ref] $Output) {
|
||||
return $this.RunSQLCommandCore($Command, $ErrorMessage, $Output)
|
||||
}
|
||||
|
||||
# Run a 'Plex SQLite' command
|
||||
[bool] RunSQLCommandCore([string] $Command, [string] $ErrorMessage, [ref] $Output) {
|
||||
$SqlError = $null
|
||||
$SqlResult = $null
|
||||
$ExitCode = 0
|
||||
try {
|
||||
Invoke-Expression "& ""$($this.PlexSQL)"" $Command" -ev sqlError -OutVariable sqlResult -EA Stop *>$null
|
||||
$ExitCode = $LASTEXITCODE
|
||||
} catch {
|
||||
$Err = $Error -join "`n"
|
||||
$this.ExitDBMaintenance("Failed to run command '$Command': '$Err'", $false)
|
||||
$Error.Clear()
|
||||
return $false
|
||||
}
|
||||
|
||||
if ($SqlError -or $ExitCode) {
|
||||
$Err = $SqlError -join "`n"
|
||||
if (!$Err) { $Err = "Process exited with error code $ExitCode" }
|
||||
$Msg = $ErrorMessage
|
||||
if (!$Msg) {
|
||||
$Msg = "Plex SQLite operation failed"
|
||||
}
|
||||
|
||||
if ($this.Options.IgnoreErrors -and $this.Options.CanIgnore) {
|
||||
$this.OutputWarn("Ignoring database errors - ${Msg}: $Err")
|
||||
} else {
|
||||
$this.ExitDBMaintenance("${Msg}: $Err", $false)
|
||||
return $false
|
||||
}
|
||||
}
|
||||
|
||||
if ($null -ne $Output.Value) {
|
||||
$Output.Value = $SqlResult
|
||||
}
|
||||
|
||||
return $true
|
||||
}
|
||||
|
||||
# Import an exported .sql file into a new database
|
||||
[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
|
||||
$ExitCode = 0
|
||||
$Err = $null
|
||||
try {
|
||||
# Use Start-Process, since PowerShell doesn't have '<', and alternatives ("Get-Content X | SQLite.exe OutDB") are subpar at best when dealing with large files like these database exports.
|
||||
$process = Start-Process $this.PlexSQL -ArgumentList @("""$Destination""") -RedirectStandardInput $Source -NoNewWindow -Wait -PassThru -EA Stop -ErrorVariable ImportError
|
||||
$ExitCode = $process.ExitCode
|
||||
} catch {
|
||||
$Err = $Error -join "`n"
|
||||
$Error.Clear()
|
||||
}
|
||||
|
||||
if ($ImportError) {
|
||||
$Err = $ImportError -join "`n"
|
||||
} elseif ($ExitCode) {
|
||||
if ($this.Options.IgnoreErrors) {
|
||||
$this.OutputWarn("Ignoring errors found during import")
|
||||
} else {
|
||||
$Err = "Process exited with error code $ExitCode (constraint error?)"
|
||||
}
|
||||
}
|
||||
|
||||
if ($Err) {
|
||||
$this.ExitDBMaintenance("Failed to import Plex database (importing '$Source' into '$Destination'): $Err", $false)
|
||||
return $false
|
||||
}
|
||||
|
||||
return $true
|
||||
}
|
||||
|
||||
[bool] IntegrityCheck([string] $Database, [string] $DbName) {
|
||||
$this.Options.CanIgnore = $false
|
||||
$VerifyResult = ""
|
||||
$result = $this.GetSQLCommandResult("""$Database"" ""PRAGMA integrity_check(1)""", "Failed to verify $dbName DB", [ref]$VerifyResult)
|
||||
if ($result) {
|
||||
$this.Output("$DbName DB verification check is: $VerifyResult")
|
||||
if ($VerifyResult -ne "ok") {
|
||||
$this.ExitDBMaintenance("$DbName DB verification failed: $VerifyResult", $false)
|
||||
$result = $false
|
||||
}
|
||||
}
|
||||
|
||||
$this.Options.CanIgnore = $true
|
||||
return $result
|
||||
}
|
||||
|
||||
# Clear out the temp database directory. If $Confirm is $true, asks the user before doing so.
|
||||
[void] CleanDBTemp([bool] $Confirm) {
|
||||
if ($Confirm -and !$this.GetYesNo("Ok to remove temporary databases/workfiles for this session")) {
|
||||
$this.Output("Retaining all temporary work files.")
|
||||
$this.WriteLog("Exit - Retain temp files.")
|
||||
return
|
||||
}
|
||||
|
||||
$DBTemp = Join-Path $this.PlexDBDir -ChildPath "dbtmp"
|
||||
if ($this.DirExists($DBTemp)) {
|
||||
try {
|
||||
Remove-Item $DBTemp -Recurse -Force -EA Stop
|
||||
$this.Output("Deleted all temporary work files.")
|
||||
$this.WriteLog("Exit - Deleted temp files.")
|
||||
} catch {
|
||||
$Err = $Error -join "`n"
|
||||
$this.OutputWarn("Failed to remove temporary directory: $Err")
|
||||
$this.WriteLog("Exit - Failed to remove temporary files: $Err")
|
||||
$Error.Clear()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
### Miscellaneous Helpers ###
|
||||
|
||||
# Return whether PMS is running
|
||||
[bool] PMSRunning() {
|
||||
return $null -ne $this.GetPMS()
|
||||
}
|
||||
|
||||
# Retrieve the PMS process, if running
|
||||
[System.Diagnostics.Process] GetPMS() {
|
||||
return Get-Process -EA Ignore -Name "Plex Media Server"
|
||||
}
|
||||
|
||||
# Ask the user a yes or no question, continuing to prompt them until
|
||||
# their input starts with either a 'Y' or 'N'
|
||||
[bool] GetYesNo([string] $Prompt) {
|
||||
$Response = (Read-Host "$Prompt [Y/N]? ").ToLower()
|
||||
$Ch = $Response.Substring(0, [Math]::Min($Response.Length, 1))
|
||||
while (($Ch -ne "y") -and ($Ch -ne "n")) {
|
||||
Write-Host "Invalid input, please enter [Y]es or [N]o"
|
||||
$Response = (Read-Host "$Prompt [Y/N]? ").ToLower()
|
||||
$Ch = $Response.Substring(0, [Math]::Min($Response.Length, 1))
|
||||
}
|
||||
|
||||
return $Ch -eq "y"
|
||||
}
|
||||
}
|
||||
|
||||
# Contains miscellaneous options/state over the course of a session.
|
||||
class DBRepairOptions {
|
||||
[bool] $Scripted # Whether we're running in scripted or interactive mode
|
||||
[bool] $ShowMenu # Whether to show the menu after each command executes
|
||||
[bool] $IgnoreErrors # Whether to honor or ignore constraint errors on import
|
||||
[bool] $CanIgnore # Some errors can't be ignored (e.g. integrity_check)
|
||||
[int32] $CacheAge # The date cutoff for pruning PhotoTranscoder cached images
|
||||
|
||||
DBRepairOptions() {
|
||||
$this.CacheAge = 30
|
||||
$this.ShowMenu = $true
|
||||
$this.Scripted = $false
|
||||
$this.IgnoreErrors = $false
|
||||
$this.CanIgnore = $true
|
||||
}
|
||||
}
|
||||
|
||||
# Contains relevant data about a PhotoTranscoder `prune` attempt
|
||||
class CleanCacheResult {
|
||||
[int32] $TotalFiles # Total number of PhotoTranscoder files
|
||||
[int32] $PrunableFiles # Total number of files that are older than the cutoff
|
||||
[string] $SpaceSavings # Friendly string of (potential) space savings
|
||||
|
||||
CleanCacheResult([int32] $TotalFiles, [int32] $PrunableFiles, [int32] $PrunableBytes) {
|
||||
$this.TotalFiles = $TotalFiles
|
||||
$this.PrunableFiles = $PrunableFiles
|
||||
$this.SpaceSavings = "$($PrunableBytes) bytes"
|
||||
|
||||
if ($PrunableBytes -gt 1GB) {
|
||||
$this.SpaceSavings = "$([math]::round($PrunableBytes / 1GB, 2)) GiB";
|
||||
} elseif ($PrunableBytes -gt 1MB) {
|
||||
$this.SpaceSavings = "$([math]::round($PrunableBytes / 1MB, 2)) MiB";
|
||||
} elseif ($PrunableBytes -gt 1KB) {
|
||||
$this.SpaceSavings = "$([math]::round($PrunableBytes / 1KB, 2)) KiB";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Ensure the console can handle utf-8, as the export process pipes utf-8 data from the console to our temporary sql file.
|
||||
$InputEncodingSave = [console]::InputEncoding
|
||||
$OutputEncodingSave = [console]::OutputEncoding
|
||||
[console]::InputEncoding = [console]::OutputEncoding = New-Object System.Text.UTF8Encoding
|
||||
|
||||
[void]([DBRepair]::new($args, $DBRepairVersion))
|
||||
|
||||
[console]::OutputEncoding = $OutputEncodingSave
|
||||
[console]::InputEncoding = $InputEncodingSave
|
||||
BIN
Windows/DBRepair-Windows.zip
Normal file
BIN
Windows/DBRepair-Windows.zip
Normal file
Binary file not shown.
76
Windows/README-Windows.md
Normal file
76
Windows/README-Windows.md
Normal file
@@ -0,0 +1,76 @@
|
||||
# DBRepair-Windows
|
||||
|
||||
DBRepair-Windows.ps1 (and DBRepair-Windows.bat) are scripts run from the command line, which have
|
||||
sufficient privilege to read/write the Plex databases in the
|
||||
[Plex data directory](https://support.plex.tv/articles/202915258-where-is-the-plex-media-server-data-directory-located/).
|
||||
|
||||
## DBRepair-Windows.ps1 vs. DBRepair-Windows.bat
|
||||
|
||||
Currently, there are two separate Windows scripts, a batch script (.bat) and a PowerShell script
|
||||
(.ps1). The batch script is a one-shot, zero-input script that attempts automatic database
|
||||
maintenance (repair/rebuild, check, and reindex). The PowerShell script is intended to align with
|
||||
DBRepair.sh, offering command-name-based functionality that can either be scripted or
|
||||
interactive.
|
||||
|
||||
In the future, DBRepair-Windows.bat will be removed in favor of DBRepair-Windows.ps1. The batch
|
||||
file is currently kept as a backup while the PowerShell script continues to be expanded and
|
||||
tested. If any unexpected issues arise with the PowerShell script, please open an
|
||||
[issue](https://github.com/ChuckPa/DBRepair/issues) so it can be investigated.
|
||||
|
||||
## Functions provided
|
||||
|
||||
The Windows utility aims to provide a similar interface to DBRepair.sh as outlined in the main
|
||||
[README file](README.md), but currently only offers a subset of its functionality. For a full
|
||||
description of the features below, consult that main README file.
|
||||
|
||||
The following commands (or their number) are currently supported on Windows.
|
||||
|
||||
```
|
||||
AUTO(matic)
|
||||
EXIT
|
||||
PRUN(e)
|
||||
STAR(t)
|
||||
STOP
|
||||
```
|
||||
|
||||
Run `.\DBRepair-Windows.ps1 -Help` for more complete documentation.
|
||||
|
||||
# Installation and usage instructions
|
||||
|
||||
DBRepair-Windows can be downloaded to any location. However, the PowerShell script might require
|
||||
some prerequisite work in order to run as expected. By default, PowerShell scripts are blocked on
|
||||
Windows machines, so in order to run DBRepair-Windows.ps1, you may need to do one of the following:
|
||||
|
||||
1. From an administrator PowerShell prompt, run `Set-ExecutionPolicy RemoteSigned`, then run the
|
||||
script from a normal PowerShell prompt. If PowerShell still will not run the script, you can do
|
||||
one of the following:
|
||||
* In Windows Explorer, right-click DBRepair-Windows.ps1, select Properties, and check 'Unblock'
|
||||
at the bottom of the dialog.
|
||||
* In PowerShell, run `Unblock-File <path\to\DBRepair-Windows.ps1>`
|
||||
* Run `Set-ExecutionPolicy Unrestricted` - this may result in an "are you sure" prompt before
|
||||
running the script. `Set-ExecutionPolicy Bypass` will get around this, but is not recommended,
|
||||
as it allows _any_ downloaded script to run without notification, not just DBRepair.
|
||||
2. Explicitly set the `ExecutionPolicy` when running the script, e.g.:
|
||||
```powershell
|
||||
powershell -ExecutionPolicy Bypass ".\DBRepair-Windows.ps1 stop auto start"
|
||||
```
|
||||
Note that this method may not work if your machine is managed with Group Policy, which
|
||||
can block manual `ExecutionPolicy` overrides.
|
||||
|
||||
3. Similar to 2, but make it a batch script (e.g. `DBRepair.bat`) that lives alongside
|
||||
the powershell script:
|
||||
```batch
|
||||
@echo off
|
||||
powershell -ExecutionPolicy Bypass -Command ".\DBRepair-Windows.ps1 %*"
|
||||
```
|
||||
Then run that script directly:
|
||||
```batch
|
||||
.\DBRepair.bat stop auto start
|
||||
```
|
||||
|
||||
Also note that the PowerShell script cannot be run directly from a Command Prompt window.
|
||||
If you are running this from Command Prompt, you must launch it via PowerShell:
|
||||
|
||||
```cmd
|
||||
powershell .\DBRepair-Windows.ps1 [args]
|
||||
```
|
||||
33
Windows/ReleaseNotes-Windows
Normal file
33
Windows/ReleaseNotes-Windows
Normal file
@@ -0,0 +1,33 @@
|
||||
# DBRepair-Windows
|
||||
|
||||
Release notes for the Windows counterpart to DBRepair.sh (DBRepair-Windows.ps1)
|
||||
|
||||
# Release Info
|
||||
|
||||
v1.01.02
|
||||
- Remove `statistics_bandwidth` pruning, as it's now part of PMS 1.41.8
|
||||
- Change `quit` to 98 and `exit` to 99
|
||||
- Minor logging refactoring
|
||||
|
||||
v1.01.01
|
||||
- Add `staticstics_bandwidth` pruning during automatic repair.
|
||||
|
||||
v1.01.00
|
||||
- Rename this tool to be compliant with Plex inc. Trademark Policy.
|
||||
|
||||
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
|
||||
- Bug Fix: Ensure UTF-8 characters get exported/imported properly.
|
||||
|
||||
v1.00.00
|
||||
- Initial Windows PowerShell script release, aiming to provide a similar experience as DBRepair.sh, with command-name-based input.
|
||||
- Initial command support:
|
||||
- AUTO(matic)
|
||||
- EXIT
|
||||
- PRUN(e)
|
||||
- STAR(t)
|
||||
- STOP
|
||||
Reference in New Issue
Block a user