diff --git a/CompactGUI.Core/WOFHelper.cs b/CompactGUI.Core/WOFHelper.cs index b0580687..0581f63e 100644 --- a/CompactGUI.Core/WOFHelper.cs +++ b/CompactGUI.Core/WOFHelper.cs @@ -47,6 +47,46 @@ public static WOFCompressionAlgorithm WOFConvertCompressionLevel(CompressionMode }; } + // Native WOF values are API identifiers, not an ordering by compression strength. + public static WOFCompressionAlgorithm GetDominantCompressionMode( + IEnumerable? analysedFiles, + WOFCompressionAlgorithm fallback = WOFCompressionAlgorithm.NO_COMPRESSION) + { + if (analysedFiles is null) return fallback; + + var dominant = analysedFiles + .Where(file => file.CompressionMode is + WOFCompressionAlgorithm.XPRESS4K or + WOFCompressionAlgorithm.XPRESS8K or + WOFCompressionAlgorithm.XPRESS16K or + WOFCompressionAlgorithm.LZX) + .GroupBy(file => file.CompressionMode) + .Select(group => new + { + Mode = group.Key, + UncompressedBytes = group.Sum(file => file.UncompressedSize), + FileCount = group.Count() + }) + .OrderByDescending(group => group.FileCount) + .ThenByDescending(group => group.UncompressedBytes) + .ThenByDescending(group => GetCompressionStrength(group.Mode)) + .FirstOrDefault(); + + return dominant?.Mode ?? fallback; + } + + private static int GetCompressionStrength(WOFCompressionAlgorithm mode) + { + return mode switch + { + WOFCompressionAlgorithm.XPRESS4K => 0, + WOFCompressionAlgorithm.XPRESS8K => 1, + WOFCompressionAlgorithm.XPRESS16K => 2, + WOFCompressionAlgorithm.LZX => 3, + _ => -1 + }; + } + [StructLayout(LayoutKind.Sequential)] public struct WOF_FILE_COMPRESSION_INFO_V1 { diff --git a/CompactGUI.Watcher/BackgroundCompactor.vb b/CompactGUI.Watcher/BackgroundCompactor.vb index 2a714b48..29718b9e 100644 --- a/CompactGUI.Watcher/BackgroundCompactor.vb +++ b/CompactGUI.Watcher/BackgroundCompactor.vb @@ -93,7 +93,7 @@ Public Class BackgroundCompactor If folders.Contains(folder) Then 'Ensure the folder is still in the original collection before updating. Using analyser As New Core.Analyser(folder.Folder, NullLogger(Of Core.Analyser).Instance) - Dim analysed = Await analyser.GetAnalysedFilesAsync(cancellationTokenSource.Token) + Await analyser.GetAnalysedFilesAsync(cancellationTokenSource.Token) If cancellationTokenSource.IsCancellationRequested Then Return False @@ -102,10 +102,6 @@ Public Class BackgroundCompactor folder.LastCompressedSize = analyser.CompressedBytes folder.LastSystemModifiedDate = DateTime.Now - If analysed IsNot Nothing AndAlso analysed.Count > 0 Then - folder.CompressionLevel = analysed.Select(Function(f) f.CompressionMode).Max - End If - folder.LastCompressedDate = DateTime.Now folder.HasTargetChanged = False End Using diff --git a/CompactGUI.Watcher/Watcher.vb b/CompactGUI.Watcher/Watcher.vb index 1d1a1c8b..6d350614 100644 --- a/CompactGUI.Watcher/Watcher.vb +++ b/CompactGUI.Watcher/Watcher.vb @@ -276,7 +276,7 @@ Partial Public Class Watcher : Inherits ObservableRecipient : Implements IRecipi End Sub - Public Async Sub UpdateWatched(folder As String, analyser As Analyser, isFreshlyCompressed As Boolean, Optional immediateFlushToDisk As Boolean = True) + Public Async Function UpdateWatched(folder As String, analyser As Analyser, isFreshlyCompressed As Boolean, Optional immediateFlushToDisk As Boolean = True, Optional targetCompressionLevel As WOFCompressionAlgorithm = WOFCompressionAlgorithm.NO_COMPRESSION) As Task Dim existingItem = WatchedFolders.FirstOrDefault(Function(f) f.Folder = folder) @@ -288,8 +288,10 @@ Partial Public Class Watcher : Inherits ObservableRecipient : Implements IRecipi existingItem.LastCheckedSize = analyser.CompressedBytes existingItem.LastUncompressedSize = analyser.UncompressedBytes existingItem.LastSystemModifiedDate = DateTime.Now - If analysedFiles?.Count <> 0 Then - existingItem.CompressionLevel = analysedFiles.Select(Function(f) f.CompressionMode).Max + If isFreshlyCompressed AndAlso targetCompressionLevel <> WOFCompressionAlgorithm.NO_COMPRESSION Then + existingItem.CompressionLevel = targetCompressionLevel + Else + existingItem.CompressionLevel = WOFHelper.GetDominantCompressionMode(analysedFiles) End If If isFreshlyCompressed Then @@ -304,7 +306,7 @@ Partial Public Class Watcher : Inherits ObservableRecipient : Implements IRecipi OnPropertyChanged(NameOf(TotalSaved)) If immediateFlushToDisk Then WriteToFile() End If - End Sub + End Function Private Sub UpdateFolderProperties(existingItem As WatchedFolder, newItem As WatchedFolder) With existingItem @@ -500,8 +502,9 @@ Partial Public Class Watcher : Inherits ObservableRecipient : Implements IRecipi watched.LastSystemModifiedDate = watched.LastChangedDate If analysedFiles?.Count <> 0 Then - Dim mainCompressionLVL = analysedFiles.Select(Function(f) f.CompressionMode).Max - watched.CompressionLevel = If(mainCompressionLVL <> WOFCompressionAlgorithm.NO_COMPRESSION, mainCompressionLVL, watched.CompressionLevel) + If watched.CompressionLevel = WOFCompressionAlgorithm.NO_COMPRESSION Then + watched.CompressionLevel = WOFHelper.GetDominantCompressionMode(analysedFiles) + End If If checkDiskModified Then Dim lastDiskWriteTime = analysedFiles.Select(Function(fl) diff --git a/CompactGUI/ViewModels/FolderViewModel.vb b/CompactGUI/ViewModels/FolderViewModel.vb index a8d96cfd..b4459eb8 100644 --- a/CompactGUI/ViewModels/FolderViewModel.vb +++ b/CompactGUI/ViewModels/FolderViewModel.vb @@ -148,7 +148,7 @@ Public NotInheritable Class FolderViewModel : Inherits ObservableObject : Implem Private Async Function Uncompress() As Task Await _compressableFolderService.UncompressFolder(Folder) - _watcher.UpdateWatched(Folder.FolderName, Folder.Analyser, False) + Await _watcher.UpdateWatched(Folder.FolderName, Folder.Analyser, False) End Function diff --git a/CompactGUI/ViewModels/HomeViewModel.vb b/CompactGUI/ViewModels/HomeViewModel.vb index f65b1743..edc4a567 100644 --- a/CompactGUI/ViewModels/HomeViewModel.vb +++ b/CompactGUI/ViewModels/HomeViewModel.vb @@ -251,7 +251,8 @@ Partial Public NotInheritable Class HomeViewModel : Inherits ObservableRecipient End If - _watcher.UpdateWatched(folder.FolderName, folder.Analyser, True) + Dim targetCompressionLevel = Core.WOFHelper.WOFConvertCompressionLevel(folder.CompressionOptions.SelectedCompressionMode) + Await _watcher.UpdateWatched(folder.FolderName, folder.Analyser, ret, targetCompressionLevel:=targetCompressionLevel) 'For Each poorext In folder.PoorlyCompressedFiles ' Debug.WriteLine($"{poorext.extension} : {poorext.totalFiles} with ratio of {poorext.cRatio}") @@ -264,7 +265,8 @@ Partial Public NotInheritable Class HomeViewModel : Inherits ObservableRecipient Compressing = False For Each folder In Folders.Where(Function(f) f.CompressionOptions.WatchFolderForChanges) - AddOrUpdateFolderWatcher(folder) + Dim wasCompressedInCurrentBatch = foldersToCompress.Contains(folder) AndAlso folder.IsFreshlyCompressed + AddOrUpdateFolderWatcher(folder, wasCompressedInCurrentBatch) Next RemoveFolderCommand.NotifyCanExecuteChanged() @@ -278,7 +280,7 @@ Partial Public NotInheritable Class HomeViewModel : Inherits ObservableRecipient End Function - Public Sub AddOrUpdateFolderWatcher(folder As CompressableFolder) + Public Sub AddOrUpdateFolderWatcher(folder As CompressableFolder, wasCompressedInCurrentBatch As Boolean) HomeViewModelLog.AddingFolderToWatcher(_logger, folder.FolderName) Dim newWatched = New Watcher.WatchedFolder(folder.FolderName, folder.DisplayName) @@ -289,7 +291,14 @@ Partial Public NotInheritable Class HomeViewModel : Inherits ObservableRecipient newWatched.LastCheckedDate = DateTime.Now newWatched.LastCheckedSize = folder.CompressedBytes newWatched.LastSystemModifiedDate = DateTime.Now - newWatched.CompressionLevel = If(folder.AnalysisResults.Any(), folder.AnalysisResults.Max(Function(f) f.CompressionMode), Core.WOFCompressionAlgorithm.NO_COMPRESSION) + Dim existingWatched = _watcher.WatchedFolders.FirstOrDefault(Function(w) w.Folder = folder.FolderName) + If wasCompressedInCurrentBatch Then + newWatched.CompressionLevel = Core.WOFHelper.WOFConvertCompressionLevel(folder.CompressionOptions.SelectedCompressionMode) + ElseIf existingWatched IsNot Nothing Then + newWatched.CompressionLevel = existingWatched.CompressionLevel + Else + newWatched.CompressionLevel = Core.WOFHelper.GetDominantCompressionMode(folder.AnalysisResults) + End If _watcher.AddOrUpdateWatched(newWatched)