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