From 0310e06f3ca15a89822f215bdf0787bdda8a7225 Mon Sep 17 00:00:00 2001 From: Mike Phares Date: Sun, 12 Nov 2023 18:09:22 -0700 Subject: [PATCH] Used to re-organized folders --- Metadata/Models/A_Metadata.cs | 115 ++++++++++++------ .../Models/Binder/MetadataConfiguration.cs | 1 + Metadata/Models/Binder/ResultConfiguration.cs | 1 + Metadata/Models/Stateless/Dimensions.cs | 83 +++---------- Metadata/Models/Stateless/Exif.cs | 4 +- Rename/Models/Binder/AppSettings.cs | 1 + Rename/Models/Binder/RenameConfiguration.cs | 7 ++ Rename/Models/RenameConfiguration.cs | 4 +- Rename/Rename.cs | 113 ++++++++++------- .../Models/Properties/IRenameConfiguration.cs | 11 ++ Shared/Models/Stateless/Methods/IRename.cs | 4 +- 11 files changed, 193 insertions(+), 151 deletions(-) create mode 100644 Shared/Models/Properties/IRenameConfiguration.cs diff --git a/Metadata/Models/A_Metadata.cs b/Metadata/Models/A_Metadata.cs index c820f5f..f3c9c96 100644 --- a/Metadata/Models/A_Metadata.cs +++ b/Metadata/Models/A_Metadata.cs @@ -4,6 +4,7 @@ using System.Text.Json; using View_by_Distance.Metadata.Models.Stateless; using View_by_Distance.Metadata.Models.Stateless.Methods; using View_by_Distance.Shared.Models; +using View_by_Distance.Shared.Models.Properties; using View_by_Distance.Shared.Models.Stateless.Methods; namespace View_by_Distance.Metadata.Models; @@ -26,21 +27,50 @@ public class A_Metadata _FileGroups = IPath.GetKeyValuePairs(metadataConfiguration.ResultConfiguration, bResultsFullGroupDirectory, [metadataConfiguration.ResultConfiguration.ResultSingleton]); } - private FileInfo GetFileInfo(ResultConfiguration resultConfiguration, FilePath filePath) + private (int, FileInfo) GetFileInfo(ResultConfiguration resultConfiguration, FilePath filePath) { FileInfo result; FileInfo fileInfo = new(filePath.FullName); (_, int directoryIndex) = IPath.GetDirectoryNameAndIndex(resultConfiguration, filePath); DateTime minimumDateTime = fileInfo.CreationTime < fileInfo.LastWriteTime ? fileInfo.CreationTime : fileInfo.LastWriteTime; - int minimumYear = minimumDateTime.Year < resultConfiguration.EpicYear ? resultConfiguration.EpicYear : minimumDateTime.Year; - result = new(Path.Combine(_FileGroups[minimumYear][_MetadataConfiguration.ResultConfiguration.ResultSingleton][directoryIndex], $"{filePath.NameWithoutExtension}{filePath.ExtensionLowered}.json")); - return result; + int fileInfoMinimumYear = minimumDateTime.Year < resultConfiguration.EpicYear ? resultConfiguration.EpicYear : minimumDateTime.Year; + result = new(Path.Combine(_FileGroups[fileInfoMinimumYear][_MetadataConfiguration.ResultConfiguration.ResultSingleton][directoryIndex], $"{filePath.NameWithoutExtension}{filePath.ExtensionLowered}.json")); + return (fileInfoMinimumYear, result); + } + + private (int, string) GetJsonFile(ResultConfiguration resultConfiguration, FilePath filePath, ExifDirectory exifDirectory) + { + string? result; + DateTime? dateTime; + dateTime = IDate.GetDateTimeOriginal(exifDirectory); + dateTime ??= IDate.GetMinimum(exifDirectory); + (_, int directoryIndex) = IPath.GetDirectoryNameAndIndex(resultConfiguration, filePath); + int exifYear = dateTime.Value.Year < resultConfiguration.EpicYear ? resultConfiguration.EpicYear : dateTime.Value.Year; + result = Path.Combine(_FileGroups[exifYear][_MetadataConfiguration.ResultConfiguration.ResultSingleton][directoryIndex], $"{filePath.NameWithoutExtension}{filePath.ExtensionLowered}.json"); + return (exifYear, result); + } + + private static (string, ExifDirectory?) Get(string jsonFile) + { + ExifDirectory? result; + string json = File.ReadAllText(jsonFile); + try + { + result = JsonSerializer.Deserialize(json, ExifDirectorySourceGenerationContext.Default.ExifDirectory); + if (result is null) + throw new Exception(); + } + catch (Exception) + { + result = null; + } + return (json, result); } public (FileInfo, ExifDirectory) GetMetadataCollection(MetadataConfiguration metadataConfiguration, FilePath filePath, DeterministicHashCode deterministicHashCode) { - ExifDirectory? results; - FileInfo fileInfo = GetFileInfo(metadataConfiguration.ResultConfiguration, filePath); + ExifDirectory? result; + (int fileInfoMinimumYear, FileInfo fileInfo) = GetFileInfo(metadataConfiguration.ResultConfiguration, filePath); if (_MetadataConfiguration.ForceMetadataLastWriteTimeToCreationTime && !fileInfo.Exists && File.Exists(Path.ChangeExtension(fileInfo.FullName, ".delete"))) { File.Move(Path.ChangeExtension(fileInfo.FullName, ".delete"), fileInfo.FullName); @@ -52,9 +82,9 @@ public class A_Metadata fileInfo.Refresh(); } if (_MetadataConfiguration.PropertiesChangedForMetadata) - results = null; + result = null; else if (!fileInfo.Exists) - results = null; + result = null; else if (!fileInfo.FullName.EndsWith(".json") && !fileInfo.FullName.EndsWith(".old")) throw new ArgumentException("must be a *.json file"); else @@ -62,59 +92,74 @@ public class A_Metadata string json = File.ReadAllText(fileInfo.FullName); try { - results = JsonSerializer.Deserialize(json, ExifDirectorySourceGenerationContext.Default.ExifDirectory); - if (results is null) + result = JsonSerializer.Deserialize(json, ExifDirectorySourceGenerationContext.Default.ExifDirectory); + if (result is null) throw new Exception(); } catch (Exception) { - results = null; + result = null; } } - if (results is null) + if (result is null) { - System.Drawing.Size? size; - try - { size = Dimensions.GetDimensions(filePath.FullName); } - catch (Exception) { size = null; } + string json; + System.Drawing.Size? size = Dimensions.GetDimensions(filePath.FullName); IReadOnlyList directories = ImageMetadataReader.ReadMetadata(filePath.FullName); - results = Exif.Covert(filePath, deterministicHashCode, size, directories); - string json = JsonSerializer.Serialize(results, ExifDirectorySourceGenerationContext.Default.ExifDirectory); + result = Exif.Covert(filePath, deterministicHashCode, size, directories); + (int exifYear, string jsonFile) = GetJsonFile(metadataConfiguration.ResultConfiguration, filePath, result); + if (exifYear == fileInfoMinimumYear) + json = JsonSerializer.Serialize(result, ExifDirectorySourceGenerationContext.Default.ExifDirectory); + else + { + fileInfo = new(jsonFile); + if (!File.Exists(jsonFile)) + json = JsonSerializer.Serialize(result, ExifDirectorySourceGenerationContext.Default.ExifDirectory); + else + { + (string checkJson, ExifDirectory? exifDirectory) = Get(jsonFile); + if (exifDirectory is null) + json = JsonSerializer.Serialize(result, ExifDirectorySourceGenerationContext.Default.ExifDirectory); + else + { + json = checkJson; + result = exifDirectory; + } + } + } if (IPath.WriteAllText(fileInfo.FullName, json, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null) && _MetadataConfiguration.ForceMetadataLastWriteTimeToCreationTime) { File.SetLastWriteTime(fileInfo.FullName, fileInfo.CreationTime); fileInfo.Refresh(); } } - return (fileInfo, results); + return (fileInfo, result); } - public static Action SetExifDirectoryCollection(IRename rename, MetadataConfiguration metadataConfiguration, A_Metadata metadata, List<(string, FileInfo, ExifDirectory)> exifDirectories, Action tick) + public static Action SetExifDirectoryCollection(IRename rename, IRenameConfiguration renameConfiguration, A_Metadata metadata, List<(string, FileInfo, ExifDirectory)> exifDirectories) { return file => { - tick.Invoke(); + rename.Tick(); FileInfo fileInfo; FilePath? ffmpegFilePath; ExifDirectory exifDirectory; ReadOnlyCollection ffmpegFiles; DeterministicHashCode deterministicHashCode; - FilePath filePath = IId.GetFilePath(metadataConfiguration, file); - if (filePath.ExtensionLowered is not ".paddedId" and not ".lsv") + FilePath filePath = IId.GetFilePath(renameConfiguration.MetadataConfiguration, file); + if (!renameConfiguration.SkipIdFiles || filePath.Id is null || !filePath.IsIdFormat && !filePath.IsPaddedIdFormat) { - if (filePath.Id is null || (!filePath.IsIdFormat && !filePath.IsPaddedIdFormat)) - { - (ffmpegFiles, ffmpegFilePath) = rename.ConvertAndGetFfmpegFiles(filePath); - if (ffmpegFilePath is not null) - filePath = ffmpegFilePath; - deterministicHashCode = filePath.Id is not null ? deterministicHashCode = new(null, filePath.Id, null) : deterministicHashCode = rename.GetDeterministicHashCode(filePath); - (fileInfo, exifDirectory) = metadata.GetMetadataCollection(metadataConfiguration, filePath, deterministicHashCode); - lock (exifDirectories) - exifDirectories.Add(new(file, fileInfo, exifDirectory)); - foreach (string ffmpegFile in ffmpegFiles) - File.Delete(ffmpegFile); - } + (ffmpegFiles, ffmpegFilePath) = rename.ConvertAndGetFfmpegFiles(renameConfiguration, filePath); + if (ffmpegFilePath is not null) + filePath = ffmpegFilePath; + deterministicHashCode = filePath.Id is not null ? deterministicHashCode = new(null, filePath.Id, null) : deterministicHashCode = rename.GetDeterministicHashCode(filePath); + (fileInfo, exifDirectory) = metadata.GetMetadataCollection(renameConfiguration.MetadataConfiguration, filePath, deterministicHashCode); + lock (exifDirectories) + exifDirectories.Add(new(file, fileInfo, exifDirectory)); + foreach (string ffmpegFile in ffmpegFiles) + File.Delete(ffmpegFile); } + }; } diff --git a/Metadata/Models/Binder/MetadataConfiguration.cs b/Metadata/Models/Binder/MetadataConfiguration.cs index aae05a9..57b0e4b 100644 --- a/Metadata/Models/Binder/MetadataConfiguration.cs +++ b/Metadata/Models/Binder/MetadataConfiguration.cs @@ -32,6 +32,7 @@ public class MetadataConfiguration continue; throw new NotSupportedException(physicalFileProvider.Root); } + throw new NotSupportedException("Not Found!"); } } diff --git a/Metadata/Models/Binder/ResultConfiguration.cs b/Metadata/Models/Binder/ResultConfiguration.cs index 9f936b5..a248e48 100644 --- a/Metadata/Models/Binder/ResultConfiguration.cs +++ b/Metadata/Models/Binder/ResultConfiguration.cs @@ -39,6 +39,7 @@ public class ResultConfiguration continue; throw new NotSupportedException(physicalFileProvider.Root); } + throw new NotSupportedException("Not Found!"); } } diff --git a/Metadata/Models/Stateless/Dimensions.cs b/Metadata/Models/Stateless/Dimensions.cs index 1daf27a..fd53715 100644 --- a/Metadata/Models/Stateless/Dimensions.cs +++ b/Metadata/Models/Stateless/Dimensions.cs @@ -5,10 +5,8 @@ namespace View_by_Distance.Metadata.Models.Stateless.Methods; internal static class Dimensions { - const string _ErrorMessage = "Could not recognize image format."; - #pragma warning disable IDE0230 - private static readonly Dictionary> _ImageFormatDecoders = new() + private static readonly Dictionary> _ImageFormatDecoders = new() { { new byte[] { 0x42, 0x4D }, DecodeBitmap }, { new byte[] { 0x47, 0x49, 0x46, 0x38, 0x37, 0x61 }, DecodeGif }, @@ -23,10 +21,9 @@ internal static class Dimensions { for (int i = 0; i < thatBytes.Length; i += 1) { - if (thisBytes[i] != thatBytes[i]) - { - return false; - } + if (thisBytes[i] == thatBytes[i]) + continue; + return false; } return true; } @@ -35,9 +32,7 @@ internal static class Dimensions { byte[] bytes = new byte[sizeof(short)]; for (int i = 0; i < sizeof(short); i += 1) - { bytes[sizeof(short) - 1 - i] = binaryReader.ReadByte(); - } return BitConverter.ToInt16(bytes, 0); } @@ -45,13 +40,11 @@ internal static class Dimensions { byte[] bytes = new byte[sizeof(int)]; for (int i = 0; i < sizeof(int); i += 1) - { bytes[sizeof(int) - 1 - i] = binaryReader.ReadByte(); - } return BitConverter.ToInt32(bytes, 0); } - private static Size DecodeBitmap(BinaryReader binaryReader) + private static Size? DecodeBitmap(BinaryReader binaryReader) { _ = binaryReader.ReadBytes(16); int width = binaryReader.ReadInt32(); @@ -59,14 +52,14 @@ internal static class Dimensions return new Size(width, height); } - private static Size DecodeGif(BinaryReader binaryReader) + private static Size? DecodeGif(BinaryReader binaryReader) { int width = binaryReader.ReadInt16(); int height = binaryReader.ReadInt16(); return new Size(width, height); } - private static Size DecodePng(BinaryReader binaryReader) + private static Size? DecodePng(BinaryReader binaryReader) { _ = binaryReader.ReadBytes(8); int width = ReadLittleEndianInt32(binaryReader); @@ -74,100 +67,60 @@ internal static class Dimensions return new Size(width, height); } - private static Size DecodeJfif(BinaryReader binaryReader) + private static Size? DecodeJfif(BinaryReader binaryReader) { while (binaryReader.ReadByte() == 0xff) { byte marker = binaryReader.ReadByte(); short chunkLength = ReadLittleEndianInt16(binaryReader); - if (marker == 0xc0) { _ = binaryReader.ReadByte(); - int height = ReadLittleEndianInt16(binaryReader); int width = ReadLittleEndianInt16(binaryReader); return new Size(width, height); } - - if (chunkLength < 0) + if (chunkLength >= 0) + _ = binaryReader.ReadBytes(chunkLength - 2); + else { ushort uChunkLength = (ushort)chunkLength; _ = binaryReader.ReadBytes(uChunkLength - 2); } - else - { - _ = binaryReader.ReadBytes(chunkLength - 2); - } } - - throw new ArgumentException(_ErrorMessage); + return null; } - private static Size DecodeWebP(BinaryReader binaryReader) + private static Size? DecodeWebP(BinaryReader binaryReader) { _ = binaryReader.ReadUInt32(); // Size _ = binaryReader.ReadBytes(15); // WEBP, VP8 + more _ = binaryReader.ReadBytes(3); // SYNC - int width = binaryReader.ReadUInt16() & 0b00_11111111111111; // 14 bits width int height = binaryReader.ReadUInt16() & 0b00_11111111111111; // 14 bits height - return new Size(width, height); } - /// - /// Gets the dimensions of an image. - /// - /// The path of the image to get the dimensions of. - /// The dimensions of the specified image. - /// The image was of an unrecognized format. - internal static Size GetDimensions(BinaryReader binaryReader) + internal static Size? GetDimensions(BinaryReader binaryReader) { int maxMagicBytesLength = _ImageFormatDecoders.Keys.OrderByDescending(x => x.Length).First().Length; - byte[] magicBytes = new byte[maxMagicBytesLength]; - for (int i = 0; i < maxMagicBytesLength; i += 1) { magicBytes[i] = binaryReader.ReadByte(); - - foreach (KeyValuePair> kvPair in _ImageFormatDecoders) + foreach (KeyValuePair> kvPair in _ImageFormatDecoders) { if (StartsWith(magicBytes, kvPair.Key)) - { return kvPair.Value(binaryReader); - } } } - - throw new ArgumentException(_ErrorMessage, nameof(binaryReader)); + return null; } - /// - /// Gets the dimensions of an image. - ///internal - /// The path of the image to get the dimensions of. - /// The dimensions of the specified image. - /// The image was of an unrecognized format. - internal static Size GetDimensions(string path) + internal static Size? GetDimensions(string path) { using BinaryReader binaryReader = new(File.OpenRead(path)); - try - { - return GetDimensions(binaryReader); - } - catch (ArgumentException e) - { - if (e.Message.StartsWith(_ErrorMessage)) - { - throw new ArgumentException(_ErrorMessage, nameof(path), e); - } - else - { - throw; - } - } + return GetDimensions(binaryReader); } } \ No newline at end of file diff --git a/Metadata/Models/Stateless/Exif.cs b/Metadata/Models/Stateless/Exif.cs index 1c8812d..1804621 100644 --- a/Metadata/Models/Stateless/Exif.cs +++ b/Metadata/Models/Stateless/Exif.cs @@ -357,10 +357,10 @@ internal abstract class Exif internal static string GetMaker(Shared.Models.ExifDirectoryBase exifDirectoryBase) { string result; - if (string.IsNullOrEmpty(exifDirectoryBase.Make)) + if (string.IsNullOrEmpty(exifDirectoryBase.Make?.ToString().Trim())) result = "Unknown"; else - result = $"{exifDirectoryBase.Make[0].ToString().ToUpper()}{exifDirectoryBase.Make[1..]}".Trim(); + result = $"{exifDirectoryBase.Make[0].ToString().ToUpper()}{exifDirectoryBase.Make[1..].ToLower()}".Trim(); return result; } diff --git a/Rename/Models/Binder/AppSettings.cs b/Rename/Models/Binder/AppSettings.cs index 02b9769..8639e79 100644 --- a/Rename/Models/Binder/AppSettings.cs +++ b/Rename/Models/Binder/AppSettings.cs @@ -31,6 +31,7 @@ public class AppSettings continue; throw new NotSupportedException(physicalFileProvider.Root); } + throw new NotSupportedException("Not Found!"); } } diff --git a/Rename/Models/Binder/RenameConfiguration.cs b/Rename/Models/Binder/RenameConfiguration.cs index d48fe04..657646a 100644 --- a/Rename/Models/Binder/RenameConfiguration.cs +++ b/Rename/Models/Binder/RenameConfiguration.cs @@ -8,6 +8,8 @@ public class RenameConfiguration { public string[]? IgnoreExtensions { get; set; } + public bool? MoveFilesToRoot { get; set; } + public bool? SkipIdFiles { get; set; } public string[]? ValidImageFormatExtensions { get; set; } public override string ToString() @@ -30,6 +32,7 @@ public class RenameConfiguration continue; throw new NotSupportedException(physicalFileProvider.Root); } + throw new NotSupportedException("Not Found!"); } } @@ -44,10 +47,14 @@ public class RenameConfiguration Models.RenameConfiguration result; if (configuration is null) throw new NullReferenceException(nameof(configuration)); if (configuration.IgnoreExtensions is null) throw new NullReferenceException(nameof(configuration.IgnoreExtensions)); + if (configuration.MoveFilesToRoot is null) throw new NullReferenceException(nameof(configuration.MoveFilesToRoot)); + if (configuration.SkipIdFiles is null) throw new NullReferenceException(nameof(configuration.SkipIdFiles)); if (configuration.ValidImageFormatExtensions is null) throw new NullReferenceException(nameof(configuration.ValidImageFormatExtensions)); Verify(configuration); result = new(metadataConfiguration, configuration.IgnoreExtensions, + configuration.MoveFilesToRoot.Value, + configuration.SkipIdFiles.Value, configuration.ValidImageFormatExtensions); return result; } diff --git a/Rename/Models/RenameConfiguration.cs b/Rename/Models/RenameConfiguration.cs index 9162f86..aa3bc03 100644 --- a/Rename/Models/RenameConfiguration.cs +++ b/Rename/Models/RenameConfiguration.cs @@ -6,7 +6,9 @@ namespace View_by_Distance.Rename.Models; public record RenameConfiguration(Shared.Models.MetadataConfiguration MetadataConfiguration, string[] IgnoreExtensions, - string[] ValidImageFormatExtensions) + bool MoveFilesToRoot, + bool SkipIdFiles, + string[] ValidImageFormatExtensions) : Shared.Models.Properties.IRenameConfiguration { public override string ToString() diff --git a/Rename/Rename.cs b/Rename/Rename.cs index 48d1344..416899d 100644 --- a/Rename/Rename.cs +++ b/Rename/Rename.cs @@ -10,6 +10,7 @@ using View_by_Distance.Metadata.Models; using View_by_Distance.Metadata.Models.Stateless.Methods; using View_by_Distance.Rename.Models; using View_by_Distance.Shared.Models; +using View_by_Distance.Shared.Models.Properties; using View_by_Distance.Shared.Models.Stateless.Methods; namespace View_by_Distance.Rename; @@ -20,8 +21,7 @@ public class Rename : IRename private record ToDo(string? Directory, FileHolder FileHolder, string File, bool JsonFile); private record Record(DateTime DateTime, ExifDirectory ExifDirectory, string File, string JsonFile); - private readonly AppSettings _AppSettings; - private readonly RenameConfiguration _RenameConfiguration; + private ProgressBar? _ProgressBar; public Rename(List args, ILogger? logger, IConfigurationRoot configurationRoot, AppSettings appSettings, bool isSilent, IConsole console) { @@ -31,31 +31,24 @@ public class Rename : IRename throw new NullReferenceException(nameof(args)); if (console is null) throw new NullReferenceException(nameof(console)); - _AppSettings = appSettings; + IRename rename = this; long ticks = DateTime.Now.Ticks; ResultConfiguration resultConfiguration = Metadata.Models.Binder.ResultConfiguration.Get(configurationRoot, appSettings.RequireRootDirectoryExists); MetadataConfiguration metadataConfiguration = Metadata.Models.Binder.MetadataConfiguration.Get(configurationRoot, resultConfiguration); RenameConfiguration renameConfiguration = Models.Binder.RenameConfiguration.Get(configurationRoot, metadataConfiguration); - _RenameConfiguration = renameConfiguration; - DirectoryInfo directoryInfo = new(Path.GetFullPath(resultConfiguration.RootDirectory)); - logger?.LogInformation("{RootDirectory}", directoryInfo.FullName); - ReadOnlyCollection exifDirectories = GetExifDirectoryCollection(directoryInfo); - ReadOnlyCollection toDoCollection = GetToDoCollection(logger, ticks, exifDirectories); - ReadOnlyCollection lines = RenameFilesInDirectories(toDoCollection); - if (lines.Count != 0) - { - File.WriteAllLines($"D:/Tmp/Phares/{DateTime.Now.Ticks}.tsv", lines); - _ = IPath.DeleteEmptyDirectories(directoryInfo.FullName); - } + RenameWork(logger, appSettings, rename, ticks, renameConfiguration); } - (ReadOnlyCollection, FilePath?) IRename.ConvertAndGetFfmpegFiles(FilePath filePath) + void IRename.Tick() => + _ProgressBar?.Tick(); + + (ReadOnlyCollection, FilePath?) IRename.ConvertAndGetFfmpegFiles(IRenameConfiguration renameConfiguration, FilePath filePath) { List results = []; FilePath? result; bool isIgnoreExtension; - bool isValidImageFormatExtension = _RenameConfiguration.ValidImageFormatExtensions.Contains(filePath.ExtensionLowered); - isIgnoreExtension = isValidImageFormatExtension && _RenameConfiguration.IgnoreExtensions.Contains(filePath.ExtensionLowered); + bool isValidImageFormatExtension = renameConfiguration.ValidImageFormatExtensions.Contains(filePath.ExtensionLowered); + isIgnoreExtension = isValidImageFormatExtension && renameConfiguration.IgnoreExtensions.Contains(filePath.ExtensionLowered); if (!isIgnoreExtension && isValidImageFormatExtension) result = null; else @@ -69,11 +62,11 @@ public class Rename : IRename results.AddRange(Directory.GetFiles(filePath.DirectoryName, $"{filePath.Name}-*.jpg", SearchOption.TopDirectoryOnly)); if (results.Count == 0) throw new Exception(); - result = IId.GetFilePath(_RenameConfiguration.MetadataConfiguration, results[0]); + result = IId.GetFilePath(renameConfiguration.MetadataConfiguration, results[0]); if (!result.Name.EndsWith("-0001.jpg")) throw new Exception(); - isValidImageFormatExtension = _RenameConfiguration.ValidImageFormatExtensions.Contains(result.ExtensionLowered); - isIgnoreExtension = isValidImageFormatExtension && _RenameConfiguration.IgnoreExtensions.Contains(result.ExtensionLowered); + isValidImageFormatExtension = renameConfiguration.ValidImageFormatExtensions.Contains(result.ExtensionLowered); + isIgnoreExtension = isValidImageFormatExtension && renameConfiguration.IgnoreExtensions.Contains(result.ExtensionLowered); if (isIgnoreExtension || !isValidImageFormatExtension) throw new Exception(); if (result.DirectoryName is null) @@ -117,7 +110,7 @@ public class Rename : IRename #pragma warning restore CA1416 - private void GetExifDirectoryCollection(IRename rename, List<(string, FileInfo, ExifDirectory)> exifDirectories, IEnumerable files, A_Metadata metadata) + private static void GetExifDirectoryCollection(IRename rename, RenameConfiguration renameConfiguration, List<(string, FileInfo, ExifDirectory)> exifDirectories, IEnumerable files, A_Metadata metadata) { FileInfo fileInfo; FilePath filePath; @@ -127,21 +120,18 @@ public class Rename : IRename DeterministicHashCode deterministicHashCode; foreach (string file in files) { - filePath = IId.GetFilePath(_RenameConfiguration.MetadataConfiguration, file); - if (filePath.ExtensionLowered is ".paddedId" or ".lsv") + rename.Tick(); + filePath = IId.GetFilePath(renameConfiguration.MetadataConfiguration, file); + if (renameConfiguration.SkipIdFiles && filePath.Id is not null && (filePath.IsIdFormat || filePath.IsPaddedIdFormat)) continue; - if (files.Contains($"{filePath.FullName}.paddedId")) - continue; - if (filePath.Id is not null && (filePath.IsIdFormat || filePath.IsPaddedIdFormat)) - continue; - (ffmpegFiles, ffmpegFilePath) = rename.ConvertAndGetFfmpegFiles(filePath); + (ffmpegFiles, ffmpegFilePath) = rename.ConvertAndGetFfmpegFiles(renameConfiguration, filePath); if (ffmpegFilePath is not null) filePath = ffmpegFilePath; if (filePath.Id is not null) deterministicHashCode = new(null, filePath.Id, null); else deterministicHashCode = rename.GetDeterministicHashCode(filePath); - (fileInfo, exifDirectory) = metadata.GetMetadataCollection(_RenameConfiguration.MetadataConfiguration, filePath, deterministicHashCode); + (fileInfo, exifDirectory) = metadata.GetMetadataCollection(renameConfiguration.MetadataConfiguration, filePath, deterministicHashCode); exifDirectories.Add(new(file, fileInfo, exifDirectory)); foreach (string ffmpegFile in ffmpegFiles) File.Delete(ffmpegFile); @@ -161,24 +151,24 @@ public class Rename : IRename return new(results); } - private ReadOnlyCollection GetExifDirectoryCollection(DirectoryInfo directoryInfo) + private ReadOnlyCollection GetExifDirectoryCollection(IRename rename, AppSettings appSettings, RenameConfiguration renameConfiguration, DirectoryInfo directoryInfo) { ReadOnlyCollection results; - IRename rename = this; List<(string, FileInfo, ExifDirectory)> exifDirectories = []; - int appSettingsMaxDegreeOfParallelism = _AppSettings.MaxDegreeOfParallelism; + int appSettingsMaxDegreeOfParallelism = appSettings.MaxDegreeOfParallelism; IEnumerable files = Directory.EnumerateFiles(directoryInfo.FullName, "*", SearchOption.AllDirectories); - A_Metadata metadata = new(_RenameConfiguration.MetadataConfiguration); + A_Metadata metadata = new(renameConfiguration.MetadataConfiguration); + _ProgressBar = new(123000, "EnumerateFiles load", new ProgressBarOptions() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }); if (appSettingsMaxDegreeOfParallelism == 1) - GetExifDirectoryCollection(rename, exifDirectories, files, metadata); + GetExifDirectoryCollection(rename, renameConfiguration, exifDirectories, files, metadata); else { ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = appSettingsMaxDegreeOfParallelism }; - ProgressBar progressBar = new(123000, "EnumerateFiles load", new ProgressBarOptions() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }); - files.AsParallel().ForAll(A_Metadata.SetExifDirectoryCollection(rename, _RenameConfiguration.MetadataConfiguration, metadata, exifDirectories, () => progressBar.Tick())); - if (progressBar.CurrentTick != exifDirectories.Count) + files.AsParallel().ForAll(A_Metadata.SetExifDirectoryCollection(rename, renameConfiguration, metadata, exifDirectories)); + if (_ProgressBar.CurrentTick != exifDirectories.Count) throw new NotSupportedException(); } + _ProgressBar.Dispose(); results = GetExifDirectoryCollection(exifDirectories); return results; } @@ -194,16 +184,26 @@ public class Rename : IRename } } - private ReadOnlyCollection GetToDoCollection(ILogger? logger, long ticks, ReadOnlyCollection exifDirectories) + private static string GetCheckDirectory(RenameConfiguration renameConfiguration, Record record, FileHolder fileHolder) + { + string? checkDirectory; + if (fileHolder.DirectoryName is null) + throw new NullReferenceException(nameof(fileHolder.DirectoryName)); + (int season, string seasonName) = IDate.GetSeason(record.DateTime.DayOfYear); + string maker = IMetadata.GetMaker(record.ExifDirectory.ExifDirectoryBase); + string splat = fileHolder.DirectoryName[^3..][1] == '!' ? fileHolder.DirectoryName[^3..] : string.Empty; + string rootDirectory = renameConfiguration.MoveFilesToRoot ? renameConfiguration.MetadataConfiguration.ResultConfiguration.RootDirectory : fileHolder.DirectoryName; + checkDirectory = Path.Combine(rootDirectory, "_ Destination _", $"{record.DateTime.Year}.{season} {seasonName} {maker.Split(' ')[0]}{splat}"); + return checkDirectory; + } + + private static ReadOnlyCollection GetToDoCollection(ILogger? logger, RenameConfiguration renameConfiguration, long ticks, ReadOnlyCollection exifDirectories) { List results = []; - int season; - string maker; Record record; string jsonFile; string paddedId; string checkFile; - string seasonName; string directoryName; FileHolder fileHolder; string? checkDirectory; @@ -214,6 +214,7 @@ public class Rename : IRename string jsonFileSubDirectory; int intMinValueLength = int.MinValue.ToString().Length; VerifyIntMinValueLength(exifDirectories, intMinValueLength); + ResultConfiguration resultConfiguration = renameConfiguration.MetadataConfiguration.ResultConfiguration; ReadOnlyCollection records = new((from l in exifDirectories orderby l.DateTime select l).ToArray()); for (int i = 0; i < records.Count; i++) { @@ -223,12 +224,10 @@ public class Rename : IRename fileHolder = new(record.File); if (fileHolder.DirectoryName is null) continue; - maker = IMetadata.GetMaker(record.ExifDirectory.ExifDirectoryBase); - (season, seasonName) = IDate.GetSeason(record.DateTime.DayOfYear); + checkDirectory = GetCheckDirectory(renameConfiguration, record, fileHolder); checkFileExtension = fileHolder.ExtensionLowered == jpeg ? jpg : fileHolder.ExtensionLowered; - checkDirectory = Path.Combine(fileHolder.DirectoryName, $"{record.DateTime.Year}.{season} {seasonName}{maker}"); jsonFileSubDirectory = Path.GetDirectoryName(Path.GetDirectoryName(record.JsonFile)) ?? throw new Exception(); - paddedId = IId.GetPaddedId(intMinValueLength, _RenameConfiguration.MetadataConfiguration.Offset + i, record.ExifDirectory.Id.Value); + paddedId = IId.GetPaddedId(intMinValueLength, renameConfiguration.MetadataConfiguration.Offset + i, record.ExifDirectory.Id.Value); checkFile = Path.Combine(checkDirectory, $"{paddedId}{checkFileExtension}"); if (checkFile == fileHolder.FullName) continue; @@ -238,7 +237,7 @@ public class Rename : IRename if (File.Exists(checkFile)) continue; } - (directoryName, _) = IPath.GetDirectoryNameAndIndex(_RenameConfiguration.MetadataConfiguration.ResultConfiguration, record.ExifDirectory.Id.Value); + (directoryName, _) = IPath.GetDirectoryNameAndIndex(resultConfiguration, record.ExifDirectory.Id.Value); jsonFile = Path.Combine(jsonFileSubDirectory, directoryName, $"{record.ExifDirectory.Id.Value}{checkFileExtension}.json"); if (record.JsonFile != jsonFile) results.Add(new(null, new(record.JsonFile), jsonFile, JsonFile: true)); @@ -267,8 +266,10 @@ public class Rename : IRename { List results = []; VerifyDirectories(toDoCollection); + _ProgressBar = new(toDoCollection.Count, "Move Files", new ProgressBarOptions() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }); foreach (ToDo toDo in toDoCollection) { + _ProgressBar.Tick(); if (toDo.JsonFile) { if (File.Exists(toDo.File)) @@ -281,11 +282,29 @@ public class Rename : IRename { if (File.Exists(toDo.File)) File.Delete(toDo.File); - File.Move(toDo.FileHolder.FullName, toDo.File); + try + { File.Move(toDo.FileHolder.FullName, toDo.File); } + catch (Exception) + { continue; } results.Add($"{toDo.FileHolder.FullName}\t{toDo.File}"); } } + _ProgressBar.Dispose(); return new(results); } + private void RenameWork(ILogger? logger, AppSettings appSettings, IRename rename, long ticks, RenameConfiguration renameConfiguration) + { + DirectoryInfo directoryInfo = new(Path.GetFullPath(renameConfiguration.MetadataConfiguration.ResultConfiguration.RootDirectory)); + logger?.LogInformation("{RootDirectory}", directoryInfo.FullName); + ReadOnlyCollection exifDirectories = GetExifDirectoryCollection(rename, appSettings, renameConfiguration, directoryInfo); + ReadOnlyCollection toDoCollection = GetToDoCollection(logger, renameConfiguration, ticks, exifDirectories); + ReadOnlyCollection lines = RenameFilesInDirectories(toDoCollection); + if (lines.Count != 0) + { + File.WriteAllLines($"D:/Tmp/Phares/{DateTime.Now.Ticks}.tsv", lines); + _ = IPath.DeleteEmptyDirectories(directoryInfo.FullName); + } + } + } \ No newline at end of file diff --git a/Shared/Models/Properties/IRenameConfiguration.cs b/Shared/Models/Properties/IRenameConfiguration.cs new file mode 100644 index 0000000..249c951 --- /dev/null +++ b/Shared/Models/Properties/IRenameConfiguration.cs @@ -0,0 +1,11 @@ +namespace View_by_Distance.Shared.Models.Properties; + +public interface IRenameConfiguration +{ + + public MetadataConfiguration MetadataConfiguration { init; get; } + public string[] IgnoreExtensions { init; get; } + public bool SkipIdFiles { init; get; } + public string[] ValidImageFormatExtensions { init; get; } + +} \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/IRename.cs b/Shared/Models/Stateless/Methods/IRename.cs index 28afa5f..d480310 100644 --- a/Shared/Models/Stateless/Methods/IRename.cs +++ b/Shared/Models/Stateless/Methods/IRename.cs @@ -1,11 +1,13 @@ using System.Collections.ObjectModel; +using View_by_Distance.Shared.Models.Properties; namespace View_by_Distance.Shared.Models.Stateless.Methods; public interface IRename { - (ReadOnlyCollection, FilePath?) ConvertAndGetFfmpegFiles(FilePath filePath); + (ReadOnlyCollection, FilePath?) ConvertAndGetFfmpegFiles(IRenameConfiguration renameConfiguration, FilePath filePath); DeterministicHashCode GetDeterministicHashCode(FilePath filePath); + void Tick(); } \ No newline at end of file