initial commit

This commit is contained in:
Robin Weichselbraun 2025-05-09 14:26:16 +02:00
parent f5e17b8c7b
commit 37c33f3e15
8 changed files with 233 additions and 0 deletions

1
.gitignore vendored
View File

@ -400,3 +400,4 @@ FodyWeavers.xsd
# JetBrains Rider
*.sln.iml
TestData/

118
RedundancyFinder/Finder.cs Normal file
View File

@ -0,0 +1,118 @@
using System.Security.Cryptography;
namespace RedundancyFinder
{
public class Finder
{
List<Task> tasks = new List<Task>();
Dictionary<object, Redundancy> redundancies = new Dictionary<object, Redundancy>();
public Dictionary<object, Redundancy> Redundancies { get => redundancies; }
string[] extensions;
public void FindRedundancies(string[] paths, string[] extensions)
{
this.extensions = extensions;
foreach (var path in paths)
{
if (Directory.Exists(path))
{
ProcessDirectory(path);
}
else if (File.Exists(path))
{
ProcessFile(path);
}
}
// Wait for all tasks to complete
Task.WaitAll(tasks.ToArray());
foreach (var redundancy in redundancies.Values.ToList())
{
if (redundancy.Paths.Count == 1)
{
redundancies.Remove(redundancy.Hash);
}
}
}
private void ProcessDirectory(string directoryPath)
{
try
{
// Process files in the current directory
foreach (var file in Directory.GetFiles(directoryPath))
{
ProcessFile(file);
}
// Recursively process subdirectories
foreach (var subDirectory in Directory.GetDirectories(directoryPath))
{
try
{
ProcessDirectory(subDirectory);
}
catch (UnauthorizedAccessException ex)
{
Console.WriteLine($"Access denied to directory: {subDirectory}. Skipping. Error: {ex.Message}");
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred while processing directory: {subDirectory}. Error: {ex.Message}");
}
}
}
catch (UnauthorizedAccessException ex)
{
Console.WriteLine($"Access denied to directory: {directoryPath}. Skipping. Error: {ex.Message}");
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred while processing directory: {directoryPath}. Error: {ex.Message}");
}
}
private void ProcessFile(string filePath)
{
if (!extensions.Contains(Path.GetExtension(filePath)))
{
return;
}
Task task = new(() =>
{
Console.WriteLine($"Processing file: {filePath}");
try
{
var fileHash = ComputeFileHash(filePath);
if (!redundancies.ContainsKey(fileHash))
{
long fileSize = new FileInfo(filePath).Length;
redundancies.Add(fileHash, new Redundancy() { Hash = fileHash, FileSize = fileSize });
}
redundancies[fileHash].Paths.Add(filePath);
}
catch (Exception ex)
{
Console.WriteLine($"Error processing file {filePath}: {ex.Message}");
}
});
task.Start();
tasks.Add(task);
}
private string ComputeFileHash(string filePath)
{
using var sha256 = SHA256.Create();
using var stream = File.OpenRead(filePath);
var hash = sha256.ComputeHash(stream);
return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
}
}
}

View File

@ -0,0 +1,10 @@
namespace RedundancyFinder
{
public class Redundancy
{
public List<string> Paths { get; set; } = new List<string>();
public object Hash { get; set; }
public long FileSize { get; set; }
}
}

View File

@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,42 @@
using System;
using RedundancyFinder;
if (args.Length == 0)
{
Console.WriteLine("No arguments provided.");
return;
}
Finder finder = new Finder();
finder.FindRedundancies(new[] { "D:\\" }, new[] { ".jpg", ".jpeg", ".bmp", ".gif", ".mp4", ".mp3" });
foreach (var redundancy in finder.Redundancies.Values)
{
Console.WriteLine($"Hash: {redundancy.Hash}");
Console.WriteLine("Paths:");
foreach (var path in redundancy.Paths)
{
Console.WriteLine(path);
}
Console.WriteLine();
}
ulong totalSize = finder.Redundancies.Select(x => (ulong)x.Value.FileSize).Aggregate((a, b) => a + b);
string sizeUnit = "B";
while (totalSize > 1024)
{
totalSize /= 1024;
sizeUnit = sizeUnit switch
{
"B" => "KB",
"KB" => "MB",
"MB" => "GB",
"GB" => "TB",
_ => sizeUnit
};
}
Console.WriteLine($"Total Size: {totalSize:.##} {sizeUnit}");

View File

@ -0,0 +1,8 @@
{
"profiles": {
"RedundancyFinderCLI": {
"commandName": "Project",
"commandLineArgs": "D:\\"
}
}
}

View File

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\RedundancyFinder\RedundancyFinder.csproj" />
</ItemGroup>
</Project>

31
RedundancyFixer.sln Normal file
View File

@ -0,0 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.13.35818.85 d17.13
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RedundancyFinder", "RedundancyFinder\RedundancyFinder.csproj", "{925C533F-2205-4848-B742-CB013F81DF91}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RedundancyFinderCLI", "RedundancyFinderCLI\RedundancyFinderCLI.csproj", "{7187EE24-4F0D-48F3-B76C-DAECD4A96F76}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{925C533F-2205-4848-B742-CB013F81DF91}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{925C533F-2205-4848-B742-CB013F81DF91}.Debug|Any CPU.Build.0 = Debug|Any CPU
{925C533F-2205-4848-B742-CB013F81DF91}.Release|Any CPU.ActiveCfg = Release|Any CPU
{925C533F-2205-4848-B742-CB013F81DF91}.Release|Any CPU.Build.0 = Release|Any CPU
{7187EE24-4F0D-48F3-B76C-DAECD4A96F76}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7187EE24-4F0D-48F3-B76C-DAECD4A96F76}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7187EE24-4F0D-48F3-B76C-DAECD4A96F76}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7187EE24-4F0D-48F3-B76C-DAECD4A96F76}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {7519CD9B-403D-4046-9018-2A47691E024F}
EndGlobalSection
EndGlobal