powershellhandbrakecli

Green frames / no subtitles in converted files of watching directory (Powershell) using HandbrakeCLI + JSON preset


So I needed a script that watches a directory and converts files using HandbrakeCLI. I found a part of this powershell here on stackoverflow and I adjusted some things for my project.

$global:watch = "C:\~\cmp\" ### watching directory
$global:convert = "C:\~\convert\" ### handbrakecli and preset location
$global:outp = "C:\~\output_folder\" ### output location
$global:extIn = ".mkv"
$global:extOut = ".mp4"

Write-Host "Watching directory =  $watch"
Write-Host "HandbrakeCLI / json preset location = $convert"
Write-Host "Output directory = $outp"
Write-Host "Input extension = $extIn ; Output extension = $extOut"
Write-Host "Waiting for change in directory..."

### SET FOLDER TO WATCH + FILES TO WATCH + SUBFOLDERS YES/NO
$watcher = New-Object System.IO.FileSystemWatcher;
$watcher.Path = $watch;
$watcher.Filter = "*"+$extIn;
$watcher.IncludeSubdirectories = $false;
$watcher.EnableRaisingEvents = $false;

### DEFINE ACTIONS AFTER AN EVENT IS DETECTED
$action = 
{
    $path = $Event.SourceEventArgs.FullPath;
    $handbrakecli = $convert+"HandBrakeCLI.exe";
    $fl = Split-Path $path -leaf;
    Write-Host "New file found: $fl";

    $flName, $flExt = $fl.split('.')
    $mp4File = $watch+"\"+$flName+$extOut
    $changeType = $Event.SourceEventArgs.ChangeType
    $logline = "$(Get-Date), $changeType, $path"
    Add-content -path ($convert+"log.txt") -value $logline
    Write-Host "Added entry to log"

    Write-Host "Start converting using HandbrakeCLI..."
    & cmd.exe /c $handbrakecli -i $path -o $mp4File --preset-import-file my_preset.json
    Write-Host "Done converting!"

    Write-Host "Moving file to folder..."
    & cmd.exe /c move /Y $mp4File $outp
    Write-Host "File moved!"

    & cmd.exe /c del $path /F
    Write-Host "$fl has been removed from local folder"
    }    

### DECIDE WHICH EVENTS SHOULD BE WATCHED 
    Register-ObjectEvent $watcher "Created" -Action $action
    Register-ObjectEvent $watcher "Changed" -Action $action
    Register-ObjectEvent $watcher "Renamed" -Action $action
    while ($true) {sleep 5}

While at first everything seemed to work, I started to notice that "sometimes" the subtitles were not added or green frames were inserted (or replaced original frame) after every frame (normal - green - normal - green - etc.).

An example: I added 2 mkv files to the directory, the 1st one got converted just fine with subtitles while the 2nd file didn't have any subtitles.

I'm an amateur when it comes to this stuff, but I think it has something to do with the & cmd.exe /c. I also found that you could to Start-Process in powershell, but I don't know how to use it.

So if someone could help me convert this & cmd.exe /c $handbrakecli -i $path -o $mp4File --preset-import-file my_preset.json to something with Start-Process ..., maybe it will help me out.

EDIT

So I made the changes that Tomalak suggested (simpler this way), but Move-Item and Remove-Item don't seem to work.

EDIT 2

Added -LiteralPath as argument for Move-Item / Remove-Item (needed for filenames containt square brackets)

$inputFolder = "C:\~\cmp\"
$outputFolder = "C:\~\output_folder\"
$handbrake = "C:\~\convert\HandBrakeCLI.exe"
$presetJson ="C:\~\convert\my_preset.json"
$extIn = "mkv"
$extOut = "mp4"

while ($true) {
    Get-ChildItem -Path $inputFolder -Filter "*.$extIn" | ForEach-Object {
        $inFile = $_.FullName
        $outFile = $inputFolder + $_.FullName.split('\.')[-2] + ".$extOut" #changed this because I wanted the file in the same directory as input file

        Write-Host "Converting: $inFile"
        & $handbrake -i $inFile -o $outFile --preset-import-file $presetJson

        if ($LASTEXITCODE -eq 0) {
            Move-Item -LiteralPath $outFile $outputFolder -Force #move to output folder
            Write-Host "Done: $outFile"
            Remove-Item -LiteralPath $inFile -Force #removing the input item, not output
            Write-Host "Removed input file!"
        } else {
            Write-Error "Conversion failed!"
        }
    }
    sleep 5
}

While subtitles are added to all output files, I still get green-flickering sometimes. I used 3 files as a test run, result: 1st flickering, 2nd OK, 3rd flickering. I have no clue why some are fine and some got the flickering. So I'm considering to maybe use ffmpeg instead.

EDIT 3

For future visitors: use ffmpeg instead of HandbrakeCLI:

ffmpeg.exe -i "C:\~\inputfile.mkv" -filter_complex "subtitles='C\:/Users/~/inputfile.mkv'" -c:v libx264 -preset veryfast -b:v 2750k -c:a aac $outputfile.mp4


Solution

  • Instead of using file system notifications, structure your script around a simple endless loop:

    $inputFolder = "C:\~\cmp"
    $outputFolder = "C:\~\convert"
    $handbrake = "C:\~\convert\HandBrakeCLI.exe"
    $presetJson = "C:\~\convert\my_preset.json"
    $extIn = "mkv"
    $extOut = "mp4"
    
    while ($true) {
        Get-ChildItem -Path $inputFolder -Filter "*.$extIn" | ForEach-Object {
            $inFile = $_.FullName
            $outFile = "$($_.BaseName).$extOut"
    
            if (Test-Path $outFile) { Remove-Item $outFile -Force -LiteralPath }
    
            Write-Host "Converting: $inFile"
            & $handbrake -i $inFile -o $outFile --preset-import-file $presetJson
    
            if ($LASTEXITCODE -eq 0) {
                Move-Item $outFile $outputFolder -Force -LiteralPath
                Write-Host "Done: $outFile"
            } else {
                Write-Error "Conversion not successful."
            }
        }
        sleep 5
    }
    

    The & makes Powershell execute whatever program the $handbrake variable points to.

    As an exercise you can convert the top-level variables to script parameters, so that you can re-use the script for other batch jobs.