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.
|
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.
|
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.
|
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:
|
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).
|
- 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).
|
- 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).
|
from the image sequence, ordered alphabetically (or reverse alphabetically if -Reverse is specified).
|
||||||
- -Sequence and -Metadata are not allowed in this mode.
|
- -Sequence and -Metadata are not allowed in this mode.
|
||||||
- Defaults: 24 FPS, CRF 8, no audio (unless -Audio is provided).
|
- 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
|
.PARAMETER Source
|
||||||
Path to the input video file, or a directory containing PNG files with sequence numbers (e.g. frame_0000.png).
|
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.
|
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.
|
0-based frame index to start cloning from.
|
||||||
.PARAMETER EndFrame
|
.PARAMETER EndFrame
|
||||||
0-based frame index to end cloning at. Use -1 to clone to the end (default).
|
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
|
.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.
|
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
|
.PARAMETER Size
|
||||||
@@ -79,6 +87,9 @@ param(
|
|||||||
[Parameter(Mandatory = $false)]
|
[Parameter(Mandatory = $false)]
|
||||||
[int]$EndFrame = -1,
|
[int]$EndFrame = -1,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $false)]
|
||||||
|
[string]$Frames = $null,
|
||||||
|
|
||||||
[Parameter(Mandatory = $false)]
|
[Parameter(Mandatory = $false)]
|
||||||
[string]$Sequence = $null,
|
[string]$Sequence = $null,
|
||||||
|
|
||||||
@@ -191,6 +202,13 @@ if ($NoAudio -and -not [string]::IsNullOrWhiteSpace($Audio)) {
|
|||||||
exit 1
|
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
|
$AudioPath = $null
|
||||||
$externalAudioCodec = ''
|
$externalAudioCodec = ''
|
||||||
$externalAudioCodecMismatch = $false
|
$externalAudioCodecMismatch = $false
|
||||||
@@ -266,6 +284,24 @@ if (Test-Path -LiteralPath $SourcePath -PathType Container) {
|
|||||||
|
|
||||||
$maxPngIndex = $pngFiles.Count - 1
|
$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) {
|
if ($StartFrame -lt 0 -or $StartFrame -gt $maxPngIndex) {
|
||||||
Write-Error ('StartFrame {0} is out of range. Valid range: 0 to {1}' -f $StartFrame, $maxPngIndex)
|
Write-Error ('StartFrame {0} is out of range. Valid range: 0 to {1}' -f $StartFrame, $maxPngIndex)
|
||||||
exit 1
|
exit 1
|
||||||
@@ -482,6 +518,24 @@ if (-not $totalFrames -or $totalFrames -notmatch '^\d+$') {
|
|||||||
$totalFramesInt = [int]$totalFrames
|
$totalFramesInt = [int]$totalFrames
|
||||||
$maxFrameIndex = $totalFramesInt - 1
|
$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).
|
# 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
|
$ffprobeDimOutput = & ffprobe -v error -select_streams v:0 -show_entries stream=width,height -of csv=p=0:s=x -- $SourcePath 2>&1
|
||||||
$sourceWidth = 0
|
$sourceWidth = 0
|
||||||
|
|||||||
Reference in New Issue
Block a user