2025-05-10 18:43:43 +02:00

193 lines
6.7 KiB
C#

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using Newtonsoft.Json;
using RedundancyFinder;
using Spectre.Console;
using Spectre.Console.Cli;
namespace RedundancyFinderCLI
{
internal sealed class FinderCommand : Command<FinderCommand.Settings>
{
public sealed class Settings : CommandSettings
{
[Description("Paths to search.")]
[CommandArgument(0, "<searchPaths>")]
public string[] SearchPaths { get; init; }
[Description("File extensions to search for. Comma separated.")]
[CommandOption("-e|--extensions")]
[DefaultValue(".jpg,.webp,.raw,.pdf,.xsl,.xslx,.doc,.docx,.txt,.jpeg,.mov,.mp4,.mp3,.wav,.bmp,.gif,.png,.cu,.mid,.msb ,.mov,.avi,.wmv,.flv,.m4v,.bak ,.cpr ,.xml,.psd")]
public string? Extensions { get; init; }
[Description("Show all information.")]
[CommandOption("-v|--verbose")]
[DefaultValue(false)]
public bool Verbose { get; init; }
[Description("Output path.")]
[CommandOption("-o|--output")]
[DefaultValue("redundancies.json")]
public string? OutputPath {get; init;}
}
public override int Execute([NotNull] CommandContext context, [NotNull] Settings settings)
{
var extensions = settings.Extensions.Split(",", StringSplitOptions.RemoveEmptyEntries);
Finder finder = new Finder();
int hashingTasks = 0;
int hashingTasksFinished = 0;
finder.FileError += (sender, e) =>
{
if (e.Exception is UnauthorizedAccessException)
{
if (settings.Verbose)
{
WriteLine($"[red]Access denied to file: [/]{e.Path}. Skipping. Error: [red]{e.Exception.Message}[/]");
}
}
else
{
if (settings.Verbose)
{
WriteLine($"[red] Error processing file:\n[/]Path:{e.Path}\n[red]{e.Exception.Message}[/]");
}
}
};
finder.DirectoryError += (sender, e) =>
{
if (e.Exception is UnauthorizedAccessException)
{
if (settings.Verbose)
{
WriteLine($"[red]Access denied to directory: [/]{e.Path}. Skipping. Error: [red]{e.Exception.Message}[/]");
}
}
else
{
if (settings.Verbose)
{
WriteLine($"[red] Error processing directory:\n[/]Path:{e.Path}\n[red]{e.Exception.Message}[/]");
}
}
};
finder.ProcessingFile += (sender, e) =>
{
if (settings.Verbose)
{
WriteLine($"[green]Processing file: [/]{e.Path}");
}
};
try
{
var p = AnsiConsole.Progress();
p
.Start(ctx =>
{
// Define tasks
//var hashing = ctx.AddTask("[green]Hashing Files[/]",autoStart:false,);
finder.TaskStarted += (sender, e) =>
{
hashingTasks++;
if (settings.Verbose)
{
WriteLine($"[green]Task started: [/]{e}");
}
//hashing.MaxValue = hashingTasks;
};
finder.FileFound += (sender, e) =>
{
if (settings.Verbose)
{
WriteLine($"[green]File found: [/]{e.FilePath} {GetSizeFormat((ulong)e.Size)} ");
}
hashingTasksFinished++;
//hashing.Value = hashingTasksFinished;
};
//hashing.Value = hashing.MaxValue;
finder.FindRedundancies(settings.SearchPaths, extensions);
//hashing.StopTask();
});
var json = JsonConvert.SerializeObject(finder.Redundancies, Formatting.Indented);
var outputPath = settings.OutputPath;
//check if path is relative or absolute
if (!Path.IsPathRooted(settings.OutputPath))
{
outputPath = Path.Combine(Directory.GetCurrentDirectory(), outputPath);
}
File.WriteAllText(outputPath, json);
WriteLine($"[yellow]Wrote [/]{finder.Redundancies.Count}[yellow] redundancies to [/]'{outputPath}'");
ulong totalSize = finder.Redundancies.Select(x => (ulong)x.Value.FileSize).Aggregate((a, b) => a + b);
string sizeFormat = GetSizeFormat(totalSize);
WriteLine($"Total Size: [green]{sizeFormat}[/]");
if (settings.Verbose)
{
foreach (var redundancy in finder.Redundancies.Values)
{
AnsiConsole.WriteLine($"Hash: {redundancy.Hash}");
AnsiConsole.WriteLine("Paths:");
foreach (var path in redundancy.Paths)
{
AnsiConsole.WriteLine(path);
}
AnsiConsole.WriteLine();
}
}
}
catch (Exception e)
{
WriteLine($"[red] Error:\n[/]{e.Message}");
}
return 0;
}
private void WriteLine(string v)
{
string now = Markup.Escape($"[{DateTime.Now.ToString("HH:mm:ss")}]");
AnsiConsole.MarkupLine($"[gray]{now}[/] {v}");
}
private static string GetSizeFormat(ulong totalSize)
{
string sizeUnit = "B";
while (totalSize > 1024)
{
totalSize /= 1024;
sizeUnit = sizeUnit switch
{
"B" => "KB",
"KB" => "MB",
"MB" => "GB",
"GB" => "TB",
_ => sizeUnit
};
}
string sizeFormat = $"{totalSize:.##} {sizeUnit}";
return sizeFormat;
}
}
}