Add -Frames parameter to Clone-Video.ps1 for flexible frame range specification supporting start-end ranges, positive numbers for 0-to-N, negative numbers for last N frames, % placeholder for last frame index, and 'all' keyword, with mutual exclusivity enforcement against -StartFrame, -EndFrame, and -Sequence parameters in both video and PNG sequence modes
This commit is contained in:
+55
-1
@@ -3,6 +3,7 @@
|
||||
Clones (copies) a video file using ffmpeg.
|
||||
Supports MP4 (H.264), WebM and Matroska (MKV) sources, including YouTube/Google DASH downloads encoded as VP9 + Opus/AAC.
|
||||
Non-H.264 video and non-MP4-compatible audio are automatically transcoded to H.264 / AAC so the target is always a standard MP4.
|
||||
When Frames is provided, it defines a frame range using a flexible format and is mutually exclusive with StartFrame, EndFrame and Sequence.
|
||||
When Sequence is provided and EndFrame is -1:
|
||||
- First, the last frame of the video is saved as a PNG image (using the Sequence value as filename).
|
||||
- After, the video is cloned (copied) from StartFrame to the second-to-last frame (excluding the last frame).
|
||||
@@ -13,7 +14,7 @@
|
||||
from the image sequence, ordered alphabetically (or reverse alphabetically if -Reverse is specified).
|
||||
- -Sequence and -Metadata are not allowed in this mode.
|
||||
- Defaults: 24 FPS, CRF 8, no audio (unless -Audio is provided).
|
||||
- -Size, -FPS, -CRF, -Audio, -NoAudio, -StartFrame, -EndFrame, -Reverse and -LTX are all honoured.
|
||||
- -Size, -FPS, -CRF, -Audio, -NoAudio, -StartFrame, -EndFrame, -Frames, -Reverse and -LTX are all honoured.
|
||||
.PARAMETER Source
|
||||
Path to the input video file, or a directory containing PNG files with sequence numbers (e.g. frame_0000.png).
|
||||
For video files: can be absolute or relative to current directory. If the path has no extension, .mp4, .webm and .mkv are tried in turn.
|
||||
@@ -24,6 +25,13 @@
|
||||
0-based frame index to start cloning from.
|
||||
.PARAMETER EndFrame
|
||||
0-based frame index to end cloning at. Use -1 to clone to the end (default).
|
||||
.PARAMETER Frames
|
||||
0-based frames index to extract. Supports the following formats:
|
||||
- start-end: Extract frames from start to end (e.g., "100-200")
|
||||
- positive_number: Extract frames from 0 to that number (e.g., "50")
|
||||
- -negative_number: Extract last N frames (e.g., "-10")
|
||||
- %: Represents the last frame index. Can be used alone or in ranges (e.g., "%", "10-%", "-%")
|
||||
- all: Extract all frames (same as "0-%")
|
||||
.PARAMETER Sequence
|
||||
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
|
||||
@@ -79,6 +87,9 @@ param(
|
||||
[Parameter(Mandatory = $false)]
|
||||
[int]$EndFrame = -1,
|
||||
|
||||
[Parameter(Mandatory = $false)]
|
||||
[string]$Frames = $null,
|
||||
|
||||
[Parameter(Mandatory = $false)]
|
||||
[string]$Sequence = $null,
|
||||
|
||||
@@ -191,6 +202,13 @@ if ($NoAudio -and -not [string]::IsNullOrWhiteSpace($Audio)) {
|
||||
exit 1
|
||||
}
|
||||
|
||||
if (-not [string]::IsNullOrWhiteSpace($Frames)) {
|
||||
if ($PSBoundParameters.ContainsKey('StartFrame') -or $PSBoundParameters.ContainsKey('EndFrame') -or -not [string]::IsNullOrWhiteSpace($Sequence)) {
|
||||
Write-Error '-Frames cannot be used together with -StartFrame, -EndFrame, or -Sequence'
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
$AudioPath = $null
|
||||
$externalAudioCodec = ''
|
||||
$externalAudioCodecMismatch = $false
|
||||
@@ -266,6 +284,24 @@ if (Test-Path -LiteralPath $SourcePath -PathType Container) {
|
||||
|
||||
$maxPngIndex = $pngFiles.Count - 1
|
||||
|
||||
if (-not [string]::IsNullOrWhiteSpace($Frames)) {
|
||||
$framesPattern = $Frames -replace '%', $maxPngIndex
|
||||
if ($framesPattern -ieq 'all') { $framesPattern = "0-$maxPngIndex" }
|
||||
if ($framesPattern -match '^(\d+)-(\d+)$') {
|
||||
$StartFrame = [int]$Matches[1]
|
||||
$EndFrame = [int]$Matches[2]
|
||||
} elseif ($framesPattern -match '^-(\d+)$') {
|
||||
$StartFrame = [Math]::Max(0, $maxPngIndex - [int]$Matches[1])
|
||||
$EndFrame = $maxPngIndex
|
||||
} elseif ($framesPattern -match '^\d+$') {
|
||||
$StartFrame = 0
|
||||
$EndFrame = [Math]::Min([int]$framesPattern, $maxPngIndex)
|
||||
} else {
|
||||
Write-Error ('Invalid Frames format: {0}. Expected: start-end, positive number, -N for last N frames, % for last frame, or "all"' -f $Frames)
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
if ($StartFrame -lt 0 -or $StartFrame -gt $maxPngIndex) {
|
||||
Write-Error ('StartFrame {0} is out of range. Valid range: 0 to {1}' -f $StartFrame, $maxPngIndex)
|
||||
exit 1
|
||||
@@ -482,6 +518,24 @@ if (-not $totalFrames -or $totalFrames -notmatch '^\d+$') {
|
||||
$totalFramesInt = [int]$totalFrames
|
||||
$maxFrameIndex = $totalFramesInt - 1
|
||||
|
||||
if (-not [string]::IsNullOrWhiteSpace($Frames)) {
|
||||
$framesPattern = $Frames -replace '%', $maxFrameIndex
|
||||
if ($framesPattern -ieq 'all') { $framesPattern = "0-$maxFrameIndex" }
|
||||
if ($framesPattern -match '^(\d+)-(\d+)$') {
|
||||
$StartFrame = [int]$Matches[1]
|
||||
$EndFrame = [int]$Matches[2]
|
||||
} elseif ($framesPattern -match '^-(\d+)$') {
|
||||
$StartFrame = [Math]::Max(0, $maxFrameIndex - [int]$Matches[1])
|
||||
$EndFrame = $maxFrameIndex
|
||||
} elseif ($framesPattern -match '^\d+$') {
|
||||
$StartFrame = 0
|
||||
$EndFrame = [Math]::Min([int]$framesPattern, $maxFrameIndex)
|
||||
} else {
|
||||
Write-Error ('Invalid Frames format: {0}. Expected: start-end, positive number, -N for last N frames, % for last frame, or "all"' -f $Frames)
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
# Probe source video dimensions (used by -LTX to pick the closest aspect-ratio bucket).
|
||||
$ffprobeDimOutput = & ffprobe -v error -select_streams v:0 -show_entries stream=width,height -of csv=p=0:s=x -- $SourcePath 2>&1
|
||||
$sourceWidth = 0
|
||||
|
||||
Reference in New Issue
Block a user