Changed GetDimensions to handle a stream at the end and one exit Switched to using Action? over IDlibDotNet for Tick method Switched to using AsReadOnly over new() Moved Meta Base to Shared
459 lines
21 KiB
C#
459 lines
21 KiB
C#
using System.Collections.ObjectModel;
|
|
|
|
namespace View_by_Distance.Shared.Models.Stateless.Methods;
|
|
|
|
internal abstract partial class XDirectory
|
|
{
|
|
|
|
internal static void MoveFiles(List<string> files, string find, string replace)
|
|
{
|
|
string checkFile;
|
|
string? checkDirectory;
|
|
List<string> directories = [];
|
|
foreach (string file in files)
|
|
{
|
|
checkDirectory = Path.GetDirectoryName(file.Replace(find, replace));
|
|
if (string.IsNullOrEmpty(checkDirectory) || directories.Contains(checkDirectory))
|
|
continue;
|
|
directories.Add(checkDirectory);
|
|
}
|
|
foreach (string directory in directories)
|
|
{
|
|
if (Directory.Exists(directory))
|
|
continue;
|
|
_ = Directory.CreateDirectory(directory);
|
|
}
|
|
foreach (string file in files)
|
|
{
|
|
if (!File.Exists(file))
|
|
continue;
|
|
checkFile = file.Replace(find, replace);
|
|
if (File.Exists(checkFile))
|
|
{
|
|
File.Delete(checkFile);
|
|
continue;
|
|
}
|
|
File.Move(file, checkFile);
|
|
}
|
|
}
|
|
|
|
internal static List<string> CopyOrMove(List<(FilePath, string)> toDoCollection, bool move, bool moveBack, Action? tick)
|
|
{
|
|
List<string> results = [];
|
|
FileInfo fileInfo;
|
|
List<string> distinctExtensions = [];
|
|
foreach ((FilePath filePath, string to) in toDoCollection)
|
|
{
|
|
tick?.Invoke();
|
|
fileInfo = new(to);
|
|
if (fileInfo.Exists)
|
|
{
|
|
if (filePath.Length == fileInfo.Length && filePath.LastWriteTicks == fileInfo.LastWriteTime.Ticks)
|
|
continue;
|
|
fileInfo.Delete();
|
|
}
|
|
results.Add(filePath.NameWithoutExtension);
|
|
try
|
|
{
|
|
if (!distinctExtensions.Contains(filePath.ExtensionLowered))
|
|
distinctExtensions.Add(filePath.ExtensionLowered);
|
|
if (move || moveBack)
|
|
File.Move(filePath.FullName, to);
|
|
else
|
|
File.Copy(filePath.FullName, to);
|
|
}
|
|
catch (Exception) { }
|
|
}
|
|
return results;
|
|
}
|
|
|
|
internal static ReadOnlyCollection<string[]> GetFilesCollection(string directory, string directorySearchFilter, string fileSearchFilter, bool useCeilingAverage)
|
|
{
|
|
List<string[]> results = [];
|
|
string[] files;
|
|
if (!fileSearchFilter.Contains('*'))
|
|
fileSearchFilter = string.Concat('*', fileSearchFilter);
|
|
if (!directorySearchFilter.Contains('*'))
|
|
directorySearchFilter = string.Concat('*', directorySearchFilter);
|
|
if (!Directory.Exists(directory))
|
|
_ = Directory.CreateDirectory(directory);
|
|
results.Add(Directory.GetFiles(directory, fileSearchFilter, SearchOption.TopDirectoryOnly));
|
|
string[] directories = Directory.GetDirectories(directory, directorySearchFilter, SearchOption.TopDirectoryOnly);
|
|
foreach (string innerDirectory in directories)
|
|
{
|
|
try
|
|
{
|
|
files = Directory.GetFiles(innerDirectory, fileSearchFilter, SearchOption.AllDirectories);
|
|
if (files.Length == 0)
|
|
continue;
|
|
results.Add(files);
|
|
}
|
|
catch (UnauthorizedAccessException)
|
|
{ continue; }
|
|
}
|
|
int ceilingAverage = directory[^1] == '_' || results.Count == 0 ? 0 : GetCeilingAverage(results);
|
|
if (useCeilingAverage)
|
|
results = GetFilesCollection(results, ceilingAverage);
|
|
return results.AsReadOnly();
|
|
}
|
|
|
|
private static int GetCeilingAverage(List<string[]> fileCollection)
|
|
{
|
|
List<int> counts = [];
|
|
foreach (string[] files in fileCollection)
|
|
counts.Add(files.Length);
|
|
int average = (int)Math.Ceiling(counts.Average());
|
|
return average;
|
|
}
|
|
|
|
private static List<string[]> GetFilesCollection(List<string[]> fileCollection, int ceilingAverage)
|
|
{
|
|
List<string[]> results = [];
|
|
foreach (string[] files in fileCollection)
|
|
{
|
|
if (files.Length < ceilingAverage)
|
|
results.Add(files);
|
|
}
|
|
foreach (string[] files in fileCollection)
|
|
{
|
|
if (files.Length >= ceilingAverage)
|
|
results.Add(files);
|
|
}
|
|
return results;
|
|
}
|
|
|
|
internal static int MaybeMove(Properties.IPropertyConfiguration propertyConfiguration, List<Models.FilePair> filePairs, string jsonGroupDirectory, string extension)
|
|
{
|
|
FileInfo? toFileInfo;
|
|
string checkDirectory;
|
|
List<(FilePath, string)> rename = [];
|
|
foreach (Models.FilePair filePair in filePairs)
|
|
{
|
|
if (filePair.IsUnique)
|
|
continue;
|
|
IsNotUniqueLoop(propertyConfiguration, jsonGroupDirectory, extension, filePair, rename);
|
|
}
|
|
foreach ((FilePath from, string to) in rename)
|
|
{
|
|
toFileInfo = null;
|
|
checkDirectory = to;
|
|
for (int i = 0; i < int.MaxValue; i++)
|
|
{
|
|
toFileInfo = new(checkDirectory);
|
|
if (toFileInfo.Directory is null)
|
|
continue;
|
|
if (!toFileInfo.Directory.Exists)
|
|
_ = Directory.CreateDirectory(toFileInfo.Directory.FullName);
|
|
if (checkDirectory.Length > 199)
|
|
throw new Exception();
|
|
if (!toFileInfo.Exists)
|
|
break;
|
|
else if (from.Length == toFileInfo.Length && from.LastWriteTicks == toFileInfo.LastWriteTime.Ticks)
|
|
checkDirectory = string.Concat(checkDirectory, ".del");
|
|
else
|
|
checkDirectory = string.Concat(checkDirectory, ".j");
|
|
}
|
|
File.Move(from.FullName, checkDirectory);
|
|
}
|
|
return rename.Count;
|
|
}
|
|
|
|
private static void IsNotUniqueLoop(Properties.IPropertyConfiguration propertyConfiguration, string jsonGroupDirectory, string extension, Models.FilePair filePair, List<(FilePath, string)> rename)
|
|
{
|
|
int length = propertyConfiguration.RootDirectory.Length;
|
|
foreach (FilePath path in filePair.Collection)
|
|
{
|
|
if (filePair.Match is null || path != filePair.Match)
|
|
continue;
|
|
rename.Add(new(path, string.Concat(jsonGroupDirectory, filePair.FilePath.FullName[length..], extension)));
|
|
}
|
|
}
|
|
|
|
internal static ReadOnlyCollection<ReadOnlyCollection<FilePath>> GetFilePathCollections(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<string[]> filesCollection, bool useIgnoreExtensions)
|
|
{
|
|
List<ReadOnlyCollection<FilePath>> results = [];
|
|
FilePath filePath;
|
|
List<FilePath> filePaths;
|
|
Models.FileHolder fileHolder;
|
|
foreach (string[] files in filesCollection)
|
|
{
|
|
filePaths = [];
|
|
foreach (string file in files)
|
|
{
|
|
fileHolder = IFileHolder.Get(file);
|
|
if (!fileHolder.Exists)
|
|
continue;
|
|
if (useIgnoreExtensions && propertyConfiguration.IgnoreExtensions.Contains(fileHolder.ExtensionLowered))
|
|
continue;
|
|
filePath = FilePath.Get(propertyConfiguration, fileHolder, index: null);
|
|
filePaths.Add(filePath);
|
|
}
|
|
results.Add(filePaths.AsReadOnly());
|
|
}
|
|
return results.AsReadOnly();
|
|
}
|
|
|
|
internal static ReadOnlyCollection<ReadOnlyCollection<FilePath>> GetFilePathCollections(Properties.IPropertyConfiguration propertyConfiguration, string directorySearchFilter, string fileSearchFilter, string directory, bool useIgnoreExtensions, bool useCeilingAverage)
|
|
{
|
|
ReadOnlyCollection<ReadOnlyCollection<FilePath>> results;
|
|
ReadOnlyCollection<string[]> filesCollection = GetFilesCollection(directory, directorySearchFilter, fileSearchFilter, useCeilingAverage);
|
|
results = IDirectory.GetFilePathCollections(propertyConfiguration, filesCollection, useIgnoreExtensions);
|
|
return results;
|
|
}
|
|
|
|
internal static List<Models.FilePair> GetFiles(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, IReadOnlyDictionary<int, ReadOnlyCollection<FilePath>> fileNamesToFiles, IReadOnlyDictionary<int, ReadOnlyCollection<FilePath>> compareFileNamesToFiles)
|
|
{
|
|
List<Models.FilePair> results = [];
|
|
FilePath? match;
|
|
bool uniqueFileName;
|
|
Models.FilePair filePair;
|
|
bool? isNotUniqueAndNeedsReview;
|
|
ReadOnlyCollection<FilePath>? collection;
|
|
foreach (ReadOnlyCollection<FilePath> filePaths in filePathsCollection)
|
|
{
|
|
foreach (FilePath filePath in filePaths)
|
|
{
|
|
if (filePath.Id is null)
|
|
continue;
|
|
isNotUniqueAndNeedsReview = null;
|
|
if (propertyConfiguration.IgnoreExtensions.Contains(filePath.ExtensionLowered))
|
|
continue;
|
|
if (!fileNamesToFiles.TryGetValue(filePath.Id.Value, out collection))
|
|
throw new Exception();
|
|
uniqueFileName = collection.Count == 1;
|
|
if (!uniqueFileName)
|
|
isNotUniqueAndNeedsReview = GetIsNotUniqueAndNeedsReview(filePath, collection);
|
|
if (!compareFileNamesToFiles.TryGetValue(filePath.Id.Value, out collection))
|
|
filePair = new(FilePath: filePath,
|
|
IsUnique: uniqueFileName,
|
|
IsNotUniqueAndNeedsReview: isNotUniqueAndNeedsReview,
|
|
Collection: new([]),
|
|
Match: null);
|
|
else
|
|
{
|
|
if (collection.Count == 0)
|
|
filePair = new(FilePath: filePath,
|
|
IsUnique: uniqueFileName,
|
|
IsNotUniqueAndNeedsReview: isNotUniqueAndNeedsReview,
|
|
Collection: collection,
|
|
Match: null);
|
|
else if (uniqueFileName && collection.Count == 1)
|
|
filePair = new(FilePath: filePath,
|
|
IsUnique: uniqueFileName,
|
|
IsNotUniqueAndNeedsReview: isNotUniqueAndNeedsReview,
|
|
Collection: collection,
|
|
Match: collection.First());
|
|
else
|
|
{
|
|
match = GetMatch(filePath, collection);
|
|
filePair = new(FilePath: filePath,
|
|
IsUnique: uniqueFileName,
|
|
IsNotUniqueAndNeedsReview: isNotUniqueAndNeedsReview,
|
|
Collection: collection,
|
|
Match: match);
|
|
}
|
|
}
|
|
results.Add(filePair);
|
|
}
|
|
}
|
|
return results;
|
|
}
|
|
|
|
private static bool GetIsNotUniqueAndNeedsReview(FilePath filePath, ReadOnlyCollection<FilePath> collection)
|
|
{
|
|
bool result = false;
|
|
long max;
|
|
foreach (FilePath possible in collection)
|
|
{
|
|
if (possible.FullName == filePath.FullName)
|
|
continue;
|
|
if (possible.LastWriteTicks != filePath.LastWriteTicks)
|
|
{
|
|
max = new long[] { possible.LastWriteTicks, filePath.LastWriteTicks }.Max();
|
|
File.SetLastWriteTime(filePath.FullName, new DateTime(max));
|
|
}
|
|
if (possible.LastWriteTicks == filePath.LastWriteTicks && possible.Length == filePath.Length)
|
|
continue;
|
|
if (!result)
|
|
result = true;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private static FilePath? GetMatch(FilePath filePath, ReadOnlyCollection<FilePath> collection)
|
|
{
|
|
FilePath? result = null;
|
|
List<long> lengths = [];
|
|
List<FilePath> matches = [];
|
|
List<long> lastWriteTicks = [];
|
|
foreach (FilePath possible in collection)
|
|
{
|
|
lengths.Add(possible.Length);
|
|
lastWriteTicks.Add(possible.LastWriteTicks);
|
|
if (possible.LastWriteTicks != filePath.LastWriteTicks)
|
|
continue;
|
|
matches.Add(possible);
|
|
}
|
|
if (matches.Count == 1 || (matches.Count > 0 && lengths.Distinct().Count() == 1 && lastWriteTicks.Distinct().Count() == 1))
|
|
result = matches.First();
|
|
return result;
|
|
}
|
|
|
|
internal static (string[], List<(FilePath, string)>) GetToDoCollection(Properties.IPropertyConfiguration propertyConfiguration, bool copyDuplicates, bool ifCanUseId, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, ReadOnlyDictionary<byte, ReadOnlyCollection<string>> fileGroups, ReadOnlyDictionary<int, ExifDirectory>? exifDirectoriesById, Action? tick)
|
|
{
|
|
List<(FilePath, string)> results = [];
|
|
string paddedId;
|
|
string checkFile;
|
|
string directory;
|
|
FilePath filePath;
|
|
DateTime? dateTime;
|
|
string paddedIdFile;
|
|
bool wrapped = false;
|
|
string intelligentId;
|
|
bool? hasIgnoreKeyword;
|
|
bool paddedCheck = false;
|
|
CombinedEnumAndIndex cei;
|
|
string fileDirectoryName;
|
|
bool? hasDateTimeOriginal;
|
|
List<int> distinctIds = [];
|
|
List<string> distinct = [];
|
|
ExifDirectory? exifDirectory;
|
|
Models.FileHolder fileHolder;
|
|
ReadOnlyCollection<string> keywords;
|
|
List<string> distinctDirectories = [];
|
|
FilePath[] sortedRecords = GetSortedRecords(filePathsCollection);
|
|
bool isOffsetDeterministicHashCode = IId.IsOffsetDeterministicHashCode(propertyConfiguration);
|
|
for (int i = 0; i < sortedRecords.Length; i++)
|
|
{
|
|
tick?.Invoke();
|
|
filePath = sortedRecords[i];
|
|
if (filePath.Name.EndsWith("len") || filePath.ExtensionLowered == ".id" || filePath.ExtensionLowered == ".lsv" || filePath.DirectoryFullPath is null)
|
|
continue;
|
|
fileDirectoryName = Path.GetFileName(filePath.DirectoryFullPath);
|
|
cei = IPath.GetCombinedEnumAndIndex(propertyConfiguration, filePath);
|
|
if (fileDirectoryName.Length < propertyConfiguration.ResultAllInOneSubdirectoryLength + 3 || !filePath.Name.StartsWith(fileDirectoryName))
|
|
{
|
|
if (wrapped)
|
|
continue;
|
|
if (cei.Enum == 0)
|
|
continue;
|
|
directory = fileGroups[cei.Enum][cei.Index];
|
|
}
|
|
else
|
|
{
|
|
if (!wrapped)
|
|
wrapped = true;
|
|
directory = Path.Combine(fileGroups[cei.Enum][cei.Index], fileDirectoryName);
|
|
}
|
|
if (ifCanUseId && filePath.IsIntelligentIdFormat && filePath.Id is not null && filePath.DirectoryFullPath is not null)
|
|
{
|
|
if (filePath.Id == -748161839 || filePath.FileNameFirstSegment == "740318810015")
|
|
{
|
|
if (filePath.ExtensionLowered == ".mov") // -748161839)
|
|
{ }
|
|
}
|
|
if (exifDirectoriesById is null || !exifDirectoriesById.TryGetValue(filePath.Id.Value, out exifDirectory))
|
|
{
|
|
hasIgnoreKeyword = filePath.HasIgnoreKeyword;
|
|
hasDateTimeOriginal = filePath.HasDateTimeOriginal;
|
|
}
|
|
else
|
|
{
|
|
dateTime = IDate.GetDateTimeOriginal(exifDirectory);
|
|
hasDateTimeOriginal = dateTime is not null;
|
|
if (dateTime is null && propertyConfiguration.ValidVideoFormatExtensions.Contains(filePath.ExtensionLowered))
|
|
continue;
|
|
keywords = MetaBase.GetKeywords(exifDirectory?.ExifBaseDirectories);
|
|
hasIgnoreKeyword = propertyConfiguration.IgnoreRulesKeyWords.Any(keywords.Contains);
|
|
}
|
|
paddedId = IId.GetPaddedId(propertyConfiguration, filePath.Id.Value, filePath.ExtensionLowered, hasIgnoreKeyword, hasDateTimeOriginal, i);
|
|
paddedIdFile = Path.Combine(filePath.DirectoryFullPath, $"{paddedId}{filePath.ExtensionLowered}");
|
|
if (!File.Exists(paddedIdFile))
|
|
{
|
|
File.Move(filePath.FullName, paddedIdFile);
|
|
fileHolder = IFileHolder.Get(paddedIdFile);
|
|
if (!fileHolder.Exists)
|
|
continue;
|
|
filePath = FilePath.Get(propertyConfiguration, fileHolder, index: null);
|
|
if (!paddedCheck)
|
|
paddedCheck = true;
|
|
}
|
|
}
|
|
if (filePath.IsIntelligentIdFormat || !ifCanUseId)
|
|
checkFile = Path.Combine(directory, $"{filePath.NameWithoutExtension}{filePath.ExtensionLowered}");
|
|
else
|
|
{
|
|
if (filePath.Id is null)
|
|
throw new NullReferenceException(nameof(filePath.Id));
|
|
intelligentId = IId.GetIntelligentId(propertyConfiguration, filePath.Id.Value, filePath.ExtensionLowered, filePath.HasIgnoreKeyword, filePath.HasDateTimeOriginal);
|
|
if (!isOffsetDeterministicHashCode)
|
|
checkFile = Path.Combine(directory, $"{intelligentId}{filePath.ExtensionLowered}");
|
|
else
|
|
{
|
|
if (filePath.DirectoryFullPath is null)
|
|
continue;
|
|
paddedId = IId.GetPaddedId(propertyConfiguration, filePath.Id.Value, filePath.ExtensionLowered, filePath.HasIgnoreKeyword, filePath.HasDateTimeOriginal, i);
|
|
paddedIdFile = Path.Combine(filePath.DirectoryFullPath, $"{paddedId}{filePath.ExtensionLowered}");
|
|
if (File.Exists(paddedIdFile))
|
|
continue;
|
|
File.Move(filePath.FullName, paddedIdFile);
|
|
if (!paddedCheck)
|
|
paddedCheck = true;
|
|
continue;
|
|
}
|
|
}
|
|
if ((filePath.Id is not null && distinctIds.Contains(filePath.Id.Value)) || distinct.Contains(checkFile))
|
|
{
|
|
if (string.IsNullOrEmpty(filePath.DirectoryFullPath))
|
|
continue;
|
|
if (!copyDuplicates)
|
|
continue;
|
|
for (int j = 1; j < int.MaxValue; j++)
|
|
{
|
|
fileHolder = IFileHolder.Get(checkFile);
|
|
if (!fileHolder.Exists || fileHolder.LastWriteTime is null || filePath.Length == fileHolder.Length && filePath.LastWriteTicks == fileHolder.LastWriteTime.Value.Ticks)
|
|
checkFile = Path.Combine(directory, $"{filePath.NameWithoutExtension}.{j}dup{filePath.ExtensionLowered}");
|
|
else
|
|
checkFile = Path.Combine(directory, $"{filePath.NameWithoutExtension}.{j}why{filePath.ExtensionLowered}");
|
|
if (filePath.Id is not null)
|
|
{
|
|
if (distinctIds.Contains(filePath.Id.Value))
|
|
continue;
|
|
distinctIds.Add(filePath.Id.Value);
|
|
}
|
|
if (distinct.Contains(checkFile))
|
|
continue;
|
|
distinct.Add(checkFile);
|
|
results.Add(new(filePath, checkFile));
|
|
if (!distinctDirectories.Contains(directory))
|
|
distinctDirectories.Add(directory);
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
distinct.Add(checkFile);
|
|
if (filePath.Id is not null)
|
|
distinctIds.Add(filePath.Id.Value);
|
|
results.Add(new(filePath, checkFile));
|
|
if (!distinctDirectories.Contains(directory))
|
|
distinctDirectories.Add(directory);
|
|
}
|
|
if (!isOffsetDeterministicHashCode)
|
|
throw new Exception("Change Configuration Offset after creating iso file with images sorted!");
|
|
if (paddedCheck)
|
|
throw new Exception("Maybe need to restart application!");
|
|
return (distinctDirectories.ToArray(), results);
|
|
}
|
|
|
|
private static FilePath[] GetSortedRecords(ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection)
|
|
{
|
|
List<FilePath> results = [];
|
|
foreach (ReadOnlyCollection<FilePath> filePaths in filePathsCollection)
|
|
{
|
|
foreach (FilePath filePath in filePaths)
|
|
results.Add(filePath);
|
|
}
|
|
return (from l in results orderby l.CreationTicks, l.FullName.Length descending select l).ToArray();
|
|
}
|
|
|
|
} |