Add -Size parameter to Crop-Clips.ps1 for custom output resolution with cover-scaling and center-cropping, supporting named presets (320p-4K) that transpose for portrait sources and custom "width:height" format, overriding the cropProfiles table to process videos of any resolution instead of skipping non-standard sizes, with even-number dimension rounding for yuv420p compatibility and skipping of videos already at requested size, plus fix output info reporting to skip when -WhatIf or declined -Confirm leaves
This commit is contained in:
1 parent
9088022ef7
commit
45f1396769
1 file changed
+101
-20
+101
-20
@@ -5,10 +5,20 @@
|
||||
Source directory containing video files.
|
||||
.PARAMETER Target
|
||||
Target directory for cropped video files.
|
||||
.PARAMETER Size
|
||||
Size of the output video. Uses the known non-standard generator resolutions table if not specified.
|
||||
Possible values: 320p, 384p, 480p, 720p, 900p, 1080p, HD, 1440p, 2K, 2160p, 4K, or custom size in format "width:height".
|
||||
When specified, the resolutions table is overridden and every video is scaled (preserving aspect
|
||||
ratio) so it fully covers the requested size, then centre-cropped to it, so videos of any
|
||||
resolution are processed instead of being skipped.
|
||||
Named presets are landscape (e.g. 1080p = 1920x1080) and are transposed for portrait sources
|
||||
(a 1080x1920 source with -Size 1080p stays 1080x1920); a custom "width:height" is used exactly as
|
||||
given. Videos already at the requested size are skipped.
|
||||
.DESCRIPTION
|
||||
Crops video clips to standard resolutions using ffmpeg. Handles the non-standard resolutions
|
||||
produced by LTX-2, Kling and Wan (see the $cropProfiles table), plus any square (1:1) source,
|
||||
which is scaled to 1080x1080. Videos with any other resolution are skipped.
|
||||
which is scaled to 1080x1080. Videos with any other resolution are skipped, unless -Size is
|
||||
used, which overrides the table and converts every video to the requested size.
|
||||
Supports -WhatIf and -Confirm for safe execution.
|
||||
#>
|
||||
[CmdletBinding(SupportsShouldProcess)]
|
||||
@@ -17,7 +27,10 @@ param(
|
||||
[string]$Source = '.',
|
||||
|
||||
[Parameter(Mandatory = $false)]
|
||||
[string]$Target = '.'
|
||||
[string]$Target = '.',
|
||||
|
||||
[Parameter(Mandatory = $false)]
|
||||
[string]$Size = $null
|
||||
)
|
||||
|
||||
Set-StrictMode -Version Latest
|
||||
@@ -133,6 +146,39 @@ $cropProfiles = @(
|
||||
@{ Width = 1284; Height = 716; Filter = 'scale=-2:720,crop=1280:720:(in_w-1280)/2:(in_h-720)/2' }
|
||||
)
|
||||
|
||||
# --- Optional -Size: overrides the $cropProfiles lookup for every video ---
|
||||
# Named presets are stored in landscape form and transposed for portrait sources, so a 9:16
|
||||
# clip stays 9:16. A custom 'width:height' is always used exactly as given. Parsing happens
|
||||
# once here so an invalid value fails before any video is touched.
|
||||
$sizePresets = @{
|
||||
'320p' = @{ Width = 570; Height = 320 }
|
||||
'384p' = @{ Width = 684; Height = 384 }
|
||||
'480p' = @{ Width = 854; Height = 480 }
|
||||
'720p' = @{ Width = 1280; Height = 720 }
|
||||
'900p' = @{ Width = 1600; Height = 900 }
|
||||
'1080p' = @{ Width = 1920; Height = 1080 }
|
||||
'HD' = @{ Width = 1920; Height = 1080 }
|
||||
'1440p' = @{ Width = 2560; Height = 1440 }
|
||||
'2K' = @{ Width = 2560; Height = 1440 }
|
||||
'2160p' = @{ Width = 3840; Height = 2160 }
|
||||
'4K' = @{ Width = 3840; Height = 2160 }
|
||||
}
|
||||
|
||||
$sizePreset = $null
|
||||
$sizeExact = $null
|
||||
|
||||
if (-not [string]::IsNullOrWhiteSpace($Size)) {
|
||||
if ($sizePresets.ContainsKey($Size)) {
|
||||
$sizePreset = $sizePresets[$Size]
|
||||
}
|
||||
elseif ($Size -match '^(\d+):(\d+)$') {
|
||||
$sizeExact = @{ Width = [int]$Matches[1]; Height = [int]$Matches[2] }
|
||||
}
|
||||
else {
|
||||
throw "Invalid -Size value: $Size. Possible values: 320p, 384p, 480p, 720p, 900p, 1080p, HD, 1440p, 2K, 2160p, 4K, or custom size in format ""width:height""."
|
||||
}
|
||||
}
|
||||
|
||||
$sourceFull = (Resolve-Path -LiteralPath $Source).Path
|
||||
$targetFull = (Resolve-Path -LiteralPath $Target).Path
|
||||
|
||||
@@ -162,22 +208,53 @@ foreach ($video in $videos) {
|
||||
$inQuality = Format-Quality -BitRate $inInfo.BitRate
|
||||
$inAudioText = if ($inInfo.HasAudio) { 'yes' } else { 'no' }
|
||||
|
||||
$cropProfile = $cropProfiles | Where-Object { $_.Width -eq $inInfo.Width -and $_.Height -eq $inInfo.Height } | Select-Object -First 1
|
||||
if ($sizePreset -or $sizeExact) {
|
||||
# -Size given: ignore the resolution table and fit every video to the requested size by
|
||||
# cover-scaling (force_original_aspect_ratio=increase keeps the pixel aspect ratio and
|
||||
# guarantees full coverage) then centre-cropping to the exact size.
|
||||
if ($sizeExact) {
|
||||
$outW = $sizeExact.Width
|
||||
$outH = $sizeExact.Height
|
||||
}
|
||||
elseif ($inInfo.Height -gt $inInfo.Width) {
|
||||
# Portrait source: use the portrait form of the preset.
|
||||
$outW = $sizePreset.Height
|
||||
$outH = $sizePreset.Width
|
||||
}
|
||||
else {
|
||||
$outW = $sizePreset.Width
|
||||
$outH = $sizePreset.Height
|
||||
}
|
||||
|
||||
if ($cropProfile) {
|
||||
$vf = $cropProfile.Filter
|
||||
}
|
||||
elseif ($inInfo.Width -eq $inInfo.Height -and $inInfo.Width -ne 1080) {
|
||||
# Any square (1:1) source other than 1080x1080 is scaled to 1080x1080; no crop needed.
|
||||
$vf = 'scale=1080:1080'
|
||||
# libx264/yuv420p requires even dimensions.
|
||||
if ($outW % 2) { $outW++ }
|
||||
if ($outH % 2) { $outH++ }
|
||||
|
||||
if ($inInfo.Width -eq $outW -and $inInfo.Height -eq $outH) {
|
||||
Write-Host "Skipping: $($inInfo.FileName) (already ${outW}x${outH})"
|
||||
continue
|
||||
}
|
||||
|
||||
$vf = 'scale={0}:{1}:force_original_aspect_ratio=increase,crop={0}:{1}' -f $outW, $outH
|
||||
}
|
||||
else {
|
||||
$vf = $null
|
||||
}
|
||||
$cropProfile = $cropProfiles | Where-Object { $_.Width -eq $inInfo.Width -and $_.Height -eq $inInfo.Height } | Select-Object -First 1
|
||||
|
||||
if (-not $vf) {
|
||||
Write-Host "Skipping: $($inInfo.FileName) (resolution $inRes)"
|
||||
continue
|
||||
if ($cropProfile) {
|
||||
$vf = $cropProfile.Filter
|
||||
}
|
||||
elseif ($inInfo.Width -eq $inInfo.Height -and $inInfo.Width -ne 1080) {
|
||||
# Any square (1:1) source other than 1080x1080 is scaled to 1080x1080; no crop needed.
|
||||
$vf = 'scale=1080:1080'
|
||||
}
|
||||
else {
|
||||
$vf = $null
|
||||
}
|
||||
|
||||
if (-not $vf) {
|
||||
Write-Host "Skipping: $($inInfo.FileName) (resolution $inRes)"
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "Input : Filename=$($inInfo.FileName) Resolution=$inRes FPS=$inFpsText Quality=$inQuality Audio=$inAudioText"
|
||||
@@ -244,13 +321,17 @@ foreach ($video in $videos) {
|
||||
Write-Host "WhatIf: Would crop video with ffmpeg $($formattedArgs -join ' ')"
|
||||
}
|
||||
|
||||
$outInfo = Get-VideoInfo -Path $outFinalPath
|
||||
$outRes = "{0}x{1}" -f $outInfo.Width, $outInfo.Height
|
||||
$outFpsText = if ($outInfo.Fps) { "{0:0.###}" -f $outInfo.Fps } else { 'unknown' }
|
||||
$outQuality = Format-Quality -BitRate $outInfo.BitRate
|
||||
$outAudioText = if ($outInfo.HasAudio) { 'yes' } else { 'no' }
|
||||
# Under -WhatIf (or -Confirm declined) nothing was written, so there is no output file to
|
||||
# probe; reporting is skipped rather than failing on the missing file.
|
||||
if (Test-Path -LiteralPath $outFinalPath) {
|
||||
$outInfo = Get-VideoInfo -Path $outFinalPath
|
||||
$outRes = "{0}x{1}" -f $outInfo.Width, $outInfo.Height
|
||||
$outFpsText = if ($outInfo.Fps) { "{0:0.###}" -f $outInfo.Fps } else { 'unknown' }
|
||||
$outQuality = Format-Quality -BitRate $outInfo.BitRate
|
||||
$outAudioText = if ($outInfo.HasAudio) { 'yes' } else { 'no' }
|
||||
|
||||
Write-Host "Output: Filename=$($outInfo.FileName) Resolution=$outRes FPS=$outFpsText Quality=$outQuality Audio=$outAudioText"
|
||||
Write-Host "Output: Filename=$($outInfo.FileName) Resolution=$outRes FPS=$outFpsText Quality=$outQuality Audio=$outAudioText"
|
||||
}
|
||||
|
||||
$processedCount++
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user