From de91c5c55bc9ba106a05edf978d54a38c2ba9486 Mon Sep 17 00:00:00 2001 From: "Joshua.Couper" Date: Sun, 18 Jan 2026 19:46:58 +0000 Subject: [PATCH] Update ConvertTo-mkvh265-GPU.ps1 --- ConvertTo-mkvh265-GPU.ps1 | 160 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) diff --git a/ConvertTo-mkvh265-GPU.ps1 b/ConvertTo-mkvh265-GPU.ps1 index e69de29..ecc78be 100644 --- a/ConvertTo-mkvh265-GPU.ps1 +++ b/ConvertTo-mkvh265-GPU.ps1 @@ -0,0 +1,160 @@ +# ================= CONFIG ================= +$SourcePath = "" +$LogPath = "C:\ffmpeg_encode.log" + +$CQ = 24 +$Preset = "slow" +$DryRun = $false +# ========================================= + +$videoExts = ".mp4",".mkv",".avi",".mov",".ts",".m2ts",".wmv",".flv",".webm" + +if (!(Test-Path $LogPath)) { + New-Item -ItemType File -Path $LogPath | Out-Null +} + +function Write-Log { + param ([string]$Message) + $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" + "$timestamp - $Message" | Tee-Object -FilePath $LogPath -Append +} + +function Format-Time { + param ([double]$Seconds) + if ($Seconds -lt 0) { return "00:00:00" } + [TimeSpan]::FromSeconds([math]::Round($Seconds)).ToString("hh\:mm\:ss") +} + +Write-Log "==== FFmpeg H.265 batch encode started ====" + +$files = Get-ChildItem $SourcePath -Recurse -File | + Where-Object { $_.Extension -in $videoExts } + +$total = $files.Count +$counter = 0 +$totalSaved = 0 + +# Stats +$encoded = 0 +$remuxed = 0 +$skipped = 0 +$failed = 0 + +$encodeTimes = @() + +foreach ($file in $files) { + $counter++ + + Write-Progress -Id 1 ` + -Activity "Overall Progress" ` + -Status "File $counter of $total" + + $input = $file.FullName + $output = Join-Path $file.DirectoryName ($file.BaseName + ".mkv") + $tmp = Join-Path $file.DirectoryName ($file.BaseName + ".tmp.mkv") + + $codec = & ffprobe -v error -select_streams v:0 ` + -show_entries stream=codec_name ` + -of default=noprint_wrappers=1:nokey=1 "$input" + + # Skip H.265 MKV + if (($codec -eq "hevc") -and ($file.Extension -eq ".mkv")) { + Write-Progress -Id 2 -ParentId 1 ` + -Activity "Current File" ` + -Status "SKIPPED (H.265 MKV): $($file.Name)" ` + -PercentComplete 100 + Write-Log "SKIPPED: $input" + $skipped++ + continue + } + + if ($DryRun) { + Write-Log "DRY-RUN: $input -> $output" + continue + } + + $origSize = (Get-Item $input).Length + + $duration = & ffprobe -v error -show_entries format=duration ` + -of default=noprint_wrappers=1:nokey=1 "$input" + $duration = [double]$duration + + $startTime = Get-Date + + $args = "-y -i `"$input`" -c:v hevc_nvenc -preset $Preset -rc vbr_hq -cq $CQ -c:a copy `"$tmp`" -progress pipe:1 -nostats" + + $psi = New-Object System.Diagnostics.ProcessStartInfo + $psi.FileName = "ffmpeg" + $psi.Arguments = $args + $psi.RedirectStandardOutput = $true + $psi.UseShellExecute = $false + $psi.CreateNoWindow = $true + + $p = New-Object System.Diagnostics.Process + $p.StartInfo = $psi + $p.Start() | Out-Null + + while (-not $p.HasExited) { + $line = $p.StandardOutput.ReadLine() + if ($line -match "^out_time_ms=(\d+)$") { + $doneSec = $matches[1] / 1000000 + $percent = [math]::Min([math]::Round(($doneSec / $duration) * 100), 100) + + $elapsed = (Get-Date) - $startTime + $rate = if ($doneSec -gt 0) { $elapsed.TotalSeconds / $doneSec } else { 0 } + $etaFile = ($duration - $doneSec) * $rate + + $avgEncode = if ($encodeTimes.Count -gt 0) { + ($encodeTimes | Measure-Object -Average).Average + } else { 0 } + + $etaQueue = ($total - $counter) * $avgEncode + + Write-Progress -Id 2 -ParentId 1 ` + -Activity "Current File" ` + -Status "Encoding: $($file.Name) ($percent%) | File ETA: $(Format-Time $etaFile) | Queue ETA: $(Format-Time $etaQueue)" ` + -PercentComplete $percent + } + } + + if (Test-Path $tmp) { + try { + Remove-Item $input -Force + Rename-Item $tmp $output -Force + + $newSize = (Get-Item $output).Length + $saved = $origSize - $newSize + $totalSaved += $saved + + $encodeTime = ((Get-Date) - $startTime).TotalSeconds + $encodeTimes += $encodeTime + + $encoded++ + Write-Log "ENCODED: $input | Saved $([math]::Round($saved/1MB,2)) MB" + } + catch { + Write-Log "FAILED TO REPLACE: $input" + $failed++ + } + } + else { + Write-Log "FAILED: $input" + $failed++ + } +} + +Write-Progress -Id 2 -Completed +Write-Progress -Id 1 -Completed + +$totalSavedMB = [math]::Round($totalSaved / 1MB, 2) + +Write-Host "" +Write-Host "===== SUMMARY =====" +Write-Host "Total files : $total" +Write-Host "Encoded : $encoded" +Write-Host "Skipped : $skipped" +Write-Host "Failed : $failed" +Write-Host "Space saved : $totalSavedMB MB" +Write-Host "===================" + +Write-Log "==== FFmpeg H.265 batch encode finished ===="