alekdavis / PlexBackup

PowerShell script to back up and restore Plex application data on a Windows system.
MIT License
179 stars 23 forks source link

an error that I cant fix in the plex backup script (or config file) #42

Closed chriskeeganhw closed 3 years ago

chriskeeganhw commented 3 years ago

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 } }

alekdavis commented 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?

chriskeeganhw commented 3 years ago

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.

chriskeeganhw commented 3 years ago

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.

alekdavis commented 3 years ago

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.

alekdavis commented 3 years ago

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.

chriskeeganhw commented 3 years ago

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

http://www.avg.com/email-signature?utm_medium=email&utm_source=link&utm_campaign=sig-email&utm_content=emailclient

Virus-free. http://www.avg.com/email-signature?utm_medium=email&utm_source=link&utm_campaign=sig-email&utm_content=emailclient www.avg.com

alekdavis commented 3 years ago

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.

chriskeeganhw commented 3 years ago

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

------------------------------[ HELP INFO ]-------------------------------

<# .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.

>

------------------------------[ IMPORTANT ]-------------------------------

<# 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.

>

------------------------[ RUN-TIME REQUIREMENTS ]-------------------------

Requires -Version 4.0

Requires -RunAsAdministrator

------------------------[ COMMAND-LINE SWITCHES ]-------------------------

Script command-line arguments (see descriptions in the .PARAMETER comments

above). These parameters can also be specified in the accompanying

configuration (.json) file.

[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

)

---------------[ VARIABLES CONFIGURABLE VIA CONFIG FILE ]-----------------

The following Plex application folders do not need to be backed up.

$ExcludeDirs = @( "Diagnostics", "Crash Reports", "Updates", "Logs" )

The following file types do not need to be backed up:

*.bif - thumbnail previews

Transcode - cache subfolder used for transcoding and syncs (could be huge)

$ExcludeFiles = @( "*.bif", "Transcode" )

Subfolders that cannot be archived because the path may be too long.

Long (over 260 characters) paths cause Compress-Archive to fail,

so before running the archival steps, we will move these folders to

the backup directory, and copy it back once the archival step completes.

On restore, we'll copy these folders from the backup directory to the

Plex app data folder.

$SpecialDirs = @( "Plug-in Support\Data\com.plexapp.system\DataItems\Deactivated" )

Regular expression used to find display names of the Plex Windows service(s).

$PlexServiceName = "^Plex"

Name of the Plex Media Server executable file.

$PlexServerFileName = "Plex Media Server.exe"

If Plex Media Server is not running, define path to the executable here.

$PlexServerPath = $null

7-zip command-line option for compression.

$ArchiverOptionsCompress = @( $null )

7-zip command-line option for decompression.

$ArchiverOptionsExpand = @( $null )

-------------------------[ MODULE DEPENDENCIES ]--------------------------

Module to get script version info:

https://www.powershellgallery.com/packages/ScriptVersion

Module to initialize script parameters and variables from a config file:

https://www.powershellgallery.com/packages/ConfigFile

Module implementing logging to file and console routines:

https://www.powershellgallery.com/packages/StreamLogging

$MODULES = @("ScriptVersion", "ConfigFile", "StreamLogging", "SingleInstance")

------------------------------[ CONSTANTS ]-------------------------------

Mutex name (to enforce single instance).

$MUTEX_NAME = $PSCommandPath.Replace("\", "/")

Plex registry key path.

$PLEX_REG_KEYS = @( "HKCU:\Software\Plex, Inc.\Plex Media Server", "HKU:.DEFAULT\Software\Plex, Inc.\Plex Media Server" )

Default path of the Plex Media Server.exe.

$DEFAULT_PLEX_SERVER_EXE_PATH = "${env:ProgramFiles(x86)}\Plex\Plex Media Server\Plex Media Server.exe"

File extensions.

$FILE_EXT_ZIP = ".zip" $FILE_EXT_7ZIP = ".7z" $FILE_EXT_REG = ".reg" $FILE_EXT_CRED = ".xml"

File names.

$VERSION_FILE_NAME = "Version.txt"

Backup mode types.

$MODE_BACKUP = "Backup" $MODE_CONTINUE = "Continue" $MODE_RESTORE = "Restore"

Backup types.

$TYPE_ROBOCOPY = "Robocopy" $TYPE_7ZIP = "7zip"

Backup folder format: YYYYMMDDhhmmss

$REGEX_BACKUPDIRNAMEFORMAT = "^\d{14}$"

Format of the backup folder name (so it can be easily sortable).

$BACKUP_DIRNAMEFORMAT = "yyyyMMddHHmmss"

Subfolders in the backup directory.

$SUBDIR_FILES = "1" $SUBDIR_FOLDERS = "2" $SUBDIR_REGISTRY = "3" $SUBDIR_SPECIAL = "4"

Name of the common backup files.

$BACKUP_FILENAME = "Plex"

Send mail settings.

$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"

Set variables for email notification.

$SUBJECT_ERROR = "Plex backup failed :-(" $SUBJECT_SUCCESS = "Plex backup completed :-)"

------------------------------[ EXIT CODES]-------------------------------

$EXITCODE_SUCCESS = 0 # success $EXITCODE_ERROR = 1 # error

----------------------[ NON-CONFIGURABLE VARIABLES ]----------------------

File extensions.

$ZipFileExt = $FILE_EXT_ZIP $RegFileExt = $FILE_EXT_REG

Files and folders.

[string]$BackupDirName = $null [string]$VersionFilePath = $null

Version info.

[string]$PlexVersion = $null [string]$BackupVersion = $null

Save time for logging purposes.

[DateTime]$StartTime = Get-Date [DateTime]$EndTime = $StartTime [string] $Duration = $null

Mail credentials object.

[PSCredential]$Credential = $null

Error message set in case of error.

[string]$ErrorResult = $null

Backup stats.

[string]$ObjectCount = "UNKNOWN" [string]$BackupSize = "UNKNOWN"

The default exit code indicates error (we'll set it to success at the end).

$ExitCode = $EXITCODE_ERROR

--------------------------[ STANDARD FUNCTIONS ]--------------------------

--------------------------------------------------------------------------

SetModulePath

Adds custom folders to the module path.

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."

}

--------------------------------------------------------------------------

LoadModule

Installs (if needed) and loads a PowerShell module.

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."

}

--------------------------------------------------------------------------

GetScriptVersion

Returns script version info.

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"])

}

--------------------------------------------------------------------------

GetCommandLineArgs

Returns command-line arguments as a string.

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()

}

--------------------------------------------------------------------------

FormatError

Returns error message from exception and inner exceptions.

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

}

--------------------------------------------------------------------------

WriteException

Writes exception to console.

function WriteException { [CmdletBinding()] param ( $errors )

WriteError (FormatError $errors)

}

--------------------------------------------------------------------------

WriteLogException

Logs exception and inner exceptions.

function WriteLogException { [CmdletBinding()] param ( $errors )

Write-LogError (FormatError $errors)

}

--------------------------------------------------------------------------

WriteDebug

Writes debug messages to console.

function WriteDebug {

[CmdletBinding()]

param (
    $message
)
if ($message -and $DebugPreference -ne 'SilentlyContinue') {
    Write-Verbose $message
}

}

--------------------------------------------------------------------------

WriteError

Writes error message to console.

function WriteError { [CmdletBinding()] param ( $message )

if ($message) {
    [Console]::ForegroundColor = 'red'
    [Console]::BackgroundColor = 'black'
    [Console]::WriteLine($message)
    [Console]::ResetColor()
}

}

--------------------------------------------------------------------------

StartLogging

Initializes log settings.

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."

}

--------------------------------------------------------------------------

StopLogging

Clears logging resources.

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."

}

--------------------------------------------------------------------------

PreMain

Performs common action before the main execution logic.

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."

}

--------------------------------------------------------------------------

PostMain

Performs common action after the main execution logic.

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."

}

---------------------------[ CUSTOM FUNCTIONS ]---------------------------

TODO: IMPLEMENT THE FOLLOWING FUNCTIONS AND ADD YOUR OWN IF NEEDED.

--------------------------------------------------------------------------

InitGlobals

Initializes global variables.

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."

}

--------------------------------------------------------------------------

FormatRegFilename

Converts registry key path to filename

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

}

--------------------------------------------------------------------------

GetTimestamp

Returns current timestamp in a consistent format.

function GetTimestamp { return $(Get-Date).ToString("yyyy/MM/dd HH:mm:ss.fff") }

--------------------------------------------------------------------------

WakeUpDir

Attempts to wake a remote host just in case if the backup folder is

hosted on a remote share (pseudo Wake-onLAN command).

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."

}

--------------------------------------------------------------------------

GetNewBackupDirName

Generates the new name of the backup subfolder based on the current

timestamp in the format: YYYYMMDDhhmmss.

function GetNewBackupDirName { param ( )

return ($Script:StartTime).ToString($BACKUP_DIRNAMEFORMAT)

}

--------------------------------------------------------------------------

GetLastBackupDirPath

Returns the path of the most recent backup folder.

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

}

--------------------------------------------------------------------------

GetBackupDirAndPath

Returns name and path of the backup directory that will be used for the

running backup job (it can be an existing or a new directory depending

on the backup mode).

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

}

--------------------------------------------------------------------------

InitMail

Initializes SMTP and mail notification setting.

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."

}

--------------------------------------------------------------------------

Validate7Zip

Validates path to the 7-zip command-line tool.

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."

}

--------------------------------------------------------------------------

ValidateVersion

Validates backup version.

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."

}

--------------------------------------------------------------------------

ValidateData

Validates required directories.

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."

}

--------------------------------------------------------------------------

ValidateSingleInstance

Validates that only one instance of the script is running.

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."

}

--------------------------------------------------------------------------

MustSendMail

Returns 'true' if conditions for sending email notification about the

operation result are met; returns 'false' otherwise.

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

}

--------------------------------------------------------------------------

MustSendAttachment

Returns 'true' if conditions for sending the log file as an attachment

are met; returns 'false' otherwise.

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

}

--------------------------------------------------------------------------

GetPlexServerPath

Returns the path of the running Plex Media Server executable.

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

}

--------------------------------------------------------------------------

GetPlexVersion

Returns either file version of the current Plex Media Server executable.

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

}

--------------------------------------------------------------------------

GetBackupVersion

Returns either version of the last saved backup (from the version.txt file).

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

}

--------------------------------------------------------------------------

SavePlexVersion

Saves current file version of the Plex Media Server executable to

the version.txt file.

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."

}

--------------------------------------------------------------------------

GetPlexServices

Returns the list of Plex Windows services (identified by display names).

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

}

--------------------------------------------------------------------------

StopPlexServices

Stops running Plex Windows 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

}

--------------------------------------------------------------------------

StartPlexServices

Starts Plex Windows services.

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."

}

--------------------------------------------------------------------------

StopPlexMediaServer

Stops a running instance of Plex Media Server.

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."

}

--------------------------------------------------------------------------

StartPlexMediaServer

Launches Plex Media Server.

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
alekdavis commented 3 years ago

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.

chriskeeganhw commented 3 years ago

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

alekdavis commented 3 years ago

There is something wrong with the attached file. Please try downloading the latest release (v2.0.3) and see if it helps.

chriskeeganhw commented 3 years ago

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

alekdavis commented 3 years ago

Which version of PowerShell do you have? Run this command and post the result:

$PSVersionTable.PSVersion
chriskeeganhw commented 3 years ago

Hi

When I start power shell is says copyright 2009.

When I type in the command I get

Major. Minor. Build. Revision

    1. -1. -1

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.

alekdavis commented 3 years ago

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

chriskeeganhw commented 3 years ago

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

alekdavis commented 3 years ago

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?

chriskeeganhw commented 3 years ago

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

http://www.avg.com/email-signature?utm_medium=email&utm_source=link&utm_campaign=sig-email&utm_content=emailclient

Virus-free. http://www.avg.com/email-signature?utm_medium=email&utm_source=link&utm_campaign=sig-email&utm_content=emailclient www.avg.com

alekdavis commented 3 years ago

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).

chriskeeganhw commented 3 years ago

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

chriskeeganhw commented 3 years ago

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

plex backup screenshots.docx

alekdavis commented 3 years ago

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).

alekdavis commented 3 years ago

Also read the Script execution section.

alekdavis commented 3 years ago

I assume this was resolved.

chriskeeganhw commented 3 years ago

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.