Closed chriskeeganhw closed 3 years ago
For starters, you need to use double backslashes for each backslash in config file, e.g. "value": "\\BackupPlex". Btw, are you intending to use BackupPlex folder at the root of whatever current hard drive would happen to be at the time of running?
Hi Alex
Thanks for the response
I have got double black slashes in Backuprootdir” setting
Have attached the file.
I actually intend to backup my plex server to a NAS.
But totally confused about slashes in pathnames and was trying to resort to the simplest path to see if I could make it work.
So if my ultimate aim is to backup plex server to the following path...
\nas540\<\nas540\PlexServerBackup>PlexServerBackup
Then the value in the config file for BackupRootDir should be
\\nas540\PlexServerBackup
I tried this and keep getting the same error message
Thanks and regards Chris
From: Alek Davis notifications@github.com Sent: Sunday, January 24, 2021 1:06:39 AM To: alekdavis/PlexBackup PlexBackup@noreply.github.com Cc: chriskeeganhw chriskeeganhw@gmail.com; Author author@noreply.github.com Subject: Re: [alekdavis/PlexBackup] an error that I cant fix in the plex backup script (or config file) (#42)
For starters, you need to use double backslashes for each backslash in config file, e.g. "value": "\BackupPlex". Btw, are you intending to use BackupPlwx folder at the root of whatever current hard drive would happen to be at the time of running?
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHubhttps://github.com/alekdavis/PlexBackup/issues/42#issuecomment-766255063, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AK4EE4DGJRV6EKYE2VZQWATS3NXB7ANCNFSM4WP2GT3Q.
Also meant to ask if you can use drive letters in the value for the rootbackupdir
So instead of \\nas540 can I just refer to my nas drives leeter of y: and then change it to y:\plexserverbckup?
Thanks and regards Chris
From: Chris Keegan chriskeeganhw@gmail.com Sent: Sunday, January 24, 2021 6:09:11 PM To: alekdavis/PlexBackup reply@reply.github.com; alekdavis/PlexBackup PlexBackup@noreply.github.com Cc: Author author@noreply.github.com Subject: Re: [alekdavis/PlexBackup] an error that I cant fix in the plex backup script (or config file) (#42)
Hi Alex
Thanks for the response
I have got double black slashes in Backuprootdir” setting
Have attached the file.
I actually intend to backup my plex server to a NAS.
But totally confused about slashes in pathnames and was trying to resort to the simplest path to see if I could make it work.
So if my ultimate aim is to backup plex server to the following path...
\nas540\<\nas540\PlexServerBackup>PlexServerBackup
Then the value in the config file for BackupRootDir should be
\\nas540\PlexServerBackup
I tried this and keep getting the same error message
Thanks and regards Chris
From: Alek Davis notifications@github.com Sent: Sunday, January 24, 2021 1:06:39 AM To: alekdavis/PlexBackup PlexBackup@noreply.github.com Cc: chriskeeganhw chriskeeganhw@gmail.com; Author author@noreply.github.com Subject: Re: [alekdavis/PlexBackup] an error that I cant fix in the plex backup script (or config file) (#42)
For starters, you need to use double backslashes for each backslash in config file, e.g. "value": "\BackupPlex". Btw, are you intending to use BackupPlwx folder at the root of whatever current hard drive would happen to be at the time of running?
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHubhttps://github.com/alekdavis/PlexBackup/issues/42#issuecomment-766255063, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AK4EE4DGJRV6EKYE2VZQWATS3NXB7ANCNFSM4WP2GT3Q.
Yes, if you have your NAS share permanently mapped to a drive, you can use a drive letter instead of the UNC path. Generally, I'd recommend other media, like a USB flash drive, instead of NASes, because once network gets involved, a whole bunch of things can go wrong. I started doing backup to a NAS, and then just bought a cheap 32HB flash drive and do it locally. This saves time, too, since I can skip the temp folder.
Regarding the error, it looks like you either have a corrupted file or something really weird. Could you attach your copy of the PlexBackup.ps1 file? I do not think it's about config file, so we can just put this one aside for a moment.
Hi Alek
Thanks for the replies
Have tried to send the files a couple of ways but they get stripped out
So have uploaded them to my 4shared files sharing service
The link is https://www.4shared.com/folder/qrvth5NH/Alik.html https://www.4shared.com/folder/qrvth5NH/Alik.html
Please let me know if it doesn’t wor.
Basically these files were downloaded from github and apart from the modification to the config file and the PDFs of the instructions are downloaded as is.
Thanks and regards
Chris
From: Alek Davis [mailto:notifications@github.com] Sent: 24 January 2021 19:27 To: alekdavis/PlexBackup Cc: chriskeeganhw; Author Subject: Re: [alekdavis/PlexBackup] an error that I cant fix in the plex backup script (or config file) (#42)
Regarding the error, it looks like you either have a corrupted file or something really weird. Could you attach your copy of the PlexBackup.ps1 file? I do not think it's about config file, so we can just put this one aside for a moment.
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/alekdavis/PlexBackup/issues/42#issuecomment-766417567 , or unsubscribe https://github.com/notifications/unsubscribe-auth/AK4EE4GLS5K537BLPYXOBC3S3RX65ANCNFSM4WP2GT3Q . https://github.com/notifications/beacon/AK4EE4BSQONOFDGJEYXCTGTS3RX65A5CNFSM4WP2GT32YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOFWXJVHY.gif
Virus-free. http://www.avg.com/email-signature?utm_medium=email&utm_source=link&utm_campaign=sig-email&utm_content=emailclient www.avg.com
You uploaded a bunch of files including two versions of PlexBackup.ps1 and a zip file, which I assume also includes PS scripts. I just need one file that you actually use. Just change the extension from ps1 to txt and upload it here.
Hi
Single file renamed to txt as requested.
Regards and thanks again for helping out
http://www.avg.com/email-signature?utm_medium=email&utm_source=link&utm_campaign=sig-email&utm_content=webmail Virus-free. www.avg.com http://www.avg.com/email-signature?utm_medium=email&utm_source=link&utm_campaign=sig-email&utm_content=webmail <#DAB4FAD8-2DD7-40BB-A1B8-4E2AA1F9FDF2>
On Mon, 25 Jan 2021 at 02:56, Alek Davis notifications@github.com wrote:
You uploaded a bunch of files including two versions of PlexBackup.ps1 and a zip file, which I assume also includes PS scripts. I just need one file that you actually use. Just change the extension from ps1 to txt and upload it here.
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/alekdavis/PlexBackup/issues/42#issuecomment-766506945, or unsubscribe https://github.com/notifications/unsubscribe-auth/AK4EE4EU56KHPEF5J2GHX5TS3TMVXANCNFSM4WP2GT3Q .
-- Thanks and regards Chris
<# .SYNOPSIS Backs up or restores Plex application data files and registry keys on a Windows system.
.DESCRIPTION The script can run in these modes:
The script backs up the contents of the 'Plex Media Server' folder (app data folder) with the exception of some top-level, non-essential folders. You can customize the list of folders that do not need to be backed up. By default, the following top-level app data folders are not backed up:
The backup process compresses the contents Plex app data folders to the ZIP files (with the exception of folders containing subfolders and files with really long paths). For efficiency reasons, the script first compresses the data in a temporary folder and then copies the compressed (ZIP) files to the backup destination folder. You can compress data to the backup destination folder directly (bypassing the saving to the temp folder step) by setting the value of the $TempZipFileDir variable to null or empty string. Folders holding subfolders and files with very long paths get special treatment: instead of compressing them, before performing the backup, the script moves them to the backup folder as-is, and after the backup, it copies them to their original locations. Alternatively, backup can create a mirror of the essential app data folder via the Robocopy command (to use this option, set the -Robocopy switch).
In addition to backing up Plex app data, the script also backs up the contents of the Plex Windows Registry key.
The backup is created in the specified backup folder under a subfolder which name reflects the script start time. It deletes the old backup folders (you can specify the number of old backup folders to keep).
If the backup process does not complete due to error, you can run backup in the Continue mode (using the '-Mode Continue' command-line switch) and it will resume from where it left off. By default, the script picks up the most recent backup folder, but you can specify the backup folder using the -BackupDirPath command-line switch.
When restoring Plex application data from a backup, the script expects the backup folder structure to be the same as the one it creates when it runs in the backup mode. By default, it use the backup folder with the name reflecting the most recent timestamp, but you can specify an alternative backup folder.
Before creating backup or restoring data from a backup, the script stops all running Plex services and the Plex Media Server process (it restarts them once the operation is completed). You can force the script to not start the Plex Media Server process via the -Shutdown switch.
To override the default script settings, modify the values of script parameters and global variables inline or specify them in a config file.
The script can send an email notification on the operation completion.
The config file must use the JSON format. Not every script variable can be specified in the config file (for the list of overridable variables, see the sample config file). Only non-null values from config file will be used. The default config file is named after the running script with the '.json' extension, such as: PlexBackup.ps1.json. You can specify a custom config file via the -ConfigFile command-line switch. Config file is optional. All config values in the config file are optional. The non-null config file settings override the default and command-line paramateres, e.g. if the command-line -Mode switch is set to 'Backup' and the corresponding element in the config file is set to 'Restore' then the script will run in the Restore mode.
On success, the script will set the value of the $LASTEXITCODE variable to 0; on error, it will be greater than zero.
This script must run as an administrator.
The execution policy must allow running scripts. To check execution policy, run the following command:
Get-ExecutionPolicy
If the execution policy does not allow running scripts, do the following:
(1) Start Windows PowerShell with the "Run as Administrator" option. (2) Run the following command:
Set-ExecutionPolicy RemoteSigned
This will allow running unsigned scripts that you write on your local computer and signed scripts from Internet.
Alternatively, you may want to run the script as:
start powershell.exe -noprofile -executionpolicy bypass -file .\PlexBackup.ps1 -ConfigFile .\PlexBackup.ps1.json
See also 'Running Scripts' at Microsoft TechNet Library:
https://docs.microsoft.com/en-us/previous-versions//bb613481(v=vs.85)
.PARAMETER Mode Specifies the mode of operation:
.PARAMETER Backup Shortcut for '-Mode Backup'.
.PARAMETER Continue Shortcut for '-Mode Continue'.
.PARAMETER Restore Shortcut for '-Mode Restore'.
.PARAMETER Type Specifies the non-default type of backup method:
By default, the script will use the built-in compression.
.PARAMETER SevenZip Shortcut for '-Type 7zip'
.PARAMETER Robocopy Shortcut for '-Type Robocopy'.
.PARAMETER ModuleDir Optional path to directory holding the modules used by this script. This can be useful if the script runs on the system with no or restricted access to the Internet. By default, the module path will point to the 'Modules' folder in the script's folder.
.PARAMETER ConfigFile Path to the optional custom config file. The default config file is named after the script with the '.json' extension, such as 'PlexBackup.ps1.json'.
.PARAMETER PlexAppDataDir Location of the Plex Media Server application data folder.
.PARAMETER BackupRootDir Path to the root backup folder holding timestamped backup subfolders. If not specified, the script folder will be used.
.PARAMETER BackupDirPath When running the script in the 'Restore' mode, holds path to the backup folder (by default, the subfolder with the most recent timestamp in the name located in the backup root folder will be used).
.PARAMETER TempDir Temp folder used to stage the archiving job (use local drive for efficiency). To bypass the staging step, set this parameter to null or empty string.
.PARAMETER WakeUpDir Optional path to a remote share that may need to be woken up before starting Plex Media Server.
.PARAMETER ArchiverPath Defines the path to the 7-zip command line tool (7z.exe) which is required when running the script with the '-Type 7zip' or '-SevenZip' switch. Default: $env:ProgramFiles\7-Zip\7z.exe.
.PARAMETER Quiet Set this switch to suppress log entries sent to a console.
.PARAMETER LogLevel Specifies the log level of the output:
.PARAMETER Log When set to true, informational messages will be written to a log file. The default log file will be created in the backup folder and will be named after this script with the '.log' extension, such as 'PlexBackup.ps1.log'.
.PARAMETER LogFile Use this switch to specify a custom log file location. When this parameter is set to a non-null and non-empty value, the '-Log' switch can be omitted.
.PARAMETER ErrorLog When set to true, error messages will be written to an error log file. The default error log file will be created in the backup folder and will be named after this script with the '.err.log' extension, such as 'PlexBackup.ps1.err.log'.
.PARAMETER ErrorLogFile Use this switch to specify a custom error log file location. When this parameter is set to a non-null and non-empty value, the '-ErrorLog' switch can be omitted.
.PARAMETER Keep Number of old backups to keep:
0 - retain all previously created backups, 1 - latest backup only, 2 - latest and one before it, 3 - latest and two before it,
and so on.
.PARAMETER Retries The number of retries on failed copy operations (corresponds to the Robocopy /R switch).
.PARAMETER RetryWaitSec Specifies the wait time between retries in seconds (corresponds to the Robocopy /W switch).
.PARAMETER RawOutput Set this switch to display raw output from the external commands, such as Robocopy or 7-zip.
.PARAMETER Inactive When set, allows the script to continue if Plex Media Server is not running.
.PARAMETER NoRestart Set this switch to not start the Plex Media Server process at the end of the operation (could be handy for restores, so you can double check that all is good before launching Plex media Server).
.PARAMETER NoSingleton Set this switch to ignore check for multiple script instances running concurrently.
.PARAMETER NoVersion Forces restore to ignore version mismatch between the current version of Plex Media Server and the version of Plex Media Server active during backup.
.PARAMETER NoLogo Specify this command-line switch to not print version and copyright info.
.PARAMETER Test When turned on, the script will not generate backup files or restore Plex app data from the backup files.
.PARAMETER SendMail Indicates in which case the script must send an email notification about the result of the operation:
.PARAMETER SmtpServer Defines the SMTP server host. If not specified, the notification will not be sent.
.PARAMETER Port Specifies an alternative port on the SMTP server. Default: 0 (zero, i.e. default port 25 will be used).
.PARAMETER From Specifies the email address when email notification sender. If this value is not provided, the username from the credentails saved in the credentials file or entered at the credentials prompt will be used. If the From address cannot be determined, the notification will not be sent.
.PARAMETER To Specifies the email address of the email recipient. If this value is not provided, the addressed defined in the To parameter will be used.
.PARAMETER NoSsl Tells the script not to use the Secure Sockets Layer (SSL) protocol when connecting to the SMTP server. By default, SSL is used.
.PARAMETER CredentialFile Path to the file holding username and encrypted password of the account that has permission to send mail via the SMTP server. You can generate the file via the following PowerShell command:
Get-Credential | Export-CliXml -Path "PathToFile.xml"
The default log file will be created in the backup folder and will be named after this script with the '.xml' extension, such as 'PlexBackup.ps1.xml'.
.PARAMETER SaveCredential When set, the SMTP credentials will be saved in a file (encrypted with user- and machine-specific key) for future use.
.PARAMETER Anonymous Tells the script to not use credentials when sending email notifications.
.PARAMETER SendLogFile Indicates in which case the script must send an attachment along with th email notification
.PARAMETER Logoff Specify this command-line switch to log off all user accounts (except the running one) before starting Plex Media Server. This may help address issues with remote drive mappings under the wrong credentials.
.PARAMETER Reboot Reboots the computer after a successful backup operation (ignored on restore).
.PARAMETER ForceReboot Forces an immediate restart of the computer after a successfull backup operation (ignored on restore).
.NOTES Version : 2.0.3 Author : Alek Davis Created on : 2020-12-13 License : MIT License LicenseLink: https://github.com/alekdavis/PlexBackup/blob/master/LICENSE Copyright : (c) 2020 Alek Davis
.LINK https://github.com/alekdavis/PlexBackup
.INPUTS None.
.OUTPUTS None.
.EXAMPLE PlexBackup.ps1 Backs up compressed Plex application data to the default backup location.
.EXAMPLE PlexBackup.ps1 -Robocopy Backs up Plex application data to the default backup location using the Robocopy command instead of the file and folder compression.
.EXAMPLE PlexBackup.ps1 -SevenZip Backs up Plex application data to the default backup location using the 7-zip command-line tool (7z.exe). 7-zip command-line tool must be installed and the script must know its path.
.EXAMPLE PlexBackup.ps1 -BackupRootDir "\MYNAS\Backup\Plex" Backs up Plex application data to the specified backup location on a network share.
.EXAMPLE PlexBackup.ps1 -Continue Continues a previous backup process from where it left off.
.EXAMPLE PlexBackup.ps1 -Restore Restores Plex application data from the latest backup in the default folder.
.EXAMPLE PlexBackup.ps1 -Restore -Robocopy Restores Plex application data from the latest backup in the default folder created using the Robocopy command.
.EXAMPLE PlexBackup.ps1 -Mode Restore -BackupDirPath "\MYNAS\PlexBackup\20190101183015" Restores Plex application data from a backup in the specified remote folder.
.EXAMPLE PlexBackup.ps1 -SendMail Always -Prompt -Save -SendLogFile OnError -SmtpServer smtp.gmail.com -Port 587 Runs a backup job and sends an email notification over an SSL channel. If the backup operation fails, the log file will be attached to the email message. The sender's and the recipient's email addresses will determined from the username of the credential object. The credential object will be set either from the credential file or, if the file does not exist, via a user prompt (in the latter case, the credential object will be saved in the credential file with password encrypted using a user- and computer-specific key).
.EXAMPLE Get-Help .\PlexBackup.ps1 View help information.
<# PLEASE MAKE SURE THAT THE SCRIPT STARTS WITH THE COMMENT HEADER ABOVE AND THE HEADER IS FOLLOWED BY AT LEAST ONE BLANK LINE; OTHERWISE, GET-HELP AND GETVERSION COMMANDS WILL NOT WORK.
[Diagnostics.CodeAnalysis.SuppressMessageAttribute( 'PSAvoidUsingPlainTextForPassword', 'CredentialFile',
Justification='No need for SecureString, since it holds path, not secret.')]
[CmdletBinding(DefaultParameterSetName="default")]
param (
[Parameter(ParameterSetName="Mode")]
[Parameter(Mandatory, ParameterSetName="ModeType")]
[Parameter(Mandatory, ParameterSetName="ModeSevenZip")]
[Parameter(Mandatory, ParameterSetName="ModeRobocopy")]
[ValidateSet("", "Backup", "Continue", "Restore")]
[string]
$Mode = "",
[Parameter(ParameterSetName="Backup")]
[Parameter(Mandatory, ParameterSetName="BackupType")]
[Parameter(Mandatory, ParameterSetName="BackupSevenZip")]
[Parameter(Mandatory, ParameterSetName="BackupRobocopy")]
[switch]
$Backup,
[Parameter(ParameterSetName="Continue")]
[Parameter(Mandatory, ParameterSetName="ContinueType")]
[Parameter(Mandatory, ParameterSetName="ContinueSevenZip")]
[Parameter(Mandatory, ParameterSetName="ContinueRobocopy")]
[switch]
$Continue,
[Parameter(ParameterSetName="Restore")]
[Parameter(Mandatory, ParameterSetName="RestoreType")]
[Parameter(Mandatory, ParameterSetName="RestoreSevenZip")]
[Parameter(Mandatory, ParameterSetName="RestoreRobocopy")]
[switch]
$Restore,
[Parameter(ParameterSetName="Type")]
[Parameter(Mandatory, ParameterSetName="ModeType")]
[Parameter(Mandatory, ParameterSetName="BackupType")]
[Parameter(Mandatory, ParameterSetName="ContinueType")]
[Parameter(Mandatory, ParameterSetName="RestoreType")]
[ValidateSet("", "7zip", "Robocopy")]
[string]
$Type = "",
[Parameter(ParameterSetName="SevenZip")]
[Parameter(Mandatory, ParameterSetName="ModeSevenZip")]
[Parameter(Mandatory, ParameterSetName="BackupSevenZip")]
[Parameter(Mandatory, ParameterSetName="ContinueSevenZip")]
[Parameter(Mandatory, ParameterSetName="RestoreSevenZip")]
[switch]
$SevenZip,
[Parameter(ParameterSetName="Robocopy")]
[Parameter(Mandatory, ParameterSetName="ModeRobocopy")]
[Parameter(Mandatory, ParameterSetName="BackupRobocopy")]
[Parameter(Mandatory, ParameterSetName="ContinueRobocopy")]
[Parameter(Mandatory, ParameterSetName="RestoreRobocopy")]
[switch]
$Robocopy,
[string]
$ModuleDir = "$PSScriptRoot\Modules",
[Alias("Config")]
[string]
$ConfigFile,
[string]
$PlexAppDataDir = "$env:LOCALAPPDATA\Plex Media Server",
[string]
$BackupRootDir = $PSScriptRoot,
[Alias("BackupDirPath")]
[string]
$BackupDir = $null,
[Alias("TempZipFileDir")]
[AllowEmptyString()]
[string]
$TempDir = $env:TEMP,
[string]
$WakeUpDir = $null,
[string]
$ArchiverPath = "$env:ProgramFiles\7-Zip\7z.exe",
[Alias("Q")]
[switch]
$Quiet,
[ValidateSet("None", "Error", "Warning", "Info", "Debug")]
[string]
$LogLevel = "Info",
[Alias("L")]
[switch]
$Log,
[string]
$LogFile,
[switch]
$ErrorLog,
[string]
$ErrorLogFile,
[ValidateRange(0,[int]::MaxValue)]
[int]
$Keep = 3,
[ValidateRange(0,[int]::MaxValue)]
[int]
$Retries = 5,
[ValidateRange(0,[int]::MaxValue)]
[int]
$RetryWaitSec = 10,
[switch]
$RawOutput,
[switch]
$Inactive,
[Alias("Shutdown")]
[switch]
$NoRestart,
[switch]
$NoSingleton,
[Alias("Force")]
[Alias("NoVersionCheck")]
[switch]
$NoVersion,
[switch]
$NoLogo,
[switch]
$Test,
[ValidateSet(
"Never", "Always", "OnError", "OnSuccess",
"OnBackup", "OnBackupError", "OnBackupSuccess",
"OnRestore", "OnRestoreError", "OnRestoreSuccess")]
[string]
$SendMail = "Never",
[string]
$SmtpServer,
[ValidateRange(0,[int]::MaxValue)]
[int]
$Port = 0,
[string]
$From = $null,
[string]
$To,
[switch]
$NoSsl,
[Alias("Credential")] [string] $CredentialFile = $null,
[switch]
$SaveCredential,
[switch]
$Anonymous,
[ValidateSet("Never", "OnError", "OnSuccess", "Always")]
[string]
$SendLogFile = "Never",
[switch]
$Logoff,
[switch]
$Reboot,
[switch]
$ForceReboot
)
$ExcludeDirs = @( "Diagnostics", "Crash Reports", "Updates", "Logs" )
$ExcludeFiles = @( "*.bif", "Transcode" )
$SpecialDirs = @( "Plug-in Support\Data\com.plexapp.system\DataItems\Deactivated" )
$PlexServiceName = "^Plex"
$PlexServerFileName = "Plex Media Server.exe"
$PlexServerPath = $null
$ArchiverOptionsCompress = @( $null )
$ArchiverOptionsExpand = @( $null )
$MODULES = @("ScriptVersion", "ConfigFile", "StreamLogging", "SingleInstance")
$MUTEX_NAME = $PSCommandPath.Replace("\", "/")
$PLEX_REG_KEYS = @( "HKCU:\Software\Plex, Inc.\Plex Media Server", "HKU:.DEFAULT\Software\Plex, Inc.\Plex Media Server" )
$DEFAULT_PLEX_SERVER_EXE_PATH = "${env:ProgramFiles(x86)}\Plex\Plex Media Server\Plex Media Server.exe"
$FILE_EXT_ZIP = ".zip" $FILE_EXT_7ZIP = ".7z" $FILE_EXT_REG = ".reg" $FILE_EXT_CRED = ".xml"
$VERSION_FILE_NAME = "Version.txt"
$MODE_BACKUP = "Backup" $MODE_CONTINUE = "Continue" $MODE_RESTORE = "Restore"
$TYPE_ROBOCOPY = "Robocopy" $TYPE_7ZIP = "7zip"
$REGEX_BACKUPDIRNAMEFORMAT = "^\d{14}$"
$BACKUP_DIRNAMEFORMAT = "yyyyMMddHHmmss"
$SUBDIR_FILES = "1" $SUBDIR_FOLDERS = "2" $SUBDIR_REGISTRY = "3" $SUBDIR_SPECIAL = "4"
$BACKUP_FILENAME = "Plex"
$SEND_MAIL_NEVER = "Never" $SEND_MAIL_ALWAYS = "Always" $SEND_MAIL_ERROR = "OnError" $SEND_MAIL_SUCCESS = "OnSuccess" $SEND_MAIL_BACKUP = "OnBackup" $SEND_MAIL_RESTORE = "OnRestore" <# $SEND_MAIL_BACKUP_ERROR = "OnBackupError" $SEND_MAIL_BACKUP_SUCCESS = "OnBackupSuccess" $SEND_MAIL_RESTORE_ERROR = "OnRestoreError" $SEND_MAIL_RESTORE_SUCCESS = "OnRestoreSuccess"
$SEND_LOGFILE_NEVER = "Never" $SEND_LOGFILE_ALWAYS = "Always" $SEND_LOGFILE_ERROR = "OnError" $SEND_LOGFILE_SUCCESS = "OnSuccess"
$SUBJECT_ERROR = "Plex backup failed :-(" $SUBJECT_SUCCESS = "Plex backup completed :-)"
$EXITCODE_SUCCESS = 0 # success $EXITCODE_ERROR = 1 # error
$ZipFileExt = $FILE_EXT_ZIP $RegFileExt = $FILE_EXT_REG
[string]$BackupDirName = $null [string]$VersionFilePath = $null
[string]$PlexVersion = $null [string]$BackupVersion = $null
[DateTime]$StartTime = Get-Date [DateTime]$EndTime = $StartTime [string] $Duration = $null
[PSCredential]$Credential = $null
[string]$ErrorResult = $null
[string]$ObjectCount = "UNKNOWN" [string]$BackupSize = "UNKNOWN"
$ExitCode = $EXITCODE_ERROR
function SetModulePath { [CmdletBinding()] param( ) WriteDebug "Entered SetModulePath."
if ($Script:ModuleDir) {
if ($env:PSModulePath -notmatch ";$") {
$env:PSModulePath += ";"
}
$paths = $Script:ModuleDir -split ";"
foreach ($path in $paths){
$path = $path.Trim();
if (-not ($env:PSModulePath.ToLower().
Contains(";$path;".ToLower()))) {
$env:PSModulePath += "$path;"
}
}
}
WriteDebug "Exiting SetModulePath."
}
function LoadModules { [CmdletBinding()] param( ) WriteDebug "Entered LoadModules."
# Make sure we got the modules.
if (!($MODULES) -or ($MODULES.Count -eq 0)) {
return
}
[string]$moduleName = $null
try {
foreach ($module in $MODULES) {
$moduleName = $module
# If module is not loaded into the process.
if (!(Get-Module -Name $module)) {
# Check if module is locally available.
if (!(Get-Module -Listavailable -Name $module)) {
# Download module if needed.
Write-Verbose "Installing module '$module'."
Install-Module -Name $module `
-Force -Scope CurrentUser -ErrorAction Stop
}
}
# Import module into the process.
Write-Verbose "Importing module '$module'."
Import-Module $module -ErrorAction Stop -Force
}
}
catch {
throw (New-Object System.Exception( `
"Cannot load module '$moduleName'.", $_.Exception))
}
WriteDebug "Exiting LoadModules."
}
function GetScriptVersion { [CmdletBinding()] param ( ) WriteDebug "Entered GetScriptVersion."
$versionInfo = Get-ScriptVersion
$scriptName = (Get-Item $PSCommandPath).Basename
WriteDebug "Exiting GetScriptVersion."
return ($scriptName +
" v" + $versionInfo["Version"] +
" " + $versionInfo["Copyright"])
}
function GetCommandLineArgs { [CmdletBinding()] param ( ) WriteDebug "Entered GetCommandLineArgs."
$commandLine = ""
if ($args.Count -gt 0) {
for ($i = 0; $i -lt $args.Count; $i++) {
if ($args[$i].Contains(" ")) {
$commandLine = $commandLine + '"' + $args[$i] + '" '
}
else {
$commandLine = $commandLine + $args[$i] + ' '
}
}
}
WriteDebug "Exiting GetCommandLineArgs."
return $commandLine.Trim()
}
function FormatError { [CmdletBinding()] param ( $errors )
if (!$errors -or $errors.Count -lt 1) {
return $null
}
[System.Exception]$ex = $errors[0].Exception
[string]$message = $null
while ($ex) {
if ($message) {
$message += " $($ex.Message)"
}
else {
$message = $ex.Message
}
$ex = $ex.InnerException
}
return $message
}
function WriteException { [CmdletBinding()] param ( $errors )
WriteError (FormatError $errors)
}
function WriteLogException { [CmdletBinding()] param ( $errors )
Write-LogError (FormatError $errors)
}
function WriteDebug {
param (
$message
)
if ($message -and $DebugPreference -ne 'SilentlyContinue') {
Write-Verbose $message
}
}
function WriteError { [CmdletBinding()] param ( $message )
if ($message) {
[Console]::ForegroundColor = 'red'
[Console]::BackgroundColor = 'black'
[Console]::WriteLine($message)
[Console]::ResetColor()
}
}
function StartLogging { [CmdletBinding()] param( ) WriteDebug "Entered StartLogging."
$logArgs = @{}
if ($Script:LogLevel) {
$logArgs.Add("LogLevel", $Script:LogLevel)
}
if ($Script:Log -and !$Script:LogFile) {
$logFileName = "$Script:Mode.log"
$Script:LogFile = Join-Path $Script:BackupDir $logFileName
}
if ($Script:LogFile) {
Write-Verbose "Setting log file to '$Script:LogFile'."
$logArgs.Add("FilePath", $Script:LogFile)
}
else {
Write-Verbose "Not using log file."
$logArgs.Add("File", $false)
}
if ($script:ErrorLog -and !$script:ErrorLogFile) {
$errorLogFileName = "$Script:Mode.err.log"
$Script:ErrorLogFile = Join-Path $Script:BackupDir $errorLogFileName
}
if ($Script:ErrorLogFile) {
Write-Verbose "Setting error log file to '$Script:ErrorLogFile'."
$logArgs.Add("ErrorFilePath", $Script:ErrorLogFile)
}
else {
Write-Verbose "Not using error log file."
$logArgs.Add("ErrorFile", $false)
}
# If script was launched with the -Quiet switch, do not output log to console.
if ($Script:Quiet) {
Write-Verbose "Not logging to console because of the '-Quiet' switch."
$logArgs.Add("Console", $false)
}
# Initialize log settings.
Write-Verbose "Initializing logging."
Start-Logging @logArgs
WriteDebug "Exiting StartLogging."
}
function StopLogging { [CmdletBinding()] param( ) WriteDebug "Entered StopLogging."
if (Test-LoggingStarted) {
try {
Write-Verbose "Uninitializing logging."
Stop-Logging
}
catch {
WriteError "Cannot stop logging."
WriteException $_
$Error.Clear()
}
}
else {
Write-Verbose "Logging has not started, so nothing to uninitialize."
}
WriteDebug "Exiting StopLogging."
}
function Prologue { [CmdletBinding()] param( ) WriteDebug "Entered Prologue."
# Display script version info.
if (!($Script:NoLogo)){
Write-LogInfo (GetScriptVersion)
}
Write-LogInfo "Script started at:"
Write-LogInfo $Script:StartTime -Indent 1
# Get script
$scriptArgs = GetCommandLineArgs
# Only write command-line arguments to the log file.
if ($scriptArgs) {
Write-LogInfo "Command-line arguments:"
Write-LogInfo $scriptArgs -Indent 1 -NoConsole
}
# Only write logging configuration to the log file.
$loggingConfig = Get-LoggingConfig -Compress
Write-LogDebug "Logging configuration:" -NoConsole
Write-LogDebug $loggingConfig -Indent 1 -NoConsole
WriteDebug "Exiting Prologue."
}
function Epilogue { [CmdletBinding()] param( ) WriteDebug "Entered Epilogue."
try {
if (!$Script:ErrorResult) {
if ($Script:Mode -ne $MODE_RESTORE) {
Write-LogInfo "Plex backup size:"
}
else {
Write-LogInfo "Plex app data size:"
}
Write-LogInfo "$($Script:ObjectCount) files and folders" -Indent 1
Write-LogInfo "$($Script:BackupSize) GB of data" -Indent 1
}
$runTime = (New-TimeSpan -Start $Script:StartTime -End $Script:EndTime).
ToString("hh\:mm\:ss\.fff")
Write-LogInfo "Script ended at:"
Write-LogInfo $Script:EndTime -Indent 1
Write-LogInfo "Script ran for (hr:min:sec.msec):"
Write-LogInfo $runTime -Indent 1
Write-LogInfo "Script execution result:"
if ($Script:ErrorResult) {
Write-LogInfo "ERROR" -Indent 1
}
else {
Write-LogInfo "SUCCESS" -Indent 1
}
Write-LogInfo "Done."
}
catch {
WriteLogException $_
$Error.Clear()
}
WriteDebug "Exiting Epilogue."
}
function InitGlobals { [CmdletBinding()] param( ) WriteDebug "Entered InitGlobals."
# First, set up the script execution mode.
Write-Verbose "Validating script execution mode."
if ($Script:Backup) {
$Script:Mode = $MODE_BACKUP
}
elseif ($Script:Continue) {
$Script:Mode = $MODE_CONTINUE
}
elseif ($Script:Restore) {
$Script:Mode = $MODE_RESTORE
}
else {
if (!$Script:Mode) {
$Script:Mode = $MODE_BACKUP
}
}
# Set up the backup type.
Write-Verbose "Validating backup type."
if ($Script:Robocopy) {
$Script:Type = $TYPE_ROBOCOPY
}
elseif ($Script:SevenZip) {
$Script:Type = $TYPE_7ZIP
}
# For 7-zip archival, change default '.zip' extension to '.7z'.
if ($Script:Type -eq $TYPE_7ZIP) {
$Script:ZipFileExt = $FILE_EXT_7ZIP
}
# If backup folder is specified, use its parent as the root.
if ($Script:BackupDir) {
$Script:BackupRootDir = Split-Path -Path $Script:BackupDir -Parent
}
# Get the name and path of the backup directory.
try {
$Script:BackupDirName, $Script:BackupDir = GetBackupDirAndPath
}
catch {
throw (New-Object System.Exception( `
"Cannot determine name and/or path of the backup folder.", `
$_.Exception))
}
try {
# Determine path to Plex Media Server executable.
$Script:PlexServerPath =
GetPlexServerPath
}
catch {
if (!$Script:Inactive) {
throw (New-Object System.Exception( `
"Cannot validate Plex Media Server executable path.", `
$_.Exception))
}
else {
Write-Verbose (FormatError $_)
Write-Verbose "Will continue because the '-Inactive' switch is turned on."
}
}
# Get version information.
$Script:VersionFilePath= Join-Path $Script:BackupDir $VERSION_FILE_NAME
$Script:PlexVersion = GetPlexVersion
$Script:BackupVersion = GetBackupVersion
WriteDebug "Exiting InitGlobals."
}
function FormatRegFilename { [CmdletBinding()] param ( $regKeyPath )
#foreach ($token in $REG_KEY_CHAR_SUBS.GetEnumerator()) {
# $regKeyPath = $regKeyPath.Replace($token.key, $token.Value)
#}
$bytes = [System.Text.Encoding]::UTF8.GetBytes($regKeyPath)
$algorithm = [System.Security.Cryptography.HashAlgorithm]::Create('MD5')
$stringBuilder = New-Object System.Text.StringBuilder
$algorithm.ComputeHash($bytes) |
ForEach-Object {
$null = $StringBuilder.Append($_.ToString("x2"))
}
return $stringBuilder.ToString()
}
Function FormatFileSize() { param ( [string] $path ) $result = $null
if (!(Test-Path -Path $path -PathType Leaf)) {
return $result
}
$size = (Get-Item $path).length
if ($size -gt 1TB) {
$result = [string]::Format("{0:0.0} TB", $size / 1TB)
}
elseif ($size -gt 1GB) {
$result = [string]::Format("{0:0.0} GB", $size / 1GB)
}
elseif ($size -gt 1MB) {
$result = [string]::Format("{0:0.0} MB", $size / 1MB)
}
elseif ($size -gt 1KB) {
$result = [string]::Format("{0:0.0} KB", $size / 1KB)
}
elseif ($size -gt 0) {
$result = [string]::Format("{0:0.0} B", $size)
}
return $result
}
function GetTimestamp { return $(Get-Date).ToString("yyyy/MM/dd HH:mm:ss.fff") }
function WakeUpDir { param ( [string] $path,
[int]
$attempts = 6,
[int]
$sleepTimeSec = 5
)
WriteDebug "Entered WakeUpdDir."
if ($path) {
# Just in case path points to a remote share on a sleeping device,
# try waking it up (a directory listing should do it).
for ($i = 0; $i -lt $attempts; $i++) {
try {
Write-Verbose "Waking up '$path'."
Get-ChildItem -Path $path | Out-Null
break
}
catch {
$Error.Clear()
Start-Sleep -Seconds $sleepTimeSec
}
}
}
WriteDebug "Exiting WakeUpDir."
}
function GetNewBackupDirName { param ( )
return ($Script:StartTime).ToString($BACKUP_DIRNAMEFORMAT)
}
function GetLastBackupDirPath { param ( ) WriteDebug "Entered GetLastBackupDirPath." [string]$path = $null
WakeUpDir $Script:BackupRootDir
Write-Verbose "Checking backup root folder '$Script:BackupRootDir'."
if (!(Test-Path -Path $Script:BackupRootDir -PathType Container)) {
throw "Backup root folder '$Script:BackupRootDir' does not exist."
}
# Get all folders with names from newest to oldest.
$oldBackupDirs = Get-ChildItem -Path $Script:BackupRootDir -Directory |
Where-Object { $_.Name -match $REGEX_BACKUPDIRNAMEFORMAT } |
Sort-Object -Descending
# Check if there is at least one matching subdirectory.
if ($oldBackupDirs.Count -gt 0) {
$path = $oldBackupDirs[0].FullName
}
WriteDebug "Exiting GetLastBackupDirPath."
return $path
}
function GetBackupDirAndPath { param ( ) WriteDebug "Entered GetBackupDirAndPath."
[string]$name = $null
[string]$path = $null
if ($Script:BackupDir) {
WakeUpDir $Script:BackupDir
$name = (Split-Path -Path $Script:BackupDir -Leaf)
$path = $Script:BackupDir
}
else {
WakeUpDir $Script:BackupRootDir
# For Restore and Continue, get the latest timestamped subfolder from the backup root.
if ($Script:Mode -ne $MODE_BACKUP) {
$path = GetLastBackupDirPath
# For Restore mode we must have at least one matching folder;
# for Continue, it's okay if none are found (we'll create a new one a
# as if running in the Backup mode).
if (!$path -and ($Script:Mode -eq $MODE_RESTORE)) {
throw "No folder matching the timestamp regex format " +
"'$REGEX_BACKUPDIRNAMEFORMAT' found in the backup root folder '$Script:BackupRootDir'."
}
}
if ($path) {
$name = (Split-Path -Path $path -Leaf)
}
else {
# For the Backup mode, generate a new timestamp-based folder name.
# Do the same for Continue mode if we did not find the last backup folder.
$name = GetNewBackupDirName
$path = (Join-Path $Script:BackupRootDir $name)
}
}
WriteDebug "Exiting GetBackupDirAndPath."
return $name, $path
}
function InitMail { [CmdletBinding()] param( ) WriteDebug "Entered InitMail."
# Set up mail configuration.
if (!$Script:SmtpServer) {
if ($Script:SendMail -ne $SEND_MAIL_NEVER) {
Write-Verbose "Will not send email notification because SMTP server is not specified."
}
WriteDebug "Exiting InitSmtp."
return
}
if (!(MustSendMail)) {
if ($Script:SendMail -ne $SEND_MAIL_NEVER) {
Write-Verbose "Will not send email notification because the condition is not met."
$Script:SendMail = $SEND_MAIL_NEVER
}
else {
Write-Verbose "Email notification will not be sent."
}
WriteDebug "Exiting InitSmtp."
return
}
# If we do not support anonymous SMTP, need to get credentials.
if (!$Script:Anonymous) {
# If credential file path is not specified, use the default.
if (!$Script:CredentialFile) {
$Script:CredentialFile = $PSCommandPath + $FILE_EXT_CRED
}
# If credential file exists, read credentials from the file.
if (Test-Path -Path $Script:CredentialFile -PathType Leaf) {
try {
Write-Verbose "Importing SMTP credentials from '$Script:CredentialFile'."
$Script:Credential = Import-CliXml -Path $Script:CredentialFile
}
catch {
Write-Verbose ("Cannot import SMTP credentials from '$Script:CredentialFile'. " +
"If the file is no longer valid, please delete it and try again.")
}
# If we did not get credentail from the file and user prompt not specified,
# nothing we can do.
if (!$Script:Credential) {
Write-Verbose ("SMTP credentials in '$Script:CredentialFile' are empty. " +
"If the file is no longer valid, please delete it and try again.")
}
}
# Show prompt to allow user to enter credentials.
if (!$Script:Credential) {
try {
$Script:Credential = $Host.UI.PromptForCredential(
"SMTP Server Authentication",
"Please enter your credentials for " + $Script:SmtpServer,
"",
"",
[System.Management.Automation.PSCredentialTypes]::Generic,
[System.Management.Automation.PSCredentialUIOptions]::None)
}
catch {
throw (New-Object System.Exception( `
"Cannot get SMTP credentials from the Windows prompt.", $_.Exception))
}
# If we did not get credentail from the file and user prompt not specified,
# nothing we can do.
if (!$Script:Credential) {
throw "No SMTP credentials provided."
}
# Save entered credentials if needed.
if ($Script:SaveCredential) {
try {
Write-Verbose "Exporting SMTP credentials to '$Script:CredentialFile'."
Export-CliXml -Path $Script:CredentialFile -InputObject $Script:Credential
}
catch {
throw (New-Object System.Exception( `
"Cannot export SMTP credentials to '$Script:CredentialFile'.", $_.Exception))
}
}
}
}
if (!$Script:From -and !$Script:To -and (!$Script:Credential -or !$Script:Credential.UserName)) {
throw "No address specified for email notification."
}
# If the From address is not specified, get it from credential object or To address.
if (!$Script:From) {
if ($Script:Credential -and $Script:Credential.UserName) {
$Script:From = $Script:Credential.UserName
}
else {
$Script:From = $Script:To
}
}
# If the To address is not specified, get it from the From address.
if (!$Script:To) {
$Script:To = $Script:From
}
# If the To or From address not specified, do not send mail.
if (!$Script:From -or !$Script:To) {
Write-Verbose "Will not send email notification because of missing address."
$Script:SendMail = $SEND_MAIL_NEVER
}
WriteDebug "Exiting InitMail."
}
function Validate7Zip { [CmdletBinding()] param( ) WriteDebug "Entered Validate7Zip."
if ($Script:Type -eq $TYPE_7ZIP) {
if (!$Script:ArchiverPath) {
throw "Please set the value of parameter 'ArchiverPath' " +
"to point to the 7-zip command-line tool (7z.exe)."
}
if (!(Test-Path -Path $Script:ArchiverPath -PathType Leaf)) {
throw "The 7-zip command line tool is not found in '$Script:ArchiverPath'. " +
"Please define a valid path in parameter 'ArchiverPath'."
}
}
WriteDebug "Exiting Validate7Zip."
}
function ValidateVersion { [CmdletBinding()] param( ) WriteDebug "Entered ValidateVersion."
if ($Script:Mode -eq $MODE_BACKUP) {
Write-Verbose "Version validation skipped during backup."
}
else {
if (!$Script:BackupVersion) {
Write-Verbose `
"Version validation skipped because of the missing backup version."
}
elseif (!$Script:PlexVersion) {
Write-Verbose `
"Version validation skipped because of the missing Plex Media Server version."
}
else {
Write-Verbose ("Validating the backup version '$BackupVersion' " +
"against the Plex Media Server version '$Script:PlexVersion'.")
if ($Script:PlexVersion -ne $Script:BackupVersion) {
Write-Verbose "Version mismatch is detected."
if ($Script:NoVersion) {
}
else {
throw "Backup version '$Script:BackupVersion' does not match " +
"version '$Script:PlexVersion' of the Plex Media Server. " +
"To ignore version check, run the script with the " +
"'-NoVersion' flag."
}
}
}
}
WriteDebug "Exiting ValidateVersion."
}
function ValidateData { [CmdletBinding()] param( ) WriteDebug "Entered ValidateData."
# Make sure we have backup folder.
if (!$Script:BackupDir) {
throw "Path to backup data folder is not specified. " +
"Please, make sure you set one of the following switches: " +
"'-BackupRootDir', '-BackupDir'."
}
# For restore, we need the backup folder.
if ($Script:Mode -eq $MODE_RESTORE) {
if (!(Test-Path -Path $Script:BackupDir -PathType Container)) {
throw "Backup data folder '$Script:BackupDir' does not exist."
}
}
# We always need Plex app data folder.
if (!$Script:PlexAppDataDir) {
throw "Path to Plex app data folder is not specified. " +
"It must default to '$env:LOCALAPPDATA\Plex Media Server' " +
"but it is empty now. What have you done?"
}
# Plex app data folder must exist.
if ($Script:Mode -ne $MODE_RESTORE) {
if (!(Test-Path -Path $Script:PlexAppDataDir -PathType Container)) {
throw "Plex app data folder '$Script:PlexAppDataDir' does not exist."
}
}
# Check the important registry key.
if ($Script:Mode -ne $MODE_RESTORE) {
$key = $PLEX_REG_KEYS[0]
if (!(Test-Path -Path $key)) {
throw "Registry key '$key' does not exist."
}
}
WriteDebug "Exiting ValidateData."
}
function ValidateSingleInstance { [CmdletBinding()] param( ) WriteDebug "Entered ValidateSingleInstance."
# Mutex for single-instance operation.
if (!$Script:NoSingleton) {
Write-Verbose "Making sure the script is not already running."
if (!(Enter-SingleInstance $MUTEX_NAME)) {
throw "The script is already running."
}
}
WriteDebug "Exiting ValidateSingleInstance."
}
function MustSendMail { param ( [ValidateSet($null, $true, $false)] [object] $success = $null )
if ($Script:SendMail -eq $SEND_MAIL_NEVER) {
return $false
}
if ($Script:SendMail -eq $SEND_MAIL_ALWAYS) {
return $true
}
if ($Script:SendMail -eq $SEND_MAIL_SUCCESS) {
if ($success -ne $false) {
return $true
}
else {
return $false
}
}
if ($Script:SendMail -eq $SEND_MAIL_ERROR) {
if ($success -ne $true) {
return $true
}
else {
return $false
}
}
if ($Script:SendMail.StartsWith($SEND_MAIL_BACKUP)) {
if ($Script:Mode -eq $MODE_RESTORE) {
return $false
}
if (($Script:SendMail.EndsWith("Error")) -and ($success -eq $true)) {
return $false
}
if (($Script:SendMail.EndsWith("Success")) -and ($success -eq $false)) {
return $false
}
return $true
}
if ($Script:SendMail.StartsWith($SEND_MAIL_RESTORE)) {
if ($Script:Mode -ne $MODE_RESTORE) {
return $false
}
if (($Script:SendMail.EndsWith("Error")) -and ($success -eq $true)) {
return $false
}
if (($Script:SendMail.EndsWith("Success")) -and ($success -eq $false)) {
return $false
}
return $true
}
return $true
}
function MustSendAttachment { [CmdletBinding()] param ( ) if ($Script:SendLogFile -eq $SEND_LOGFILE_NEVER) { return $false }
$success = $true
if ($Script:ErrorResult) {
$success = $false
}
if (!$Script:LogFile) {
return $false
}
if (!(Test-Path -Path $Script:LogFile -PathType Leaf)) {
return $false
}
if ($Script:SendLogFile -eq $SEND_LOGFILE_ALWAYS) {
return $true
}
if ($Script:SendLogFile -eq $SEND_LOGFILE_SUCCESS) {
if ($success -ne $true) {
return $false
}
else {
return $true
}
}
if ($Script:SendLogFile -eq $SEND_LOGFILE_ERROR) {
if ($success -ne $false) {
return $false
}
else {
return $true
}
}
return $true
}
function GetPlexServerPath { param ( ) WriteDebug "Entered GetPlexServerPath."
# Get path of the Plex Media Server executable.
if (!$Script:PlexServerPath) {
$Script:PlexServerPath = Get-Process |
Where-Object {$_.Path -match $Script:PlexServerFileName + "$" } |
Select-Object -ExpandProperty Path
}
# Make sure we got the Plex Media Server executable file path.
if (!$Script:PlexServerPath) {
throw "Cannot determine path of the the Plex Media Server executable file " +
"'$Script:PlexServerFileName' because it is not running."
}
# Verify that the Plex Media Server executable file exists.
if (!(Test-Path -Path $Script:PlexServerPath -PathType Leaf)) {
throw "Plex Media Server executable file '$Script:PlexServerPath' does not exist."
}
WriteDebug "Exiting GetPlexServerPath."
return $Script:PlexServerPath
}
function GetPlexVersion { param ( ) WriteDebug "Entered GetPlexVersion."
[string]$version = $null
[string]$path = $null
if ($Script:PlexServerPath) {
$path = $Script:PlexServerPath
}
elseif ($DEFAULT_PLEX_SERVER_EXE_PATH) {
$path = $DEFAULT_PLEX_SERVER_EXE_PATH
}
Write-Verbose "Setting Plex Media Server path for version checking to '$path'."
if (($path) -and (Test-Path -Path $path -PathType Leaf)) {
try {
# Get version info from the assembly.
Write-Verbose "Reading Plex Media Server version from '$path'."
$version = (Get-Item $path).VersionInfo.FileVersion.Trim()
Write-Verbose "Plex Media Server version: '$version'."
}
catch {
throw (New-Object System.Exception( `
"Cannot get Plex Media Server version from '$path'.", `
$_.Exception))
}
}
WriteDebug "Exiting GetPlexVersion."
return $version
}
function GetBackupVersion { param ( ) WriteDebug "Entered GetBackupVersion."
[string]$version = $null
if (($Script:VersionFilePath) -and (Test-Path -Path $Script:VersionFilePath -PathType Leaf)) {
try {
# Assume that this is a previous backup's version.txt file.
Write-Verbose "Reading backup version from '$Script:VersionFilePath'."
$version = (Get-Content -Path $Script:VersionFilePath).Trim()
Write-Verbose "Backup version: '$version'."
}
catch {
throw (New-Object System.Exception( `
"Cannot get backup version from '$Script:VersionFilePath'.", `
$_.Exception))
}
}
WriteDebug "Exiting GetBackupVersion."
return $version
}
function SavePlexVersion { param ( ) WriteDebug "Entered SavePlexVersion."
if (!$Script:PlexVersion) {
Write-LogWarning "Undefined Plex Media Server Version, so it will not be saved."
}
elseif (!$Script:VersionFilePath) {
Write-LogWarning "Plex Media Server Version will not be saved because version file path is undefined."
}
else {
if (!(Test-Path -Path $Script:VersionFilePath -PathType Leaf)) {
Write-Verbose "Overwriting '$Script:VersionFilePath'."
if (!$Script:Test) {
New-Item -Path $Script:VersionFilePath -Type file -Force | Out-Null
}
}
Write-LogInfo "Saving version:"
Write-LogInfo $Script:PlexVersion -Indent 1
Write-LogInfo "to:"
Write-LogInfo $Script:VersionFilePath -Indent 1
if (!$Script:Test) {
$Script:PlexVersion | Set-Content -Path $Script:VersionFilePath
}
}
WriteDebug "Exiting SavePlexVersion."
}
function GetPlexServices { param ( ) WriteDebug "Entered GetPlexServices."
$services = Get-Service |
Where-Object {$_.DisplayName -match $PlexServiceName} |
Where-Object {$_.status -eq 'Running'}
if ($services) {
Write-Verbose "$($services.Count) Plex service(s) detected running."
}
else {
Write-Verbose "No Plex services detected running."
}
WriteDebug "Exiting GetPlexServices."
return $services
}
function StopPlexServices { param ( [object[]] $services ) WriteDebug "Entered StopPlexServices."
# We'll keep track of every Plex service we successfully stopped.
$stoppedPlexServices = [System.Collections.ArrayList]@()
if ($services.Count -gt 0) {
Write-LogInfo "Stopping Plex services:"
foreach ($service in $services) {
Write-LogInfo $service.DisplayName -Indent 1
try {
Stop-Service -Name $service.Name -Force
($stoppedPlexServices.Add($service)) | Out-Null
}
catch {
Write-LogError "Failed to stop Windows service '$($service.DisplayName)'."
WriteLogException $_
$Error.Clear()
return $false, $stoppedPlexServices
}
}
}
WriteDebug "Exiting StopPlexServices."
return $true, $stoppedPlexServices
}
function StartPlexServices { param ( [object[]] $services ) WriteDebug "Entered StartPlexServices."
if ($services -and ($services.Count -le 0)) {
Write-LogInfo "Starting Plex services:"
foreach ($service in $services) {
Write-LogInfo $service.DisplayName -Indent 1
try {
Start-Service -Name $service.Name
}
catch {
Write-LogError "Failed to start Windows service '$($service.DisplayName)'."
WriteLogException $_
$Error.Clear()
# Non-critical error; can continue.
}
}
}
WriteDebug "Exiting StartPlexServices."
}
function StopPlexMediaServer { [CmdletBinding()] param ( ) WriteDebug "Entered StopPlexMediaServer."
# If we have the path to Plex Media Server executable, see if it's running.
if ($Script:PlexServerPath) {
$exeFileName = Get-Process |
Where-Object {$_.Path -eq $Script:PlexServerPath } |
Select-Object -ExpandProperty Name
# Stop Plex Media Server executable (if it is running).
if ($exeFileName) {
Write-LogInfo "Stopping Plex Media Server process:"
Write-LogInfo $Script:PlexServerFileName -Indent 1
try {
# First, let's try to close Plex gracefully.
Write-Verbose "Trying to stop Plex Media Server process gracefully."
taskkill /im $Script:PlexServerFileName >$nul 2>&1
$timeoutSeconds = 60
# Keep checking to see if Plex is not longer running for at most 60 seconds.
Write-Verbose "Checking if Plex Media Server process stopped gracefully."
do {
# Sleep for a second.
Start-Sleep -Seconds 1
$timeoutSeconds--
} while (($timeoutSeconds -gt 0) -and
(Get-Process -ErrorAction SilentlyContinue |
Where-Object {$_.Path -match $Script:PlexServerFileName + "$" }))
# If Plex is still running, kill it along with all child processes forcefully.
if (Get-Process -ErrorAction SilentlyContinue |
Where-Object {$_.Path -match $Script:PlexServerFileName + "$" }) {
Write-Verbose "Failed to stop Plex Media Server process gracefully."
Write-Verbose "Killing Plex Media Server process."
taskkill /f /im $Script:PlexServerFileName /t >$nul 2>&1
}
}
catch {
throw (New-Object System.Exception( `
"Error stopping Plex Media Server.", $_.Exception))
}
}
}
WriteDebug "Exiting StopPlexMediaServer."
}
function StartPlexMediaServer { [CmdletBinding()] param ( ) WriteDebug "Entered StartPlexMediaServer."
if ($Script:PlexServerPath) {
Write-Verbose "Checking if Plex Media Server is already running."
# Get name of the Plex Media Server executable (just to see if it is running).
$exe
No, not like this. Respond to the message via the Git website and drop the file on to the the comment section so it gets attached to the comment, not inlined.
Hi Alik
Is this correct?
PlexBackup.txt
Renamed the filetype of the Plexbackup.ps1 I downloaded from here to a txt and added it (I hope)
Regards
There is something wrong with the attached file. Please try downloading the latest release (v2.0.3) and see if it helps.
Hi
Is v 2.0.3 the one currently on the Github website?
I just went onto Github, clicked code and downloaded it as a zip file. Unzipped, started power as administrator, ran the backup, prompted me to run it. the error message is slightly different. Now its 319 and 26 (before it was different numbers)
The "=" operator is missing after a named argument. At C:\Users\Administrator\desktop\PlexBackup-master\plexbackup-master\PlexBackup.ps1:319 char:26
I assume that its version 2.0.3 on the github site
Is it something to do with my environment?
I have another windows laptop but its no the plex server. Is there some way to run it o another machine and point in the config file to the machine where the plex media server itself ir runing?
Regards
Which version of PowerShell do you have? Run this command and post the result:
$PSVersionTable.PSVersion
Hi
When I start power shell is says copyright 2009.
When I type in the command I get
Major. Minor. Build. Revision
Thanks and regards Chris
From: Alek Davis notifications@github.com Sent: Monday, January 25, 2021 11:25:27 PM To: alekdavis/PlexBackup PlexBackup@noreply.github.com Cc: chriskeeganhw chriskeeganhw@gmail.com; Author author@noreply.github.com Subject: Re: [alekdavis/PlexBackup] an error that I cant fix in the plex backup script (or config file) (#42)
Which version of PowerShell do you have? Run this command and post the result:
$PSVersionTable.PSVersion
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHubhttps://github.com/alekdavis/PlexBackup/issues/42#issuecomment-767177595, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AK4EE4AKWAQ6KB4FJPLWJQDS3X4WPANCNFSM4WP2GT3Q.
Okay, this explains it. You need to upgrade PowerShell to at least version 4: https://docs.microsoft.com/en-us/powershell/scripting/windows-powershell/install/installing-windows-powershell?view=powershell-7.1
Hi Alek
Thank you for your continued help.
Upgraded to the latest version of PS
I ran, as elevated admin mode, ps and ran the .\Plexbackup.ps1 script
After a few seconds I got a message asking me ro Run which I did.
Then I got an "error" message about some help files missing (I attached a screenshot of the message) but the script seemed to keep running.
After about 40 minutes it stopped (the cursor re-appeared at the > prompt).
A bunch of folders had been created on my backup drive (a USB stick) with a log file (also attached).
Unfortunately all the backup folders were empty and there is an error about not being able to zip up a folder
Any further help gratefully appreciated
Regards
On Tue, 26 Jan 2021 at 21:15, Alek Davis notifications@github.com wrote:
Okay, this explains it. You need to upgrade PowerShell to at least version 4: https://docs.microsoft.com/en-us/powershell/scripting/windows-powershell/install/installing-windows-powershell?view=powershell-7.1
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/alekdavis/PlexBackup/issues/42#issuecomment-767832302, or unsubscribe https://github.com/notifications/unsubscribe-auth/AK4EE4GTI4Q4XD4DCM42Y6LS34WGXANCNFSM4WP2GT3Q .
-- Thanks and regards Chris
1.21.1.3830
Don't see any attachments, but you should not be getting any prompts to run. Just try running it with two switches: -Test -Inactive
. What happens?
Hi Alik
The prompt to run the file is the one from powershell that says Do not run, run once, suspend. Have pasted what I am seeing below. I choose R for run (I also put a screen shot as a word document attached, is it github that strips out attachments meaning you have to go onto the web page to attach rather than reply to emails and add attachments which is how I am communicating with you)
The other two attachments were text files. Have pasted the content here in the email
The backup log output file contains this
PlexBackup v
Script started at:
01/27/2021 18:36:53
Operation mode:
BACKUP
Backup type:
DEFAULT
Plex version:
1.21.1.3830
Log file:
H:\20210127183653\Backup.log
Stopping Plex Media Server process:
Plex Media Server.exe
Backup will be saved in:
H:\20210127183653
Creating task-specific subfolders in:
H:\20210127183653
1
2
3
4
Saving version:
1.21.1.3830
to:
H:\20210127183653\Version.txt
Backing up special folders.
Moving:
C:\Users\Administrator\AppData\Local\Plex Media Server\Plug-in Support\Data\com.plexapp.system\DataItems\Deactivated
to:
H:\20210127183653\4\Plug-in Support\Data\com.plexapp.system\DataItems\Deactivated
at:
2021/01/27 18:37:14.239
Completed at:
2021/01/27 18:37:14.742
Backing up Plex app data folders.
Archiving:
C:\Users\Administrator\AppData\Local\Plex Media Server\Cache
to:
H:\20210127183653\2\Cache.zip
at:
2021/01/27 18:37:14.910
Restoring special folders.
Copying:
H:\20210127183653\4\Plug-in Support\Data\com.plexapp.system\DataItems\Deactivated
to:
C:\Users\Administrator\AppData\Local\Plex Media Server\Plug-in Support\Data\com.plexapp.system\DataItems\Deactivated
at:
2021/01/27 19:18:22.684
Completed at:
2021/01/27 19:18:29.909
Error compressing Plex app data files. Error compressing folder 'C:\Users\Administrator\AppData\Local\Plex Media Server\Cache'. Exception calling "Dispose" with "0" argument(s): "The volume for a file has been externally altered so that the opened file is no longer valid. : 'H:\20210127183653\2\Cache.zip'" The volume for a file has been externally altered so that the opened file is no longer valid. : 'H:\20210127183653\2\Cache.zip'
Starting Plex Media Server:
C:\Program Files (x86)\Plex\Plex Media Server\Plex Media Server.exe
Script ended at:
01/27/2021 19:18:30
Script ran for (hr:min:sec.msec):
00:41:36.802
Script execution result:
ERROR
Done.
The version file that gets produced says this
1.21.1.3830
Thanks and regards
Chris
From: Alek Davis [mailto:notifications@github.com] Sent: 27 January 2021 20:54 To: alekdavis/PlexBackup Cc: chriskeeganhw; Author Subject: Re: [alekdavis/PlexBackup] an error that I cant fix in the plex backup script (or config file) (#42)
Don't see any attachments, but you should not be getting any prompts to run. Just try running it with two switches: -Test -Inactive. What happens?
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/alekdavis/PlexBackup/issues/42#issuecomment-768570148 , or unsubscribe https://github.com/notifications/unsubscribe-auth/AK4EE4BUTFPEYKEDGM275NDS4B4N3ANCNFSM4WP2GT3Q . https://github.com/notifications/beacon/AK4EE4CAB4O635W22NLIVCLS4B4N3A5CNFSM4WP2GT32YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOFXHXGJA.gif
Virus-free. http://www.avg.com/email-signature?utm_medium=email&utm_source=link&utm_campaign=sig-email&utm_content=emailclient www.avg.com
When you reply from email, all your attachments get lost (and formatting gets messed up), so it's better to reply from GitHub.
From your log file info, it looks like you still do not have the dependent modules working correctly (based on the missing script version: PlexBackup v
).
Regurding the The volume for a file has been externally altered so that the opened file is no longer valid error, I have no idea what it means, but if your H drive points to a NAS share, I'd try using a local drive first (to see if it works).
hi
no, my h drive is a usb stick
i shall try fixing the help problem (even though I did follow the instructions after the run had stopped
regards
http://www.avg.com/email-signature?utm_medium=email&utm_source=link&utm_campaign=sig-email&utm_content=webmail Virus-free. www.avg.com http://www.avg.com/email-signature?utm_medium=email&utm_source=link&utm_campaign=sig-email&utm_content=webmail <#DAB4FAD8-2DD7-40BB-A1B8-4E2AA1F9FDF2>
On Thu, 28 Jan 2021 at 18:42, Alek Davis notifications@github.com wrote:
When you reply from email, all your attachments get lost (and formatting gets messed up), so it's better to reply from GitHub.
From your log file info, it looks like you still do not have the dependent modules working correctly (based on the missing script version: PlexBackup v).
Regurding the The volume for a file has been externally altered so that the opened file is no longer valid error, I have no idea what it means, but if your H drive points to a NAS share, I'd try using a local drive first (to see if it works).
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/alekdavis/PlexBackup/issues/42#issuecomment-769293012, or unsubscribe https://github.com/notifications/unsubscribe-auth/AK4EE4GUBLOLEL7HTVT5SW3S4GVXXANCNFSM4WP2GT3Q .
-- Thanks and regards Chris
Hi Alek Again.
I "documented" what I did and took screen shots and pasted them into a word file which I will hopefully be able to attach here for you to see
DO I need to be actually logged onto github to download the help files needed (as you will see the screenshots point to thats where it is getting the helpfiles from but fails)
Hope you can see the word file. Will email it anyway in case it has been stripped or corrupted
Regards
Yeah, it looks like you did not follow the instructions to adjust execution policy settings. Try following the instructions first. You should not get any prompt other than a one-time prompt to update Nuget (also mentioned in the instructions).
Also read the Script execution section.
I assume this was resolved.
Hi Alek
Sorry for late reply
Have not tried it recently what with working away, illness and other things
Thanks for the help in the past.
Regards
Thanks and regards Chris
From: Alek Davis notifications@github.com Sent: Tuesday, February 16, 2021 11:43:03 PM To: alekdavis/PlexBackup PlexBackup@noreply.github.com Cc: chriskeeganhw chriskeeganhw@gmail.com; Author author@noreply.github.com Subject: Re: [alekdavis/PlexBackup] an error that I cant fix in the plex backup script (or config file) (#42)
Closed #42https://github.com/alekdavis/PlexBackup/issues/42.
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHubhttps://github.com/alekdavis/PlexBackup/issues/42#event-4338750682, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AK4EE4CBUFG4MHXPPL7FSZDS7L7IPANCNFSM4WP2GT3Q.
Hi Alek
so I have tried to get your powershell script working to backup my plex db
When I execute the script, |I get an error
PS C:\plexbackupscripts> .\plexbackup.ps1 The "=" operator is missing after a named argument. At C:\plexbackupscripts\PlexBackup.ps1:320 char:26
the only thing I changed in the config file was to replace "null" in the backup dir with my backup location (which is pasted below) and is on the same drive as the backup script at c:\PlexBackupData"
Any help gratefully received
Thanks
{ "_meta": { "version": "1.0", "strict": false, "description": "Sample run-time settings for the PlexBackup.ps1 script." }, "Mode": { "_meta": { "set": "Backup,Continue,Restore", "default": "Backup" }, "value": null }, "Type": { "_meta": { "set": ",7zip,Robocopy", "default": "" }, "value": null }, "PlexAppDataDir": { "_meta": { "default": "$env:LOCALAPPDATA\Plex Media Server" }, "value": null }, "BackupRootDir": { "_meta": { "default": "$PSScriptRoot" }, "value": "\BackupPlex" }, "BackupDir": { "_meta": { "default": null }, "value": null }, "TempDir": { "_meta": { "default": "$env:TEMP" }, "hasValue": true, "value": null }, "WakeUpDir": { "_meta": { "default": null }, "value": null }, "ArchiverPath": { "_meta": { "default": "$env:ProgramFiles\7-Zip\7z.exe" }, "value": null }, "Quiet": { "value": null }, "LogLevel": { "_meta": { "default": "None,Error,Warning,Info,Debug" }, "value": null }, "Log": { "value": true }, "LogFile": { "value": null }, "ErrorLog": { "value": null }, "ErrorLogFile": { "value": null }, "Keep": { "_meta": { "range": "0-[int]::MaxValue", "default": "3" }, "value": null }, "Retries": { "_meta": { "range": "0-[int]::MaxValue", "default": "5" }, "value": null }, "RetryWaitSec": { "_meta": { "range": "0-[int]::MaxValue", "default": "10" }, "value": null }, "RawOutput": { "value": null }, "Inactive": { "value": null }, "NoRestart": { "value": null }, "NoSingleton": { "value": null }, "NoVersion": { "value": null }, "NoLogo": { "value": null }, "Test": { "value": false }, "SendMail": { "_meta": { "set": "Never,Always,OnError,OnSuccess,OnBackup,OnBackupError,OnBackupSuccess,OnRestore,OnRestoreError,OnRestoreSuccess", "default": "Never" }, "value": null }, "SmtpServer": { "value": "smtp.gmail.com" }, "Port": { "_meta": { "range": "0-[int]::MaxValue", "default": "0" }, "value": 587 }, "From": { "value": null }, "To": { "value": null }, "NoSsl": { "value": null }, "CredentialFile": { "value": null }, "NoCredential": { "value": null }, "Anonymous": { "value": null }, "SendLogFile": { "_meta": { "set": "Never,OnError,OnSuccess,Always", "default": "Never" }, "value": "OnError" }, "Logoff": { "value": null }, "Reboot": { "value": null }, "ForceReboot": { "value": null }, "ExcludeDirs": { "_meta": { "default": ["Diagnostics","Crash Reports","Updates","Logs"] }, "value": null }, "ExcludeFiles": { "_meta": { "default": ["*.bif"] }, "value": null }, "SpecialDirs": { "_meta": { "default": ["Plug-in Support\Data\com.plexapp.system\DataItems\Deactivated"] }, "value": null }, "PlexServiceName": { "_meta": { "default": "^Plex" }, "hasValue": false, "value": null }, "PlexServerFileName": { "_meta": { "default": "Plex Media Server.exe" }, "value": null }, "PlexServerPath": { "value": null }, "ArchiverOptionsCompress": { "_meta": { "comment": "The default options will always be applied. To include additional options, define them as an array.", "default": ["-r","-y"] }, "value": null }, "ArchiverOptionsExpand": { "_meta": { "comment": "The default options will always be applied. To include additional options, define them as an array.", "default": ["-aoa","-y"] }, "value": null } }