diff --git a/Get-Frame.cmd b/Get-Frame.cmd new file mode 100644 index 0000000..25cc29f --- /dev/null +++ b/Get-Frame.cmd @@ -0,0 +1,17 @@ +@echo off +for /f "delims=" %%i in ('where Get-Frame.ps1 2^>nul') do set "_script=%%i" & goto :found +echo Error: Get-Frame.ps1 not found in PATH +exit /b 1 +:found +set "_video=%~1" +set "_frame=%~2" +shift +shift +set "_rest=" +:loop +if "%~1"=="" goto done +set "_rest=%_rest% %1" +shift +goto loop +:done +pwsh.exe -NoProfile -ExecutionPolicy Bypass -File "%_script%" -Video "%_video%" -Frame "%_frame%"%_rest% diff --git a/Get-Frame.ps1 b/Get-Frame.ps1 new file mode 100644 index 0000000..2b24ee5 --- /dev/null +++ b/Get-Frame.ps1 @@ -0,0 +1,81 @@ +<# +.SYNOPSIS + Extracts a specific frame of a video file to a PNG image using Get-LastFrame.ps1, requiring -Video and -Frame parameters from that script. + The image filename used for the -Image parameter is automatically derived from the -Video filename, with a "-" followed by the "-Frame" value padded with zeros (maximum of 4 digits) inserted before the image file extension (e.g., P01.mp4 for frame 10 becomes P01-0010.png). +.PARAMETER Video + Path to the input video file. Can be absolute or relative to current directory. +.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). +#> +[CmdletBinding(SupportsShouldProcess)] +param( + [Parameter(Mandatory = $true)] + [string]$Video, + + [Parameter(Mandatory = $true)] + [int]$Frame +) + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +$VideoPath = $Video + +if (-not (Test-Path -LiteralPath $VideoPath)) { + Write-Error ('Video file not found: {0}' -f $VideoPath) + exit 1 +} + +$PSNativeCommandUseErrorActionPreference = $false + +# Probe total frame count to resolve negative frame indices and derive the output filename +$totalFramesOutput = & ffprobe -v error -select_streams v:0 -count_frames -show_entries stream=nb_read_frames -of csv=p=0 -- $VideoPath 2>&1 +$ffprobeExit = $LASTEXITCODE + +if ($ffprobeExit -ne 0) { + $totalFramesOutput | ForEach-Object { Write-Error $_ } + Write-Error ('ffprobe failed to read video file: {0}' -f $VideoPath) + exit 1 +} + +$totalFrames = $totalFramesOutput | Select-Object -First 1 + +if (-not $totalFrames -or ([string]$totalFrames).Trim() -notmatch '^\d+$') { + Write-Error ('Could not determine frame count for: {0}' -f $VideoPath) + exit 1 +} + +$totalFramesInt = [int]([string]$totalFrames).Trim() +$maxFrameIndex = $totalFramesInt - 1 + +# Resolve frame index (handles negative values that count from the end) +$frameToExtract = if ($Frame -lt 0) { + $calculatedFrame = $totalFramesInt + $Frame + [Math]::Max(0, [Math]::Min($calculatedFrame, $maxFrameIndex)) +} else { + if ($Frame -gt $maxFrameIndex) { + Write-Error ('Frame index {0} is out of range. Valid range: 0 to {1}' -f $Frame, $maxFrameIndex) + exit 1 + } + $Frame +} + +# Derive the output image path: insert "-XXXX" (zero-padded resolved frame index) before .png extension +$videoBase = [System.IO.Path]::GetFileNameWithoutExtension($VideoPath) +$videoDir = [System.IO.Path]::GetDirectoryName($VideoPath) +$framePadded = $frameToExtract.ToString().PadLeft(4, '0') +$imageName = "$videoBase-$framePadded.png" +$imagePath = if ([string]::IsNullOrEmpty($videoDir)) { $imageName } else { Join-Path $videoDir $imageName } + +$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path +$getLastFrameScript = Join-Path $scriptDir 'Get-LastFrame.ps1' + +# Forward common parameters to Get-LastFrame.ps1 +$passThrough = @() +if ($PSBoundParameters.ContainsKey('WhatIf')) { $passThrough += '-WhatIf' } +if ($PSBoundParameters.ContainsKey('Confirm')) { $passThrough += '-Confirm' } + +& $getLastFrameScript -Video $VideoPath -Image $imagePath -Frame $frameToExtract @passThrough +exit $LASTEXITCODE diff --git a/Get-Frames.cmd b/Get-FramesRange.cmd similarity index 82% rename from Get-Frames.cmd rename to Get-FramesRange.cmd index 0c9454c..ac42f21 100644 --- a/Get-Frames.cmd +++ b/Get-FramesRange.cmd @@ -1,2 +1,2 @@ @echo off -pwsh.exe -NoProfile -ExecutionPolicy Bypass -File "%~dp0Get-Frames.ps1" %* +pwsh.exe -NoProfile -ExecutionPolicy Bypass -File "%~dp0Get-FramesRange.ps1" %* diff --git a/Get-Frames.ps1 b/Get-FramesRange.ps1 similarity index 100% rename from Get-Frames.ps1 rename to Get-FramesRange.ps1