161 lines
4.5 KiB
PowerShell
161 lines
4.5 KiB
PowerShell
# ================= 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 ===="
|