Switch to ExifDirectory from Property

This commit is contained in:
2025-04-06 18:23:57 -07:00
parent 3f7affceef
commit c7ded16e50
50 changed files with 2647 additions and 1846 deletions

View File

@ -67,57 +67,6 @@ internal abstract partial class XDirectory
return results;
}
internal static int MaybeMove(Properties.IPropertyConfiguration propertyConfiguration, List<FilePair> filePairs, string jsonGroupDirectory, string extension)
{
FileInfo? toFileInfo;
FileInfo fromFileInfo;
string checkDirectory;
List<(string, string)> rename = [];
foreach (FilePair filePair in filePairs)
{
if (filePair.IsUnique)
continue;
IsNotUniqueLoop(propertyConfiguration, jsonGroupDirectory, extension, filePair, rename);
}
foreach ((string from, string to) in rename)
{
toFileInfo = null;
checkDirectory = to;
fromFileInfo = new(from);
if (!fromFileInfo.Exists)
continue;
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 (fromFileInfo.Length == toFileInfo.Length && fromFileInfo.LastWriteTime == toFileInfo.LastWriteTime)
checkDirectory = string.Concat(checkDirectory, ".del");
else
checkDirectory = string.Concat(checkDirectory, ".j");
}
File.Move(from, checkDirectory);
}
return rename.Count;
}
private static void IsNotUniqueLoop(Properties.IPropertyConfiguration propertyConfiguration, string jsonGroupDirectory, string extension, FilePair filePair, List<(string, string)> rename)
{
int length = propertyConfiguration.RootDirectory.Length;
foreach (string path in filePair.Collection)
{
if (filePair.Match is null || path != filePair.Match)
continue;
rename.Add(new(path, string.Concat(jsonGroupDirectory, filePair.Path[length..], extension)));
}
}
internal static ReadOnlyCollection<string[]> GetFilesCollection(string directory, string directorySearchFilter, string fileSearchFilter, bool useCeilingAverage)
{
List<string[]> results = [];
@ -173,7 +122,54 @@ internal abstract partial class XDirectory
return results;
}
internal static ReadOnlyCollection<ReadOnlyCollection<FilePath>> GetFilePathCollections(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<string[]> filesCollection)
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;
@ -185,7 +181,9 @@ internal abstract partial class XDirectory
foreach (string file in files)
{
fileHolder = IFileHolder.Get(file);
if (propertyConfiguration.IgnoreExtensions.Contains(fileHolder.ExtensionLowered))
if (!fileHolder.Exists)
continue;
if (useIgnoreExtensions && propertyConfiguration.IgnoreExtensions.Contains(fileHolder.ExtensionLowered))
continue;
filePath = FilePath.Get(propertyConfiguration, fileHolder, index: null);
filePaths.Add(filePath);
@ -195,38 +193,38 @@ internal abstract partial class XDirectory
return results.AsReadOnly();
}
internal static ReadOnlyCollection<ReadOnlyCollection<FilePath>> GetFilePathCollections(Properties.IPropertyConfiguration propertyConfiguration, string directorySearchFilter, string fileSearchFilter, string directory, bool useCeilingAverage)
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);
results = IDirectory.GetFilePathCollections(propertyConfiguration, filesCollection, useIgnoreExtensions);
return results;
}
internal static List<FilePair> GetFiles(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, IReadOnlyDictionary<string, List<string>> fileNamesToFiles, IReadOnlyDictionary<string, List<string>> compareFileNamesToFiles)
internal static List<Models.FilePair> GetFiles(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, IReadOnlyDictionary<int, List<FilePath>> fileNamesToFiles, IReadOnlyDictionary<int, List<FilePath>> compareFileNamesToFiles)
{
List<FilePair> results = [];
string? match;
FilePair filePair;
List<Models.FilePair> results = [];
FilePath? match;
bool uniqueFileName;
List<string>? collection;
List<FilePath>? collection;
Models.FilePair filePair;
bool? isNotUniqueAndNeedsReview;
string fileNameWithoutExtensionMinusOne;
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;
fileNameWithoutExtensionMinusOne = filePath.NameWithoutExtension[..^1];
if (!fileNamesToFiles.TryGetValue(fileNameWithoutExtensionMinusOne, out collection))
if (!fileNamesToFiles.TryGetValue(filePath.Id.Value, out collection))
throw new Exception();
uniqueFileName = collection.Count == 1;
if (!uniqueFileName)
isNotUniqueAndNeedsReview = GetIsNotUniqueAndNeedsReview(filePath.FullName, collection);
if (!compareFileNamesToFiles.TryGetValue(fileNameWithoutExtensionMinusOne, out collection))
filePair = new(Path: filePath.FullName,
isNotUniqueAndNeedsReview = GetIsNotUniqueAndNeedsReview(filePath, collection);
if (!compareFileNamesToFiles.TryGetValue(filePath.Id.Value, out collection))
filePair = new(FilePath: filePath,
IsUnique: uniqueFileName,
IsNotUniqueAndNeedsReview: isNotUniqueAndNeedsReview,
Collection: [],
@ -234,21 +232,21 @@ internal abstract partial class XDirectory
else
{
if (collection.Count == 0)
filePair = new(Path: filePath.FullName,
filePair = new(FilePath: filePath,
IsUnique: uniqueFileName,
IsNotUniqueAndNeedsReview: isNotUniqueAndNeedsReview,
Collection: collection,
Match: null);
else if (uniqueFileName && collection.Count == 1)
filePair = new(Path: filePath.FullName,
filePair = new(FilePath: filePath,
IsUnique: uniqueFileName,
IsNotUniqueAndNeedsReview: isNotUniqueAndNeedsReview,
Collection: collection,
Match: collection.First());
else
{
match = GetMatch(filePath.FullName, collection);
filePair = new(Path: filePath.FullName,
match = GetMatch(filePath, collection);
filePair = new(FilePath: filePath,
IsUnique: uniqueFileName,
IsNotUniqueAndNeedsReview: isNotUniqueAndNeedsReview,
Collection: collection,
@ -261,19 +259,20 @@ internal abstract partial class XDirectory
return results;
}
private static bool GetIsNotUniqueAndNeedsReview(string file, List<string> collection)
private static bool GetIsNotUniqueAndNeedsReview(FilePath filePath, List<FilePath> collection)
{
bool result = false;
FileInfo possibleFileInfo;
FileInfo fileInfo = new(file);
foreach (string possible in collection)
long max;
foreach (FilePath possible in collection)
{
if (possible == file)
if (possible.FullName == filePath.FullName)
continue;
possibleFileInfo = new(possible);
if (possibleFileInfo.LastWriteTime != fileInfo.LastWriteTime)
File.SetLastWriteTime(file, new DateTime[] { possibleFileInfo.LastWriteTime, fileInfo.LastWriteTime }.Max());
if (possibleFileInfo.LastWriteTime == fileInfo.LastWriteTime && possibleFileInfo.Length == fileInfo.Length)
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;
@ -281,50 +280,48 @@ internal abstract partial class XDirectory
return result;
}
private static string? GetMatch(string file, List<string> collection)
private static FilePath? GetMatch(FilePath filePath, List<FilePath> collection)
{
string? result = null;
FileInfo possibleFileInfo;
FilePath? result = null;
List<long> lengths = [];
List<string> matches = [];
FileInfo fileInfo = new(file);
List<DateTime> creationTimes = [];
foreach (string possible in collection)
List<FilePath> matches = [];
List<long> lastWriteTicks = [];
foreach (FilePath possible in collection)
{
possibleFileInfo = new(possible);
lengths.Add(possibleFileInfo.Length);
creationTimes.Add(possibleFileInfo.CreationTime);
if (possibleFileInfo.CreationTime != fileInfo.LastWriteTime)
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 && creationTimes.Distinct().Count() == 1))
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, IReadOnlyDictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> fileGroups, Action? tick)
internal static (string[], List<(FilePath, string)>) GetToDoCollection(Properties.IPropertyConfiguration propertyConfiguration, bool copyDuplicates, bool ifCanUseId, ReadOnlyCollection<ReadOnlyCollection<FilePath>> filePathsCollection, ReadOnlyDictionary<byte, ReadOnlyCollection<string>> fileGroups, Dictionary<int, ExifDirectory>? exifDirectoriesById, Action? tick)
{
List<(FilePath, string)> results = [];
string paddedId;
string checkFile;
string directory;
FileInfo fileInfo;
FilePath filePath;
DateTime? dateTime;
string paddedIdFile;
bool wrapped = false;
string intelligentId;
CombinedEnumAndIndex cei;
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);
ReadOnlyDictionary<byte, ReadOnlyCollection<string>>? keyValuePairs;
if (!fileGroups.TryGetValue(propertyConfiguration.ResultContent, out keyValuePairs))
throw new NotImplementedException();
bool isOffsetDeterministicHashCode = IId.IsOffsetDeterministicHashCode(propertyConfiguration);
for (int i = 0; i < sortedRecords.Length; i++)
{
@ -338,23 +335,45 @@ internal abstract partial class XDirectory
{
if (wrapped)
continue;
directory = keyValuePairs[cei.Enum][cei.Index];
if (cei.Enum == 0)
continue;
directory = fileGroups[cei.Enum][cei.Index];
}
else
{
if (!wrapped)
wrapped = true;
directory = Path.Combine(keyValuePairs[cei.Enum][cei.Index], fileDirectoryName);
directory = Path.Combine(fileGroups[cei.Enum][cei.Index], fileDirectoryName);
}
if (ifCanUseId && filePath.IsIntelligentIdFormat && filePath.Id is not null && filePath.DirectoryFullPath is not null)
{
paddedId = IId.GetPaddedId(propertyConfiguration, filePath.Id.Value, filePath.HasIgnoreKeyword, filePath.HasDateTimeOriginal, i);
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);
fileInfo = new(paddedIdFile);
fileHolder = Models.FileHolder.Get(fileInfo);
fileHolder = IFileHolder.Get(paddedIdFile);
if (!fileHolder.Exists)
continue;
filePath = FilePath.Get(propertyConfiguration, fileHolder, index: null);
if (!paddedCheck)
paddedCheck = true;
@ -366,14 +385,14 @@ internal abstract partial class XDirectory
{
if (filePath.Id is null)
throw new NullReferenceException(nameof(filePath.Id));
intelligentId = IId.GetIntelligentId(propertyConfiguration, filePath.Id.Value, filePath.HasIgnoreKeyword, filePath.HasDateTimeOriginal);
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.HasIgnoreKeyword, filePath.HasDateTimeOriginal, i);
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;
@ -391,8 +410,8 @@ internal abstract partial class XDirectory
continue;
for (int j = 1; j < int.MaxValue; j++)
{
fileInfo = new(checkFile);
if (!fileInfo.Exists || filePath.Length == fileInfo.Length && filePath.LastWriteTicks == fileInfo.LastWriteTime.Ticks)
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}");