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
|
# JetBrains Rider
|
||||||
*.sln.iml
|
*.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