using CliWrap; using Microsoft.Extensions.Logging; using OriginalToDeterministicHashCode.Models; using ShellProgressBar; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Text.Json; using System.Threading; using System.Threading.Tasks; using View_by_Distance.Metadata.Models; using View_by_Distance.Metadata.Models.Stateless; using View_by_Distance.Shared.Models; using View_by_Distance.Shared.Models.Properties; using View_by_Distance.Shared.Models.Stateless; namespace OriginalToDeterministicHashCode.Services; public class RenameService : IRename, IDisposable { private sealed record ToDo(string? Directory, FilePath FilePath, string File, bool JsonFile); private sealed record RecordA(ExifDirectory ExifDirectory, bool FastForwardMovingPictureExpertsGroupUsed, FileInfo FileInfo, FilePath FilePath, ReadOnlyCollection SidecarFiles); private sealed record RecordB(DateTime DateTime, ExifDirectory ExifDirectory, bool FastForwardMovingPictureExpertsGroupUsed, FilePath FilePath, ReadOnlyCollection SidecarFiles, bool HasDateTimeOriginal, bool HasIgnoreKeyword, string JsonFile); private ProgressBar? _ProgressBar; private readonly AppSettings _AppSettings; private readonly ILogger _Logger; public RenameService(ILogger logger, AppSettings appSettings) { _Logger = logger; _AppSettings = appSettings; } void IRename.Tick() => _ProgressBar?.Tick(); void IDisposable.Dispose() { _ProgressBar?.Dispose(); GC.SuppressFinalize(this); } ReadOnlyCollection IRename.ConvertAndGetFastForwardMovingPictureExpertsGroupFiles(IRenameSettings renameSettings, FilePath filePath) { List results = []; bool isValidVideoFormatExtensions = renameSettings.ValidVideoFormatExtensions.Contains(filePath.ExtensionLowered); if (isValidVideoFormatExtensions) { bool check; try { CommandTask commandTask = Cli.Wrap("L:/Git/ffmpeg-2024-10-02-git-358fdf3083-full_build/bin/ffmpeg.exe") .WithArguments(["-i", filePath.FullName, "-vf", "select=eq(n\\,0)", "-q:v", "1", $"{filePath.Name}-%4d.jpg"]) .WithWorkingDirectory(filePath.DirectoryFullPath) .ExecuteAsync(); commandTask.Task.Wait(); check = true; } catch (Exception) { check = false; } if (check) { results.AddRange(Directory.GetFiles(filePath.DirectoryFullPath, $"{filePath.Name}-*.jpg", SearchOption.TopDirectoryOnly)); if (results.Count == 0) throw new Exception(); File.SetCreationTime(results[0], new(filePath.CreationTicks)); File.SetLastWriteTime(results[0], new(filePath.LastWriteTicks)); Thread.Sleep(100); } } return results.AsReadOnly(); } #pragma warning disable CA1416 DeterministicHashCode IRename.GetDeterministicHashCode(FilePath filePath) { DeterministicHashCode result; int? id; int? width; int? height; try { using Image image = Image.FromFile(filePath.FullName); width = image.Width; height = image.Height; using Bitmap bitmap = new(image); Rectangle rectangle = new(0, 0, image.Width, image.Height); BitmapData bitmapData = bitmap.LockBits(rectangle, ImageLockMode.ReadOnly, bitmap.PixelFormat); IntPtr intPtr = bitmapData.Scan0; int length = bitmapData.Stride * bitmap.Height; byte[] bytes = new byte[length]; Marshal.Copy(intPtr, bytes, 0, length); bitmap.UnlockBits(bitmapData); id = IId.GetDeterministicHashCode(bytes); } catch (Exception) { id = null; width = null; height = null; } result = new(height, id, width); return result; } #pragma warning restore CA1416 private void NonParallelismAndInPlace(AppSettings appSettings, ReadOnlyCollection ids, ExifDirectory exifDirectory, FileInfo fileInfo, FilePath filePath, bool fastForwardMovingPictureExpertsGroupUsed, ReadOnlyCollection sidecarFiles) { if (exifDirectory.FilePath.Id is null) throw new NotImplementedException(); int i = 0; ToDo toDo; const string jpg = ".jpg"; const string jpeg = ".jpeg"; List toDoCollection = []; DateTime? dateTime = IDate.GetDateTimeOriginal(exifDirectory); ReadOnlyCollection keywords = IMetadata.GetKeywords(exifDirectory); bool hasIgnoreKeyword = appSettings.MetadataSettings.IgnoreRulesKeyWords.Any(keywords.Contains); string checkFileExtension = filePath.ExtensionLowered == jpeg ? jpg : filePath.ExtensionLowered; bool hasDateTimeOriginal = dateTime is not null; string paddedId = IId.GetPaddedId(appSettings.ResultSettings, appSettings.MetadataSettings, exifDirectory.FilePath.Id.Value, hasIgnoreKeyword, hasDateTimeOriginal, i); string checkDirectory = appSettings.RenameSettings.InPlaceWithOriginalName ? Path.Combine(filePath.DirectoryFullPath, filePath.FileNameFirstSegment) : filePath.DirectoryFullPath; string checkFile = Path.Combine(checkDirectory, $"{paddedId}{checkFileExtension}"); if (checkFile != filePath.FullName) { if (File.Exists(checkFile)) { checkFile = string.Concat(checkFile, ".del"); if (File.Exists(checkFile)) throw new NotImplementedException(); } toDo = new(checkDirectory, filePath, checkFile, JsonFile: false); toDoCollection.Add(toDo); if (sidecarFiles.Count != 0) { if (appSettings.RenameSettings.InPlace) throw new NotSupportedException($"Must use {nameof(appSettings.RenameSettings.InPlaceWithOriginalName)} when sidecar file(s) are present!"); dateTime ??= IDate.GetMinimum(exifDirectory); RecordB recordB = new(dateTime.Value, exifDirectory, fastForwardMovingPictureExpertsGroupUsed, filePath, sidecarFiles, hasDateTimeOriginal, hasIgnoreKeyword, fileInfo.FullName); toDoCollection.AddRange(GetSidecarFiles(appSettings, recordB, [], checkDirectory, paddedId)); } _ = RenameFilesInDirectories(appSettings.RenameSettings, new(toDoCollection)); string jsonFile = Path.Combine(checkDirectory, $"{paddedId}{checkFileExtension}.json"); File.Move(fileInfo.FullName, jsonFile, overwrite: true); if (appSettings.RenameSettings.InPlaceWithOriginalName && ids.Count > 0) { string contains = ids.Contains(exifDirectory.FilePath.Id.Value) ? "_ Exists _" : "_ New _"; string idCheck = Path.Combine(checkDirectory, contains, fastForwardMovingPictureExpertsGroupUsed ? "Video" : "Image"); if (!Directory.Exists(idCheck)) _ = Directory.CreateDirectory(idCheck); } } } private List GetRecordACollection(ILogger? logger, AppSettings appSettings, IRename rename, long ticks, ReadOnlyCollection ids, IEnumerable files, A_Metadata metadata) { List results = []; int index = -1; RecordA recordA; FileInfo fileInfo; FilePath filePath; TimeSpan timeSpan; string directoryName; ExifDirectory exifDirectory; List sidecarFiles; DeterministicHashCode deterministicHashCode; bool fastForwardMovingPictureExpertsGroupUsed; FilePath? fastForwardMovingPictureExpertsGroupFilePath; ReadOnlyCollection? fastForwardMovingPictureExpertsGroupFiles; ReadOnlyDictionary> keyValuePairs = IMetadata.GetKeyValuePairs(files); foreach (KeyValuePair> keyValuePair in keyValuePairs) { index += 1; rename.Tick(); if (keyValuePair.Value.Count > 1 && !appSettings.RenameSettings.ForceNewId) { if (appSettings.RenameSettings.InPlaceMoveDirectory) continue; throw new NotSupportedException($"When sidecar files are present {nameof(appSettings.RenameSettings.ForceNewId)} must be true!"); } if (keyValuePair.Value.Count > 2) throw new NotSupportedException("Too many sidecar files!"); foreach (FileHolder fileHolder in keyValuePair.Value) { timeSpan = new(DateTime.Now.Ticks - ticks); if (appSettings.RenameSettings.MaxMilliSecondsPerCall > timeSpan.TotalMilliseconds) break; if (appSettings.RenameSettings.SidecarExtensions.Contains(fileHolder.ExtensionLowered)) continue; if (appSettings.RenameSettings.IgnoreExtensions.Contains(fileHolder.ExtensionLowered)) continue; filePath = FilePath.Get(appSettings.ResultSettings, appSettings.MetadataSettings, fileHolder, index); if (appSettings.RenameSettings.SkipIdFiles && filePath.Id is not null && (filePath.IsIntelligentIdFormat || filePath.SortOrder is not null)) continue; if (!appSettings.RenameSettings.ForceNewId && filePath.Id is not null) { fastForwardMovingPictureExpertsGroupFiles = null; deterministicHashCode = new(null, filePath.Id, null); directoryName = Path.GetFileName(filePath.DirectoryFullPath); if (appSettings.RenameSettings.InPlaceWithOriginalName || (appSettings.RenameSettings.InPlace && directoryName.EndsWith(filePath.Id.Value.ToString()))) continue; } else { fastForwardMovingPictureExpertsGroupFiles = rename.ConvertAndGetFastForwardMovingPictureExpertsGroupFiles(appSettings.RenameSettings, filePath); fastForwardMovingPictureExpertsGroupFilePath = fastForwardMovingPictureExpertsGroupFiles.Count == 0 ? null : FilePath.Get(appSettings.ResultSettings, appSettings.MetadataSettings, FileHolder.Get(fastForwardMovingPictureExpertsGroupFiles[0]), index); deterministicHashCode = fastForwardMovingPictureExpertsGroupFilePath is null ? rename.GetDeterministicHashCode(filePath) : rename.GetDeterministicHashCode(fastForwardMovingPictureExpertsGroupFilePath); } sidecarFiles = []; filePath = FilePath.Get(filePath, deterministicHashCode); for (int i = 0; i < keyValuePair.Value.Count; i++) { if (keyValuePair.Value[i].ExtensionLowered == fileHolder.ExtensionLowered) continue; sidecarFiles.Add(keyValuePair.Value[i]); } try { (fileInfo, exifDirectory) = metadata.GetMetadataCollection(appSettings.ResultSettings, appSettings.MetadataSettings, filePath); } catch (Exception) { logger?.LogWarning("<{filePath}>", filePath.FullName); continue; } fastForwardMovingPictureExpertsGroupUsed = fastForwardMovingPictureExpertsGroupFiles is not null && fastForwardMovingPictureExpertsGroupFiles.Count > 0; if (fastForwardMovingPictureExpertsGroupUsed && fastForwardMovingPictureExpertsGroupFiles is not null) { foreach (string fastForwardMovingPictureExpertsGroupFile in fastForwardMovingPictureExpertsGroupFiles) File.Delete(fastForwardMovingPictureExpertsGroupFile); } if (appSettings.RenameSettings.InPlace || appSettings.RenameSettings.InPlaceWithOriginalName) NonParallelismAndInPlace(appSettings, ids, exifDirectory, fileInfo, filePath, fastForwardMovingPictureExpertsGroupUsed, new(sidecarFiles)); if (!fastForwardMovingPictureExpertsGroupUsed && appSettings.RenameSettings.InPlaceMoveDirectory && appSettings.RenameSettings.ValidVideoFormatExtensions.Contains(filePath.ExtensionLowered)) fastForwardMovingPictureExpertsGroupUsed = true; recordA = new(exifDirectory, fastForwardMovingPictureExpertsGroupUsed, fileInfo, filePath, new(sidecarFiles)); results.Add(recordA); } } return results; } private static ReadOnlyCollection GetRecordBCollection(AppSettings appSettings, List recordACollection) { List results = []; RecordB recordB; DateTime? dateTime; bool hasIgnoreKeyword; bool hasDateTimeOriginal; ReadOnlyCollection keywords; foreach (RecordA recordA in recordACollection) { dateTime = IDate.GetDateTimeOriginal(recordA.ExifDirectory); hasDateTimeOriginal = dateTime is not null; dateTime ??= IDate.GetMinimum(recordA.ExifDirectory); keywords = IMetadata.GetKeywords(recordA.ExifDirectory); hasIgnoreKeyword = appSettings.MetadataSettings.IgnoreRulesKeyWords.Any(l => keywords.Contains(l)); recordB = new(dateTime.Value, recordA.ExifDirectory, recordA.FastForwardMovingPictureExpertsGroupUsed, recordA.FilePath, recordA.SidecarFiles, hasDateTimeOriginal, hasIgnoreKeyword, recordA.FileInfo.FullName); results.Add(recordB); } return results.AsReadOnly(); } private ReadOnlyCollection GetRecordBCollection(ILogger? logger, AppSettings appSettings, IRename rename, long ticks, ReadOnlyCollection ids, DirectoryInfo directoryInfo) { ReadOnlyCollection results; RecordA recordA; List recordACollection = []; A_Metadata metadata = new(appSettings.ResultSettings, appSettings.MetadataSettings); int appSettingsMaxDegreeOfParallelism = appSettings.RenameSettings.MaxDegreeOfParallelism; IEnumerable files = appSettingsMaxDegreeOfParallelism == 1 ? Directory.GetFiles(directoryInfo.FullName, "*", SearchOption.AllDirectories) : Directory.EnumerateFiles(directoryInfo.FullName, "*", SearchOption.AllDirectories); int filesCount = appSettingsMaxDegreeOfParallelism == 1 ? files.Count() : 123000; _ProgressBar = new(filesCount, "EnumerateFiles load", new ProgressBarOptions() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }); if (appSettingsMaxDegreeOfParallelism == 1) recordACollection.AddRange(GetRecordACollection(logger, appSettings, rename, ticks, ids, files, metadata)); else { List distinct = []; List metadataGroups = []; ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = appSettingsMaxDegreeOfParallelism }; files.AsParallel().ForAll(IMetadata.SetExifDirectoryCollection(rename, appSettings.ResultSettings, appSettings.MetadataSettings, appSettings.RenameSettings, metadata, distinct, metadataGroups)); if (_ProgressBar.CurrentTick != recordACollection.Count) throw new NotSupportedException(); foreach (MetadataGroup metadataGroup in metadataGroups) { if (metadataGroup.FastForwardMovingPictureExpertsGroupUsed || !appSettings.RenameSettings.InPlaceMoveDirectory || !appSettings.RenameSettings.ValidVideoFormatExtensions.Contains(metadataGroup.FilePath.ExtensionLowered)) recordA = new(metadataGroup.ExifDirectory, metadataGroup.FastForwardMovingPictureExpertsGroupUsed, metadataGroup.FileInfo, metadataGroup.FilePath, metadataGroup.SidecarFiles); else recordA = new(metadataGroup.ExifDirectory, FastForwardMovingPictureExpertsGroupUsed: true, metadataGroup.FileInfo, metadataGroup.FilePath, metadataGroup.SidecarFiles); recordACollection.Add(recordA); } } _ProgressBar.Dispose(); results = GetRecordBCollection(appSettings, recordACollection); return results; } private static void VerifyIntMinValueLength(MetadataSettings metadataSettings, ReadOnlyCollection recordBCollection) { foreach (RecordB recordB in recordBCollection) { if (recordB.ExifDirectory.FilePath.Id is null) continue; if (metadataSettings.IntMinValueLength < recordB.ExifDirectory.FilePath.Id.Value.ToString().Length) throw new NotSupportedException(); } } private static string GetTFW(RecordB record, bool? isWrongYear) => string.Concat(record.HasDateTimeOriginal ? "T" : "F", isWrongYear is not null && isWrongYear.Value ? "W" : record.FastForwardMovingPictureExpertsGroupUsed ? "V" : "I"); private static string GetDirectoryName(string year, string tfw, string prefix, string? splat, int seasonValue, string seasonName, string makerSplit) => splat is null ? $"{prefix}{year} {tfw}{year}.{seasonValue} {seasonName}{makerSplit}" : $"{prefix}{year} {tfw}{year}{splat}"; private static string? GetCheckDirectory(AppSettings appSettings, RecordB record, ReadOnlyCollection ids, bool multipleDirectoriesWithFiles, string paddedId) { string? result; string year = record.DateTime.Year.ToString(); string checkDirectoryName = Path.GetFileName(record.FilePath.DirectoryFullPath); if (multipleDirectoriesWithFiles && !checkDirectoryName.Contains(year)) result = null; else { (bool? isWrongYear, string[] years) = IDate.IsWrongYear(record.FilePath, record.ExifDirectory); if (appSettings.RenameSettings.InPlaceMoveDirectory && !record.FilePath.FileNameFirstSegment.Contains(paddedId)) result = null; else { string tfw = GetTFW(record, isWrongYear); string? maker = IMetadata.GetMaker(record.ExifDirectory); string rootDirectory = appSettings.ResultSettings.RootDirectory; string[] segments = checkDirectoryName.Split(years, StringSplitOptions.None); (int seasonValue, string seasonName) = IDate.GetSeason(record.DateTime.DayOfYear); string? splat = checkDirectoryName.Length > 3 && checkDirectoryName[^3..][1] == '!' ? checkDirectoryName[^3..] : null; string contains = record.ExifDirectory.FilePath.Id is null || ids.Contains(record.ExifDirectory.FilePath.Id.Value) ? "_ Exists _" : "_ New-Destination _"; string makerSplit = string.IsNullOrEmpty(maker) ? string.IsNullOrEmpty(appSettings.RenameSettings.DefaultMaker) ? string.Empty : appSettings.RenameSettings.DefaultMaker : $" {maker.Split(' ')[0]}"; string directoryName = GetDirectoryName(year, tfw, segments[0], splat, seasonValue, seasonName, makerSplit); result = Path.GetFullPath(Path.Combine(rootDirectory, contains, directoryName)); } } return result; } private static List GetSidecarFiles(AppSettings appSettings, RecordB record, List distinct, string checkDirectory, string paddedId) { List results = []; ToDo toDo; string checkFile; FilePath filePath; string checkFileExtension; foreach (FileHolder fileHolder in record.SidecarFiles) { checkFileExtension = fileHolder.ExtensionLowered; filePath = FilePath.Get(appSettings.ResultSettings, appSettings.MetadataSettings, fileHolder, index: null); checkFile = Path.Combine(checkDirectory, $"{paddedId}{checkFileExtension}"); if (checkFile == filePath.FullName) continue; if (File.Exists(checkFile)) { checkFile = string.Concat(checkFile, ".del"); if (File.Exists(checkFile)) continue; } if (distinct.Contains(checkFile)) continue; distinct.Add(checkFile); toDo = new(checkDirectory, filePath, checkFile, JsonFile: false); results.Add(toDo); } return results; } private static bool? GetDirectoryCheck(ResultSettings resultSettings) { bool? result = null; IEnumerable files; string[] directories = Directory.GetDirectories(resultSettings.RootDirectory, "*", SearchOption.TopDirectoryOnly); foreach (string directory in directories) { files = Directory.EnumerateFiles(directory, "*", SearchOption.AllDirectories); foreach (string _ in files) { if (result is null) result = false; else if (result.Value) result = true; break; } if (result is not null && result.Value) break; } return result; } private static ReadOnlyCollection GetToDoCollection(AppSettings appSettings, ReadOnlyCollection ids, ReadOnlyCollection recordBCollection) { List results = []; ToDo toDo; RecordB record; string jsonFile; string paddedId; string checkFile; FilePath filePath; string directoryName; FileHolder fileHolder; string? checkDirectory; const string jpg = ".jpg"; string checkFileExtension; List distinct = []; const string jpeg = ".jpeg"; string jsonFileSubDirectory; bool? directoryCheck = GetDirectoryCheck(appSettings.ResultSettings); VerifyIntMinValueLength(appSettings.MetadataSettings, recordBCollection); bool multipleDirectoriesWithFiles = directoryCheck is not null && directoryCheck.Value; ReadOnlyCollection sorted = (from l in recordBCollection orderby l.DateTime select l).ToArray().AsReadOnly(); for (int i = 0; i < sorted.Count; i++) { record = sorted[i]; if (record.ExifDirectory.FilePath.Id is null) continue; paddedId = IId.GetPaddedId(appSettings.ResultSettings, appSettings.MetadataSettings, record.ExifDirectory.FilePath.Id.Value, record.HasIgnoreKeyword, record.HasDateTimeOriginal, i); checkDirectory = GetCheckDirectory(appSettings, record, ids, multipleDirectoriesWithFiles, paddedId); if (string.IsNullOrEmpty(checkDirectory)) continue; checkFileExtension = record.FilePath.ExtensionLowered == jpeg ? jpg : record.FilePath.ExtensionLowered; jsonFileSubDirectory = Path.GetDirectoryName(Path.GetDirectoryName(record.JsonFile)) ?? throw new Exception(); checkFile = Path.Combine(checkDirectory, $"{paddedId}{checkFileExtension}"); if (checkFile == record.FilePath.FullName) continue; if (File.Exists(checkFile)) { checkFile = string.Concat(checkFile, ".del"); if (File.Exists(checkFile)) continue; } (directoryName, _) = IPath.GetDirectoryNameAndIndex(appSettings.ResultSettings, record.ExifDirectory.FilePath.Id.Value); jsonFile = Path.Combine(jsonFileSubDirectory, directoryName, $"{record.ExifDirectory.FilePath.Id.Value}{checkFileExtension}.json"); if (record.JsonFile != jsonFile) { fileHolder = FileHolder.Get(record.JsonFile); filePath = FilePath.Get(appSettings.ResultSettings, appSettings.MetadataSettings, fileHolder, index: null); toDo = new(null, filePath, jsonFile, JsonFile: true); results.Add(toDo); } if (distinct.Contains(checkFile)) continue; distinct.Add(checkFile); toDo = new(checkDirectory, record.FilePath, checkFile, JsonFile: false); results.Add(toDo); if (record.SidecarFiles.Count == 0) continue; results.AddRange(GetSidecarFiles(appSettings, record, distinct, checkDirectory, paddedId)); } return results.AsReadOnly(); } private static void VerifyDirectories(ReadOnlyCollection toDoCollection) { List distinct = []; foreach (ToDo toDo in toDoCollection) { if (toDo.Directory is null || distinct.Contains(toDo.Directory)) continue; if (!Directory.Exists(toDo.Directory)) _ = Directory.CreateDirectory(toDo.Directory); distinct.Add(toDo.Directory); } } private ReadOnlyCollection RenameFilesInDirectories(RenameSettings renameSettings, ReadOnlyCollection toDoCollection) { List results = []; VerifyDirectories(toDoCollection); bool useProgressBar = !renameSettings.InPlace && !renameSettings.InPlaceWithOriginalName; if (useProgressBar) _ProgressBar = new(toDoCollection.Count, "Move Files", new ProgressBarOptions() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }); foreach (ToDo toDo in toDoCollection) { if (useProgressBar) _ProgressBar?.Tick(); if (toDo.JsonFile) { if (File.Exists(toDo.File)) File.Delete(toDo.File); try { File.Move(toDo.FilePath.FullName, toDo.File); } catch (Exception) { continue; } } else if (toDo.Directory is null) throw new NotSupportedException(); else { if (File.Exists(toDo.File)) File.Delete(toDo.File); try { File.Move(toDo.FilePath.FullName, toDo.File); } catch (Exception) { continue; } results.Add($"{toDo.FilePath.FullName}\t{toDo.File}"); } } if (useProgressBar) _ProgressBar?.Dispose(); return results.AsReadOnly(); } private static void SaveIdentifiersToDisk(long ticks, AppSettings appSettings, ReadOnlyCollection recordBCollection) { string paddedId; Identifier identifier; List identifiers = []; string aMetadataCollectionDirectory = IResult.GetResultsDateGroupDirectory(appSettings.ResultSettings, nameof(A_Metadata), appSettings.ResultSettings.ResultCollection); foreach (RecordB record in recordBCollection) { if (record.ExifDirectory.FilePath.Id is null) continue; paddedId = IId.GetPaddedId(appSettings.ResultSettings, appSettings.MetadataSettings, record.ExifDirectory.FilePath.Id.Value, record.HasIgnoreKeyword, record.HasDateTimeOriginal, index: null); identifier = new([], record.HasDateTimeOriginal, record.ExifDirectory.FilePath.Id.Value, record.FilePath.Length, paddedId, record.DateTime.Ticks); identifiers.Add(identifier); } string json = JsonSerializer.Serialize(identifiers.OrderBy(l => l.PaddedId).ToArray(), IdentifierCollectionSourceGenerationContext.Default.IdentifierArray); _ = IPath.WriteAllText(Path.Combine(aMetadataCollectionDirectory, $"{ticks}.json"), json, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null); } private static ReadOnlyCollection GetIds(RenameSettings renameSettings) { ReadOnlyCollection results; string? propertyCollectionFile = string.IsNullOrEmpty(renameSettings.RelativePropertyCollectionFile) ? null : renameSettings.RelativePropertyCollectionFile; string? json = !File.Exists(propertyCollectionFile) ? null : File.ReadAllText(propertyCollectionFile); Identifier[]? identifiers = json is null ? null : JsonSerializer.Deserialize(json, IdentifierCollectionSourceGenerationContext.Default.IdentifierArray); if (identifiers is null && !string.IsNullOrEmpty(renameSettings.RelativePropertyCollectionFile)) throw new Exception($"Invalid {nameof(renameSettings.RelativePropertyCollectionFile)}"); results = identifiers is null ? new([]) : new((from l in identifiers select l.Id).ToArray()); return results; } private void RenameWork(ILogger? logger, AppSettings appSettings, IRename rename, long ticks) { ReadOnlyCollection ids = GetIds(appSettings.RenameSettings); _ = IPath.DeleteEmptyDirectories(appSettings.ResultSettings.RootDirectory); DirectoryInfo directoryInfo = new(Path.GetFullPath(appSettings.ResultSettings.RootDirectory)); logger?.LogInformation("{Ticks} {RootDirectory}", ticks, directoryInfo.FullName); ReadOnlyCollection recordBCollection = GetRecordBCollection(logger, appSettings, rename, ticks, ids, directoryInfo); SaveIdentifiersToDisk(ticks, appSettings, recordBCollection); if (appSettings.RenameSettings.InPlace || appSettings.RenameSettings.InPlaceWithOriginalName) { if (recordBCollection.Count > 0) recordBCollection = new([]); string aMetadataSingletonDirectory = IResult.GetResultsGroupDirectory(appSettings.ResultSettings, nameof(A_Metadata)); _ = IPath.DeleteEmptyDirectories(aMetadataSingletonDirectory); } if (!appSettings.RenameSettings.OnlySaveIdentifiersToDisk) { ReadOnlyCollection toDoCollection = GetToDoCollection(appSettings, ids, recordBCollection); ReadOnlyCollection lines = RenameFilesInDirectories(appSettings.RenameSettings, toDoCollection); if (lines.Count != 0) { File.WriteAllLines($"D:/Tmp/Phares/{DateTime.Now.Ticks}.tsv", lines); _ = IPath.DeleteEmptyDirectories(directoryInfo.FullName); } } } public void RenameFiles() { IRename rename = this; long ticks = DateTime.Now.Ticks; RenameWork(_Logger, _AppSettings, rename, ticks); } }