全自动视频压缩画质省空间脚本
如下,需要ffmpeg并且设定路径,它自己会尝试遍历并生成压缩尺寸后的视频:
$ErrorActionPreference = 'Continue'
$ProgressPreference = 'SilentlyContinue'
$folders = @(
'F:\电影\神探狄仁杰1',
'F:\电影\神探狄仁杰2',
'F:\电影\神探狄仁杰3',
'F:\电影\神探狄仁杰4'
)
$exts = '*.mp4','*.mkv','*.avi','*.mov','*.wmv','*.webm','*.ts','*.m2ts'
Write-Host '===== Scanning =====' -ForegroundColor Cyan
$all = @()
foreach ($d in $folders) {
if (-not (Test-Path -LiteralPath $d)) { continue }
foreach ($e in $exts) {
$found = Get-ChildItem -LiteralPath $d -Filter $e -Recurse -ErrorAction SilentlyContinue
foreach ($f in $found) {
if ($f.FullName -notmatch '[\\/]compressed[\\/]') {
$all += $f
}
}
}
}
Write-Host ('Found ' + $all.Count + ' files') -ForegroundColor Cyan
# Phase 1: probe
Write-Host ''
Write-Host '===== Probing =====' -ForegroundColor Cyan
$todo = [System.Collections.ArrayList]::new()
$skipList = [System.Collections.ArrayList]::new()
$badList = [System.Collections.ArrayList]::new()
for ($i = 0; $i -lt $all.Count; $i++) {
$f = $all[$i]
Write-Host ('[' + ($i+1) + '/' + $all.Count + '] ' + $f.Name) -ForegroundColor Gray
$tmpFile = [System.IO.Path]::GetTempFileName()
$probeArgs = '-v','error','-select_streams','v:0','-show_entries','stream=width,height','-of','csv=s=x:p=0',$f.FullName
$p = Start-Process -FilePath ffprobe -ArgumentList $probeArgs -NoNewWindow -Wait -PassThru -RedirectStandardOutput $tmpFile
$info = (Get-Content -LiteralPath $tmpFile -Raw).Trim()
Remove-Item -LiteralPath $tmpFile -Force -ErrorAction SilentlyContinue
if ($p.ExitCode -ne 0 -or [string]::IsNullOrEmpty($info)) {
Write-Host ' FAIL: ffprobe' -ForegroundColor Red
[void]$badList.Add($f.Name)
continue
}
$parts = $info -split 'x'
$w = 0; $h = 0
try { $w = [int]($parts[0].Trim()); $h = [int]($parts[1].Trim()) } catch {}
if ($w -eq 0 -or $h -eq 0) {
Write-Host (' FAIL: bad res ' + $info) -ForegroundColor Red
[void]$badList.Add($f.Name)
continue
}
if ($w -le 3200 -and $h -le 1440) {
$resStr = $w.ToString() + 'x' + $h.ToString()
Write-Host (' SKIP: ' + $resStr) -ForegroundColor DarkGray
[void]$skipList.Add($f.Name + ' (' + $resStr + ')')
} else {
$resStr = $w.ToString() + 'x' + $h.ToString()
Write-Host (' QUEUE: ' + $resStr + ' -> 3200x1440') -ForegroundColor Yellow
[void]$todo.Add([PSCustomObject]@{File=$f;W=$w;H=$h})
}
}
Write-Host ''
Write-Host ('Result: todo=' + $todo.Count + ' skip=' + $skipList.Count + ' fail=' + $badList.Count) -ForegroundColor White
if ($todo.Count -eq 0) {
Write-Host 'Nothing to process.' -ForegroundColor Yellow
exit 0
}
# Phase 2: encode
Write-Host ''
Write-Host ('===== Encoding: ' + $todo.Count + ' videos =====') -ForegroundColor Cyan
$ok = 0
$ng = 0
$dup = 0
$sw = [System.Diagnostics.Stopwatch]::StartNew()
for ($i = 0; $i -lt $todo.Count; $i++) {
$item = $todo[$i]
$f = $item.File
$w = $item.W
$h = $item.H
$bn = [System.IO.Path]::GetFileNameWithoutExtension($f.Name)
$root = ''
foreach ($d in $folders) {
$dn = $d.Replace('/','\')
if ($f.FullName.StartsWith($dn, [StringComparison]::OrdinalIgnoreCase)) {
$root = $d
break
}
}
if ($root -eq '') { $root = $f.DirectoryName }
$outDir = Join-Path $root 'compressed'
$outPath = Join-Path $outDir ($bn + '.mkv')
Write-Host ('[' + ($i+1) + '/' + $todo.Count + '] ' + $f.Name + ' (' + $w + 'x' + $h + ')') -ForegroundColor Green
if (-not (Test-Path -LiteralPath $outDir)) {
New-Item -ItemType Directory -Path $outDir -Force | Out-Null
}
if ((Test-Path -LiteralPath $outPath) -and (Get-Item -LiteralPath $outPath).Length -gt 10240) {
Write-Host ' SKIP: output exists' -ForegroundColor DarkGray
$dup++
continue
}
Write-Host ' Encoding...' -ForegroundColor DarkYellow
$ffArgs = @(
'-i', $f.FullName,
'-map', '0',
'-c:v', 'hevc_amf',
'-rc', 'cqp',
'-qp_i', '22',
'-qp_p', '24',
'-qp_b', '24',
'-quality', 'quality',
'-vf', 'scale=3200:1440:force_original_aspect_ratio=decrease:flags=lanczos,format=yuv420p',
'-c:a', 'copy',
'-c:s', 'copy',
'-y',
$outPath
)
$fp = Start-Process -FilePath ffmpeg -ArgumentList $ffArgs -NoNewWindow -Wait -PassThru
if ($fp.ExitCode -eq 0 -and (Test-Path -LiteralPath $outPath) -and (Get-Item -LiteralPath $outPath).Length -gt 10240) {
$inMB = [math]::Round($f.Length / 1MB, 1)
$outMB = [math]::Round((Get-Item -LiteralPath $outPath).Length / 1MB, 1)
Write-Host (' DONE: ' + $bn + '.mkv in=' + $inMB + 'MB out=' + $outMB + 'MB') -ForegroundColor Green
$ok++
} else {
Write-Host (' FAIL: code=' + $fp.ExitCode) -ForegroundColor Red
Remove-Item -LiteralPath $outPath -Force -ErrorAction SilentlyContinue
$ng++
}
Write-Host ''
}
$sw.Stop()
Write-Host '========================================' -ForegroundColor Cyan
Write-Host ' Complete!' -ForegroundColor Cyan
Write-Host '========================================' -ForegroundColor Cyan
Write-Host (' Compressed : ' + $ok) -ForegroundColor Green
Write-Host (' Skipped : ' + ($skipList.Count + $dup)) -ForegroundColor Yellow
Write-Host (' Failed : ' + $ng) -ForegroundColor Red
$t = [math]::Floor($sw.Elapsed.TotalMinutes).ToString() + 'm ' + $sw.Elapsed.Seconds.ToString() + 's'
Write-Host (' Time : ' + $t) -ForegroundColor White
Write-Host ' Output : F:\电影\神探狄仁杰1\compressed\' -ForegroundColor Cyan
Write-Host '========================================' -ForegroundColor Cyan
