Compare commits

..

4 Commits

Author SHA1 Message Date
45796d9bb2 release 3.0 2025-05-10 19:20:57 +02:00
1bb7d461e0 reload implemented 2025-05-10 19:17:29 +02:00
63c1c88393 save redundancies on exit 2025-05-10 18:57:49 +02:00
5800f2229f Added Timestamp 2025-05-10 18:43:43 +02:00
3 changed files with 100 additions and 79 deletions

BIN
0.3.zip Normal file

Binary file not shown.

View File

@ -10,6 +10,8 @@ namespace RedundancyFinder
string[] extensions; string[] extensions;
List<string> ignorePaths = new List<string>();
public event EventHandler<DirectoryErrorEventArgs>? DirectoryError; public event EventHandler<DirectoryErrorEventArgs>? DirectoryError;
public event EventHandler<FileErrorEventArgs>? FileError; public event EventHandler<FileErrorEventArgs>? FileError;
public event EventHandler<FileFoundEventArgs>? FileFound; public event EventHandler<FileFoundEventArgs>? FileFound;
@ -17,6 +19,7 @@ namespace RedundancyFinder
public event EventHandler<ProcessingFileEventArgs>? ProcessingFile; public event EventHandler<ProcessingFileEventArgs>? ProcessingFile;
public void FindRedundancies(string[] paths, string[] extensions) public void FindRedundancies(string[] paths, string[] extensions)
{ {
redundancies?.Values.SelectMany(x => x.Paths).ToList().ForEach(x => ignorePaths.Add(x));
this.extensions = extensions; this.extensions = extensions;
foreach (var path in paths) foreach (var path in paths)
{ {
@ -35,8 +38,6 @@ namespace RedundancyFinder
{ {
try try
{ {
// Process files in the current directory // Process files in the current directory
foreach (var file in Directory.GetFiles(directoryPath)) foreach (var file in Directory.GetFiles(directoryPath))
{ {
@ -72,47 +73,45 @@ namespace RedundancyFinder
private void ProcessFile(string filePath) private void ProcessFile(string filePath)
{ {
if(ignorePaths.Contains(filePath))
{
return;
}
if (!extensions.Contains(Path.GetExtension(filePath))) if (!extensions.Contains(Path.GetExtension(filePath)))
{ {
return; return;
} }
TaskStarted?.Invoke(this, filePath); TaskStarted?.Invoke(this, filePath);
ProcessingFile?.Invoke(this, new ProcessingFileEventArgs() { Path = filePath }); ProcessingFile?.Invoke(this, new ProcessingFileEventArgs() { Path = filePath });
try try
{ {
var fileHash = ComputeFileHash(filePath); var fileHash = ComputeFileHash(filePath);
if (fileHash == null) if (fileHash == null)
{ {
return; return;
} }
long fileSize = new FileInfo(filePath).Length; long fileSize = new FileInfo(filePath).Length;
lock (redundancies) lock (redundancies)
{ {
if (redundancies.ContainsKey(fileHash)) if (!redundancies.ContainsKey(fileHash))
{ {
return; var redundancy = new Redundancy() { Hash = fileHash, FileSize = fileSize };
} redundancies.Add(fileHash, redundancy);
}
redundancies[fileHash].Paths.Add(filePath);
}
FileFound?.Invoke(this, new FileFoundEventArgs(filePath, fileHash, fileSize));
}
catch (Exception ex)
{
FileError?.Invoke(this, new FileErrorEventArgs() { Exception = ex, Path = filePath });
}
if (!redundancies.ContainsKey(fileHash)) }
{
var redundancy = new Redundancy() { Hash = fileHash, FileSize = fileSize };
redundancies.Add(fileHash, redundancy);
}
redundancies[fileHash].Paths.Add(filePath);
}
FileFound?.Invoke(this, new FileFoundEventArgs(filePath, fileHash, fileSize));
}
catch (Exception ex)
{
FileError?.Invoke(this, new FileErrorEventArgs() { Exception = ex, Path = filePath });
}
}
private static string ComputeFileHash(string filePath) private static string ComputeFileHash(string filePath)
{ {

View File

@ -38,19 +38,42 @@ namespace RedundancyFinderCLI
} }
public override int Execute([NotNull] CommandContext context, [NotNull] Settings settings) public override int Execute([NotNull] CommandContext context, [NotNull] Settings settings)
{ {
var extensions = settings.Extensions.Split(",", StringSplitOptions.RemoveEmptyEntries); var extensions = settings.Extensions.Split(",", StringSplitOptions.RemoveEmptyEntries);
Finder finder = new Finder(); Finder finder = new Finder();
int hashingTasks = 0; int hashingTasks = 0;
int hashingTasksFinished = 0; int hashingTasksFinished = 0;
// Register the ProcessExit event to save redundancies on exit
AppDomain.CurrentDomain.ProcessExit += (sender, e) =>
{
SaveRedundancies(finder, settings.OutputPath);
};
// Load existing redundancies if the output file exists
if (File.Exists(settings.OutputPath))
{
try
{
var existingData = File.ReadAllText(settings.OutputPath);
var existingRedundancies = JsonConvert.DeserializeObject<Dictionary<string, Redundancy>>(existingData);
if (existingRedundancies != null)
{
foreach (var entry in existingRedundancies)
{
finder.Redundancies[entry.Key] = entry.Value;
}
WriteLine($"[yellow]Resumed from existing output file: [/]'{settings.OutputPath}'");
WriteLine($"[yellow]Loaded {finder.Redundancies.Count} redundancies from the file.[/]");
}
}
catch (Exception ex)
{
WriteLine($"[red]Failed to load existing output file: {ex.Message}[/]");
}
}
finder.FileError += (sender, e) => finder.FileError += (sender, e) =>
{ {
@ -58,14 +81,14 @@ namespace RedundancyFinderCLI
{ {
if (settings.Verbose) if (settings.Verbose)
{ {
AnsiConsole.MarkupLine($"[red]Access denied to file: [/]{e.Path}. Skipping. Error: [red]{e.Exception.Message}[/]"); WriteLine($"[red]Access denied to file: [/]{e.Path}. Skipping. Error: [red]{e.Exception.Message}[/]");
} }
} }
else else
{ {
if (settings.Verbose) if (settings.Verbose)
{ {
AnsiConsole.MarkupLine($"[red] Error processing file:\n[/]Path:{e.Path}\n[red]{e.Exception.Message}[/]"); WriteLine($"[red] Error processing file:\n[/]Path:{e.Path}\n[red]{e.Exception.Message}[/]");
} }
} }
}; };
@ -76,14 +99,14 @@ namespace RedundancyFinderCLI
{ {
if (settings.Verbose) if (settings.Verbose)
{ {
AnsiConsole.MarkupLine($"[red]Access denied to directory: [/]{e.Path}. Skipping. Error: [red]{e.Exception.Message}[/]"); WriteLine($"[red]Access denied to directory: [/]{e.Path}. Skipping. Error: [red]{e.Exception.Message}[/]");
} }
} }
else else
{ {
if (settings.Verbose) if (settings.Verbose)
{ {
AnsiConsole.MarkupLine($"[red] Error processing directory:\n[/]Path:{e.Path}\n[red]{e.Exception.Message}[/]"); WriteLine($"[red] Error processing directory:\n[/]Path:{e.Path}\n[red]{e.Exception.Message}[/]");
} }
} }
}; };
@ -92,69 +115,42 @@ namespace RedundancyFinderCLI
{ {
if (settings.Verbose) if (settings.Verbose)
{ {
AnsiConsole.MarkupLine($"[green]Processing file: [/]{e.Path}"); WriteLine($"[green]Processing file: [/]{e.Path}");
} }
}; };
try try
{ {
var p = AnsiConsole.Progress(); var p = AnsiConsole.Progress();
p p.Start(ctx =>
.Start(ctx =>
{ {
// Define tasks
//var hashing = ctx.AddTask("[green]Hashing Files[/]",autoStart:false,);
finder.TaskStarted += (sender, e) => finder.TaskStarted += (sender, e) =>
{ {
hashingTasks++; hashingTasks++;
if (settings.Verbose) if (settings.Verbose)
{ {
AnsiConsole.MarkupLine($"[green]Task started: [/]{e}"); WriteLine($"[green]Task started: [/]{e}");
} }
//hashing.MaxValue = hashingTasks;
}; };
finder.FileFound += (sender, e) => finder.FileFound += (sender, e) =>
{ {
if (settings.Verbose) if (settings.Verbose)
{ {
AnsiConsole.MarkupLine($"[green]File found: [/]{e.FilePath} {GetSizeFormat((ulong)e.Size)} "); WriteLine($"[green]File found: [/]{e.FilePath} {GetSizeFormat((ulong)e.Size)} ");
} }
hashingTasksFinished++; hashingTasksFinished++;
//hashing.Value = hashingTasksFinished;
}; };
//hashing.Value = hashing.MaxValue;
finder.FindRedundancies(settings.SearchPaths, extensions); finder.FindRedundancies(settings.SearchPaths, extensions);
//hashing.StopTask();
}); });
var json = JsonConvert.SerializeObject(finder.Redundancies, Formatting.Indented); SaveRedundancies(finder, settings.OutputPath);
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);
AnsiConsole.MarkupLine($"[yellow]Wrote [/]{finder.Redundancies.Count}[yellow] redundancies to [/]'{outputPath}'");
ulong totalSize = finder.Redundancies.Select(x => (ulong)x.Value.FileSize).Aggregate((a, b) => a + b); ulong totalSize = finder.Redundancies.Select(x => (ulong)x.Value.FileSize).Aggregate((a, b) => a + b);
string sizeFormat = GetSizeFormat(totalSize); string sizeFormat = GetSizeFormat(totalSize);
AnsiConsole.MarkupLine($"Total Size: [green]{sizeFormat}[/]"); WriteLine($"Total Size: [green]{sizeFormat}[/]");
if (settings.Verbose) if (settings.Verbose)
{ {
foreach (var redundancy in finder.Redundancies.Values) foreach (var redundancy in finder.Redundancies.Values)
@ -167,17 +163,43 @@ namespace RedundancyFinderCLI
} }
AnsiConsole.WriteLine(); AnsiConsole.WriteLine();
} }
} }
} }
catch (Exception e) catch (Exception e)
{ {
AnsiConsole.MarkupLine($"[red] Error:\n[/]{e.Message}"); WriteLine($"[red] Error:\n[/]{e.Message}");
} }
return 0; return 0;
} }
private void SaveRedundancies(Finder finder, string outputPath)
{
try
{
var json = JsonConvert.SerializeObject(finder.Redundancies, Formatting.Indented);
// Check if path is relative or absolute
if (!Path.IsPathRooted(outputPath))
{
outputPath = Path.Combine(Directory.GetCurrentDirectory(), outputPath);
}
File.WriteAllText(outputPath, json);
WriteLine($"[yellow]Wrote [/]{finder.Redundancies.Count}[yellow] redundancies to [/]'{outputPath}'");
}
catch (Exception ex)
{
WriteLine($"[red]Failed to save redundancies: {ex.Message}[/]");
}
}
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) private static string GetSizeFormat(ulong totalSize)
{ {
string sizeUnit = "B"; string sizeUnit = "B";