using ShellProgressBar; using System.Text; using System.Text.Json; using View_by_Distance.Property.Models.Stateless; using View_by_Distance.Shared.Models; using View_by_Distance.Shared.Models.Properties; namespace View_by_Distance.Property.Models; public class A_Property { protected readonly List _ExceptionsDirectories; public bool Reverse { get; } public List ExceptionsDirectories => _ExceptionsDirectories; private readonly Serilog.ILogger? _Log; private readonly string _OutputExtension; private readonly int _MaxDegreeOfParallelism; private readonly ASCIIEncoding _ASCIIEncoding; private readonly Configuration _Configuration; private readonly List _AngleBracketCollection; private readonly IPropertyConfiguration _PropertyConfiguration; private readonly IReadOnlyDictionary _FileGroups; private readonly JsonSerializerOptions _WriteIndentedJsonSerializerOptions; public A_Property(int maxDegreeOfParallelism, Configuration propertyConfiguration, string outputExtension, bool reverse, string aResultsFullGroupDirectory) { Reverse = reverse; _ExceptionsDirectories = new(); _OutputExtension = outputExtension; _ASCIIEncoding = new ASCIIEncoding(); _Configuration = propertyConfiguration; _Log = Serilog.Log.ForContext(); _AngleBracketCollection = new List(); _PropertyConfiguration = propertyConfiguration; _MaxDegreeOfParallelism = maxDegreeOfParallelism; _WriteIndentedJsonSerializerOptions = new JsonSerializerOptions { WriteIndented = true }; _FileGroups = Shared.Models.Stateless.Methods.IPath.GetKeyValuePairs(propertyConfiguration, aResultsFullGroupDirectory, new string[] { propertyConfiguration.ResultSingleton }); } public override string ToString() { string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true }); return result; } private long LogDelta(long ticks, string? methodName) { long result; if (_Log is null) throw new NullReferenceException(nameof(_Log)); double delta = new TimeSpan(DateTime.Now.Ticks - ticks).TotalMilliseconds; _Log.Debug($"{methodName} took {Math.Floor(delta)} millisecond(s)"); result = DateTime.Now.Ticks; return result; } private Shared.Models.Property GetImageProperty(Shared.Models.Methods.IMetadata metadata, Item item, List> sourceDirectoryFileTuples, List parseExceptions, bool isIgnoreExtension) { Shared.Models.Property? result; int? id = null; FileInfo fileInfo; string? json = null; bool hasWrongYearProperty = false; string[] changesFrom = Array.Empty(); string angleBracket = _AngleBracketCollection[0]; bool populateId = _Configuration.PopulatePropertyId; if (!item.IsUniqueFileName) fileInfo = new(Path.Combine(angleBracket.Replace("<>", _PropertyConfiguration.ResultSingleton), $"{item.ImageFileHolder.NameWithoutExtension}{item.ImageFileHolder.ExtensionLowered}.json")); else { (_, int directoryIndex) = Shared.Models.Stateless.Methods.IPath.GetDirectoryNameAndIndex(_PropertyConfiguration.ResultAllInOneSubdirectoryLength, item.ImageFileHolder.Name); fileInfo = new(Path.Combine(_FileGroups[_PropertyConfiguration.ResultSingleton][directoryIndex], $"{item.ImageFileHolder.NameWithoutExtension}{item.ImageFileHolder.ExtensionLowered}.json")); } List dateTimes = (from l in sourceDirectoryFileTuples where l is not null && changesFrom.Contains(l.Item1) select l.Item2).ToList(); if (_Configuration.ForcePropertyLastWriteTimeToCreationTime && !fileInfo.Exists && File.Exists(Path.ChangeExtension(fileInfo.FullName, ".delete"))) { File.Move(Path.ChangeExtension(fileInfo.FullName, ".delete"), fileInfo.FullName); fileInfo.Refresh(); } if (_Configuration.ForcePropertyLastWriteTimeToCreationTime && fileInfo.Exists && fileInfo.LastWriteTime != fileInfo.CreationTime) { File.SetLastWriteTime(fileInfo.FullName, fileInfo.CreationTime); fileInfo.Refresh(); } if (_Configuration.PropertiesChangedForProperty) result = null; else if (!fileInfo.Exists) result = null; else if (!fileInfo.FullName.EndsWith(".json") && !fileInfo.FullName.EndsWith(".old")) throw new ArgumentException("must be a *.json file"); else if (dateTimes.Any() && dateTimes.Max() > fileInfo.LastWriteTime) result = null; else { json = File.ReadAllText(fileInfo.FullName); try { if (item.Property is not null) result = item.Property; else result = JsonSerializer.Deserialize(json); if (result is not null && json.Contains("WrongYear")) { id = result.Id; hasWrongYearProperty = true; result = null; } if (!isIgnoreExtension && item.IsValidImageFormatExtension && ((populateId && result?.Id is null) || result?.Width is null || result.Height is null)) { id = result?.Id; result = null; } if (!isIgnoreExtension && item.IsValidImageFormatExtension && populateId && result is not null && result.LastWriteTime != item.ImageFileHolder.LastWriteTime) { id = null; result = null; } if (!isIgnoreExtension && item.IsValidImageFormatExtension && result?.Width is not null && result.Height is not null && result.Width.Value == result.Height.Value && item.ImageFileHolder.Exists) { id = result.Id; result = null; if (result?.Width is not null && result.Height is not null && result.Width.Value != result.Height.Value) throw new Exception("Was square!"); } if (!isIgnoreExtension && item.IsValidImageFormatExtension && result is not null && result.FileSize != item.ImageFileHolder.Length) { id = result.Id; result = null; } if (result is not null) { sourceDirectoryFileTuples.Add(new Tuple(nameof(A_Property), fileInfo.LastWriteTime)); if (fileInfo.CreationTime != result.LastWriteTime) { File.SetCreationTime(fileInfo.FullName, result.LastWriteTime); File.SetLastWriteTime(fileInfo.FullName, fileInfo.LastWriteTime); } } } catch (Exception) { result = null; parseExceptions.Add(nameof(A_Property)); } } if (result is null) { id ??= item.ImageFileHolder.Id; (_, _, result) = Stateless.Property.GetProperty(populateId, metadata, item.ImageFileHolder, result, isIgnoreExtension, item.IsValidImageFormatExtension, id, _ASCIIEncoding); json = JsonSerializer.Serialize(result, _WriteIndentedJsonSerializerOptions); if (populateId && Shared.Models.Stateless.Methods.IPath.WriteAllText(fileInfo.FullName, json, updateDateWhenMatches: true, compareBeforeWrite: true)) { File.SetCreationTime(fileInfo.FullName, result.LastWriteTime); if (!_Configuration.ForcePropertyLastWriteTimeToCreationTime) sourceDirectoryFileTuples.Add(new Tuple(nameof(A_Property), DateTime.Now)); else { File.SetLastWriteTime(fileInfo.FullName, fileInfo.CreationTime); fileInfo.Refresh(); sourceDirectoryFileTuples.Add(new Tuple(nameof(A_Property), fileInfo.CreationTime)); } } } else if (hasWrongYearProperty) { json = JsonSerializer.Serialize(result, _WriteIndentedJsonSerializerOptions); if (Shared.Models.Stateless.Methods.IPath.WriteAllText(fileInfo.FullName, json, updateDateWhenMatches: true, compareBeforeWrite: true)) { File.SetCreationTime(fileInfo.FullName, result.LastWriteTime); File.SetLastWriteTime(fileInfo.FullName, fileInfo.CreationTime); fileInfo.Refresh(); sourceDirectoryFileTuples.Add(new Tuple(nameof(A_Property), fileInfo.CreationTime)); } } return result; } private void SavePropertyParallelForWork(Shared.Models.Methods.IMetadata metadata, string sourceDirectory, List> sourceDirectoryFileTuples, List> sourceDirectoryChanges, Item item) { Shared.Models.Property property; List parseExceptions = new(); bool isIgnoreExtension = item.IsValidImageFormatExtension && _PropertyConfiguration.IgnoreExtensions.Contains(item.ImageFileHolder.ExtensionLowered); string filteredSourceDirectoryFileExtensionLowered = Path.Combine(sourceDirectory, $"{item.ImageFileHolder.NameWithoutExtension}{item.ImageFileHolder.ExtensionLowered}"); if (item.IsValidImageFormatExtension && item.ImageFileHolder.FullName.Length == filteredSourceDirectoryFileExtensionLowered.Length && item.ImageFileHolder.FullName != filteredSourceDirectoryFileExtensionLowered) File.Move(item.ImageFileHolder.FullName, filteredSourceDirectoryFileExtensionLowered); if (item.FileSizeChanged is null || item.FileSizeChanged.Value || item.LastWriteTimeChanged is null || item.LastWriteTimeChanged.Value || item.Property is null) { property = GetImageProperty(metadata, item, sourceDirectoryFileTuples, parseExceptions, isIgnoreExtension); lock (sourceDirectoryChanges) sourceDirectoryChanges.Add(new Tuple(nameof(A_Property), DateTime.Now)); lock (item) item.Update(property); } } private void SavePropertyParallelWork(int maxDegreeOfParallelism, Shared.Models.Methods.IMetadata metadata, List exceptions, List> sourceDirectoryChanges, Container container, List items, string message) { List> sourceDirectoryFileTuples = new(); ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism }; ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; using ProgressBar progressBar = new(items.Count, message, options); _ = Parallel.For(0, items.Count, parallelOptions, (i, state) => { try { long ticks = DateTime.Now.Ticks; DateTime dateTime = DateTime.Now; List> collection; SavePropertyParallelForWork(metadata, container.SourceDirectory, sourceDirectoryChanges, sourceDirectoryFileTuples, items[i]); if (i == 0 || sourceDirectoryChanges.Any()) progressBar.Tick(); lock (sourceDirectoryFileTuples) collection = (from l in sourceDirectoryFileTuples where l.Item2 > dateTime select l).ToList(); lock (sourceDirectoryChanges) sourceDirectoryChanges.AddRange(collection); } catch (Exception ex) { lock (exceptions) exceptions.Add(ex); } }); } public void SetAngleBracketCollection(string aResultsFullGroupDirectory, string sourceDirectory, bool anyNullOrNoIsUniqueFileName = true) { _AngleBracketCollection.Clear(); if (!anyNullOrNoIsUniqueFileName) _AngleBracketCollection.AddRange(new[] { Path.Combine(aResultsFullGroupDirectory, "<>") }); else _AngleBracketCollection.AddRange(IResult.GetDirectoryInfoCollection(_PropertyConfiguration, sourceDirectory, aResultsFullGroupDirectory, contentDescription: string.Empty, singletonDescription: "Properties for each image", collectionDescription: string.Empty, converted: false)); } private void SetAngleBracketCollection(string sourceDirectory, bool anyNullOrNoIsUniqueFileName) { _AngleBracketCollection.Clear(); string aResultsFullGroupDirectory = IResult.GetResultsFullGroupDirectory(_PropertyConfiguration, nameof(A_Property), string.Empty, includeResizeGroup: false, includeModel: false, includePredictorModel: false); SetAngleBracketCollection(aResultsFullGroupDirectory, sourceDirectory, anyNullOrNoIsUniqueFileName); } public void SavePropertyParallelWork(long ticks, Shared.Models.Methods.IMetadata metadata, int t, Container[] containers) { if (_Log is null) throw new NullReferenceException(nameof(_Log)); int total = 0; string message; int totalSeconds; Container container; bool anyNullOrNoIsUniqueFileName; List exceptions = new(); int containersLength = containers.Length; const string outputResolution = "Original"; List> sourceDirectoryChanges = new(); string propertyRoot = IResult.GetResultsGroupDirectory(_PropertyConfiguration, nameof(A_Property)); for (int i = 0; i < containers.Length; i++) { container = containers[i]; if (!container.Items.Any()) continue; sourceDirectoryChanges.Clear(); if (!container.Items.Any()) continue; anyNullOrNoIsUniqueFileName = container.Items.Any(l => !l.IsUniqueFileName); SetAngleBracketCollection(container.SourceDirectory, anyNullOrNoIsUniqueFileName); totalSeconds = (int)Math.Truncate(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds); message = $"{i + 1:000} [{container.Items.Count:000}] / {containersLength:000} - {total} / {t} total - {totalSeconds} total second(s) - {outputResolution} - {container.SourceDirectory}"; SavePropertyParallelWork(_MaxDegreeOfParallelism, metadata, exceptions, sourceDirectoryChanges, container, container.Items, message); foreach (Exception exception in exceptions) _Log.Error(string.Concat(container.SourceDirectory, Environment.NewLine, exception.Message, Environment.NewLine, exception.StackTrace), exception); if (exceptions.Count == container.Items.Count) throw new Exception(string.Concat("All in [", container.SourceDirectory, "]failed!")); if (exceptions.Count != 0) _ExceptionsDirectories.Add(container.SourceDirectory); if (Directory.GetFiles(propertyRoot, "*.txt", SearchOption.TopDirectoryOnly).Any()) { for (int y = 0; y < int.MaxValue; y++) { _Log.Information("Press \"Y\" key when ready to continue or close console"); if (System.Console.ReadKey().Key == ConsoleKey.Y) break; } _Log.Information(". . ."); } total += container.Items.Count; } } public Shared.Models.Property GetProperty(Shared.Models.Methods.IMetadata metadata, Item item, List> sourceDirectoryFileTuples, List parseExceptions) { Shared.Models.Property result; bool angleBracketCollectionAny = _AngleBracketCollection.Any(); if (!angleBracketCollectionAny) { if (item.ImageFileHolder.DirectoryName is null) throw new NullReferenceException(nameof(item.ImageFileHolder.DirectoryName)); SetAngleBracketCollection(item.ImageFileHolder.DirectoryName, !item.IsUniqueFileName); } bool isIgnoreExtension = item.IsValidImageFormatExtension && _PropertyConfiguration.IgnoreExtensions.Contains(item.ImageFileHolder.ExtensionLowered); result = GetImageProperty(metadata, item, sourceDirectoryFileTuples, parseExceptions, isIgnoreExtension); if (!angleBracketCollectionAny) _AngleBracketCollection.Clear(); return result; } }