Add -Reverse parameter to reencode video backward while keeping audio forward, and add 1284x716 to 1280x720 crop support in Crop-ClipsWan

This commit is contained in:
2026-06-07 16:44:21 -04:00
parent 201cc2625f
commit fcfa99dddc
2 changed files with 55 additions and 13 deletions
+41 -10
View File
@@ -33,6 +33,11 @@
Constant Rate Factor for video encoding (0-51, lower is better quality). When specified, overrides both the video bitrate and the default -crf 8 value. Only used when re-encoding is required.
.PARAMETER Metadata
When present, copies available JSON metadata from the source video file to the target video file.
.PARAMETER Reverse
When present, re-encodes the video so it plays backward (from the last selected frame to the first).
Honours all other parameters (StartFrame/EndFrame range, Size, FPS, CRF, container choice, etc.).
Audio is NOT reversed; it is processed exactly as it would be without -Reverse (trimmed/replaced/copied as applicable) and plays forward alongside the reversed video.
Forces a video re-encode, since the reverse filter cannot be applied to a stream-copied video.
.DESCRIPTION
This script uses ffmpeg to clone (copy) a video file with optional frame range and audio exclusion.
When Sequence is provided, it extracts a specific frame to PNG and then clones the video accordingly.
@@ -71,7 +76,10 @@ param(
[int]$CRF = -1,
[Parameter(Mandatory = $false)]
[switch]$Metadata
[switch]$Metadata,
[Parameter(Mandatory = $false)]
[switch]$Reverse
)
Set-StrictMode -Version Latest
@@ -173,7 +181,10 @@ if (-not [string]::IsNullOrWhiteSpace($Audio)) {
# rather than stream-copied.
$ffprobeExtAudioOutput = & ffprobe -v error -select_streams a:0 -show_entries stream=codec_name -of default=noprint_wrappers=1:nokey=1 -- $AudioPath 2>&1
if ($LASTEXITCODE -eq 0) {
$externalAudioCodec = ($ffprobeExtAudioOutput | Select-Object -First 1).Trim().ToLowerInvariant()
$extAudioLine = $ffprobeExtAudioOutput | Select-Object -First 1
if ($extAudioLine) {
$externalAudioCodec = ([string]$extAudioLine).Trim().ToLowerInvariant()
}
}
}
@@ -215,7 +226,8 @@ if ($ffprobeExit -ne 0) {
exit 1
}
$fpsRatio = ($ffprobeFpsOutput | Select-Object -First 1).Trim()
$fpsRatioLine = $ffprobeFpsOutput | Select-Object -First 1
$fpsRatio = if ($fpsRatioLine) { ([string]$fpsRatioLine).Trim() } else { '' }
if (-not $fpsRatio -or $fpsRatio -notmatch '^\d+/\d+$') {
Write-Error ('Could not determine frame rate for: {0}' -f $SourcePath)
@@ -235,7 +247,8 @@ if ($ffprobeExit -ne 0) {
exit 1
}
$totalFrames = ($ffprobeFramesOutput | Select-Object -First 1).Trim()
$totalFramesLine = $ffprobeFramesOutput | Select-Object -First 1
$totalFrames = if ($totalFramesLine) { ([string]$totalFramesLine).Trim() } else { '' }
if (-not $totalFrames -or $totalFrames -notmatch '^\d+$') {
Write-Error ('Could not determine frame count for: {0}' -f $SourcePath)
@@ -250,9 +263,12 @@ $ffprobeExit = $LASTEXITCODE
$videoBitrate = $null
if ($ffprobeExit -eq 0) {
$bitrateValue = ($ffprobeBitrateOutput | Select-Object -First 1).Trim()
if ($bitrateValue -and $bitrateValue -match '^\d+$') {
$videoBitrate = $bitrateValue
$bitrateLine = $ffprobeBitrateOutput | Select-Object -First 1
if ($bitrateLine) {
$bitrateValue = ([string]$bitrateLine).Trim()
if ($bitrateValue -and $bitrateValue -match '^\d+$') {
$videoBitrate = $bitrateValue
}
}
}
@@ -262,13 +278,19 @@ if ($ffprobeExit -eq 0) {
$ffprobeVideoCodecOutput = & ffprobe -v error -select_streams v:0 -show_entries stream=codec_name -of default=noprint_wrappers=1:nokey=1 -- $SourcePath 2>&1
$sourceVideoCodec = ''
if ($LASTEXITCODE -eq 0) {
$sourceVideoCodec = ($ffprobeVideoCodecOutput | Select-Object -First 1).Trim().ToLowerInvariant()
$vCodecLine = $ffprobeVideoCodecOutput | Select-Object -First 1
if ($vCodecLine) {
$sourceVideoCodec = ([string]$vCodecLine).Trim().ToLowerInvariant()
}
}
$ffprobeAudioCodecOutput = & ffprobe -v error -select_streams a:0 -show_entries stream=codec_name -of default=noprint_wrappers=1:nokey=1 -- $SourcePath 2>&1
$sourceAudioCodec = ''
if ($LASTEXITCODE -eq 0) {
$sourceAudioCodec = ($ffprobeAudioCodecOutput | Select-Object -First 1).Trim().ToLowerInvariant()
$aCodecLine = $ffprobeAudioCodecOutput | Select-Object -First 1
if ($aCodecLine) {
$sourceAudioCodec = ([string]$aCodecLine).Trim().ToLowerInvariant()
}
}
# A source codec not on the target container's stream-copy list forces a re-encode
@@ -364,6 +386,9 @@ if ($scaleWidth -gt 0) {
if ($FPS -gt 0) {
Write-Host (' FPS: {0}' -f $FPS)
}
if ($Reverse) {
Write-Host ' Reverse: video reversed, audio unchanged'
}
Write-Host (' Target: {0}' -f $TargetPath)
$ffmpegArgs = @(
@@ -375,7 +400,7 @@ if ($AudioPath) {
$ffmpegArgs += @('-i', $AudioPath)
}
$needsReencode = $scaleWidth -gt 0 -or $FPS -gt 0 -or $videoCodecMismatch
$needsReencode = $scaleWidth -gt 0 -or $FPS -gt 0 -or $videoCodecMismatch -or $Reverse
if ($StartFrame -gt 0 -or $actualEndFrame -lt $maxFrameIndex) {
$filterParts = @('select=between(n\,{0}\,{1})' -f $StartFrame, $actualEndFrame)
@@ -386,6 +411,9 @@ if ($StartFrame -gt 0 -or $actualEndFrame -lt $maxFrameIndex) {
if ($FPS -gt 0) {
$filterParts += ('fps={0}' -f $FPS)
}
if ($Reverse) {
$filterParts += 'reverse'
}
$videoFilter = $filterParts -join ','
$ffmpegArgs += @(
'-vf', $videoFilter
@@ -410,6 +438,9 @@ if ($StartFrame -gt 0 -or $actualEndFrame -lt $maxFrameIndex) {
if ($FPS -gt 0) {
$filterParts += ('fps={0}' -f $FPS)
}
if ($Reverse) {
$filterParts += 'reverse'
}
if ($filterParts.Count -gt 0) {
$videoFilter = $filterParts -join ','
$ffmpegArgs += @('-vf', $videoFilter)