Fix -Frames negative range calculation to extract last N frames correctly (off-by-one error), add validation to reject invalid '-%' and '-0' formats with exit code 1, change '%' alone to extract single last frame instead of range, remove duplicate last frame from PNG sequence concat list, update Get-LastFrame.ps1 and Get-Frame.ps1 to clamp out-of-range frame indices instead of erroring and improve error messages with inline stderr output, add -y flag and -- separator to ffprobe calls, replace deprecated

This commit is contained in:
guru committed 2026-11-02 06:30:38 -05:00
1 parent cf41fb0ce5
commit 1227c6e153
13 files changed
+100 -86

No files matched your search

+18 -19
View File
@@ -12,7 +12,8 @@
.PARAMETER Frame
0-based frame index to extract. Negative values count from the end:
-1 extracts the last frame (default), -2 extracts second-to-last, etc.
The resulting frame index is clamped to the valid range (0 to total frames - 1).
The resulting frame index is clamped to the valid range (0 to total frames - 1): a positive value past the last frame
gives the last frame, and a negative value before the first frame gives frame 0.
.DESCRIPTION
Extracts a specific frame of a video file to a PNG image using ffmpeg.
Supports negative frame indices to count from the end of the video.
@@ -36,14 +37,14 @@ $ErrorActionPreference = 'Stop'
$VideoPath = $Video
if (-not (Test-Path $VideoPath)) {
if (-not (Test-Path -LiteralPath $VideoPath)) {
Write-Error ('Video file not found: {0}' -f $VideoPath)
exit 1
}
$PSNativeCommandUseErrorActionPreference = $false
$totalFrames = & ffprobe -v error -select_streams v:0 -count_frames -show_entries stream=nb_read_frames -of csv=p=0 $VideoPath 2>&1 | Select-Object -First 1
$totalFrames = & ffprobe -v error -select_streams v:0 -count_frames -show_entries stream=nb_read_frames -of csv=p=0 -- $VideoPath 2>&1 | Select-Object -First 1
if (-not $totalFrames -or $totalFrames -notmatch '^\d+$') {
Write-Error ('Could not determine frame count for: {0}' -f $VideoPath)
@@ -51,21 +52,15 @@ if (-not $totalFrames -or $totalFrames -notmatch '^\d+$') {
}
$totalFramesInt = [int]$totalFrames
if ($totalFramesInt -lt 1) {
Write-Error ('Video has no frames: {0}' -f $VideoPath)
exit 1
}
$maxFrameIndex = $totalFramesInt - 1
$frameToExtract = if ($Frame -lt 0) {
# Negative values count from the end
$calculatedFrame = $totalFramesInt + $Frame
# Clamp to valid range [0, maxFrameIndex]
[Math]::Max(0, [Math]::Min($calculatedFrame, $maxFrameIndex))
} else {
# Positive values are direct indices
if ($Frame -gt $maxFrameIndex) {
Write-Error ('Frame index {0} is out of range. Valid range: 0 to {1}' -f $Frame, $maxFrameIndex)
exit 1
}
$Frame
}
# Negative values count from the end; the result is clamped to [0, maxFrameIndex]
$requestedFrame = if ($Frame -lt 0) { $totalFramesInt + $Frame } else { $Frame }
$frameToExtract = [Math]::Max(0, [Math]::Min($requestedFrame, $maxFrameIndex))
Write-Host ('Total frames: {0} - extracting frame index {1}' -f $totalFramesInt, $frameToExtract)
@@ -98,7 +93,7 @@ if ($PSCmdlet.ShouldProcess($ImagePath, ('Extract frame {0} to PNG' -f $frameToE
'-y'
'-i', $VideoPath
'-vf', $videoFilter
'-vsync', 'vfr'
'-fps_mode', 'vfr'
'-frames:v', '1'
'-q:v', '1'
'--', $ImagePath
@@ -107,8 +102,12 @@ if ($PSCmdlet.ShouldProcess($ImagePath, ('Extract frame {0} to PNG' -f $frameToE
$ffmpegStderr = & ffmpeg @ffmpegArgs 2>&1
if ($LASTEXITCODE -ne 0) {
$ffmpegStderr | ForEach-Object { Write-Error $_ }
Write-Error 'ffmpeg failed to extract the last frame.'
Write-Error ('ffmpeg failed to extract frame {0}: {1}' -f $frameToExtract, (($ffmpegStderr | Out-String).Trim()))
exit 1
}
if (-not (Test-Path -LiteralPath $ImagePath)) {
Write-Error ('ffmpeg reported success but no image was written for frame {0}: {1}' -f $frameToExtract, $ImagePath)
exit 1
}