using System.Collections.ObjectModel; namespace View_by_Distance.Shared.Models.Stateless.Methods; internal abstract partial class XDirectory { internal static void MoveFiles(List files, string find, string replace) { string checkFile; string? checkDirectory; List 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 CopyOrMove(List<(FilePath, string)> toDoCollection, bool move, bool moveBack, Action? tick) { List results = []; FileInfo fileInfo; List 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 GetFilesCollection(string directory, string directorySearchFilter, string fileSearchFilter, bool useCeilingAverage) { List 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 fileCollection) { List counts = []; foreach (string[] files in fileCollection) counts.Add(files.Length); int average = (int)Math.Ceiling(counts.Average()); return average; } private static List GetFilesCollection(List fileCollection, int ceilingAverage) { List 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 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> GetFilePathCollections(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection filesCollection, bool useIgnoreExtensions) { List> results = []; FilePath filePath; List 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(new(filePaths)); } return results.AsReadOnly(); } internal static ReadOnlyCollection> GetFilePathCollections(Properties.IPropertyConfiguration propertyConfiguration, string directorySearchFilter, string fileSearchFilter, string directory, bool useIgnoreExtensions, bool useCeilingAverage) { ReadOnlyCollection> results; ReadOnlyCollection filesCollection = GetFilesCollection(directory, directorySearchFilter, fileSearchFilter, useCeilingAverage); results = IDirectory.GetFilePathCollections(propertyConfiguration, filesCollection, useIgnoreExtensions); return results; } internal static List GetFiles(Properties.IPropertyConfiguration propertyConfiguration, ReadOnlyCollection> filePathsCollection, IReadOnlyDictionary> fileNamesToFiles, IReadOnlyDictionary> compareFileNamesToFiles) { List results = []; FilePath? match; bool uniqueFileName; List? collection; Models.FilePair filePair; bool? isNotUniqueAndNeedsReview; foreach (ReadOnlyCollection 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: [], 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, List 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, List collection) { FilePath? result = null; List lengths = []; List matches = []; List 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> filePathsCollection, ReadOnlyDictionary> fileGroups, Dictionary? 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 distinctIds = []; List distinct = []; ExifDirectory? exifDirectory; Models.FileHolder fileHolder; ReadOnlyCollection keywords; List 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> filePathsCollection) { List results = []; foreach (ReadOnlyCollection 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(); } }