initial commit
This commit is contained in:
parent
f5e17b8c7b
commit
37c33f3e15
1
.gitignore
vendored
1
.gitignore
vendored
@ -400,3 +400,4 @@ FodyWeavers.xsd
|
||||
# JetBrains Rider
|
||||
*.sln.iml
|
||||
|
||||
TestData/
|
||||
118
RedundancyFinder/Finder.cs
Normal file
118
RedundancyFinder/Finder.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
10
RedundancyFinder/Redundancy.cs
Normal file
10
RedundancyFinder/Redundancy.cs
Normal 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; }
|
||||
}
|
||||
}
|
||||
9
RedundancyFinder/RedundancyFinder.csproj
Normal file
9
RedundancyFinder/RedundancyFinder.csproj
Normal file
@ -0,0 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
42
RedundancyFinderCLI/Program.cs
Normal file
42
RedundancyFinderCLI/Program.cs
Normal 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}");
|
||||
8
RedundancyFinderCLI/Properties/launchSettings.json
Normal file
8
RedundancyFinderCLI/Properties/launchSettings.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"profiles": {
|
||||
"RedundancyFinderCLI": {
|
||||
"commandName": "Project",
|
||||
"commandLineArgs": "D:\\"
|
||||
}
|
||||
}
|
||||
}
|
||||
14
RedundancyFinderCLI/RedundancyFinderCLI.csproj
Normal file
14
RedundancyFinderCLI/RedundancyFinderCLI.csproj
Normal 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
31
RedundancyFixer.sln
Normal 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
|
||||
Loading…
x
Reference in New Issue
Block a user