Public/Invoke-FoPluginChain.ps1
|
function Invoke-FoPluginChain { <# .SYNOPSIS Runs the full plugin chain for a single file. .DESCRIPTION Builds an execution plan, runs each active step in order, applies output mode when the result is smaller, and returns a result object with status and sizes. MissingToolsPolicy controls required-tool gaps: Error (default) fails the file; SkipTool continues other steps; SkipFile skips the whole file. A missing portable plugin bundle (no Install-FoPlugins tree) always fails unless PluginSearchMode is PathOnly. .PARAMETER Path File to optimize. .PARAMETER Settings Merged settings from Get-FoConfig. .PARAMETER ShowProgress Show per-step progress. .EXAMPLE Invoke-FoPluginChain -Path .\photo.png -Settings (Get-FoConfig) -WhatIf #> [CmdletBinding(SupportsShouldProcess)] [OutputType([pscustomobject])] param( [Parameter(Mandatory)] [string]$Path, [Parameter(Mandatory)] [hashtable]$Settings, [switch]$ShowProgress ) $plan = Get-FoExecutionPlan -Path $Path -Settings $Settings $groupNames = @($plan.Plans | ForEach-Object { $_.GroupName }) $allMissing = @($plan.Plans | ForEach-Object { $_.Missing } | Select-Object -Unique) $allSteps = @($plan.Plans | ForEach-Object { $_.Steps }) $optimizeAction = 'Optimize via {0}' -f ($groupNames -join ', ') if (-not $PSCmdlet.ShouldProcess($Path, $optimizeAction)) { foreach ($p in $plan.Plans) { Write-Host ('WHATIF: Would optimize {0} via {1} ({2} steps) [OutputMode={3}]' -f $Path, $p.GroupName, $p.Steps.Count, $Settings.OutputMode) foreach ($step in $p.Steps) { Write-Host ('WHATIF: {0}' -f $step.Name) } } if ($allMissing.Count -gt 0) { Write-Host ('WHATIF: missing tools: {0}' -f ($allMissing -join ', ')) } return [PSCustomObject]@{ Path = $Path Status = 'WhatIf' Groups = $groupNames Steps = $allSteps Missing = $allMissing } } Assert-FoPluginBundleVersionForOptimize -Settings $Settings $missingPolicy = [string]$Settings.MissingToolsPolicy if ([string]::IsNullOrWhiteSpace($missingPolicy)) { $missingPolicy = 'Error' } if ($allMissing.Count -gt 0) { switch ($missingPolicy) { 'SkipFile' { Write-Warning "Skipping '$Path' - missing tools: $($allMissing -join ', ')." return [PSCustomObject]@{ Path = $Path Status = 'Skipped' Reason = 'MissingTools' Groups = $groupNames Missing = $allMissing BytesSaved = 0 Steps = @() } } 'SkipTool' { if ($Settings.LogLevel -ge 2) { Write-Host "Missing tools for '$Path' (skipping those steps): $($allMissing -join ', ')" } } default { throw ("Required plugin tool(s) missing for '{0}': {1}. Run Install-FoPlugins to install the bundle, or set MissingToolsPolicy to SkipTool (skip individual steps) or SkipFile (skip this file)." -f $Path, ($allMissing -join ', ')) } } } if ($Settings.LogLevel -ge 2) { Write-Host ('Optimizing {0} via {1}' -f $Path, ($groupNames -join ', ')) } $workFile = Join-Path ([System.IO.Path]::GetTempPath()) ('FileOptimizer_work_{0}_{1}' -f ([guid]::NewGuid().ToString('N')), [System.IO.Path]::GetFileName($Path)) Copy-Item -LiteralPath $Path -Destination $workFile -Force $originalSize = (Get-Item -LiteralPath $Path).Length $stepLog = @() $sw = [System.Diagnostics.Stopwatch]::StartNew() try { foreach ($p in $plan.Plans) { foreach ($step in $p.Steps) { if ($ShowProgress) { Write-Progress -Activity 'Optimizing' -Status $step.Name -CurrentOperation $Path } $result = Invoke-FoPlugin -Step $step -InputFile $workFile -Settings $Settings -PluginPath $Settings.PluginPath -SearchMode $Settings.PluginSearchMode if (-not $result.Skipped) { $stepLog += [PSCustomObject]@{ Step = $step.Name Group = $p.GroupName ExitCode = $result.ExitCode Reason = $result.Reason SizeBefore = $result.SizeBefore SizeAfter = $result.SizeAfter Accepted = $result.Accepted DurationMs = $result.DurationMs } if ($Settings.LogLevel -ge 2 -and $result.Accepted) { $pct = if ($result.SizeBefore -gt 0) { [math]::Round((1 - $result.SizeAfter / $result.SizeBefore) * 100, 1) } else { 0 } Write-Host (' {0}: {1} -> {2} (-{3}%)' -f $step.Name, (Format-FoFileSize $result.SizeBefore), (Format-FoFileSize $result.SizeAfter), $pct) } } } } $finalSize = (Get-Item -LiteralPath $workFile).Length $sw.Stop() if ($finalSize -ge $originalSize) { $unchangedReason = $null if ($stepLog.Count -eq 0 -and $allMissing.Count -gt 0) { $unchangedReason = 'MissingTools' } return [PSCustomObject]@{ Path = $Path Status = 'Unchanged' Reason = $unchangedReason Groups = $groupNames OriginalSize = $originalSize FinalSize = $originalSize BytesSaved = 0 PercentSaved = 0 OutputPath = $Path OriginalPath = $Path BackupPath = $null Missing = $allMissing Steps = $stepLog DurationMs = $sw.ElapsedMilliseconds } } $outputResult = Invoke-FoOutputMode -SourceFile $workFile -TargetPath $Path -Settings $Settings return [PSCustomObject]@{ Path = $Path Status = 'Optimized' Reason = $null Groups = $groupNames OriginalSize = $originalSize FinalSize = $finalSize BytesSaved = ($originalSize - $finalSize) PercentSaved = if ($originalSize -gt 0) { [math]::Round(($originalSize - $finalSize) / $originalSize * 100, 1) } else { 0 } OutputPath = $outputResult.OptimizedPath OriginalPath = $outputResult.OriginalPath BackupPath = $outputResult.BackupPath OutputMode = $Settings.OutputMode Steps = $stepLog DurationMs = $sw.ElapsedMilliseconds } } finally { if (Test-Path -LiteralPath $workFile) { Remove-Item -LiteralPath $workFile -Force -ErrorAction SilentlyContinue } Write-Progress -Activity 'Optimizing' -Completed } } |