Home > PowerShell > Script: New-HandBrakeConversion.ps1 – Convert Entire Folders of Files to .mp4 With HandBrake

Script: New-HandBrakeConversion.ps1 – Convert Entire Folders of Files to .mp4 With HandBrake

I watch at LOT of movies. Since I’m generally away from home at least 200 days a year, I spend a lot of time in airports, on airplanes, in hotels, etc. When I work from home, I watch movies and TV shows as well. My iPad and my 5TB iTunes library keep me entertained.

Many of the videos I have start in something other than the .mp4/.m4v format that iTunes requires. Either it’s something I’ve downloaded, something someone sent me, training (CBT) videos, or something pulled from my HD video camera’s hard drive. The files usually are either .avi or .mkv.

There are plenty of utilities out there that will convert video files from one format to another. Some of them are free, and some cost a few bucks. By far the best, most flexible utility I’ve found is the popular HandBrake. HandBrake has many options that let you get down “in the weeds” with every setting conceivable for converting videos. It has a popular queuing feature that lets you add several files to the queue, and HandBrake will process them, one at a time. Processing time depends on the source file size and format, as well as the destination format. And, it’s FREE.

Since I am away from my monster desktop machine so much, I put it’s i7 processor’s idle time to good use by having it process videos, saving them as .mp4 videos. But manually adding a bunch of videos to HandBrake’s queue can take a while. Plus, since I’m not around when the processing is taking place, I don’t need the fancy GUI from HandBrake. When HandBrake is installed, a CLI version is also automatically installed So I created a PowerShell script that will use the CLI version, take a source folder, and process every video in that directory that doesn’t have a corresponding .mp4 file of the same name. That way, if the script stops and you need to restart it, it won’t re-process video’s it’s already converted. It will only attempt to convert files with .avi, mkv, .ogm, and .wmv extensions, but only the first two, .avi, and .mkv, have been extensively tested.

The script will automatically detect if/where HandBrake is installed, so there is nothing really to configure. Run the script with no switches, and you’ll be prompted for the source folder that contains the videos to be converted:

.\New-HandBrakeConversion.ps1

Or, you can specify the source folder and avoid the prompt:

.\New-HandBrakeConversion.ps1 -dir "c:\path\to\files"

For those of you who are hardcore HandBrake users, the configuration settings I use are the following:

-e x264 -b 1500 -a 1 -E faac -B 160 -R Auto -6 dpl2 -f mp4 -m -2 -T -x ref=2:bframes=2:me=umh -n eng

Let me know if you have any questions or comments.

Installation

Execution Policy: Third-party PowerShell scripts may require that the PowerShell Execution Policy be set to either AllSigned, RemoteSigned, or Unrestricted. The default is Restricted, which prevents scripts – even code signed scripts – from running. For more information about setting your Execution Policy, see Using the Set-ExecutionPolicy Cmdlet.

There is no installation routine for this script. It’s meant to be manually run.

Donations

I’ve never been one to really solicit donations for my work. My offerings are created because *I* need to solve a problem, and once I do, it makes sense to offer the results of my work to the public. I mean, let’s face it: I can’t be the only one with that particular issue, right? Quite often, to my surprise, I’m asked why I don’t have a “donate” button so people can donate a few bucks. I’ve never really put much thought into it. But those inquiries are coming more often now, so I’m yielding to them. If you’d like to donate, you can send a few bucks via PayPal at https://www.paypal.me/PatRichard. Money collected from that will go to the costs of my website (hosting and domain names), as well as to my home lab.

Download

v1.3 New-HandBrakeConversion.v1.3.zip

Changelog

See the changelog for this script for all version information

Categories: PowerShell Tags: ,
  1. Jeff
    May 30th, 2012 at 16:39 | #1

    I’m in the process of learning powershell and trying to get some work done as well. I’m working on a similar project, but using FFMPEG for the conversion process. I noticed in your script when you test to see if Handbrake is installed you end the “Get-ItemProperty” with “.’Default’ – replace” I understand the search in the registry and the replace at the end, but what does .’Default’ do? I can’t seem to find any reference to it anywhere. Nice script!

    Tks, Jeff..

  2. David
    September 28th, 2012 at 09:19 | #2

    This is just what I was looking for. I am excited for the deleting of source file after processing listed in the wish list. Keep up the great work on all your powershell scripts.

    -= Symalla

  3. JPB
    October 20th, 2012 at 13:59 | #3

    It seems that you could delete the source file simply by adding a line underneath the “New-BalloonTip” line ending in “Encoding Completed” with the following:

    Remove-Item $file

    Someone who knows more about Powershell, feel free to correct me if I am wrong.

    • Pat Richard
      October 20th, 2012 at 15:22 | #4

      That would likely work. I want to add that as an option. I’ve never liked auto deleting files incase there is a quality issue with the .mp4 file.

      • JPB
        October 20th, 2012 at 18:09 | #5

        I agree. There would be few cases where I’d want to do that. I would either have to feel really confident in my Handbrake job settings or be able to easily obtain the source file again.

      • JPB
        October 20th, 2012 at 18:16 | #6

        By the way, thanks for sharing this. It is a very intelligent way to accomplish this task. With a few more file types and by pre-defining my path I am trying this out as a scheduled task that runs regularly to create kind of a drop-folder scenario. The apps out there that do this all have a shortcoming or two that I can resolve with this script. (Namely to search sub folders, to move it to another folder upon completion, and to correct itself if it should crash by using scheduled tasks to restart it regularly)

  4. October 27th, 2012 at 22:20 | #7

    I don’t understand how to add/change the .PARAMETER value to point to the Video folder I need to convert. Also, I need to convert .avi and .mp4 to .mkv. What do I need to change in the script to do this?
    Thanks

    • Pat Richard
      October 27th, 2012 at 22:23 | #8

      You don’t change .parameter. You change dir.

      I don’t know what would be changed. Certainly more than just some variables. This script is designed to create .mp4 files.

    • JPB
      October 28th, 2012 at 00:27 | #9

      If everything is going to be an mkv instead of mp4 you would need to set it up in handbrake how you would want it and get the command/switches to replace it in the script Pat has here. Then, you’d need to add the additional extensions to the part of Pat’s script that is looking for the files to convert. I’ve done the latter, but I use mp4 so I only changed a setting or two within the handbrake code Pat has so it worked better for the Apple TV.

  5. Pat Richard
    October 28th, 2012 at 09:01 | #10

    JPB – post what you have for $HandBrakeCLIOptions. Curious to see what you use. I have Apple TV boxes all over the house.

    • JPB
      October 28th, 2012 at 10:04 | #11

      This is what has worked best for me, giving me enough quality and keeping the file sizes from being huge (the file size thing maybe being different from your agenda with the conversion)…

      [string] $handbrake = $handbrakeclishortpath + ” -i `”$file`” -o `”$justName.mp4`”-f mp4 -4 -w 720 –loose-anamorphic -e x264 -q 20 -r 29.97 –pfr -a 1,1 -E faac,copy:ac3 -6 dpl2,auto -R Auto,Auto -B 160,auto -D 0.0,0.0 -m –verbose=1″

      I’m not a video snob when it comes to the quality. This doesn’t look far different than the Apple TV 2 preset or whatever it’s called, yet has yielded much smaller file sizes. It also deals with a variety of different resolutions without distorting them (not that yours does, but I have had issues with some of the presets in that way — they’d use hard constraints).

  6. Fish5802
    February 17th, 2014 at 14:04 | #12

    Pat, Great script it works as intended and is saving me a ton of work digitizing my DVD Collection for use on my AppleTV. I made a few changes on your script. There are a few things I would like to share.
    If your Power Management plan is set to balanced or power save your computer will sleep while this script is running and halt any progress in your queue. This Function fixes that
    ####
    function SetPowerPlan([string]$PreferredPlan)
    {
    #”High performance”, “Balanced”, “Power saver”
    Write-Host “Setting Powerplan to $PreferredPlan”
    $guid = (Get-WmiObject -Class win32_powerplan -Namespace root\cimv2\power -Filter “ElementName=’$PreferredPlan'”).InstanceID.tostring()
    $regex = [regex]”{(.*?)}$”
    $newpowerVal = $regex.Match($guid).groups[1].value

    # setting power setting to high performance
    powercfg -S $newpowerVal
    }
    ####

    Also when you are building your Bubble notifications you shouldn’t be calling this line every time.
    ####
    New-BalloonTip -BalloonTipIcon
    ####
    This is causing the system to create a new icon every time you run this New-BallonTip and fills your system tray up with icons.

    Instead Scrap your function and create the balloontip once and then change the text, Title, Icon and show as needed.

    example:
    #############

    $balloon = New-Object System.Windows.Forms.NotifyIcon
    $path = Get-Process -id $pid | Select-Object -ExpandProperty Path
    $icon = [System.Drawing.Icon]::ExtractAssociatedIcon($path)
    $balloon.Icon = $icon

    $files = (Get-ChildItem “$dir\*” -include *.avi,*.mkv,*.ogm,*.wmv)
    if ($files.length -ge 1){
    ForEach ($file in $files){
    [string] $justName = $file.name.substring(0,$file.name.length-4)
    if (!(Test-Path “$justname.mp4”)){
    $balloon.BalloonTipTitle = “Encoding”
    $balloon.BalloonTipText = “Encoding: $file”
    $balloon.BalloonTipText += “`n”
    $balloon.Visible = $true
    $balloon.ShowBalloonTip(10000)
    [string] $handbrake = $handbrakeclishortpath + ” -i `”$file`” -o `”$justName.mp4`” –preset=`”AppleTV 3`” –subtitle-forced”
    Invoke-Expression $handbrake
    $balloon.BalloonTipText += “Completed: $justname.mp4 ”
    $balloon.BalloonTipTitle = ‘Encode Complete’
    $balloon.Visible = $true
    $balloon.ShowBalloonTip(10000)
    }
    else{
    $balloon.BalloonTipText += “$justName.mp4 already exists”
    $balloon.BalloonTipText += “`n”
    $balloon.BalloonTip1Title = “Duplicate Filename”
    $balloon.Visible = $true
    $balloon.ShowBalloonTip(10000)
    }
    }
    }

    ################

    Note: The += will allow you to keep adding the notification message it is limited (can’t remember the exact count) to something like 250 char. Also the “`n” creates a new line in the Notification bubble.

    as you can see above I also added a preset since that is what I prefer to use when I encode.

    I will be Looking at two other thing when I get some free time. I have a Powershell script to rename MKV files to the parent folders name (removing “_” and replacing them with ” ” as it runs) and move them into another directory for processing by your script. I also have a script that I have been playing with to add a context menu to the icon in the system tray giving you a “Exit” and “Show Last Notification” options. I am going to attempt to integrate both of the above scripts into yours.

    • Pat Richard
      February 24th, 2014 at 23:48 | #13

      I have this function:
      function Set-PowerPlan {
      [CmdletBinding(SupportsShouldProcess = $True)]
      param (
      [ValidateSet(“High performance”, “Balanced”, “Power saver”)]
      [ValidateNotNullOrEmpty()]
      [string] $PreferredPlan = “High Performance”
      )

      Write-Verbose “Setting power plan to `”$PreferredPlan`””
      $guid = (Get-WmiObject -Class Win32_PowerPlan -Namespace root\cimv2\power -Filter “ElementName=’$PreferredPlan'”).InstanceID.ToString()
      $regex = [regex]”{(.*?)}$”
      $plan = $regex.Match($guid).groups[1].value

      # setting power setting to high performance
      powercfg -S $plan
      $Output = “Power plan set to ”
      $Output += “`”” + ((Get-WmiObject -Class Win32_PowerPlan -Namespace root\cimv2\power -Filter “IsActive=’$True'”).ElementName) + “`””
      Write-Verbose $Output
      }

      to handle the power plan, but I am keeping the function for the balloon tip. I merely need to dispose() once it’s done to resolve the issue. I haven’t used Handbrake in a while now, but I should update the script and release it. Thanks for the info.

  7. Vlad
    May 2nd, 2015 at 22:37 | #14

    Script works great. But my goal is for the new MP4 files to replace the original MTS files in my personal video library. I need the MP4 file to retain the original Date Created tag (file property) and/or have the new file name appended with a suffix of the original file create date. In other words, I’d like to know when the video was originally taken. Can the script be updated for this?

  8. Santosh
    July 26th, 2016 at 04:23 | #15

    I am getting following gerror whe trying to convert media file.

    Invoke-Expression : Missing expression after unary operator ‘-‘.
    At C:\Software\New-HandBrakeConversion.v1.3\New-HandBrakeConversion.ps1:144 char:30
    + Invoke-Expression <<<< $handbrake
    + CategoryInfo : ParserError: (-:String) [Invoke-Expression], ParseException
    + FullyQualifiedErrorId : MissingExpressionAfterOperator,Microsoft.PowerShell.Commands.InvokeExpressionCommand

    • Pat Richard
      July 26th, 2016 at 13:43 | #16

      Do you have handbrake installed?

  9. john
    April 5th, 2018 at 11:25 | #17

    I get an error. I have handbrake and it is current and works well manually.

    Processing C:\Users\MSI\Desktop\Leathal Weapon\Lethal Weapon S02E06.mkv
    *******************************************************************************

    -i : The term ‘-i’ is not recognized as the name of a cmdlet, function, script file, or operable program. Check the
    spelling of the name, or if a path was included, verify that the path is correct and try again.
    At line:1 char:2
    + -i “C:\Users\MSI\Desktop\Leathal Weapon\Lethal Weapon S02E06.mkv” -o …
    + ~~
    + CategoryInfo : ObjectNotFound: (-i:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

  10. Michael
    July 20th, 2018 at 09:22 | #18

    I had the same issue. It seems as if the the registry key for the App Path has changed.
    I had to change line 105 from

    […] -path “HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\Handbrake.exe” […]

    to

    […] -path “HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\Handbrake” -erroraction […]

  11. Tyler Thomas
    September 3rd, 2021 at 04:40 | #19

    Hi,

    I’ve been using HandBreak for some time now and still can’t figure out why every time I drop a folder into the Source Selection and then select Add All (the entire folder) to the queue and RUN the queue, it always runs all but one of the files that were in the folder.

    SO: If I have 10 files in a folder that I want to convert to .mp4 files, and I drop the folder into the Source Selection and Add All to the queue and RUN the queue, it only runs 9 files and not all 10 that were in the folder. It skips the first file in the list every time.

    It does this EVERY TIME. Any help would be greatly appreciated.

  1. October 4th, 2011 at 09:02 | #1
  2. March 5th, 2013 at 23:38 | #2