From 9088022ef7a8a5ba08f916c7fb62087f2811a04d Mon Sep 17 00:00:00 2001 From: Claude Toupin Date: Thu, 22 Oct 2026 01:44:58 -0400 Subject: [PATCH] Add printf-style numbering token support to Get-LastFrame.ps1 for -Image parameter (e.g. frame_%04d.png), resolving token to actual 0-based frame index before passing to ffmpeg to avoid image2 sequence counter behavior, with automatic output directory creation when path does not exist and updated documentation matching Get-FramesRange.ps1 functionality --- Get-LastFrame.ps1 | 44 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/Get-LastFrame.ps1 b/Get-LastFrame.ps1 index ff30f01..028b0cb 100644 --- a/Get-LastFrame.ps1 +++ b/Get-LastFrame.ps1 @@ -5,6 +5,10 @@ Path to the input video file. Can be absolute or relative to current directory. .PARAMETER Image Path to the output PNG image file. Can be absolute or relative to current directory. + Supports a printf-style numbering token (e.g. frame_%04d.png or C:\Output\frame_%04d.png), which is + replaced with the extracted 0-based frame index, zero-padded to the width given in the token + (%d = no padding, %04d = 4 digits). Without a token the path is used literally. + The output directory is created when it does not exist. .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. @@ -12,6 +16,7 @@ .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. + Supports a printf-style numbering token in -Image, like Get-FramesRange.ps1. Supports -WhatIf and -Confirm for safe execution. #> [CmdletBinding(SupportsShouldProcess)] @@ -64,9 +69,42 @@ $frameToExtract = if ($Frame -lt 0) { Write-Host ('Total frames: {0} - extracting frame index {1}' -f $totalFramesInt, $frameToExtract) -if ($PSCmdlet.ShouldProcess($Image, ('Extract frame {0} to PNG' -f $frameToExtract))) { +# Resolve any printf-style numbering token (%d, %04d) in -Image to the extracted frame +# index. The substitution is done here rather than left to ffmpeg: ffmpeg would treat the +# token as an image2 sequence counter starting at 1 instead of the actual video frame index. +$ImagePath = $Image +$tokenPattern = [regex]'%(\d*)d' +while ($ImagePath -match $tokenPattern) { + $padWidth = if ($Matches[1]) { [int]$Matches[1] } else { 0 } + $ImagePath = $tokenPattern.Replace($ImagePath, ([string]$frameToExtract).PadLeft($padWidth, '0'), 1) +} + +if ($ImagePath -ne $Image) { + Write-Host ('Output pattern resolved to: {0}' -f $ImagePath) +} + +$outputDir = Split-Path -Parent $ImagePath +if ($outputDir -and -not (Test-Path -LiteralPath $outputDir)) { + if ($PSCmdlet.ShouldProcess($outputDir, 'Create output directory')) { + New-Item -ItemType Directory -Path $outputDir -Force | Out-Null + Write-Host ('Created output directory: {0}' -f $outputDir) + } +} + +if ($PSCmdlet.ShouldProcess($ImagePath, ('Extract frame {0} to PNG' -f $frameToExtract))) { $videoFilter = 'select=eq(n\,{0})' -f $frameToExtract - $ffmpegStderr = & ffmpeg -y -i $VideoPath -vf $videoFilter -vsync vfr -frames:v 1 -q:v 1 $Image 2>&1 + + $ffmpegArgs = @( + '-y' + '-i', $VideoPath + '-vf', $videoFilter + '-vsync', 'vfr' + '-frames:v', '1' + '-q:v', '1' + '--', $ImagePath + ) + + $ffmpegStderr = & ffmpeg @ffmpegArgs 2>&1 if ($LASTEXITCODE -ne 0) { $ffmpegStderr | ForEach-Object { Write-Error $_ } @@ -74,5 +112,5 @@ if ($PSCmdlet.ShouldProcess($Image, ('Extract frame {0} to PNG' -f $frameToExtra exit 1 } - Write-Host ('Frame saved to: {0}' -f $Image) + Write-Host ('Frame saved to: {0}' -f $ImagePath) }