Public/Undo-FoOptimization.ps1
|
function Undo-FoOptimization { <# .SYNOPSIS Rolls back previous optimizations using the history file. .DESCRIPTION Restores originals from backups recorded when OutputMode supports reversal (TempMove, BackupSuffix, BackupMove, OptimizedSuffix). Updates history entry status. History file format (history.json): Each entry records one optimization. Key fields: - TargetPath — user-visible path where the optimized file was written (undo restore destination) - OriginalPath — same value as TargetPath on each entry - OptimizedPath — optimized file path (equals TargetPath for in-place modes; sibling for OptimizedSuffix) - BackupPath — pre-optimization bytes for reversible modes - OutputMode — TempMove, BackupSuffix, BackupMove, OptimizedSuffix, or Replace - ReversalStatus — Pending, Reversed, NotReversible, or Error Replace mode is not reversible. See also Get-FoHistory and about_FileOptimizer. .PARAMETER Path Roll back entries matching these original or optimized paths. .PARAMETER Last Roll back the N most recent pending history entries. .PARAMETER HistoryPath Override path to history.json. Defaults to settings or global history path. .EXAMPLE Undo-FoOptimization -Last 3 .EXAMPLE .\Scripts\Undo-Optimization.ps1 -Path .\images\photo.png .EXAMPLE Undo-FoOptimization -Last 1 -WhatIf #> [CmdletBinding(SupportsShouldProcess)] [OutputType([object[]])] param( [string[]]$Path, [int]$Last, [string]$HistoryPath ) $pathHist = if ($HistoryPath) { $HistoryPath } else { Get-FoDefaultHistoryPath } $normalized = @() if ($Path) { foreach ($p in $Path) { $resolved = Resolve-Path -LiteralPath $p -ErrorAction SilentlyContinue if (-not $resolved) { throw "Path not found: $p" } $normalized += [System.IO.Path]::GetFullPath($resolved.Path) } } elseif (-not ($Last -gt 0)) { throw 'Specify -Path or -Last.' } $results = [System.Collections.Generic.List[object]]::new() Invoke-FoHistoryFileLock -HistoryPath $pathHist -Action { $data = Get-FoHistoryData -HistoryPath $pathHist $entries = @($data.Entries | Where-Object { $_.ReversalStatus -eq 'Pending' } | Sort-Object Timestamp -Descending) if ($Path) { $entries = @($entries | Where-Object { $restore = Get-FoHistoryRestorePath -Entry $_ $rp = [System.IO.Path]::GetFullPath($restore) $op = [System.IO.Path]::GetFullPath($_.OriginalPath) $opt = if ($_.OptimizedPath) { [System.IO.Path]::GetFullPath($_.OptimizedPath) } else { '' } ($normalized -contains $rp) -or ($normalized -contains $op) -or ($opt -and ($normalized -contains $opt)) }) } elseif ($Last -gt 0) { $entries = @($entries | Select-Object -First $Last) } foreach ($entry in $entries) { if ($WhatIfPreference) { $r = Invoke-FoRollback -Entry $entry -Confirm:$false Write-Host "WHATIF: $($r.Message)" continue } if ($PSCmdlet.ShouldProcess((Get-FoHistoryRestorePath -Entry $entry), 'Rollback optimization')) { $r = Invoke-FoRollback -Entry $entry -Confirm:$false foreach ($e in $data.Entries) { if ($e.Id -eq $entry.Id) { $e.ReversalStatus = $r.Status $e.ErrorMessage = if ($r.Success) { $null } else { $r.Message } } } if ($r.Success) { Write-Host $r.Message } else { Write-Warning $r.Message } $results.Add([PSCustomObject]@{ Id = $entry.Id; Path = $entry.OriginalPath; Status = $r.Status; Message = $r.Message }) } } Save-FoHistoryData -Data $data -HistoryPath $pathHist } return @($results) } |