[Windows] Introduction to PowerShell Post-Scripting!
Posted: April 1st, 2015, 7:57 pm
Hey guys,
I've Been lurking around here for a while in search for some code snippets to build a custom script for post-processing.
I've noticed these forums are a bit lackluster for anything related to PowerShell scripting.
A few months back I started delving deep into PS at work to automate a few of my admin tasks and haven't looked back since. It was one of those things I knew existed, but never really paid attention to it for years.
Today I am going to give a small introduction into the basic PowerShell scripting and how you can incorporate them into Post-Processing for SABnzb.
Despite the name, PowerShell is a bloody powerful and efficient tool with little coding needed to get the output results you desire.
Over time I will also post up more scripts as I develop them to hopefully give you a better insight and give you some inspiration to write your own
This post is aimed at individuals who are already familiar with cmd/.bat scripting, who can easily adopt their skills to PS and reap the benefits.
Prerequisites:
Before we start, I recommend upgrading PowerShell to 4.0 if possible as its a long way since the early days of 2.0:
You are probably familiar with hooking .exe or .bat files in SABnzb for processing your completed jobs.
We can use a Bootstrap to fire up our PS scripts and parsing the same variables from SABnzb onto the PS script as you would with any other scripting language.
The PSBootStrap.bat:
So what's going on here?
We are calling the powershell.exe shell and specifying our .ps1 script to execute with -File parameter.
As described by the User Script Wiki Article, We can parse variables from our job onto our ProcessMovies.ps1 script. In this example we are passing -JobName "%3" and -CompleteJobDir "%1"
So we are on the same page, our job example will be -JobName "My.Home.Wedding.2015.720p.BluRay.DTS.x264-NoGRP" and -CompleteJobDir "D:\Complete\My.Home.Wedding.2015.720p.BluRay.DTS.x264-NoGRP"
PowerShell comes in 2 flavours - 32Bit and 64Bit which we are using the latter. However if you are using 32-Bit Windows you can change the path accordingly:
32-Bit: C:\WINDOWS\syswow64\Windowspowershell\v1.0\powershell.exe
64-Bit: C:\WINDOWS\system32\WindowsPowerShell\v1.0\powershell.exe
No, that's not a typo. Mind fucked right? As was I. After 15 minutes of scratching around, several sources have confirmed this. Also disregard \v1.0\ even though you are running PS 4.0.
Anyways, moving on!
Script Editing - PowerShell ISE:
One of the cool hidden gems in windows is PowerShell ISE (Integrated Scripting Environment). You can find it by typing in "ISE" into your start search. I use this on almost a daily basis at work and it provides a neat environment for real-time testing and debugging.
Here are also 10 reasons for using PowerShell ISE instead of the PowerShell console. But it wont take long to convince you.
Pro tip: As I tend to stare at my screen hours on end, it's a good idea to change the theme to something that' easier on your eyes once you get your hands more dirty down the track.
In ISE, click on Tools -> Options... -> Manage Themes and select "Dark Console, Dark Editor". You will thank me later.
Script Example - A Breakdown:
I will break my sample script down into several parts to explain best I can, then provide the full script at the end.
This is something I wrote for myself that does the following:
1. Finds movies files of a particular type and size.
2. Rename the movie file based on the job name.
3. Moves the movie file to my collection.
4. Sends recipients a email notification to say the job is complete.
Let's Start:
Here we are specifying the parameters so we can grab the completed job variables from SABnzb and pass them onto our script. These relate to the -JobName and -CompleteJobDir in the PSBootstrap.bat I mentioned earlier.
You can have as many parameters as you like to pass on variables. As a rule of thumb, I always specify parameters the first lines of my script.
Here we are obtaining the file location of our movie file based on file extension *.mkv and a size greater than 2GB.
I know this isn't an absolute fool-proof way of finding the movie file, but I chose this because:
A) I am very particular about what movie format/containers I download. All of mine have .mkv extensions and is the most popular.
B) Most if not all of my movies are larger than 2GB. This should rule out any sample.mkv files which are useless to me.
You probably also noticed $FileSizeMB = 2000. Since the Get-ChildItem cmdlet handles files in Bytes size, I started off with MegaBytes because its an easier size to specify. You can then convert MegaBytes to Bytes using ($FileSizeMB * 1024 * 1024) to pass onto the cmdlet.
This isn't really necessary, though one of the indexers I use like to put "DG" onto the end of some of their bins.
I find this rather annoying, so we can clean up the job name by trimming off "DG" off the end of the job name using every combination I've picked up in the past. This article gives a bit more insight into Trimming.
Here we are renaming our movie file based on the job name as I like to keep my collection uniform.
Sometimes when downloading bins you will notice the movie file has a very whacky filename like 5e04363a22109a1.mkv
In our example the Get-ChildItem cmdlet picked up our movie path as "D:\Complete\My.Home.Wedding.2015.720p.BluRay.DTS.x264-NoGRP\5e04363a22109a1.mkv"
We use Rename-Item cmdlet to change the file name to something more meaningful like "My.Home.Wedding.2015.720p.BluRay.DTS.x264-NoGRP.mkv"
Pro tip: We can resolve variable values on the fly by encapsulating them inside $(). "$($JobName).mkv" becomes "My.Home.Wedding.2015.720p.BluRay.DTS.x264-NoGRP.mkv"
Last but not least, we re-obtain the path to the movie file using Get-ChildItem cmdlet again since the file has been renamed.
Here we move the movie file to my final collection for storage using the Move-Item cmdlet
We then clean up our Complete directory by removing the original completed job directory using Remove-Item cmdlet.
Pretty straight forward eh?
Finally, we can send an email notification to ourselves using the Send-MailMessage cmdlet. The code above is pretty straight forward.
You can send a notification to multiple people by splitting out the recipients into an array as we have with [array]$SMTPTo.
That's it for now!
The full ProcessMovies.ps1 script:
Conclusion:
I've Been lurking around here for a while in search for some code snippets to build a custom script for post-processing.
I've noticed these forums are a bit lackluster for anything related to PowerShell scripting.
A few months back I started delving deep into PS at work to automate a few of my admin tasks and haven't looked back since. It was one of those things I knew existed, but never really paid attention to it for years.
Today I am going to give a small introduction into the basic PowerShell scripting and how you can incorporate them into Post-Processing for SABnzb.
Despite the name, PowerShell is a bloody powerful and efficient tool with little coding needed to get the output results you desire.
Over time I will also post up more scripts as I develop them to hopefully give you a better insight and give you some inspiration to write your own
This post is aimed at individuals who are already familiar with cmd/.bat scripting, who can easily adopt their skills to PS and reap the benefits.
Prerequisites:
Before we start, I recommend upgrading PowerShell to 4.0 if possible as its a long way since the early days of 2.0:
- If you are running Windows 8.1 or Server 2012 R2, then PowerShell 4.0 comes pre-installed.
- If you are running Windows 8 or Server 2012, then PowerShell 3.0 comes pre-installed.
Note: You cannot install PowerShell 4.0 on Windows 8, but you can on Server 2012. Go figure.
You should be able to get away with 3.0 for your basic scripting needs so stick to that. - If you are running Windows 7 or Server 2008 R2, then PowerShell 2.0 comes pre-installed.
You can upgrade to 4.0 using the following packages:- Windows 7 Service Pack 1 - You likely already have this, but check the properties in System.
- Microsoft .NET Framework 4.5 - The core Framework needed for PowerShell 4.0
- Windows Management Framework 4.0 - The main Framework which includes PowerShell 4.0 and PowerShell ISE
You are probably familiar with hooking .exe or .bat files in SABnzb for processing your completed jobs.
We can use a Bootstrap to fire up our PS scripts and parsing the same variables from SABnzb onto the PS script as you would with any other scripting language.
The PSBootStrap.bat:
Code: Select all
C:\WINDOWS\system32\WindowsPowerShell\v1.0\powershell.exe -File "D:\Scripts\ProcessMovies.ps1" -JobName "%3" -CompleteJobDir "%1"
We are calling the powershell.exe shell and specifying our .ps1 script to execute with -File parameter.
As described by the User Script Wiki Article, We can parse variables from our job onto our ProcessMovies.ps1 script. In this example we are passing -JobName "%3" and -CompleteJobDir "%1"
So we are on the same page, our job example will be -JobName "My.Home.Wedding.2015.720p.BluRay.DTS.x264-NoGRP" and -CompleteJobDir "D:\Complete\My.Home.Wedding.2015.720p.BluRay.DTS.x264-NoGRP"
PowerShell comes in 2 flavours - 32Bit and 64Bit which we are using the latter. However if you are using 32-Bit Windows you can change the path accordingly:
32-Bit: C:\WINDOWS\syswow64\Windowspowershell\v1.0\powershell.exe
64-Bit: C:\WINDOWS\system32\WindowsPowerShell\v1.0\powershell.exe
No, that's not a typo. Mind fucked right? As was I. After 15 minutes of scratching around, several sources have confirmed this. Also disregard \v1.0\ even though you are running PS 4.0.
Anyways, moving on!
Script Editing - PowerShell ISE:
One of the cool hidden gems in windows is PowerShell ISE (Integrated Scripting Environment). You can find it by typing in "ISE" into your start search. I use this on almost a daily basis at work and it provides a neat environment for real-time testing and debugging.
Here are also 10 reasons for using PowerShell ISE instead of the PowerShell console. But it wont take long to convince you.
Pro tip: As I tend to stare at my screen hours on end, it's a good idea to change the theme to something that' easier on your eyes once you get your hands more dirty down the track.
In ISE, click on Tools -> Options... -> Manage Themes and select "Dark Console, Dark Editor". You will thank me later.
Script Example - A Breakdown:
I will break my sample script down into several parts to explain best I can, then provide the full script at the end.
This is something I wrote for myself that does the following:
1. Finds movies files of a particular type and size.
2. Rename the movie file based on the job name.
3. Moves the movie file to my collection.
4. Sends recipients a email notification to say the job is complete.
Let's Start:
Code: Select all
param(
[string]$JobName,
[string]$CompleteJobDir
)
You can have as many parameters as you like to pass on variables. As a rule of thumb, I always specify parameters the first lines of my script.
Code: Select all
$FileSizeMB = 2000
$FileSizeB = ($FileSizeMB * 1024 * 1024)
[String]$FileLocation = Get-ChildItem -Path $CompleteJobDir –Recurse -Include *.mkv | Where-Object {$_.length -gt $FileSizeB}
I know this isn't an absolute fool-proof way of finding the movie file, but I chose this because:
A) I am very particular about what movie format/containers I download. All of mine have .mkv extensions and is the most popular.
B) Most if not all of my movies are larger than 2GB. This should rule out any sample.mkv files which are useless to me.
You probably also noticed $FileSizeMB = 2000. Since the Get-ChildItem cmdlet handles files in Bytes size, I started off with MegaBytes because its an easier size to specify. You can then convert MegaBytes to Bytes using ($FileSizeMB * 1024 * 1024) to pass onto the cmdlet.
Code: Select all
If ($JobName -contains ".~DG~") {$JobName = $JobName.TrimEnd(".~DG~")}
ElseIf ($JobName -contains ".DG") {$JobName = $JobName.TrimEnd(".DG")}
ElseIf ($JobName -contains "~DG~") {$JobName = $JobName.TrimEnd("~DG~")}
I find this rather annoying, so we can clean up the job name by trimming off "DG" off the end of the job name using every combination I've picked up in the past. This article gives a bit more insight into Trimming.
Code: Select all
Rename-Item $FileLocation "$($JobName).mkv"
[String]$FileLocation = Get-ChildItem -Path $CompleteJobDir –Recurse -Include *.mkv | Where-Object {$_.length -gt $FileSize}
Sometimes when downloading bins you will notice the movie file has a very whacky filename like 5e04363a22109a1.mkv
In our example the Get-ChildItem cmdlet picked up our movie path as "D:\Complete\My.Home.Wedding.2015.720p.BluRay.DTS.x264-NoGRP\5e04363a22109a1.mkv"
We use Rename-Item cmdlet to change the file name to something more meaningful like "My.Home.Wedding.2015.720p.BluRay.DTS.x264-NoGRP.mkv"
Pro tip: We can resolve variable values on the fly by encapsulating them inside $(). "$($JobName).mkv" becomes "My.Home.Wedding.2015.720p.BluRay.DTS.x264-NoGRP.mkv"
Last but not least, we re-obtain the path to the movie file using Get-ChildItem cmdlet again since the file has been renamed.
Code: Select all
Move-Item $FileLocation "F:\HD MOVIES" -Force
Remove-Item -Path "D:\Complete\$($JobName)" -Force -Recurse -ErrorAction SilentlyContinue
We then clean up our Complete directory by removing the original completed job directory using Remove-Item cmdlet.
Pretty straight forward eh?
Code: Select all
[array]$SMTPTo = "Joe Bloggs <[email protected]>", "Anthony Smith <[email protected]>"
$SMTPFrom = "[email protected]"
$SMTPServer = "smtp.server.com"
$SMTPPriority = "Normal"
$SMTPSubject = "Downloaded: $($JobName)"
$SMTPBody = "Movie available in: \\server\HD MOVIES\$($JobName).mkv"
Send-MailMessage -To $SMTPTo -From $SMTPFrom -SMTPServer $SMTPServer -Subject $SMTPSubject -Body $SMTPBody -Priority $SMTPPriority
You can send a notification to multiple people by splitting out the recipients into an array as we have with [array]$SMTPTo.
That's it for now!
The full ProcessMovies.ps1 script:
Code: Select all
param(
[string]$JobName,
[string]$CompleteJobDir
)
# Specify files greater than XX size below in MB
$FileSizeMB = 2000
# Convert Megabytes to Bytes
$FileSizeB = ($FileSizeMB * 1024 * 1024)
# Get location of video file in completed directory. Scan sub directories as well if its hidden deep inside.
Write-Host "Obtaining location of video file.."
[String]$FileLocation = Get-ChildItem -Path $CompleteJobDir –Recurse -Include *.mkv | Where-Object {$_.length -gt $FileSizeB}
Write-Host "Found: $($FileLocation)"
# Clean up job name if it contains unwanted text.
# One silly NZB Scraper I use puts DG on the end of releases that are dupes in different Usenet groups.
If ($JobName -contains ".~DG~") $JobName = $JobName.TrimEnd(".~DG~")}
ElseIf ($JobName -contains ".DG") {$JobName = $JobName.TrimEnd(".DG")}
ElseIf ($JobName -contains "~DG~") {$JobName = $JobName.TrimEnd("~DG~")}
# Rename video file to job name
Write-Host "Renaming movie file to $($JobName).mkv"
Rename-Item $FileLocation "$($JobName).mkv"
# Get location of renamed video file again
Write-Host "Re-obtaining location of video file..."
[String]$FileLocation = Get-ChildItem -Path $CompleteJobDir –Recurse -Include *.mkv | Where-Object {$_.length -gt $FileSizeB}
Write-Host "Found: $($FileLocation)"
# Move video file to final storage location
Write-Host "Moving movie file to F:\HD MOVIES\$($JobName).mkv"
Move-Item $FileLocation "F:\HD MOVIES" -force
# Pre-defined parameters for Email Notifications
[array]$SMTPTo = "Joe Bloggs <[email protected]>", "Anthony Smith <[email protected]>"
$SMTPFrom = "[email protected]"
$SMTPServer = "smtp.server.com"
$SMTPPriority = "Normal"
$SMTPSubject = "Downloaded: $($JobName)"
$SMTPBody = "Movie available in: \\server\HD MOVIES\$($JobName).mkv"
# Send email notification saying job is complete.
Write-Host "Emailing notifications.."
Send-MailMessage -To $SMTPTo -From $SMTPFrom -SMTPServer $SMTPServer -Subject $SMTPSubject -Body $SMTPBody -Priority $SMTPPriority
Write-Host "All done! Exit code: 0"
- In this example we made a simple process of renaming a movie file and moving it to a storage collection, and sending out a notification once the job is complete.
- I'm aware this example doesn't include .srt or .sub files. With a little creativity you can also process your sub files along with the main movie file.
- I know SABnzb can send out email notifications when jobs are complete, but they can get a bit annoying if you are downloading entire TV seasons with each episode split into multiple jobs.
I use my PSBootStrap.bat selectively for movie jobs and SickBeard for TV shows. - PowerShell has 1000's of cmdlets at your disposal and is a leap from the old Command Prompt days. You can get more in-depth with detecting different types of jobs that you processes such as TV shows and music, which I might give some examples later on.
- Another more advanced script I am working on at the moment is scraping basic information about movies from IMDB and including them in email notifications. If you have multiple recipients then it will give more context about the jobs you're downloading. Including picture thumbnails, descriptions, year, actors, synopsis, trailer URLs, etc. I will post some examples of this up soon. But for now this should give you a step in the right direction with PS Scripting!