diff --git a/Clone-Video.ps1 b/Clone-Video.ps1 index e9a2859..d6d96c1 100644 --- a/Clone-Video.ps1 +++ b/Clone-Video.ps1 @@ -22,7 +22,11 @@ Path to PNG file for extracted frame. Can be absolute or relative to current directory. .png extension is added if not provided. If EndFrame is -1, extracts the last frame and excludes it from the cloned video. If EndFrame is not -1, extracts the frame at EndFrame index and excludes it from the cloned video. .PARAMETER Size Size of the output video. Uses the input video's size if not specified. - Possible values: 480p, 720p, 1080p, HD, 1440p, 2K, 2160p, 4K, or custom size in format "width:height". + Possible values: 320p, 384p, 480p, 720p, 900p, 1080p, HD, 1440p, 2K, 2160p, 4K, or custom size in format "width:height". + For standard named sizes the output preserves the source's aspect ratio: the reference + height is used as the short-side target and the other dimension is computed from the + source AR (landscape/square: target height = reference, width computed; portrait: target + width = reference, height computed). Custom sizes (width:height) are applied exactly. Forces a video re-encode, since scaling requires re-encoding. .PARAMETER FPS Frames per second for the output video. Uses the input video's FPS if not specified. @@ -203,8 +207,11 @@ if (-not [string]::IsNullOrWhiteSpace($Audio)) { } $sizeMap = @{ + '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 } @@ -215,16 +222,18 @@ $sizeMap = @{ $scaleWidth = 0 $scaleHeight = 0 +$standardSizeHeight = 0 if (-not [string]::IsNullOrWhiteSpace($Size)) { if ($sizeMap.ContainsKey($Size)) { $scaleWidth = $sizeMap[$Size].Width $scaleHeight = $sizeMap[$Size].Height + $standardSizeHeight = $sizeMap[$Size].Height } elseif ($Size -match '^(\d+):(\d+)$') { $scaleWidth = [int]$Matches[1] $scaleHeight = [int]$Matches[2] } else { - Write-Error ('Invalid Size value: {0}. Possible values: 480p, 720p, 1080p, HD, 1440p, 2K, 2160p, 4K, or custom size in format "width:height".' -f $Size) + Write-Error ('Invalid Size value: {0}. Possible values: 320p, 384p, 480p, 720p, 900p, 1080p, HD, 1440p, 2K, 2160p, 4K, or custom size in format "width:height".' -f $Size) exit 1 } } @@ -287,6 +296,18 @@ if ($LASTEXITCODE -eq 0) { } } +if ($standardSizeHeight -gt 0 -and -not $LTX -and $sourceWidth -gt 0 -and $sourceHeight -gt 0) { + if ($sourceWidth -ge $sourceHeight) { + $scaleHeight = $standardSizeHeight + $scaleWidth = [int][Math]::Round([double]$sourceWidth * $standardSizeHeight / $sourceHeight) + if ($scaleWidth % 2) { $scaleWidth++ } + } else { + $scaleWidth = $standardSizeHeight + $scaleHeight = [int][Math]::Round([double]$sourceHeight * $standardSizeHeight / $sourceWidth) + if ($scaleHeight % 2) { $scaleHeight++ } + } +} + if ($LTX) { $ltxKey = $Size if ($ltxKey -eq 'HD') { $ltxKey = '1080p' } diff --git a/Compare-Videos.ps1 b/Compare-Videos.ps1 index 1be56a4..28accb6 100644 --- a/Compare-Videos.ps1 +++ b/Compare-Videos.ps1 @@ -21,14 +21,38 @@ - 4 videos: 2x2 grid (1920x1080 each) Portrait mode (height > width, e.g., 1080x1920) - 1K HD: - - 2 videos: Side-by-side (608x1080 each, centered with padding to 1920x1080) - - 3 videos: Side-by-side (608x1080 each, centered with padding to 1920x1080) - - 4 videos: Side-by-side (480x854 each, centered with padding to 1920x1080) + - 2 videos: Side-by-side (608x1080 each, centered with padding to 1920x1080; or 1216x1080 with -Clip) + - 3 videos: Side-by-side (608x1080 each, centered with padding to 1920x1080; or 1824x1080 with -Clip) + - 4 videos: Side-by-side (480x854 each, centered with padding to 1920x1080; or 1920x854 with -Clip) Portrait mode (height > width, e.g., 1080x1920) - 4K UHD: - - 2 videos: Side-by-side (1216x2160 each, centered with padding to 3840x2160) - - 3 videos: Side-by-side (1216x2160 each, centered with padding to 3840x2160) - - 4 videos: Side-by-side (960x1708 each, centered with padding to 3840x2160) + - 2 videos: Side-by-side (1216x2160 each, centered with padding to 3840x2160; or 2432x2160 with -Clip) + - 3 videos: Side-by-side (1216x2160 each, centered with padding to 3840x2160; or 3648x2160 with -Clip) + - 4 videos: Side-by-side (960x1708 each, centered with padding to 3840x2160; or 3840x1708 with -Clip) + + With -Sideways, all videos are arranged in a single horizontal row. Adding -Clip removes + black padding: for landscape, cells are clipped to content height; for portrait, the + outer centering pad is omitted. + + Sideways mode - Landscape (width > height) - 1K HD: + - 2 videos: Cell 960x1080, content 960x540; total 1920x1080 (or 1920x540 with -Clip) + - 3 videos: Cell 640x1080, content 640x360; total 1920x1080 (or 1920x360 with -Clip) + - 4 videos: Cell 480x1080, content 480x270; total 1920x1080 (or 1920x270 with -Clip) + + Sideways mode - Landscape (width > height) - 4K UHD: + - 2 videos: Cell 1920x2160, content 1920x1080; total 3840x2160 (or 3840x1080 with -Clip) + - 3 videos: Cell 1280x2160, content 1280x720; total 3840x2160 (or 3840x720 with -Clip) + - 4 videos: Cell 960x2160, content 960x540; total 3840x2160 (or 3840x540 with -Clip) + + Sideways mode - Portrait (height > width) - 1K HD: + - 2 videos: Cell 608x1080; output 1920x1080 (or 1216x1080 with -Clip) + - 3 videos: Cell 608x1080; output 1920x1080 (or 1824x1080 with -Clip) + - 4 videos: Cell 480x854; output 1920x1080 (or 1920x854 with -Clip) + + Sideways mode - Portrait (height > width) - 4K UHD: + - 2 videos: Cell 1216x2160; output 3840x2160 (or 2432x2160 with -Clip) + - 3 videos: Cell 1216x2160; output 3840x2160 (or 3648x2160 with -Clip) + - 4 videos: Cell 960x1706; output 3840x2160 (or 3840x1706 with -Clip) Requires ffmpeg and ffprobe in system PATH. Supports -WhatIf for dry-run testing. @@ -55,8 +79,10 @@ .PARAMETER Clip Reduces the output height to match the actual scaled height of the sources, removing - the top/bottom black letterbox padding. Only applies when exactly 2 source videos are - provided; ignored otherwise. + the top/bottom black letterbox padding. For landscape, only applies when exactly 2 + source videos are provided; ignored otherwise. For portrait, applies when 2-4 source + videos are provided (all sources must be portrait); for 2-3 videos removes the outer + side padding, for 4 videos removes the outer top/bottom padding. Behavior depends on the orientations of the two sources: @@ -72,6 +98,13 @@ occupies 1280x720 on its S1/S2 side, portrait fits in the remaining 640x720). At 4K the same pair becomes 3840x1440 (landscape 2560x1440, portrait cell 1280x1440). +.PARAMETER Sideways + Arranges all videos in a single horizontal row. For landscape mode, replaces the default + 2x2 grid layout (3 and 4 videos) with a single row. For portrait mode, the layout is + the same as the default (already side-by-side). Can be combined with -Clip to remove + black padding: for landscape, the output height equals the natural content height; for + portrait, the outer centering pad is omitted. + .EXAMPLE .\Compare-Videos.ps1 -S1 original.mp4 -S2 enhanced.mp4 -Output comparison.mp4 @@ -101,6 +134,36 @@ .\Compare-Videos.ps1 -S1 a_720p.mp4 -S2 b_720p.mp4 -Output side-by-side.mp4 -Clip Compare two 720p (1280x720) videos with no top/bottom letterboxing: output is 1920x540. + +.EXAMPLE + .\Compare-Videos.ps1 -S1 a.mp4 -S2 b.mp4 -S3 c.mp4 -Output sideways.mp4 -Sideways + + Compare three landscape videos in a single row (three 640x1080 cells, total 1920x1080). + +.EXAMPLE + .\Compare-Videos.ps1 -S1 a.mp4 -S2 b.mp4 -S3 c.mp4 -S4 d.mp4 -Output sideways.mp4 -Sideways + + Compare four landscape videos in a single row (four 480x1080 cells, total 1920x1080). + +.EXAMPLE + .\Compare-Videos.ps1 -S1 a.mp4 -S2 b.mp4 -S3 c.mp4 -Output sideways_clip.mp4 -Sideways -Clip + + Three landscape videos in a single row, clipped to content height (three 640x360 cells, total 1920x360). + +.EXAMPLE + .\Compare-Videos.ps1 -S1 portrait1.mp4 -S2 portrait2.mp4 -Output portrait_clip.mp4 -Clip + + Two portrait videos without outer side padding: output is 1216x1080 (1K) or 2432x2160 (4K with -In4K). + +.EXAMPLE + .\Compare-Videos.ps1 -S1 p1.mp4 -S2 p2.mp4 -S3 p3.mp4 -Output portrait3_clip.mp4 -Clip + + Three portrait videos without outer side padding: output is 1824x1080 (1K). All sources must be portrait; if any is not, falls back to padded 1920x1080 layout. + +.EXAMPLE + .\Compare-Videos.ps1 -S1 p1.mp4 -S2 p2.mp4 -S3 p3.mp4 -S4 p4.mp4 -Output portrait4_clip.mp4 -Clip + + Four portrait videos without outer top/bottom padding: output is 1920x854 (1K). All sources must be portrait; if any is not, falls back to padded 1920x1080 layout. #> [CmdletBinding(SupportsShouldProcess)] param( @@ -123,7 +186,9 @@ param( [switch]$In4K, - [switch]$Clip + [switch]$Clip, + + [switch]$Sideways ) $ErrorActionPreference = "Stop" @@ -158,11 +223,6 @@ $videoCount = 2 if ($S4) { $videoCount = 4 } elseif ($S3) { $videoCount = 3 } -if ($Clip -and $videoCount -ne 2) { - Write-Warning "-Clip is only supported with exactly 2 source videos; ignoring." - $Clip = $false -} - # --- Detect orientation of S1 --- Write-Host "Detecting orientation of S1..." -ForegroundColor Cyan @@ -196,10 +256,18 @@ if ($isPortrait) { Write-Host "Landscape mode detected (${width}x${height}) - Output: $resolutionLabel" -ForegroundColor Green } +if ($Clip -and -not $Sideways) { + $clipAllowed = ($isPortrait -and $videoCount -le 4) -or (-not $isPortrait -and $videoCount -eq 2) + if (-not $clipAllowed) { + Write-Warning "-Clip without -Sideways is only supported with 2 landscape videos, or 2-4 portrait videos; ignoring." + $Clip = $false + } +} + # --- Clip mode: shrink output height to the actual scaled height of the sources --- $clipMixedLandscape = $false $clipMixedFilterComplex = $null -if ($Clip) { +if ($Clip -and -not $Sideways) { Write-Host "Probing S2 dimensions for -Clip..." -ForegroundColor Cyan $probeOutput2 = & ffprobe -v error -select_streams v:0 -show_entries stream=width,height -of csv=p=0 $S2 2>&1 if ($LASTEXITCODE -ne 0) { @@ -273,6 +341,15 @@ if ($Clip) { $h2Scaled = [int][Math]::Floor($clipCellWidth * $h2 / $w2) $clippedHeight = [Math]::Max($h1Scaled, $h2Scaled) if ($clippedHeight % 2) { $clippedHeight++ } # libx264/yuv420p requires even dimensions + + # 320p and 384p sources have non-standard widths that cause off-by-one rounding + # below a true 16:9 height. Force the exact 16:9 cell height for these sources. + if (-not $isPortrait -and ($height -eq 320 -or $height -eq 384 -or $h2 -eq 320 -or $h2 -eq 384)) { + $forced16x9Height = [int]($clipCellWidth * 9 / 16) + if ($forced16x9Height % 2) { $forced16x9Height++ } + $clippedHeight = $forced16x9Height + } + if ($clippedHeight -gt $outputHeight) { $clippedHeight = $outputHeight } if ($clippedHeight -lt $outputHeight) { @@ -287,7 +364,68 @@ if ($Clip) { $filterComplex = "" $inputArgs = @("-i", $S1, "-i", $S2) -if ($clipMixedLandscape) { +if ($Sideways) { + Write-Host "Sideways mode: arranging $videoCount videos in a single row." -ForegroundColor Cyan + + if ($isPortrait) { + if ($videoCount -le 3) { + $swCellW = [int]($outputWidth * 0.6333 / 2) # ~608 for 1K, ~1216 for 4K + $swCellH = $outputHeight + } else { + $swCellW = [int]($outputWidth / 4) # 480 for 1K, 960 for 4K + $swCellH = [int]($outputHeight * 0.79) # ~854 for 1K, ~1706 for 4K + if ($swCellH % 2) { $swCellH++ } + } + } else { + $swCellW = [int]($outputWidth / $videoCount) # 960/640/480 for 1K, 1920/1280/960 for 4K + $swCellH = $outputHeight + } + + if ($Clip -and -not $isPortrait) { + $swCellH = [int]($swCellW * $height / $width) + if ($swCellH % 2) { $swCellH++ } + # 320p and 384p sources have non-standard widths that cause off-by-one rounding + # below a true 16:9 height. Force the exact 16:9 cell height for these sources. + if ($height -eq 320 -or $height -eq 384 -or $h2 -eq 320 -or $h2 -eq 384) { + $forced16x9Height = [int]($swCellW * 9 / 16) + if ($forced16x9Height % 2) { $forced16x9Height++ } + $swCellH = $forced16x9Height + } + if ($swCellH -gt $outputHeight) { $swCellH = $outputHeight } + Write-Host ("Sideways clip: cell height reduced to {0} ({1}x{0} per cell)" -f $swCellH, $swCellW) -ForegroundColor Yellow + } + + $swFilter = "scale=${swCellW}:${swCellH}:force_original_aspect_ratio=decrease,pad=${swCellW}:${swCellH}:(ow-iw)/2:(oh-ih)/2:black" + + if ($Clip) { + $swHstackEnd = "[outv]" + } else { + $swHstackEnd = "[stacked];[stacked]pad=${outputWidth}:${outputHeight}:(ow-iw)/2:(oh-ih)/2:black[outv]" + } + + switch ($videoCount) { + 2 { + $filterComplex = "[0:v]${swFilter}[v0];" + + "[1:v]${swFilter}[v1];" + + "[v0][v1]hstack=inputs=2${swHstackEnd}" + } + 3 { + $inputArgs += @("-i", $S3) + $filterComplex = "[0:v]${swFilter}[v0];" + + "[1:v]${swFilter}[v1];" + + "[2:v]${swFilter}[v2];" + + "[v0][v1][v2]hstack=inputs=3${swHstackEnd}" + } + 4 { + $inputArgs += @("-i", $S3, "-i", $S4) + $filterComplex = "[0:v]${swFilter}[v0];" + + "[1:v]${swFilter}[v1];" + + "[2:v]${swFilter}[v2];" + + "[3:v]${swFilter}[v3];" + + "[v0][v1][v2][v3]hstack=inputs=4${swHstackEnd}" + } + } +} elseif ($clipMixedLandscape) { # Mixed-orientation -Clip layout was prepared above; use it as-is. $filterComplex = $clipMixedFilterComplex } elseif ($isPortrait) { @@ -295,34 +433,98 @@ if ($clipMixedLandscape) { $cellWidth2 = [int]($outputWidth * 0.6333 / 2) # ~608 for 1K, ~1216 for 4K $cellWidth3 = [int]($outputWidth * 0.6333 / 2) # ~608 for 1K, ~1216 for 4K $cellWidth4 = [int]($outputWidth / 4) # 480 for 1K, 960 for 4K - $cellHeight4 = [int]($outputHeight * 0.79) # ~854 for 1K, ~1708 for 4K + $cellHeight4 = [int][Math]::Ceiling($cellWidth4 * 16.0 / 9.0) # 9:16 ratio: 854 for 1K, 1708 for 4K + if ($cellHeight4 % 2) { $cellHeight4++ } switch ($videoCount) { 2 { - # 2 videos side-by-side, centered - $filterComplex = "[0:v]scale=${cellWidth2}:${outputHeight}:force_original_aspect_ratio=decrease,pad=${cellWidth2}:${outputHeight}:(ow-iw)/2:(oh-ih)/2:black[v0];" + - "[1:v]scale=${cellWidth2}:${outputHeight}:force_original_aspect_ratio=decrease,pad=${cellWidth2}:${outputHeight}:(ow-iw)/2:(oh-ih)/2:black[v1];" + - "[v0][v1]hstack=inputs=2[stacked];" + - "[stacked]pad=${outputWidth}:${outputHeight}:(ow-iw)/2:(oh-ih)/2:black[outv]" + # 2 videos side-by-side, centered (or unpadded with -Clip) + if ($Clip) { + $clippedWidth = $cellWidth2 * 2 + Write-Host ("Clip mode (portrait): output width reduced from {0} to {1} ({1}x{2})" -f $outputWidth, $clippedWidth, $outputHeight) -ForegroundColor Yellow + $filterComplex = "[0:v]scale=${cellWidth2}:${outputHeight}:force_original_aspect_ratio=decrease,pad=${cellWidth2}:${outputHeight}:(ow-iw)/2:(oh-ih)/2:black[v0];" + + "[1:v]scale=${cellWidth2}:${outputHeight}:force_original_aspect_ratio=decrease,pad=${cellWidth2}:${outputHeight}:(ow-iw)/2:(oh-ih)/2:black[v1];" + + "[v0][v1]hstack=inputs=2[outv]" + } else { + $filterComplex = "[0:v]scale=${cellWidth2}:${outputHeight}:force_original_aspect_ratio=decrease,pad=${cellWidth2}:${outputHeight}:(ow-iw)/2:(oh-ih)/2:black[v0];" + + "[1:v]scale=${cellWidth2}:${outputHeight}:force_original_aspect_ratio=decrease,pad=${cellWidth2}:${outputHeight}:(ow-iw)/2:(oh-ih)/2:black[v1];" + + "[v0][v1]hstack=inputs=2[stacked];" + + "[stacked]pad=${outputWidth}:${outputHeight}:(ow-iw)/2:(oh-ih)/2:black[outv]" + } } 3 { - # 3 videos side-by-side, centered + # 3 videos side-by-side, centered (or unpadded with -Clip if all sources are portrait) $inputArgs += @("-i", $S3) - $filterComplex = "[0:v]scale=${cellWidth3}:${outputHeight}:force_original_aspect_ratio=decrease,pad=${cellWidth3}:${outputHeight}:(ow-iw)/2:(oh-ih)/2:black[v0];" + - "[1:v]scale=${cellWidth3}:${outputHeight}:force_original_aspect_ratio=decrease,pad=${cellWidth3}:${outputHeight}:(ow-iw)/2:(oh-ih)/2:black[v1];" + - "[2:v]scale=${cellWidth3}:${outputHeight}:force_original_aspect_ratio=decrease,pad=${cellWidth3}:${outputHeight}:(ow-iw)/2:(oh-ih)/2:black[v2];" + - "[v0][v1][v2]hstack=inputs=3[stacked];" + - "[stacked]pad=${outputWidth}:${outputHeight}:(ow-iw)/2:(oh-ih)/2:black[outv]" + $applyPortraitClip = $false + if ($Clip) { + $probeOutput3 = & ffprobe -v error -select_streams v:0 -show_entries stream=width,height -of csv=p=0 $S3 2>&1 + if ($LASTEXITCODE -ne 0) { + Write-Error "Failed to probe video dimensions for S3: $probeOutput3" + exit 1 + } + $dim3 = $probeOutput3 -split ',' + $w3 = [int]$dim3[0]; $h3 = [int]$dim3[1] + if (($h2 -gt $w2) -and ($h3 -gt $w3)) { + $applyPortraitClip = $true + } else { + Write-Warning "-Clip (portrait 3-video): not all sources are portrait; falling back to padded layout." + } + } + if ($applyPortraitClip) { + $clippedWidth = $cellWidth3 * 3 + Write-Host ("Clip mode (portrait): output width reduced from {0} to {1} ({1}x{2})" -f $outputWidth, $clippedWidth, $outputHeight) -ForegroundColor Yellow + $filterComplex = "[0:v]scale=${cellWidth3}:${outputHeight}:force_original_aspect_ratio=decrease,pad=${cellWidth3}:${outputHeight}:(ow-iw)/2:(oh-ih)/2:black[v0];" + + "[1:v]scale=${cellWidth3}:${outputHeight}:force_original_aspect_ratio=decrease,pad=${cellWidth3}:${outputHeight}:(ow-iw)/2:(oh-ih)/2:black[v1];" + + "[2:v]scale=${cellWidth3}:${outputHeight}:force_original_aspect_ratio=decrease,pad=${cellWidth3}:${outputHeight}:(ow-iw)/2:(oh-ih)/2:black[v2];" + + "[v0][v1][v2]hstack=inputs=3[outv]" + } else { + $filterComplex = "[0:v]scale=${cellWidth3}:${outputHeight}:force_original_aspect_ratio=decrease,pad=${cellWidth3}:${outputHeight}:(ow-iw)/2:(oh-ih)/2:black[v0];" + + "[1:v]scale=${cellWidth3}:${outputHeight}:force_original_aspect_ratio=decrease,pad=${cellWidth3}:${outputHeight}:(ow-iw)/2:(oh-ih)/2:black[v1];" + + "[2:v]scale=${cellWidth3}:${outputHeight}:force_original_aspect_ratio=decrease,pad=${cellWidth3}:${outputHeight}:(ow-iw)/2:(oh-ih)/2:black[v2];" + + "[v0][v1][v2]hstack=inputs=3[stacked];" + + "[stacked]pad=${outputWidth}:${outputHeight}:(ow-iw)/2:(oh-ih)/2:black[outv]" + } } 4 { - # 4 videos side-by-side, centered + # 4 videos side-by-side, centered (or unpadded with -Clip if all sources are portrait) $inputArgs += @("-i", $S3, "-i", $S4) - $filterComplex = "[0:v]scale=${cellWidth4}:${cellHeight4}:force_original_aspect_ratio=decrease,pad=${cellWidth4}:${cellHeight4}:(ow-iw)/2:(oh-ih)/2:black[v0];" + - "[1:v]scale=${cellWidth4}:${cellHeight4}:force_original_aspect_ratio=decrease,pad=${cellWidth4}:${cellHeight4}:(ow-iw)/2:(oh-ih)/2:black[v1];" + - "[2:v]scale=${cellWidth4}:${cellHeight4}:force_original_aspect_ratio=decrease,pad=${cellWidth4}:${cellHeight4}:(ow-iw)/2:(oh-ih)/2:black[v2];" + - "[3:v]scale=${cellWidth4}:${cellHeight4}:force_original_aspect_ratio=decrease,pad=${cellWidth4}:${cellHeight4}:(ow-iw)/2:(oh-ih)/2:black[v3];" + - "[v0][v1][v2][v3]hstack=inputs=4[stacked];" + - "[stacked]pad=${outputWidth}:${outputHeight}:(ow-iw)/2:(oh-ih)/2:black[outv]" + $applyPortraitClip4 = $false + if ($Clip) { + $probeOutput3 = & ffprobe -v error -select_streams v:0 -show_entries stream=width,height -of csv=p=0 $S3 2>&1 + if ($LASTEXITCODE -ne 0) { + Write-Error "Failed to probe video dimensions for S3: $probeOutput3" + exit 1 + } + $dim3 = $probeOutput3 -split ',' + $w3 = [int]$dim3[0]; $h3 = [int]$dim3[1] + $probeOutput4 = & ffprobe -v error -select_streams v:0 -show_entries stream=width,height -of csv=p=0 $S4 2>&1 + if ($LASTEXITCODE -ne 0) { + Write-Error "Failed to probe video dimensions for S4: $probeOutput4" + exit 1 + } + $dim4 = $probeOutput4 -split ',' + $w4 = [int]$dim4[0]; $h4 = [int]$dim4[1] + if (($h2 -gt $w2) -and ($h3 -gt $w3) -and ($h4 -gt $w4)) { + $applyPortraitClip4 = $true + } else { + Write-Warning "-Clip (portrait 4-video): not all sources are portrait; falling back to padded layout." + } + } + if ($applyPortraitClip4) { + Write-Host ("Clip mode (portrait): output height reduced from {0} to {1} ({2}x{1})" -f $outputHeight, $cellHeight4, $outputWidth) -ForegroundColor Yellow + $filterComplex = "[0:v]scale=${cellWidth4}:${cellHeight4}:force_original_aspect_ratio=decrease,pad=${cellWidth4}:${cellHeight4}:(ow-iw)/2:(oh-ih)/2:black[v0];" + + "[1:v]scale=${cellWidth4}:${cellHeight4}:force_original_aspect_ratio=decrease,pad=${cellWidth4}:${cellHeight4}:(ow-iw)/2:(oh-ih)/2:black[v1];" + + "[2:v]scale=${cellWidth4}:${cellHeight4}:force_original_aspect_ratio=decrease,pad=${cellWidth4}:${cellHeight4}:(ow-iw)/2:(oh-ih)/2:black[v2];" + + "[3:v]scale=${cellWidth4}:${cellHeight4}:force_original_aspect_ratio=decrease,pad=${cellWidth4}:${cellHeight4}:(ow-iw)/2:(oh-ih)/2:black[v3];" + + "[v0][v1][v2][v3]hstack=inputs=4[outv]" + } else { + $filterComplex = "[0:v]scale=${cellWidth4}:${cellHeight4}:force_original_aspect_ratio=decrease,pad=${cellWidth4}:${cellHeight4}:(ow-iw)/2:(oh-ih)/2:black[v0];" + + "[1:v]scale=${cellWidth4}:${cellHeight4}:force_original_aspect_ratio=decrease,pad=${cellWidth4}:${cellHeight4}:(ow-iw)/2:(oh-ih)/2:black[v1];" + + "[2:v]scale=${cellWidth4}:${cellHeight4}:force_original_aspect_ratio=decrease,pad=${cellWidth4}:${cellHeight4}:(ow-iw)/2:(oh-ih)/2:black[v2];" + + "[3:v]scale=${cellWidth4}:${cellHeight4}:force_original_aspect_ratio=decrease,pad=${cellWidth4}:${cellHeight4}:(ow-iw)/2:(oh-ih)/2:black[v3];" + + "[v0][v1][v2][v3]hstack=inputs=4[stacked];" + + "[stacked]pad=${outputWidth}:${outputHeight}:(ow-iw)/2:(oh-ih)/2:black[outv]" + } } } } else { diff --git a/Crop-Clips.ps1 b/Crop-Clips.ps1 index 618db7d..e933cab 100644 --- a/Crop-Clips.ps1 +++ b/Crop-Clips.ps1 @@ -131,6 +131,24 @@ foreach ($video in $videos) { elseif ($inInfo.Width -eq 448 -and $inInfo.Height -eq 832) { $vf = 'scale=480:-2,crop=480:854:(in_w-480)/2:(in_h-854)/2' } + elseif ($inInfo.Width -eq 1600 -and $inInfo.Height -eq 896) { + $vf = 'scale=-2:900,crop=1600:900:(in_w-1600)/2:(in_h-900)/2' + } + elseif ($inInfo.Width -eq 640 -and $inInfo.Height -eq 384) { + $vf = 'scale=684:-2,crop=684:384:(in_w-684)/2:(in_h-384)/2' + } + elseif ($inInfo.Width -eq 576 -and $inInfo.Height -eq 320) { + $vf = 'crop=570:320:3:0' + } + elseif ($inInfo.Width -eq 896 -and $inInfo.Height -eq 1600) { + $vf = 'scale=900:-2,crop=900:1600:(in_w-900)/2:(in_h-1600)/2' + } + elseif ($inInfo.Width -eq 384 -and $inInfo.Height -eq 640) { + $vf = 'scale=-2:684,crop=384:684:(in_w-384)/2:(in_h-684)/2' + } + elseif ($inInfo.Width -eq 320 -and $inInfo.Height -eq 576) { + $vf = 'crop=320:570:0:3' + } if (-not $vf) { Write-Host "Skipping: $($inInfo.FileName) (resolution $inRes)"