Public/Optimize-FoFile.ps1
|
function Optimize-FoFile { <# .SYNOPSIS Optimizes one or more files using FileOptimizer plugin chains. .DESCRIPTION Resolves settings, selects pipeline groups per file extension, runs the plugin chain, and optionally records history. Supports -WhatIf for dry-run output. Each invocation is one history batch when HistoryEnabled is true. .PARAMETER Path File or directory paths to optimize. Directories are not expanded unless -Recurse is set. .PARAMETER ConfigPath Optional local JSON config file merged after global config. .PARAMETER ContinueOnError When optimizing multiple files, record per-file errors and continue instead of stopping the batch. .PARAMETER Level Optimization level (0–9). Default from config. .PARAMETER PluginSearchMode How to resolve plugin executables: PortableFirst, PathFirst, PortableOnly, or PathOnly. .PARAMETER PluginPath Portable plugin directory (Plugins64/Plugins32). .PARAMETER OutputMode TempMove, Replace, OptimizedSuffix, BackupSuffix, or BackupMove. .PARAMETER ReportVerbosity Compact, Standard (default), or Verbose console/report layout. .PARAMETER SizeDisplayUnit Auto (default pretty), Bytes, KB, MB, or GB for size display. .PARAMETER ShowProgress Show per-step progress during optimization. .PARAMETER PassThru Return optimization result objects to the pipeline. Without this switch, only the host report controlled by ReportVerbosity is written (no object dump). .PARAMETER Recurse When Path is a directory, include files in subdirectories. .PARAMETER MissingToolsPolicy How to handle required plugin tools that are not found: Error (default), SkipTool (skip those steps), or SkipFile (skip the whole file). .PARAMETER AcknowledgeOutdatedPlugins Persist acknowledgment of the current minimum plugin-bundle version and continue with a warning when the installed bundle is older than required. .EXAMPLE Optimize-FoFile -Path .\images\photo.png .EXAMPLE Optimize-FoFile -Path .\docs -Recurse -WhatIf .EXAMPLE .\Scripts\Optimize-File.ps1 .\images\*.png # CLI wrapper with the same parameters. #> [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'Optimize')] [OutputType([object[]])] param( [Parameter(ParameterSetName = 'Optimize', ValueFromPipeline, ValueFromPipelineByPropertyName)] [Alias('FullName', 'PSPath')] [string[]]$Path, [string]$ConfigPath, [ValidateRange(0, 9)] [nullable[int]]$Level, [ValidateSet('PortableFirst', 'PathFirst', 'PortableOnly', 'PathOnly')] [string]$PluginSearchMode, [string]$PluginPath, [ValidateRange(0, 3)] [nullable[int]]$LogLevel, [ValidateRange(0, 3)] [nullable[int]]$ReportLogLevel, [string]$ReportPath, [ValidateSet('Compact', 'Standard', 'Verbose')] [string]$ReportVerbosity, [ValidateSet('Auto', 'Bytes', 'KB', 'MB', 'GB')] [string]$SizeDisplayUnit, [ValidateSet('Replace', 'OptimizedSuffix', 'BackupSuffix', 'BackupMove', 'TempMove')] [string]$OutputMode, [string]$BackupPath, [string]$BackupSuffix, [string]$OptimizedSuffix, [string]$TempBackupPath, [nullable[bool]]$HistoryEnabled, [string]$HistoryPath, [ValidateSet('Error', 'SkipTool', 'SkipFile')] [string]$MissingToolsPolicy, [nullable[bool]]$SkipMissingTools, [switch]$ShowProgress, [switch]$PassThru, [switch]$Recurse, [switch]$ContinueOnError, [switch]$AcknowledgeOutdatedPlugins ) begin { $script:FoBatchResults = [System.Collections.Generic.List[object]]::new() $script:FoBatchSettings = $null $script:FoBatchId = (Get-Date -Format 'yyyyMMdd-HHmmss') + '-' + ([guid]::NewGuid().ToString('N').Substring(0, 8)) } process { if (-not $Path) { return } if ($AcknowledgeOutdatedPlugins) { $min = Get-FoMinimumPluginBundleVersion $ackConfig = if ($ConfigPath) { $ConfigPath } else { $null } Set-FoAcknowledgedPluginBundleMinimum -MinimumVersion $min -ConfigPath $ackConfig } $settings = Merge-FoSettings -BoundParameters $PSBoundParameters if ($AcknowledgeOutdatedPlugins) { $settings.AcknowledgedPluginBundleMinimum = Get-FoMinimumPluginBundleVersion } $script:FoBatchSettings = $settings $targets = Get-FoTargetFiles -Path $Path -Recurse:$Recurse foreach ($file in $targets) { $gate = Test-FoFileGate -Path $file -Settings $settings if (-not $gate.Pass) { Write-FoLog -LogLevel $settings.LogLevel -RequiredLevel 1 -Message "Skipped $file ($($gate.Reason))" $skipped = [PSCustomObject]@{ Path = $file; Status = 'Skipped'; Reason = $gate.Reason } $script:FoBatchResults.Add($skipped) continue } if ((Get-Item -LiteralPath $file).Length -eq 0) { Write-FoLog -LogLevel $settings.LogLevel -RequiredLevel 1 -Message "Skipped $file (zero-byte file)" $skipped = [PSCustomObject]@{ Path = $file; Status = 'Skipped'; Reason = 'ZeroByte' } $script:FoBatchResults.Add($skipped) continue } $groups = Get-FoPipelineGroupsForFile -Path $file -Settings $settings if ($groups.Count -eq 0) { Write-FoLog -LogLevel $settings.LogLevel -RequiredLevel 1 -Message "Skipped $file (unsupported extension)" $skipped = [PSCustomObject]@{ Path = $file; Status = 'Skipped'; Reason = 'Unsupported' } $script:FoBatchResults.Add($skipped) continue } if ($PSCmdlet.ShouldProcess($file, 'Optimize file')) { try { $result = Invoke-FoPluginChain -Path $file -Settings $settings -ShowProgress:$ShowProgress -Confirm:$false if ($result.Status -eq 'Optimized') { if ($settings.HistoryEnabled) { Add-FoHistoryEntry -Result $result -Settings $settings -BatchId $script:FoBatchId } } $script:FoBatchResults.Add($result) } catch { if ($ContinueOnError) { Write-FoLog -LogLevel $settings.LogLevel -RequiredLevel 1 -Message "Error optimizing ${file}: $($_.Exception.Message)" } else { Write-Error $_ } $err = [PSCustomObject]@{ Path = $file; Status = 'Error'; Reason = $_.Exception.Message } $script:FoBatchResults.Add($err) if (-not $ContinueOnError) { throw } } } elseif ($WhatIfPreference) { $result = Invoke-FoPluginChain -Path $file -Settings $settings -ShowProgress:$false -Confirm:$false $script:FoBatchResults.Add($result) } } } end { if ($script:FoBatchSettings -and $script:FoBatchResults.Count -gt 0) { # Compact / Standard / Verbose all render here (Verbose is not gated on LogLevel). Write-FoOptimizeResults -Results @($script:FoBatchResults) -Settings $script:FoBatchSettings if ($script:FoBatchSettings.ReportPath) { Write-FoReport -Results @($script:FoBatchResults) -Settings $script:FoBatchSettings -ReportPath $script:FoBatchSettings.ReportPath } } if ($PassThru) { return @($script:FoBatchResults) } } } |