PowerShell displaying proper output data from HandBrakeCLI inside FileSystemWatcher - powershell

I'm working on a simple script that monitors a folder for large video files and if it's larger then the set size it should send it to HandBrake to convert.
The main function of the script is done and works more or less but I am having trouble displaying proper output from HandBrake.
Here is what I have written so far:
$Folder = "E:\Series" #Folder to be monitored for large video files.
$Output = "E:\Encoded" #Folder where encoding jobs go to, and that will be monitored for completed jobs to replace the original file with.
$MaxMB = "15" #Max MB a minute
$MI_CLI = "C:\Program Files\MediaInfoCLI\MediaInfo.exe" #Location oo MediaInfoCLI
$HB_CLI = "C:\Program Files\Handbrake\HandBrakeCLI.exe" #Location of HandBrakeCLI
$HB_Container = "Copy" #HandBrake container output
$Filter = '*.*'
Write-Host "Monitoring "
$fsw = New-Object IO.FileSystemWatcher $Folder, $Filter
$fsw.IncludeSubdirectories = $true
Register-ObjectEvent $fsw Changed -SourceIdentifier FileUpdated -Action {
Start-Sleep -s 1
$FilePath = $EventArgs.FullPath
$FileName = $FilePath.split('\')[-1]
if($FilePath -imatch '\.(?:mp4|mkv)'){
if((Test-Path -LiteralPath $filePath) -and -not (Test-Path -LiteralPath "$Output\$FileName")){
if(Test-FileReady $FilePath){
$fileSize = (Get-Item $FilePath).length
if($fileSize -ge 734003200){
Write-Host ""
Write-Host "Large Video Detected: `"$($FileName)`""
Write-Host "Sending To HandBrake..."
HB-Convert $FilePath $Output
}
}
}
}
}
function HB-Convert{
param ([parameter(Mandatory=$true)][string]$source,[parameter(Mandatory=$true)][string]$dest)
if(-not (Test-Path $source) -or -not (Test-Path $dest)) {return}
$FileName = $source.split('\')[-1]
start-process $HB_CLI -ArgumentList "-i `"$source`" -t 13 --angle 1 -c 1 -o `"$dest\$FileName`" -f mkv -w 1280 --crop 0:0:0:0 --loose-anamorphic --modulus 2 -e x265 -q 20 --vfr -a 1 -E copy -6 dpl2 -R Auto -B 160 -D 0 --gain 0 --audio-fallback ac3 --encoder-preset=faster --verbose=0 2> log.txt" -wait -nonewwindow
#HandBrakeCLI -i `"$source`" -t 13 --angle 1 -c 1 -o `"$dest\$FileName`" -f mkv -w 1280 --crop 0:0:0:0 --loose-anamorphic --modulus 2 -e x265 -q 20 --vfr -a 1 -E copy -6 dpl2 -R Auto -B 160 -D 0 --gain 0 --audio-fallback ac3 --encoder-preset=faster --verbose=0 2> log.txt
}
function Test-FileReady {
Param([parameter(Mandatory=$true)]$path)
if (Test-Path -LiteralPath $path) {
trap {
return $false
}
$stream = New-Object system.IO.StreamReader $path
if ($stream) {
$stream.Close()
return $true
}
}
}
Now in HB-Convert function I have 2 lines that call HandBrakeCLI.
one through Start-Process
and one using HandBrakeCLI(I have added handbrake dir to my system environment variable)
the latter one is now marked out.
When I would call HB-Convert(Using HandBrakeCLI not Start-Process) manually in the command prompt everything works like it should I only get the progress of handbrake displayed.
Encoding: task 1 of 1, 74.66 % (26.78 fps, avg 47.17 fps, ETA
00h05m40s)
Now when this is called through the FileSystemWatcher It will not display anything from handbrake it will only show output from the script it self
Large Video Detected: "FileName"
Sending To HandBrake...
it will hang there till the encoding is done
now when FileSystemWatcher calls HB-Convert with start-process handbrake will output all data not just the progress witch is very annoying.
so how would I get it to display only the progress when its called true the FileSystemWatcher.
I have been trying to get it to work for hours its driving me nuts. hope some one here can fix it.
I only learned a bit of PS for this so when I say that I am a nub in PS thats a understatement :D

Ok I got it to work
adding this to the script
function global:HB-UpdateProgress{
Process{
$position = $host.ui.rawui.cursorposition
$position.X = 0
$host.ui.rawui.cursorposition = $position
Write-Host #($input)[0] -NoNewline
}
}
then use it as a pipeline when calling HandBrakeCLI will display the output properly

Related

Need help getting FFMPEG to pipe alternate STDIO

Problem: I need to pipe ffmpeg stream to a variable using PowerShell. The code below "works" (95% of the time), the problem is some Unicode characters do not exist when printed to the console which results in a malformed image.
Question: Is there a way to pipe bytes, decimal values, or even binary values instead of Unicode to console from ffmpeg???
clear-host
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
$ffmpeg = "C:\ffmpeg\bin\ffmpeg.exe"
$input = "C:\Users\Jukari\Desktop\test\test1.mp4"
$output = "C:\Users\Jukari\Desktop\test\test1.bmp"
$screenshot = 3
[console]::OutputEncoding = New-Object System.Text.UnicodeEncoding
[string]$image = & cmd /u /c "chcp 65001 > NUL && $ffmpeg -i `"$input`" -hide_banner -loglevel error -ss $screenshot -vframes 1 -an -c:v bmp -f image2pipe - 2>&1" 2>&1 | Out-String -Stream
[console]::OutputEncoding = New-Object System.Text.UTF8Encoding
$bytes = [System.Text.Encoding]::Unicode.GetBytes($image)
$stream = new-object System.IO.MemoryStream(,$bytes)
$image_out = [system.drawing.Image]::FromStream($stream,1,1)
write-host $image_out.Height
write-host $image_out.Width
if(Test-Path -LiteralPath $output)
{
Remove-Item $output
}
$image_out.Save($output)
$image_out.Dispose()
Thank you in advance!

Powershell: Call Debug Analyzer cdb.exe as Process

i need to call the cdb.exe as a Process to check to kill the process after a few seconds.
Some Dumps cannot be analyzed so i have to do an other call.
Here you can see my code. But it doesn't work. The cdb.exe is not started correctly and i am not getting the output file.
Do you have some advises for me?
The call "before" implementing the process part starts the cdb.exe
$maximumRuntimeSeconds = 3
$path = "C:\Program Files (x86)\Windows Kits\10\Debuggers\x86\cdb.exe"
$process = Start-Process -FilePath $path "-z $unzippedFile.FullName, -c `".symfix;.reload;!analyze -v; q`""
try {
$process | Wait-Process -Timeout $maximumRuntimeSeconds -ErrorAction Stop > $outputFile
Write-Warning -Message 'Process successfully completed within timeout.'
}
catch {
Write-Warning -Message 'Process exceeded timeout, will be killed now.'
$process | Stop-Process -Force
}
# call before implementing Process
& "C:\Program Files (x86)\Windows Kits\10\Debuggers\x86\cdb.exe" -z $unzippedFile.FullName -c ".symfix;.reload;!analyze -v; q" > $outputFile
-Passthru was needed to make Wait-Process work.
I think you also need to look at how the double quoted string is expanding. I think $UnzippedFIle.Fullname might be adding a literal ".FullName" at the end of the actual fullname of the zip file. I don't have your environment, but the rudementary tests I've done show that. Try packing it in a sub-expression like:
"-z $($unzippedFile.FullName), -c `".symfix;.reload;!analyze -v; q`""
Let me know how that goes. Thanks.
C:\>dir /b ok.txt
File Not Found
C:\>type dodump.ps1
$path = "C:\Program Files\Windows Kits\10\Debuggers\x86\cdb.exe"
$process = Start-Process -PassThru -FilePath $path -ArgumentList "-z `"C:\calc.DMP`"" ,
"-c `".symfix;.reload;!analyze -v;q`"" -RedirectStandardOutput c:\\ok.txt
try {
$process | Wait-Process -Timeout 100 -ErrorAction Stop
Write-Host "Process finished within timeout"
}catch {
$process | Stop-Process
Write-Host "process killed"
}
Get-Content C:\ok.txt |Measure-Object -Line
C:\>powershell -f dodump.ps1
Process finished within timeout
Lines Words Characters Property
139

Powershell, System.Diagnostics.Process & exiftool stop working when dealing with hundreds of commands

I created a tool (to be precise: a Powershell script) that helps me with converting pictures in folders, i.e. it looks for all files of a certain ending (say, *.TIF) and converts them to JPEGs via ImageMagick. It then transfers some EXIF, IPTC and XMP information from the source image to the JPEG via exiftool:
# searching files (done before converting the files, so just listed for reproduction):
$WorkingFiles = #(Get-ChildItem -Path D:\MyPictures\Testfiles -Filter *.tif | ForEach-Object {
[PSCustomObject]#{
SourceFullName = $_.FullName
JPEGFullName = $_.FullName -Replace 'tif$','jpg'
}
})
# Then, converting is done. PowerShell will wait until every jpeg is successfully created.
# + + + + The problem occurs somewhere after this line + + + +
# Creating the exiftool process:
$psi = New-Object System.Diagnostics.ProcessStartInfo
$psi.FileName = .\exiftool.exe
$psi.Arguments = "-stay_open True -charset utf8 -# -"
$psi.UseShellExecute = $false
$psi.RedirectStandardInput = $true
$psi.RedirectStandardOutput = $true
$psi.RedirectStandardError = $true
$exiftoolproc = [System.Diagnostics.Process]::Start($psi)
# creating the string argument for every file, then pass it over to exiftool:
for($i=0; $i -lt $WorkingFiles.length; $i++){
[string]$ArgList = "-All:all=`n-charset`nfilename=utf8`n-tagsFromFile`n$($WorkingFiles[$i].SourceFullName)`n-EXIF:All`n-charset`nfilename=utf8`n$($WorkingFiles[$i].JPEGFullName)"
# using -overwrite_original makes no difference
# Also, just as good as above code:
# [string]$ArgList = "-All:All=`n-EXIF:XResolution=300`n-EXIF:YResolution=300`n-charset`nfilename=utf8`n-overwrite_original`n$($WorkingFiles[$i].JPEGFullName)"
$exiftoolproc.StandardInput.WriteLine("$ArgList`n-execute`n")
# no difference using start-sleep:
# Start-Sleep -Milliseconds 25
}
# close exiftool:
$exiftoolproc.StandardInput.WriteLine("-stay_open`nFalse`n")
# read StandardError and StandardOutput of exiftool, then print it:
[array]$outputerror = #($exiftoolproc.StandardError.ReadToEnd().Split("`r`n",[System.StringSplitOptions]::RemoveEmptyEntries))
[string]$outputout = $exiftoolproc.StandardOutput.ReadToEnd()
$outputout = $outputout -replace '========\ ','' -replace '\[1/1]','' -replace '\ \r\n\ \ \ \ '," - " -replace '{ready}\r\n',''
[array]$outputout = #($outputout.Split("`r`n",[System.StringSplitOptions]::RemoveEmptyEntries))
Write-Output "Errors:"
foreach($i in $outputerror){
Write-Output $i
}
Write-Output "Standard output:"
foreach($i in $outputout){
Write-Output $i
}
If you want to reproduce but do not have/want that many files, there is also a simpler way: let exiftool print out its version number 600 times:
$psi = New-Object System.Diagnostics.ProcessStartInfo
$psi.FileName = .\exiftool.exe
$psi.Arguments = "-stay_open True -charset utf8 -# -"
$psi.UseShellExecute = $false
$psi.RedirectStandardInput = $true
$psi.RedirectStandardOutput = $true
$psi.RedirectStandardError = $true
$exiftoolproc = [System.Diagnostics.Process]::Start($psi)
for($i=0; $i -lt 600; $i++){
try{
$exiftoolproc.StandardInput.WriteLine("-ver`n-execute`n")
Write-Output "Success:`t$i"
}catch{
Write-Output "Failed:`t$i"
}
}
# close exiftool:
try{
$exiftoolproc.StandardInput.WriteLine("-stay_open`nFalse`n")
}catch{
Write-Output "Could not close exiftool!"
}
[array]$outputerror = #($exiftoolproc.StandardError.ReadToEnd().Split("`r`n",[System.StringSplitOptions]::RemoveEmptyEntries))
[array]$outputout = #($exiftoolproc.StandardOutput.ReadToEnd().Split("`r`n",[System.StringSplitOptions]::RemoveEmptyEntries))
Write-Output "Errors:"
foreach($i in $outputerror){
Write-Output $i
}
Write-Output "Standard output:"
foreach($i in $outputout){
Write-Output $i
}
As far as I could test, it all goes well, as long as you stay < 115 files. If you go above, the 114th JPEG gets proper metadata, but exiftool stops to work after this one - it idles, and my script does so, too. I can reproduce this with different files, paths, and exiftool commands.
Neither the StandardOutput nor the StandardError show any irregularities even with exiftool's -verbose-flag - of course, they would not, as I have to kill exiftool to get them to show up.
Running ISE's / VSCode's debugger shows nothing. Exiftool's window (only showing up when debugging) shows nothing.
Is there some hard limit on commands run with System.Diagnostics.Process, is this a problem with exiftool or is this simply due to my incompetence to use something outside the most basic Powershell cmdlets? Or maybe the better question would be: How can I properly debug this?
Powershell is 5.1, exiftool is 10.80 (production) - 10.94 (latest).
After messing around with different variants of $ArgList, I found out that there is no difference when using different file commands, but using commands that produce less StdOut (like -ver) resulted in more iterations. Therefore, I took an educated guess that the output buffer is the culprit.
As per Mark Byers' answer to "ProcessStartInfo hanging on “WaitForExit”? Why?":
The problem is that if you redirect StandardOutput and/or StandardError the internal buffer can become full. [...]
The solution is to use asynchronous reads to ensure that the buffer doesn't get full.
Then, it was just a matter of searching for the right things. I found that Alexander Obersht's answer to "How to capture process output asynchronously in powershell?" provides almost everything that I needed.
The script now looks like this:
# searching files (done before converting the files, so just listed for reproduction):
$WorkingFiles = #(Get-ChildItem -Path D:\MyPictures\Testfiles -Filter *.tif | ForEach-Object {
[PSCustomObject]#{
SourceFullName = $_.FullName
JPEGFullName = $_.FullName -Replace 'tif$','jpg'
}
})
# Then, converting is done. PowerShell will wait until every jpeg is successfully created.
# Creating the exiftool process:
$psi = New-Object System.Diagnostics.ProcessStartInfo
$psi.FileName = .\exiftool.exe
$psi.Arguments = "-stay_open True -charset utf8 -# -"
$psi.UseShellExecute = $false
$psi.RedirectStandardInput = $true
$psi.RedirectStandardOutput = $true
$psi.RedirectStandardError = $true
# + + + + NEW STUFF (1/2) HERE: + + + +
# Creating process object.
$exiftoolproc = New-Object -TypeName System.Diagnostics.Process
$exiftoolproc.StartInfo = $psi
# Creating string builders to store stdout and stderr.
$exiftoolStdOutBuilder = New-Object -TypeName System.Text.StringBuilder
$exiftoolStdErrBuilder = New-Object -TypeName System.Text.StringBuilder
# Adding event handers for stdout and stderr.
$exiftoolScripBlock = {
if (-not [String]::IsNullOrEmpty($EventArgs.Data)){
$Event.MessageData.AppendLine($EventArgs.Data)
}
}
$exiftoolStdOutEvent = Register-ObjectEvent -InputObject $exiftoolproc -Action $exiftoolScripBlock -EventName 'OutputDataReceived' -MessageData $exiftoolStdOutBuilder
$exiftoolStdErrEvent = Register-ObjectEvent -InputObject $exiftoolproc -Action $exiftoolScripBlock -EventName 'ErrorDataReceived' -MessageData $exiftoolStdErrBuilder
[Void]$exiftoolproc.Start()
$exiftoolproc.BeginOutputReadLine()
$exiftoolproc.BeginErrorReadLine()
# + + + + END OF NEW STUFF (1/2) + + + +
# creating the string argument for every file, then pass it over to exiftool:
for($i=0; $i -lt $WorkingFiles.length; $i++){
[string]$ArgList = "-All:all=`n-charset`nfilename=utf8`n-tagsFromFile`n$($WorkingFiles[$i].SourceFullName)`n-EXIF:All`n-charset`nfilename=utf8`n$($WorkingFiles[$i].JPEGFullName)"
# using -overwrite_original makes no difference
# Also, just as good as above code:
# [string]$ArgList = "-All:All=`n-EXIF:XResolution=300`n-EXIF:YResolution=300`n-charset`nfilename=utf8`n-overwrite_original`n$($WorkingFiles[$i].JPEGFullName)"
$exiftoolproc.StandardInput.WriteLine("$ArgList`n-execute`n")
}
# + + + + NEW STUFF (2/2) HERE: + + + +
# close exiftool:
$exiftoolproc.StandardInput.WriteLine("-stay_open`nFalse`n")
$exiftoolproc.WaitForExit()
# Unregistering events to retrieve process output.
Unregister-Event -SourceIdentifier $exiftoolStdOutEvent.Name
Unregister-Event -SourceIdentifier $exiftoolStdErrEvent.Name
# read StandardError and StandardOutput of exiftool, then print it:
[array]$outputerror = #($exiftoolStdErrBuilder.ToString().Trim().Split("`r`n",[System.StringSplitOptions]::RemoveEmptyEntries))
[string]$outputout = $exiftoolStdOutBuilder.ToString().Trim() -replace '========\ ','' -replace '\[1/1]','' -replace '\ \r\n\ \ \ \ '," - " -replace '{ready}\r\n',''
[array]$outputout = #($outputout.Split("`r`n",[System.StringSplitOptions]::RemoveEmptyEntries))
# + + + + END OF NEW STUFF (2/2) + + + +
Write-Output "Errors:"
foreach($i in $outputerror){
Write-Output $i
}
Write-Output "Standard output:"
foreach($i in $outputout){
Write-Output $i
}
I can confirm that it works for many, many files (at least 1600).

Limit number of Start-Process running in powershell

I have tried to limit the number of Start-Process running from a Powershell, but I can't seem to get it to work.
I tried to follow this process: https://exchange12rocks.org/2015/05/24/how-to-limit-a-number-of-powershell-jobs-running-simultaneously/ and Run N parallel jobs in powershell
But these are for Jobs not Processes, and I would like to remove the -Wait from the Start-Process
My concern with the script is that if there are 1000 audio files in the folder, then FFMpeg would crash the system.
# get the folder for conversion
function mbAudioConvert {
[Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") | Out-Null
[System.Windows.Forms.Application]::EnableVisualStyles()
$fileBrowser = New-Object System.Windows.Forms.FolderBrowserDialog
$fileBrowser.SelectedPath = "B:\"
$fileBrowser.ShowNewFolderButton = $false
$fileBrowser.Description = "Select the folder with the audio which you wish to convert to Avid DNxHD 120 25P 48kHz"
$mbLoop = $true
$mbCount = 0001
$mbMaxJob = 4
while( $mbLoop ) {
if( $fileBrowser.ShowDialog() -eq "OK" ) {
$mbLoop = $false
$mbImage = ( Get-Item -Path "C:\Users\user\Desktop\lib\AudioOnly.jpg" )
$mbff32 = ( Get-Item -Path "C:\Users\user\Desktop\lib\ffmpeg32.exe" )
$mbff64 = ( Get-Item -Path "C:\Users\user\Desktop\lib\ffmpeg64.exe" )
$mbFolder = $fileBrowser.SelectedPath
$mbItemInc = ( ls $mbFolder\* -Include *.mp3, *.MP3, *.wav*, *.WAV*, *.ogg, *.OGG, *.wma, *.WMA, *.flac, *.FLAC, *.m4a, *.M4a )
$mbProgress = ( Get-ChildItem -Path $mbItemInc )
$mbHasRaw = ( $mbFolder + "\RAW" )
if( !( Test-Path -Path $mbHasRaw ) ) {
# force create a RAW folder if it does not exist
New-Item -ItemType Directory -Force -Path "$mbHasRaw"
}
foreach( $mbItem in $mbItemInc ) {
$mbCheck = $false
# output the progress
# Suggestion: You might want to consider updating this after starting the job and do the final update after running ex. Get-Job | Wait-Job to make the progress-bar stay until all processes are finished
#Write-Progress -Activity "Counting files for conversion" -status "Currently processing: $mbCount" -percentComplete ($mbCount / $mbItemInc.count*100)
# limit the run number
while ($mbCheck -eq $false) {
if( (Get-Job -State 'Running').count -lt $mbMaxJob) {
$mbScriptBlock = {
$mbItemName = $using:mbItem.BaseName
$mbNewItem = ( $using:mbFolder + "\RAW\" + $mbItemName + ".mov" )
$mbArgs = " -loop 1 -i $using:mbImage -i $using:mbItem -shortest -c:v dnxhd -b:v 120M -s 1920x1080 -pix_fmt yuv422p -r 25 -c:a pcm_s16le -ar 48k -af loudnorm=I=-12 $mbNewItem"
Start-Process -FilePath $using:mbff32 -ArgumentList $mbArgs -NoNewWindow -Wait
}
Start-Job -ScriptBlock $mbScriptBlock
#The job-thread doesn't know about $mbCount, better to increment it after starting the job
$mbCount++
$mbCheck = $true
}
}
}
} else {
$mbResponse = [System.Windows.Forms.MessageBox]::Show("You have exited out of the automation process!", "User has cancelled")
if( $mbResponse -eq "OK" ) {
return
}
}
}
$fileBrowser.SelectedPath
$fileBrowser.Dispose()
}
# call to function
mbAudioConvert
You edit $mbCheck, but the while loop is testing $Check which means the while-loop will never execute as $Check -eq $false is $false when $Check is not defined
Variables created outside the job script-block needs to be passed as an argument or you need to use the using: variable-scope to pass them in (PowerShell 3.0 or later). Added it to $mbItem, $mbff32, $mbImage and $mbFolder in the example which were not defined.
$mbMaxJob is not defined. The get running jobs-check will never be true and no processes will start
$mbCount not defined. Progress bar won't work
echo "$mbCount. $mbNewItem" won't return anything unless you use Receive-Job at some point to get the output from a job
Try:
#DemoValues
$mbItemInc = 1..10 | % { New-Item -ItemType File -Name "File$_.txt" }
$mbff32 = "something32"
$mbFolder = "c:\FooFolder"
$mbImage = "BarImage"
$mbMaxJob = 2
$mbCount = 0
foreach( $mbItem in $mbItemInc ) {
$mbCheck = $false
# output the progress
# Suggestion: You might want to consider updating this after starting the job and do the final update after running ex. Get-Job | Wait-Job to make the progress-bar stay until all processes are finished
Write-Progress -Activity "Counting files for conversion" -status "Currently processing: $mbCount" -percentComplete ($mbCount / $mbItemInc.count*100)
# limit the run number
while ($mbCheck -eq $false) {
if ((Get-Job -State 'Running').count -lt $mbMaxJob) {
$mbScriptBlock = {
Param($mbItem, $mbFolder, $mbImage, $mbff32)
#Filename without extension is already available in a FileInfo-object using the BaseName-property
$mbItemName = $mbItem.BaseName
$mbNewItem = ( $mbFolder + "\RAW\" + $mbItemName + ".mov" )
$mbArgs = "-loop 1 -i $mbImage -i $mbItem -shortest -c:v dnxhd -b:v 120M -s 1920x1080 -pix_fmt yuv422p -r 25 -c:a pcm_s16le -ar 48k -af loudnorm=I=-12 $mbNewItem"
Start-Process -FilePath $mbff32 -ArgumentList $mbArgs -NoNewWindow -Wait
}
Start-Job -ScriptBlock $mbScriptBlock -ArgumentList $mbItem, $mbFolder, $mbImage, $mbff32
#The job-thread doesn't know about $mbCount, better to increment it after starting the job
$mbCount++
$mbCheck = $true
}
}
}
I propose you my solution :
cls
$FormatNameJob="FFMPEG"
$maxConcurrentJobs = 100
$DirWithFile="C:\temp"
$DestFolder="C:\temp2"
$TraitmentDir="C:\temp\traitment"
$PathFFMpeg="C:\Temp\ffmpeg\ffmpeg\bin\ffmpeg.exe"
$HistoFolder="C:\temp\histo"
#create dir if dont exists
New-Item -ItemType Directory -Path $TraitmentDir -Force | Out-Null
New-Item -ItemType Directory -Path $DestFolder -Force | Out-Null
New-Item -ItemType Directory -Path $HistoFolder -Force | Out-Null
while ($true)
{
"Loop File"
$ListeFile=Get-ChildItem $DirWithFile -file -Filter "*.avi"
if ($ListeFile.count -eq 0 )
{
Start-Sleep -Seconds 1
continue
}
#loop file to trait
$ListeFile | %{
while ((get-job -State Running | where Name -eq $FormatNameJob ).Count -ge $maxConcurrentJobs)
{
Start-Sleep -Seconds 1
get-job -State Completed | where Name -eq $FormatNameJob | Remove-Job
}
"traitment file : {0}" -f $_.Name
#build newname and move item into traitment dir
$NewfileName="{0:yyyyMMddHHmmssfffff}_{1}" -f (get-date), $_.Name
$ItemTraitment=[io.path]::Combine($TraitmentDir, $NewfileName)
$mbNewItem ="{0}.mov" -f [io.path]::Combine($DestFolder, $_.BaseName)
Move-item $_.FullName -Destination $ItemTraitment
#build arguments and command
$mbArgs = " -loop 1 -i $ItemTraitment -shortest -c:v dnxhd -b:v 120M -s 1920x1080 -pix_fmt yuv422p -r 25 -c:a pcm_s16le -ar 48k -af loudnorm=I=-12 $mbNewItem"
$ScriptBlock=[scriptblock]::Create("Start-Process $PathFFMpeg -ArgumentList $mbArgs -Wait")
#add job
Start-Job -ScriptBlock $ScriptBlock -Name $FormatNameJob
}
}

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