<# .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 the -Suffix value (default "-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, the suffix 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 Text inserted into the derived -Target name before the file extension. Defaults to "-Clip" and cannot be empty. Example: -Suffix "-Clip-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)] [ValidateNotNullOrEmpty()] [string]$Suffix = '-Clip', [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 -Suffix 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 the suffix 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$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