mirror of
https://github.com/ChuckPa/PlexDBRepair.git
synced 2026-11-05 09:33:46 -06:00
Compare commits
1
Commits
9e54f180ff
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff87436f53 |
No files matched your search
@@ -1,49 +0,0 @@
|
||||
|
||||
## How to contribute
|
||||
|
||||
As this tool becomes more popular, it also becomes more important to coordinate and document our work.
|
||||
|
||||
To contribute to the project, please follow the following instructions:
|
||||
|
||||
### Open an Issue describing the problem or enhancement.
|
||||
- Add a label to the issue
|
||||
|
||||
### When submitting your work for review and merge:
|
||||
|
||||
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 so there is one hash to reference.
|
||||
|
||||
4. Open the pull request against the master branch
|
||||
|
||||
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://github.com/ChuckPa/DBRepair/issues/12 (if we were fixing issue 12)
|
||||
|
||||
- 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.
|
||||
|
||||
6. Be certain to request review with your pull request.
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
-3034
File diff suppressed because it is too large.
Load diff
-957
@@ -1,957 +0,0 @@
|
||||
# DBRepair
|
||||
|
||||
[](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.17.01
|
||||
|
||||
1. CheckFTS/RebuildFTS - FTSRebuild did not perform a proper CheckFTS after repairing the FTS
|
||||
tables. While this doesn't guarantee they won't corrupt again. It does
|
||||
confirm DBRepair has repaired them.
|
||||
|
||||
Thanks to @dreondre for catching and fixing this.
|
||||
|
||||
v1.17.00
|
||||
|
||||
1. BusyBox - Address edge case errors with NAS vendors' BusyBox shell implementation.
|
||||
This addresses weird command behavior on those systems
|
||||
|
||||
2. Long Term Support - DBRepair (BASH) version is entering Long Term Support (LTS) phase.
|
||||
Active development focuses on the compiled C version for all platforms.
|
||||
This existing BASH version continues to be primary tool until such time
|
||||
it becomes production ready (perhaps 2027 timeframe).
|
||||
|
||||
- DBRepair.sh will be fully maintained and enhanced as needed by the community
|
||||
until such time as it's retired to Legacy Archive status.
|
||||
|
||||
|
||||
|
||||
v1.16.00
|
||||
|
||||
1. MacOS App Dir - Older MacOS systems use /Applications. Newer MacOS systems use $HOME/Applications.
|
||||
DBRepair now detects this and supports both locations.
|
||||
|
||||
v1.15.00
|
||||
|
||||
1. DSM 6 Start/Stop - Change how DSM 6 start/stop feature implemented
|
||||
This accounts for changes in DSM 6.
|
||||
|
||||
2. Permissions/Owner - Some DSM users experience permission errors after running DBRepair
|
||||
on Synology. Begin implementation of more robust handling for
|
||||
restricted environments like DSM.
|
||||
|
||||
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
|
||||
|
||||
- Improve HOTIO support. Allow both older and newer directory structure
|
||||
|
||||
v1.0.12
|
||||
|
||||
- Update HOTIO start/stop commands (re: #404461a)
|
||||
|
||||
v1.0.11
|
||||
|
||||
- Correct PMS stop problem. Stop operation would not wait until PMS actually stopped.
|
||||
Now DBRepair waits up to 30 seconds for PMS to stop before issuing error
|
||||
|
||||
|
||||
v1.0.10
|
||||
|
||||
- Correct bug when importing watch lists. Negative watch counts could occur.
|
||||
This release corrects the issue.
|
||||
|
||||
v1.0.9
|
||||
|
||||
- When recovering a damaged database, it is sometimes necessary to ignore constraint errors resulting from damaged indexes.
|
||||
|
||||
This update provides command line options to support this:
|
||||
|
||||
1. -i or -f - Ignore DB check errors / Force acceptance.
|
||||
2. -p - When importing viewstate from another DB, purge duplicate counts.
|
||||
(Use with extreme caution).
|
||||
|
||||
Usage: DBRepair.sh [Options] commands
|
||||
Example: DBRepair.sh -i -p start auto stop exit
|
||||
|
||||
|
||||
v1.0.8
|
||||
- Require root UID (super user).
|
||||
Requiring root UID gives the script the privilege necessary to set the database ownership
|
||||
and permissions to what they were when the tool started. Without this, the resultant database
|
||||
might not be accessible by the Plex Media Server user.
|
||||
|
||||
Synology: This is achieved by typing 'sudo -su root' at the command line prompt
|
||||
and entering your password.
|
||||
|
||||
QNAP: This is achieved by typing 'sudo -su admin' at the command line prompt
|
||||
and entering your password.
|
||||
|
||||
Containers: Containers run as 'root' at the command line by default.
|
||||
|
||||
Other platforms will be similar to the above. Please try combinations of 'sudo'
|
||||
and 'sudo --help' for more details.
|
||||
|
||||
- When successful, you'll see a '#' in the prompt (signifies 'root' level privilege)
|
||||
|
||||
v1.0.7
|
||||
- Correct conditional test on Binhex container which prevented proper detection.
|
||||
Redact v1.0.6
|
||||
|
||||
v1.0.6
|
||||
- Correct detection conflict between Arch Linux native package and Binhex container.
|
||||
|
||||
v1.0.5
|
||||
- Add Arch Linux Support
|
||||
- Put system override processing in function
|
||||
- Separate Quit (99) and Exit functionality to reduce confusion about temp file removal.
|
||||
|
||||
v1.0.4
|
||||
- Correct Start/Stop problem on some hosts
|
||||
|
||||
v1.0.3
|
||||
- Use POSIX free space calculation on all systems
|
||||
|
||||
# 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.
|
||||
|
||||
While it does correct the following errors, it can't correct 'data' errors with the individual records.
|
||||
These type changes can only be performed by PMS.
|
||||
## Situations and errors commonly seen include:
|
||||
|
||||
1. Searching is sluggish
|
||||
2. Database is malformed / damaged / corrupted
|
||||
3. Database has bloated from media addition or changes
|
||||
4. Damaged indexes damaged
|
||||
|
||||
## Functions provided
|
||||
|
||||
The utility accepts command names.
|
||||
Command names may be upper/lower case and may also be abbreviated (4 character minimum).
|
||||
|
||||
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
|
||||
EXIT - Exit the utility
|
||||
IMPO(rt) - Import viewstate / watch history from another database
|
||||
REIN(dex) - Rebuild the database 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)
|
||||
STOP - Stop PMS (not available on all platforms)
|
||||
UNDO - UNDO the last operation
|
||||
VACU(um) - Vacuum the databases
|
||||
|
||||
|
||||
### The menu
|
||||
|
||||
The menu gives you the option to enter either a 'command number' or the 'command name/abbreviation'.
|
||||
For clarity, each command's name is 'quoted'.
|
||||
|
||||
|
||||
Database Repair Utility for Plex Media Server (_host_configuration_name_)
|
||||
Version v1.0.0
|
||||
|
||||
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
|
||||
|
||||
99 - exit
|
||||
|
||||
Enter command # -or- command name (4 char min) :
|
||||
|
||||
|
||||
|
||||
## Hosts currently supported
|
||||
|
||||
1. Apple (MacOS)
|
||||
2. Arch Linux
|
||||
3. ASUSTOR
|
||||
4. Docker containers via 'docker exec' command (inside the running container environment)
|
||||
- Plex,inc.
|
||||
- Linuxserver.io
|
||||
- BINHEX
|
||||
- HOTIO
|
||||
- Podman (libgpod)
|
||||
5. Linux workstation & server
|
||||
6. Netgear (OS5 Linux-based systems)
|
||||
7. QNAP (QTS & QuTS)
|
||||
8. Synology (DSM 6 & DSM 7)
|
||||
9. Western Digital (OS5)
|
||||
|
||||
# Installation
|
||||
|
||||
Where to place the utility varies from host to host.
|
||||
Please use this table as a reference.
|
||||
|
||||
```
|
||||
Vendor | Shared folder name | Recommended directory
|
||||
-------------------+---------------------+------------------------------------------
|
||||
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)
|
||||
Hotio | N/A | Container root (adjacent /config)
|
||||
Linux (wkstn/svr) | N/A | Anywhere
|
||||
Netgear (ReadyNAS) | "your_choice" | "/data/your_choice"
|
||||
QNAP (QTS/QuTS) | Public | /share/Public
|
||||
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)
|
||||
```
|
||||
|
||||
### General installation and usage instructions
|
||||
|
||||
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 '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)
|
||||
6. Elevate privilege level to root (sudo) if needed.
|
||||
7. Extract the utility from the tar or zip file
|
||||
8. 'cd' into the extraction directory
|
||||
9. Give DBRepair.sh 'execute' permission (chmod +x)
|
||||
10. Invoke ./DBRepair.sh
|
||||
|
||||
|
||||
|
||||
|
||||
### EXAMPLE: To install & launch on Synology DSM 6
|
||||
|
||||
cd /volume1/Plex
|
||||
sudo bash
|
||||
tar xf DBRepair-x.y.z.tar.gz
|
||||
cd DBRepair-x.y.z
|
||||
chmod +x DBRepair.sh
|
||||
./DBRepair.sh
|
||||
|
||||
|
||||
### EXAMPLE: Using DBRepair inside containers (manual start/stop included)
|
||||
|
||||
#### (Select containers allow stopping/starting PMS from the menu. See menu for details)
|
||||
|
||||
sudo docker exec -it plex /bin/bash
|
||||
|
||||
# extract from downloaded version file name then cd into directory
|
||||
tar xf DBRepair-1.0.0.tar.gz
|
||||
cd DBRepair-1.0.0
|
||||
chmod +x DBRepair.sh
|
||||
./DBRepair.sh
|
||||
```
|
||||
### EXAMPLE: Using DBRepair on regular Linux native host (Workstation/Server)
|
||||
```
|
||||
sudo bash
|
||||
cd /path/to/DBRepair.tar
|
||||
tar xf DBRepair-1.0.0.tar.gz
|
||||
cd DBRepair-1.0.0
|
||||
chmod +x DBRepair.sh
|
||||
./DBRepair.sh stop auto start exit
|
||||
```
|
||||
|
||||
### EXAMPLE: Using DBRepair from the command line on MacOS (on the administrator account)
|
||||
```
|
||||
osascript -e 'quit app "Plex Media Server"'
|
||||
cd ~/Downloads
|
||||
tar xvf PlexDBRepai DBRepair-1.0.0.tar.gz
|
||||
cd PlexDBRepai DBRepair-1.0.0
|
||||
|
||||
chmod +x DBRepair.sh
|
||||
./DBRepair.sh
|
||||
|
||||
|
||||
|
||||
## Typical usage
|
||||
|
||||
This utility can only operate on PMS when PMS is in the stopped state.
|
||||
If PMS is running when you startup the utility, it will tell you.
|
||||
|
||||
These examples
|
||||
|
||||
A. The most common usage will be the "Automatic" function.
|
||||
|
||||
Automatic mode is where DBRepair determines which steps are needed to make your database run optimally.
|
||||
For most users, Automatic is equivalent to 'Check, Repair, Reindex'.
|
||||
This repairs minor damage, vacuums out all the unused records, and rebuilds search indexes in one step.
|
||||
|
||||
B. Database is malformed (Backups of com.plexapp.plugins.library.db and com.plexap.plugins.library.blobs.db available)
|
||||
Note: You may attempt "Repair" sequence
|
||||
|
||||
1. (3) Check - Confirm either main or blobs database is damaged
|
||||
2. (9) Replace - Use the most recent valid backup -- OR -- (5) Repair. Check date/time stamps for best action.
|
||||
-- If Replace fails, use Repair (5)
|
||||
-- (Replace can fail if the database has been damaged for a long time.)
|
||||
3. (6) Reindex - Generate new indexes so PMS doesn't need to at startup
|
||||
4. (99) Exit
|
||||
|
||||
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.
|
||||
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.
|
||||
4. (99) Exit
|
||||
|
||||
D. 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. Exit - (Option 9)
|
||||
|
||||
E. 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)
|
||||
|
||||
Special considerations:
|
||||
|
||||
1. As stated above, this utilty requires PMS to be stopped in order to do what it does.
|
||||
2. - This utility CAN sit at the menu prompt with PMS running.
|
||||
- You did a few things and want to check BEFORE exiting the utility
|
||||
- If you don't like how it worked out,
|
||||
-- STOP PMS
|
||||
-- UNDO the last action and do something else
|
||||
-- OR do more things to the databases
|
||||
3. When satisfied, Exit the utility.
|
||||
- There is no harm in keeping the database temp files (except for space used)
|
||||
- ALL database temps are named with date-time stamps in the name to avoid confusion.
|
||||
4. The Logfile ('show' command) shows all actions performed WITH timestamp so you can locate intermediate databases
|
||||
if desired for special / manual recovery cases.
|
||||
|
||||
|
||||
## Scripting support
|
||||
|
||||
Certain platforms don't provide for each command line access.
|
||||
To support those products, this utility can be operated by adding command line arguments.
|
||||
|
||||
Another use of this feature is to automate Plex Database maintenance
|
||||
( Stop Plex, Run this sequence, Start Plex ) at a time when the server isn't busy
|
||||
|
||||
|
||||
The command line arguments are the same as if typing at the menu.
|
||||
|
||||
Example: ./DBRepair.sh stop auto start exit
|
||||
|
||||
This executes: Stop PMS, Automatic (Check, Repair, Reindex), Start PMS, and Exit commands
|
||||
|
||||
|
||||
## Exiting
|
||||
|
||||
When exiting, you will be asked whether to keep the interim temp files created during this session.
|
||||
If you've encountered any difficulties or aren't sure what to do, don't delete them.
|
||||
You'll be able to ask in the Plex forums about what to do. Be prepared to present the log file to them.
|
||||
|
||||
|
||||
## Sample interactive session
|
||||
|
||||
This is a typical manual session if you aren't sure what to do and want the tool to decide.
|
||||
|
||||
|
||||
|
||||
```
|
||||
bash-4.4# #=======================================================================================================
|
||||
bash-4.4# ./DBRepair.sh
|
||||
|
||||
|
||||
|
||||
Database Repair Utility for Plex Media Server (Synology (DSM 7))
|
||||
Version v1.0.0
|
||||
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
99 - exit
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
99 - exit
|
||||
|
||||
Enter command # -or- command name (4 char min) : auto
|
||||
|
||||
|
||||
Checking the PMS databases
|
||||
Check complete. PMS main database is OK.
|
||||
Check complete. PMS blobs database is OK.
|
||||
|
||||
Exporting current databases using timestamp: 2023-02-25_16.15.11
|
||||
Exporting Main DB
|
||||
Exporting Blobs DB
|
||||
Successfully exported the main and blobs databases. Proceeding to import into new databases.
|
||||
Importing Main DB.
|
||||
Importing Blobs DB.
|
||||
Successfully imported data from SQL files.
|
||||
Verifying databases integrity after importing.
|
||||
Verification complete. PMS main database is OK.
|
||||
Verification complete. PMS blobs database is OK.
|
||||
Saving current databases with '-BKUP-2023-02-25_16.15.11'
|
||||
Making imported databases active
|
||||
Import complete. Please check your library settings and contents for completeness.
|
||||
Recommend: Scan Files and Refresh all metadata for each library section.
|
||||
|
||||
Backing up of databases
|
||||
Backup current databases with '-BKUP-2023-02-25_16.20.41' timestamp.
|
||||
Reindexing main database
|
||||
Reindexing main database successful.
|
||||
Reindexing blobs database
|
||||
Reindexing blobs database successful.
|
||||
Reindex complete.
|
||||
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
|
||||
|
||||
Enter command # -or- command name (4 char min) : start
|
||||
|
||||
Starting PMS.
|
||||
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
|
||||
|
||||
Enter command # -or- command name (4 char min) : stat
|
||||
|
||||
|
||||
Status report: Sat Feb 25 04:38:50 PM EST 2023
|
||||
PMS is running.
|
||||
Databases are OK.
|
||||
|
||||
|
||||
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
|
||||
|
||||
Enter command # -or- command name (4 char min) : exit
|
||||
|
||||
Ok to remove temporary databases/workfiles for this session? (Y/N) ? y
|
||||
Are you sure (Y/N) ? y
|
||||
Deleting all temporary work files.
|
||||
bash-4.4#
|
||||
|
||||
```
|
||||
|
||||
## Sample (typical) scripted session (e.g. via 'cron')
|
||||
|
||||
```
|
||||
root@lizum:/sata/plex/Plex Media Server/Plug-in Support/Databases# ./DBRepair.sh stop check auto start exit
|
||||
|
||||
|
||||
|
||||
Database Repair Utility for Plex Media Server (Ubuntu 20.04.5 LTS)
|
||||
Version v1.0.0
|
||||
|
||||
|
||||
[2023-03-05 18.53.49] Stopping PMS.
|
||||
[2023-03-05 18.53.49] Stopped PMS.
|
||||
|
||||
[2023-03-05 18.53.49] Checking the PMS databases
|
||||
[2023-03-05 18.54.22] Check complete. PMS main database is OK.
|
||||
[2023-03-05 18.54.22] Check complete. PMS blobs database is OK.
|
||||
|
||||
[2023-03-05 18.54.22] Automatic Check,Repair,Index started.
|
||||
[2023-03-05 18.54.22]
|
||||
[2023-03-05 18.54.22] Checking the PMS databases
|
||||
[2023-03-05 18.54.56] Check complete. PMS main database is OK.
|
||||
[2023-03-05 18.54.56] Check complete. PMS blobs database is OK.
|
||||
[2023-03-05 18.54.56]
|
||||
[2023-03-05 18.54.56] Exporting current databases using timestamp: 2023-03-05_18.54.22
|
||||
[2023-03-05 18.54.56] Exporting Main DB
|
||||
[2023-03-05 18.55.30] Exporting Blobs DB
|
||||
[2023-03-05 18.55.33] Successfully exported the main and blobs databases. Proceeding to import into new databases.
|
||||
[2023-03-05 18.55.33] Importing Main DB.
|
||||
[2023-03-05 18.57.04] Importing Blobs DB.
|
||||
[2023-03-05 18.57.05] Successfully imported SQL data.
|
||||
[2023-03-05 18.57.05] Verifying databases integrity after importing.
|
||||
[2023-03-05 18.57.40] Verification complete. PMS main database is OK.
|
||||
[2023-03-05 18.57.40] Verification complete. PMS blobs database is OK.
|
||||
[2023-03-05 18.57.40] Saving current databases with '-BACKUP-2023-03-05_18.54.22'
|
||||
[2023-03-05 18.57.40] Making repaired databases active
|
||||
[2023-03-05 18.57.40] Repair complete. Please check your library settings and contents for completeness.
|
||||
[2023-03-05 18.57.40] Recommend: Scan Files and Refresh all metadata for each library section.
|
||||
[2023-03-05 18.57.40]
|
||||
[2023-03-05 18.57.40] Backing up of databases
|
||||
[2023-03-05 18.57.40] Backup current databases with '-BACKUP-2023-03-05_18.57.40' timestamp.
|
||||
[2023-03-05 18.57.41] Reindexing main database
|
||||
[2023-03-05 18.58.17] Reindexing main database successful.
|
||||
[2023-03-05 18.58.17] Reindexing blobs database
|
||||
[2023-03-05 18.58.17] Reindexing blobs database successful.
|
||||
[2023-03-05 18.58.17] Reindex complete.
|
||||
[2023-03-05 18.58.17] Automatic Check, Repair/optimize, & Index successful.
|
||||
|
||||
[2023-03-05 18.58.17] Starting PMS.
|
||||
[2023-03-05 18.58.17] Started PMS
|
||||
|
||||
root@lizum:/sata/plex/Plex Media Server/Plug-in Support/Databases#
|
||||
|
||||
```
|
||||
|
||||
======================
|
||||
```
|
||||
|
||||
```
|
||||
## Logfile
|
||||
|
||||
The logfile (DBRepair.log) keeps track of all commands issues and their status (PASS/FAIL) with timestamp.
|
||||
This can be useful when recovering from an interrupted session because temporary files are timestamped.
|
||||
|
||||
|
||||
```
|
||||
2023-02-25 16.14.39 - ============================================================
|
||||
2023-02-25 16.14.39 - Session start: Host is Synology (DSM 7)
|
||||
2023-02-25 16.14.56 - StopPMS - PASS
|
||||
2023-02-25 16.16.06 - Check - Check com.plexapp.plugins.library.db - PASS
|
||||
2023-02-25 16.16.06 - Check - Check com.plexapp.plugins.library.blobs.db - PASS
|
||||
2023-02-25 16.16.06 - Check - PASS
|
||||
2023-02-25 16.17.20 - Repair - Export databases - PASS
|
||||
2023-02-25 16.19.52 - Repair - Import - PASS
|
||||
2023-02-25 16.20.41 - Repair - Verify main database - PASS (Size: 399MB/399MB).
|
||||
2023-02-25 16.20.41 - Repair - Verify blobs database - PASS (Size: 1MB/1MB).
|
||||
2023-02-25 16.20.41 - Repair - Move files - PASS
|
||||
2023-02-25 16.20.41 - Repair - PASS
|
||||
2023-02-25 16.20.41 - Repair - PASS
|
||||
2023-02-25 16.20.46 - Reindex - MakeBackup com.plexapp.plugins.library.db - PASS
|
||||
2023-02-25 16.20.46 - Reindex - MakeBackup com.plexapp.plugins.library.blobs.db - PASS
|
||||
2023-02-25 16.20.46 - Reindex - MakeBackup - PASS
|
||||
2023-02-25 16.21.34 - Reindex - Reindex: com.plexapp.plugins.library.db - PASS
|
||||
2023-02-25 16.21.35 - Reindex - Reindex: com.plexapp.plugins.library.blobs.db - PASS
|
||||
2023-02-25 16.21.35 - Reindex - PASS
|
||||
2023-02-25 16.21.35 - Reindex - PASS
|
||||
2023-02-25 16.21.35 - Auto - PASS
|
||||
2023-02-25 16.38.35 - StartPMS - PASS
|
||||
2023-02-25 16.38.57 - Exit - Delete temp files.
|
||||
2023-02-25 16.38.58 - Session end.
|
||||
2023-02-25 16.38.58 - ============================================================
|
||||
2023-02-25 16.40.10 - ============================================================
|
||||
2023-02-25 16.40.10 - Session start: Host is Synology (DSM 7)
|
||||
2023-02-25 16.40.27 - StopPMS - PASS
|
||||
2023-02-25 16.42.23 - Check - Check com.plexapp.plugins.library.db - PASS
|
||||
2023-02-25 16.42.24 - Check - Check com.plexapp.plugins.library.blobs.db - PASS
|
||||
2023-02-25 16.42.24 - Check - PASS
|
||||
2023-02-25 16.43.39 - Repair - Export databases - PASS
|
||||
2023-02-25 16.46.10 - Repair - Import - PASS
|
||||
2023-02-25 16.46.58 - Repair - Verify main database - PASS (Size: 399MB/399MB).
|
||||
2023-02-25 16.46.58 - Repair - Verify blobs database - PASS (Size: 1MB/1MB).
|
||||
2023-02-25 16.46.59 - Repair - Move files - PASS
|
||||
2023-02-25 16.46.59 - Repair - PASS
|
||||
2023-02-25 16.46.59 - Repair - PASS
|
||||
2023-02-25 16.47.03 - Reindex - MakeBackup com.plexapp.plugins.library.db - PASS
|
||||
2023-02-25 16.47.03 - Reindex - MakeBackup com.plexapp.plugins.library.blobs.db - PASS
|
||||
2023-02-25 16.47.03 - Reindex - MakeBackup - PASS
|
||||
2023-02-25 16.47.52 - Reindex - Reindex: com.plexapp.plugins.library.db - PASS
|
||||
2023-02-25 16.47.52 - Reindex - Reindex: com.plexapp.plugins.library.blobs.db - PASS
|
||||
2023-02-25 16.47.52 - Reindex - PASS
|
||||
2023-02-25 16.47.52 - Reindex - PASS
|
||||
2023-02-25 16.47.52 - Auto - PASS
|
||||
2023-02-25 16.48.04 - StartPMS - PASS
|
||||
2023-02-25 16.48.05 - Exit - Delete temp files.
|
||||
2023-02-25 16.48.05 - Session end.
|
||||
|
||||
```
|
||||
|
||||
# Command Reference:
|
||||
|
||||
### Automatic
|
||||
|
||||
Automatic provides automated processing of most checks and repairs. (Check, Repair/Resequence, Index)
|
||||
In its current state, it will not automatically replace a damaged database from a backup (future)
|
||||
|
||||
It will not stop PMS as not all systems support stopping PMS from within this tool.
|
||||
|
||||
### Check
|
||||
|
||||
Checks the integrity of the Plex main and blobs databases.
|
||||
|
||||
### Exit
|
||||
|
||||
Exits the utility and removes all temporary database files created during processing.
|
||||
To save all intermediate databases, use the 'Quit' command.
|
||||
|
||||
### 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. )
|
||||
|
||||
|
||||
### 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)
|
||||
|
||||
### Repair
|
||||
|
||||
Extracts/recovers all the usable data fron the existing databases into text (SQL ascii) form.
|
||||
Repair then creates new SQLite-valid databases from the extracted/recovered data.
|
||||
|
||||
The side effect of this process is a fully defragmented database (optimal for Plex use).
|
||||
|
||||
100% validity/usability by Plex is not guaranteed as the tool cannot validate each individual
|
||||
record contained in the database. It can only validate at the SQLite level.
|
||||
|
||||
In most cases, Repair is the preferred option as the records extracted are only those SQLite deemed valid.
|
||||
|
||||
### Replace
|
||||
|
||||
Looks through the list of available PMS backups.
|
||||
|
||||
Starting with the most recent PMS backup,
|
||||
1. Check the both db files
|
||||
2. If valid, offer as a replacement choice
|
||||
3. If accepted (Y/N question) then use as the replacement
|
||||
else advance to the next available backup
|
||||
4. Upon completion, validate one final time.
|
||||
|
||||
### Quit
|
||||
|
||||
Exits the utility but leaves the temporary databases intact (useful for making exhaustive backups)
|
||||
|
||||
### Show
|
||||
|
||||
Shows the activity log. The activity log is date/time stamped of all activity.
|
||||
|
||||
### Start
|
||||
|
||||
On platform environments which support it, and when invoked by the 'root' user, the tool can start PMS.
|
||||
If not the 'root' user or on a platform which doesn't support it, "Not available" will be indicated.
|
||||
|
||||
### Stop
|
||||
|
||||
On platform environments which support it, and when invoked by the 'root' user, the tool can stop PMS.
|
||||
If not the 'root' user or on a platform which doesn't support it, "Not available" will be indicated.
|
||||
|
||||
PMS must be in the stopped state in order to operate on the database files.
|
||||
|
||||
### Undo
|
||||
|
||||
Undo allows you to "Undo" the last Import, Repair, Replace, or Vacuum command.
|
||||
At present, it only allows the ONE most recent operation.
|
||||
(Future will support undoing more actions)
|
||||
|
||||
### Vacuum
|
||||
|
||||
Instructs SQLite to remove the empty/deleted records and gaps from the databases.
|
||||
This is most beneficial after deleting whole library sections.
|
||||
|
||||
###
|
||||
@@ -1,209 +0,0 @@
|
||||
@echo off
|
||||
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 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%
|
||||
set Sec=%time:~6,2%
|
||||
|
||||
REM ## Remove spaces from Hour ##
|
||||
set Hour=%Hour: =%
|
||||
|
||||
REM ## Set TimeStamp ##
|
||||
set TimeStamp=%Hour%-%Min%-%Sec%
|
||||
|
||||
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%\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%\DBRepair.log"
|
||||
exit /B 1
|
||||
)
|
||||
|
||||
|
||||
cd "%PlexData%"
|
||||
|
||||
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%\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.
|
||||
exit /b 2
|
||||
)
|
||||
|
||||
echo %time% -- Exporting Blobs DB
|
||||
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.
|
||||
)
|
||||
|
||||
REM Now create new databases from SQL statements
|
||||
echo %time% -- Exporting Complete.
|
||||
echo %time% -- Exporting Complete. >> "%PlexData%\DBRepair.log"
|
||||
|
||||
echo %time% -- Creating Main DB
|
||||
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%\DBRepair.log"
|
||||
exit /b 3
|
||||
)
|
||||
|
||||
echo %time% -- Verifying Main DB
|
||||
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%\DBRepair.log"
|
||||
if not "%Result%" == "ok" (
|
||||
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%\DBRepair.log"
|
||||
|
||||
|
||||
echo %time% -- Creating Blobs DB
|
||||
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%\DBRepair.log"
|
||||
exit /b 5
|
||||
)
|
||||
|
||||
echo %time% -- Verifying Blobs DB
|
||||
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%\DBRepair.log"
|
||||
if not "%Result%" == "ok" (
|
||||
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%\DBRepair.log"
|
||||
echo %time% -- Import and verification complete.
|
||||
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%\DBRepair.log"
|
||||
"%PlexSQL%" "%PlexData%\com.plexapp.plugins.library.db_%TimeStamp%" "REINDEX;"
|
||||
|
||||
echo %time% -- Reindexing Blobs DB
|
||||
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%\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%\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"
|
||||
|
||||
move "%PlexData%\com.plexapp.plugins.library.blobs.db" "%PlexData%\dbtmp\com.plexapp.plugins.library.blobs.db_%TimeStamp%"
|
||||
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%\DBRepair.log"
|
||||
echo %time% -- ====== Session completed. ======
|
||||
echo %time% -- ====== Session completed. ====== >> "%PlexData%\DBRepair.log"
|
||||
|
||||
exit /b
|
||||
|
||||
|
||||
REM #### Functions
|
||||
|
||||
REM Output - Write text to the console and the log file
|
||||
:Output
|
||||
|
||||
echo %time% %~1
|
||||
echo %time% %~1 >> "%PlexData%\DBRepair.log"
|
||||
exit /B
|
||||
@@ -1,904 +0,0 @@
|
||||
#########################################################################
|
||||
# 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
|
||||
Binary file not shown.
@@ -1,76 +0,0 @@
|
||||
# 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]
|
||||
```
|
||||
@@ -1,33 +0,0 @@
|
||||
# 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