using System.Collections.ObjectModel; using System.Text.Json; namespace View_by_Distance.Shared.Models.Stateless.Methods; internal abstract class Container { private record FilePair(bool IsUnique, List Collection, FilePath FilePath, Models.Item Item) { } internal static DateTime[] GetContainerDateTimes(ReadOnlyCollection items) { DateTime[] results; long containerMinimumTicks = (from l in items select l.FilePath.LastWriteTicks).Min(); long containerMaximumTicks = (from l in items select l.FilePath.LastWriteTicks).Max(); results = [new(containerMinimumTicks), new(containerMaximumTicks)]; return results; } internal static ReadOnlyCollection GetValidImageItems(Properties.IPropertyConfiguration propertyConfiguration, Models.Container container) { List results = []; foreach (Models.Item item in container.Items) { if (!item.IsValidImageFormatExtension || propertyConfiguration.IgnoreExtensions.Contains(item.FilePath.ExtensionLowered)) continue; results.Add(item); } return container.Items.Count == results.Count ? container.Items : new(results); } internal static List GetFilePairs(IDlibDotNet? dlibDotNet, Properties.IPropertyConfiguration propertyConfiguration, string directorySearchFilter, string extension, string aPropertySingletonDirectory, ReadOnlyCollection filesCollection) { int renamed; const bool useCeilingAverage = true; List? filePairs = null; ReadOnlyCollection? jsonFilesCollection = null; IReadOnlyDictionary>? compareFileNamesToFiles = null; IReadOnlyDictionary> fileNamesToFiles = XDirectory.GetFilesKeyValuePairs(filesCollection); for (int i = 0; i < short.MaxValue; i++) { renamed = 0; dlibDotNet?.Tick(); jsonFilesCollection = IDirectory.GetFilesCollection(aPropertySingletonDirectory, directorySearchFilter, extension, useCeilingAverage); compareFileNamesToFiles = XDirectory.GetFilesKeyValuePairs(jsonFilesCollection); renamed += XDirectory.LookForAbandoned(jsonFilesCollection, fileNamesToFiles, extension); filePairs = XDirectory.GetFiles(propertyConfiguration, filesCollection, fileNamesToFiles, extension, compareFileNamesToFiles); renamed += XDirectory.MaybeMove(propertyConfiguration, filePairs, aPropertySingletonDirectory, extension); if (renamed == 0) break; if (i > 10) throw new NotImplementedException(); } if (filePairs is null || jsonFilesCollection is null || compareFileNamesToFiles is null) throw new NullReferenceException(nameof(filePairs)); return filePairs; } private static Models.Property? GetProperty(Models.FilePair filePair) { Models.Property? result; if (filePair.Match is null) result = null; else { string json = File.ReadAllText(filePair.Match); if (string.IsNullOrEmpty(json)) result = null; else result = JsonSerializer.Deserialize(json, PropertyGenerationContext.Default.Property); } return result; } private static void ParallelFor(Properties.IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory, string extension, int rootDirectoryLength, Models.FilePair filePair, List results) { bool abandoned = false; Models.FileHolder sourceDirectoryFileHolder; Models.Property? property = GetProperty(filePair); Models.FileHolder imageFileHolder = IFileHolder.Get(filePair.Path); FilePath filePath = FilePath.Get(propertyConfiguration, imageFileHolder, index: null); bool? fileSizeChanged = property is not null ? property.FileSize != filePath.Length : null; bool isValidImageFormatExtension = propertyConfiguration.ValidImageFormatExtensions.Contains(filePath.ExtensionLowered); if (property is not null && property.Keywords is not null) { if (filePath.IsIgnore is null) throw new NullReferenceException(); bool shouldIgnore = propertyConfiguration.IgnoreRulesKeyWords.Any(l => property.Keywords.Contains(l)); if (shouldIgnore) { FileInfo fileInfo = new(filePath.FullName); if (!fileInfo.Attributes.HasFlag(FileAttributes.Hidden)) File.SetAttributes(imageFileHolder.FullName, FileAttributes.Hidden); } if (filePath.IsIgnore.Value != shouldIgnore) { if (filePath.DirectoryName.Contains("Results") && filePath.DirectoryName.Contains("Resize")) File.Delete(filePath.FullName); else throw new NotSupportedException($"Rename File! <{filePath.FileNameFirstSegment}>"); } } string relativePath = IPath.GetRelativePath(filePair.Path, rootDirectoryLength, forceExtensionToLower: true); bool? lastWriteTimeChanged = property is not null ? propertyConfiguration.PropertiesChangedForProperty || property.LastWriteTime.Ticks != filePath.LastWriteTicks : null; if (filePair.Match is not null) sourceDirectoryFileHolder = IFileHolder.Get(filePair.Match); else if (!filePair.IsUnique) sourceDirectoryFileHolder = IFileHolder.Get(Path.GetFullPath(string.Concat(aPropertySingletonDirectory, relativePath, extension))); else { string fileName = Path.GetFileName(filePair.Path); (string directoryName, _) = IPath.GetDirectoryNameAndIndex(propertyConfiguration, filePath); sourceDirectoryFileHolder = IFileHolder.Get(Path.Combine(aPropertySingletonDirectory, directoryName, $"{fileName}{extension}")); } if (sourceDirectoryFileHolder.CreationTime is not null && sourceDirectoryFileHolder.LastWriteTime is not null && filePath.LastWriteTicks != sourceDirectoryFileHolder.CreationTime.Value.Ticks) { File.SetCreationTime(sourceDirectoryFileHolder.FullName, new(filePath.LastWriteTicks)); File.SetLastWriteTime(sourceDirectoryFileHolder.FullName, sourceDirectoryFileHolder.LastWriteTime.Value); } Models.Item item = new(filePath, sourceDirectoryFileHolder, relativePath, filePair.IsNotUniqueAndNeedsReview, filePair.IsUnique, isValidImageFormatExtension, property, abandoned, fileSizeChanged, lastWriteTimeChanged); lock (results) results.Add(new(filePair.IsUnique, filePair.Collection, filePath, item)); } private static List GetFilePairs(IDlibDotNet? dlibDotNet, Properties.IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory, string filesCollectionDirectory, ReadOnlyCollection filesCollection, string directorySearchFilter) { List results = []; const string extension = ".json"; int maxDegreeOfParallelism = Environment.ProcessorCount; int filesCollectionDirectoryLength = filesCollectionDirectory.Length; ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism }; List filePairs = GetFilePairs(dlibDotNet, propertyConfiguration, directorySearchFilter, extension, aPropertySingletonDirectory, filesCollection); _ = Parallel.For(0, filePairs.Count, parallelOptions, (i, state) => ParallelFor(propertyConfiguration, aPropertySingletonDirectory, extension, filesCollectionDirectoryLength, filePairs[i], results)); return results; } private static (int, Models.Container[]) GetContainers(IDlibDotNet? dlibDotNet, Properties.IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory, string filesCollectionDirectory, ReadOnlyCollection filesCollection, string directorySearchFilter) { List results = []; string? directory; List? items; Models.Container container; List directories = []; Dictionary> directoryToItems = []; foreach (string[] files in filesCollection) { if (files.Length == 0) continue; directory = Path.GetDirectoryName(files.First()); if (directory is null) continue; if (!directories.Contains(directory)) directories.Add(directory); if (!directoryToItems.TryGetValue(directory, out items)) { directoryToItems.Add(directory, []); if (!directoryToItems.TryGetValue(directory, out items)) throw new Exception(); } } List filePairs = GetFilePairs(dlibDotNet, propertyConfiguration, aPropertySingletonDirectory, filesCollectionDirectory, filesCollection, directorySearchFilter); foreach (FilePair filePair in filePairs) { if (!directoryToItems.TryGetValue(filePair.FilePath.DirectoryName, out items)) { directoryToItems.Add(filePair.FilePath.DirectoryName, []); if (!directoryToItems.TryGetValue(filePair.FilePath.DirectoryName, out items)) throw new Exception(); } items.Add(filePair.Item); } foreach (KeyValuePair> keyValuePair in directoryToItems) { if (keyValuePair.Value.Count == 0) continue; container = new(keyValuePair.Key, new(keyValuePair.Value)); results.Add(container); } return (filePairs.Count, results.ToArray()); } internal static (int, Models.Container[]) GetContainers(IDlibDotNet dlibDotNet, Properties.IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory, string filesCollectionDirectory, ReadOnlyCollection filesCollection) { int count; Models.Container[] results; const string directorySearchFilter = "*"; (count, results) = GetContainers(dlibDotNet, propertyConfiguration, aPropertySingletonDirectory, filesCollectionDirectory, filesCollection, directorySearchFilter); return (count, results); } internal static (int, Models.Container[]) GetContainers(Properties.IPropertyConfiguration propertyConfiguration, string aPropertySingletonDirectory) { int count; Models.Container[] results; IDlibDotNet? dlibDotNet = null; const bool useCeilingAverage = true; const string fileSearchFilter = "*"; const string directorySearchFilter = "*"; ReadOnlyCollection filesCollection = IDirectory.GetFilesCollection(propertyConfiguration.RootDirectory, directorySearchFilter, fileSearchFilter, useCeilingAverage); (count, results) = GetContainers(dlibDotNet, propertyConfiguration, aPropertySingletonDirectory, propertyConfiguration.RootDirectory, filesCollection, directorySearchFilter); return (count, results); } internal static List GetFilteredDistinctIds(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection readOnlyContainers) { List results = []; ReadOnlyCollection filteredItems; foreach (Models.Container container in readOnlyContainers) { if (container.Items.Count == 0) continue; filteredItems = GetValidImageItems(propertyConfiguration, container); if (filteredItems.Count == 0) continue; foreach (Models.Item item in filteredItems) { if (item.Property?.Id is null || item.ResizedFileHolder is null) continue; if (results.Contains(item.Property.Id.Value)) continue; results.Add(item.Property.Id.Value); } } return results; } internal static List GetFilteredDistinctFileNameFirstSegments(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection readOnlyContainers) { List results = []; ReadOnlyCollection filteredItems; foreach (Models.Container container in readOnlyContainers) { if (container.Items.Count == 0) continue; filteredItems = GetValidImageItems(propertyConfiguration, container); if (filteredItems.Count == 0) continue; foreach (Models.Item item in filteredItems) { if (item.Property?.Id is null || item.ResizedFileHolder is null) continue; if (results.Contains(item.FilePath.FileNameFirstSegment)) continue; results.Add(item.FilePath.FileNameFirstSegment); } } return results; } internal static ReadOnlyCollection GetValidImageItems(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection containers, bool distinctItems, bool filterItems) { List results = []; List distinct = []; ReadOnlyCollection filteredItems; foreach (Models.Container container in containers) { if (container.Items.Count == 0) continue; if (!filterItems) filteredItems = container.Items; else { filteredItems = GetValidImageItems(propertyConfiguration, container); if (filteredItems.Count == 0) continue; } foreach (Models.Item item in filteredItems) { if (item.Property?.Id is null || item.ResizedFileHolder is null) continue; if (distinctItems) { if (distinct.Contains(item.Property.Id.Value)) continue; distinct.Add(item.Property.Id.Value); } results.Add(item); } } return new(results); } }