mirror of
https://github.com/chylex/Brotli-Builder.git
synced 2024-12-22 16:42:46 +01:00
96 lines
3.7 KiB
C#
96 lines
3.7 KiB
C#
using System;
|
|
using System.ComponentModel;
|
|
using System.Linq;
|
|
using BrotliCalc.Commands.Base;
|
|
using BrotliCalc.Helpers;
|
|
using BrotliLib.Brotli.Components;
|
|
using BrotliLib.Numbers;
|
|
using DiagProcess = System.Diagnostics.Process;
|
|
|
|
namespace BrotliCalc.Commands{
|
|
class CmdCompress : ICommand{
|
|
public string FullName => "compress";
|
|
public string ShortName => "c";
|
|
|
|
public string ArgumentDesc => "<source-path> <quality|0-11|all> [window-size]";
|
|
public IntRange ArgumentCount => new IntRange(2, 3);
|
|
|
|
public static string? CustomExePath { get; set; }
|
|
|
|
private static readonly IntRange QualityRange = new IntRange(0, 11);
|
|
private const int AutoWindowSize = 0;
|
|
private const int FileLimit = 2000;
|
|
|
|
public string Process(string[] args){
|
|
var path = args[0];
|
|
var qualities = args[1] == "all" ? QualityRange : ParseQualityRange(args[1]);
|
|
var wbits = args.Length >= 3 ? new WindowSize(int.Parse(args[2])).Bits : AutoWindowSize;
|
|
|
|
if (!qualities.Values.All(QualityRange.Contains)){
|
|
throw new ArgumentException($"Compression quality must be in the range {QualityRange}.");
|
|
}
|
|
|
|
var groups = Brotli.ListPath(path).ToArray();
|
|
int totalFiles = groups.Length;
|
|
|
|
if (totalFiles > FileLimit){
|
|
throw new InvalidOperationException($"Too many files to process ({totalFiles} > {FileLimit}), cancelling command for safety.");
|
|
}
|
|
|
|
var items = qualities.Values.Cartesian(groups).ToArray();
|
|
|
|
using(var progress = new Progress(items.Length)){
|
|
try{
|
|
items.Parallelize().ForAll(item => {
|
|
var (quality, group) = item;
|
|
var file = group.Uncompressed;
|
|
|
|
progress.Start($"Processing {file} (quality {quality})");
|
|
Compress(wbits, quality, file.Path);
|
|
progress.Finish($"Completed {file} (quality {quality})");
|
|
});
|
|
}catch(Exception e) when (HasWin32Exception(e, out var we)){
|
|
if (CustomExePath == null){
|
|
throw new InvalidOperationException("Brotli executable must be named 'brotli' and placed into the working directory or environment path.", we);
|
|
}
|
|
else{
|
|
throw new InvalidOperationException("Brotli executable failed to start.", we);
|
|
}
|
|
}
|
|
}
|
|
|
|
return $"Compressed {totalFiles} file(s).";
|
|
}
|
|
|
|
private static IntRange ParseQualityRange(string arg){
|
|
if (arg.Contains('-')){
|
|
var split = arg.Split(new char[]{ '-' }, 2);
|
|
|
|
if (split.Length < 2){
|
|
throw new FormatException("Invalid quality range.");
|
|
}
|
|
|
|
return new IntRange(int.Parse(split[0]), int.Parse(split[1]));
|
|
}
|
|
else{
|
|
return IntRange.Only(int.Parse(arg));
|
|
}
|
|
}
|
|
|
|
private static void Compress(int wbits, int quality, string path){
|
|
using DiagProcess process = DiagProcess.Start(CustomExePath ?? "brotli", $"-w {wbits} -q {quality} -S .{quality}{Brotli.CompressedFileExtension} -f \"{path}\"");
|
|
process.WaitForExit();
|
|
}
|
|
|
|
private static bool HasWin32Exception(Exception e, out Win32Exception? we){
|
|
we = e switch{
|
|
Win32Exception we2 => we2,
|
|
AggregateException ae => ae.InnerException as Win32Exception,
|
|
_ => null
|
|
};
|
|
|
|
return we != null;
|
|
}
|
|
}
|
|
}
|