<# .SYNOPSIS Creates a new clipped video file using Clone-Video.ps1, requiring the -Video and -Frames parameters. The filename used for the -Target parameter is automatically derived from the -Video filename, with "-Clip" inserted before the video file extension (e.g., P01.mp4 becomes P01-Clip.mp4). When the video name carries a '.skip' marker before its extension, "-Clip" is inserted before that marker so it stays last (e.g., P01.skip.mp4 becomes P01-Clip.skip.mp4). Any extra parameters will be pass directly to Clone-Video.ps1, except for parameters -Video, -Frames, -Suffix and -Target. .PARAMETER Video Path to the input video file. Can be absolute or relative to current directory. If the path has no extension, .mp4, .webm and .mkv are tried in turn. .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 Suffix Optional text appended to the derived -Target name after "-Clip" and before the file extension (e.g. -Suffix "-Shot-1" makes P01.mp4 become P01-Clip-Shot-1.mp4). A '.skip' marker still stays last. #> [CmdletBinding(SupportsShouldProcess)] param( [Parameter(Mandatory = $true)] [string]$Video, [Parameter(Mandatory = $true)] [string]$Frames, [Parameter(Mandatory = $false)] [string]$Suffix = '', [Parameter(ValueFromRemainingArguments)] [object[]]$ExtraArgs ) Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' $videoPath = $Video # Resolve video path: accept .mp4, .webm or .mkv. If the path doesn't exist as-is, # try appending each supported extension in turn (same logic as Clone-Video.ps1). $supportedVideoExts = @('.mp4', '.webm', '.mkv') if (-not (Test-Path -LiteralPath $videoPath)) { foreach ($ext in $supportedVideoExts) { $candidate = "$videoPath$ext" if (Test-Path -LiteralPath $candidate) { $videoPath = $candidate break } } } # Derive the -Target path: insert "-Clip" before the file extension (e.g., P01.mp4 -> P01-Clip.mp4) $sourceExt = [System.IO.Path]::GetExtension($videoPath) $sourceBase = [System.IO.Path]::GetFileNameWithoutExtension($videoPath) $sourceDir = [System.IO.Path]::GetDirectoryName($videoPath) # A '.skip' marker must stay immediately before the container extension, so "-Clip" is # inserted before it rather than after (e.g., P01.skip.mp4 -> P01-Clip.skip.mp4). # The marker's original casing is preserved. $skipMarker = '' if ($sourceBase -match '^(.*)(\.skip)$') { $sourceBase = $Matches[1] $skipMarker = $Matches[2] } $targetName = "$sourceBase-Clip$Suffix$skipMarker$sourceExt" $targetPath = if ([string]::IsNullOrEmpty($sourceDir)) { $targetName } else { Join-Path $sourceDir $targetName } $scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path $cloneVideoScript = Join-Path $scriptDir 'Clone-Video.ps1' # Forward extra args plus common parameters to Clone-Video.ps1. # The remaining arguments are turned into a hashtable and splatted by name: PowerShell # passes the elements of a splatted *array* as positional arguments, so forwarding # @('-Size', '720p') that way would bind '-Size' to Clone-Video.ps1's [int]$StartFrame # instead of naming its -Size parameter. # Assigned in two steps: an empty array returned from an if-statement block is enumerated # away by PowerShell, which would leave $extra as $null. $extra = @() if ($ExtraArgs) { $extra = @($ExtraArgs) } $forwarded = @{} $index = 0 while ($index -lt $extra.Count) { $token = [string]$extra[$index] if ($token -notmatch '^-{1,2}([A-Za-z]\w*)$') { Write-Error ("Unexpected argument '{0}'. Extra parameters must be passed by name (e.g. -Size 720p)." -f $token) exit 1 } $name = $Matches[1] $next = if (($index + 1) -lt $extra.Count) { [string]$extra[$index + 1] } else { $null } # The next token is this parameter's value unless it is itself a parameter name. # A '-' followed by a digit is a value, not a name (e.g. -CRF -1). if ($null -ne $next -and (($next -notmatch '^-{1,2}[A-Za-z]') -or ($next -match '^-\d'))) { $forwarded[$name] = $extra[$index + 1] $index += 2 } else { # No value follows, so this is a switch (e.g. -NoAudio, -Reverse, -LTX). $forwarded[$name] = $true $index++ } } if ($PSBoundParameters.ContainsKey('WhatIf')) { $forwarded['WhatIf'] = [bool]$PSBoundParameters['WhatIf'] } if ($PSBoundParameters.ContainsKey('Confirm')) { $forwarded['Confirm'] = [bool]$PSBoundParameters['Confirm'] } & $cloneVideoScript -Source $videoPath -Frames $Frames -Target $targetPath @forwarded exit $LASTEXITCODE