Merge Kristy Files

This commit is contained in:
2023-07-09 00:26:58 -07:00
parent 516e494928
commit d7ed5d89d9
12 changed files with 309 additions and 113 deletions

View File

@ -1,8 +1,10 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace View_by_Distance.Rename.Models;
public record AppSettings(string Company,
string DefaultUnknownDirectoryName,
bool ForceIdName,
int MaxDegreeOfParallelism,
int MaxMinutesDelta,
@ -12,8 +14,14 @@ public record AppSettings(string Company,
public override string ToString()
{
string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true });
string result = JsonSerializer.Serialize(this, AppSettingsSourceGenerationContext.Default.AppSettings);
return result;
}
}
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(AppSettings))]
internal partial class AppSettingsSourceGenerationContext : JsonSerializerContext
{
}

View File

@ -1,5 +1,6 @@
using Microsoft.Extensions.Configuration;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace View_by_Distance.Rename.Models.Binder;
@ -7,6 +8,7 @@ public class AppSettings
{
public string? Company { get; set; }
public string? DefaultUnknownDirectoryName { get; set; }
public bool? ForceIdName { get; set; }
public int? MaxDegreeOfParallelism { get; set; }
public int? MaxMinutesDelta { get; set; }
@ -15,7 +17,7 @@ public class AppSettings
public override string ToString()
{
string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true });
string result = JsonSerializer.Serialize(this, BinderAppSettingsSourceGenerationContext.Default.AppSettings);
return result;
}
@ -24,6 +26,8 @@ public class AppSettings
Models.AppSettings result;
if (appSettings?.Company is null)
throw new NullReferenceException(nameof(appSettings.Company));
if (appSettings?.DefaultUnknownDirectoryName is null)
throw new NullReferenceException(nameof(appSettings.DefaultUnknownDirectoryName));
if (appSettings?.ForceIdName is null)
throw new NullReferenceException(nameof(appSettings.ForceIdName));
if (appSettings?.MaxDegreeOfParallelism is null)
@ -36,6 +40,7 @@ public class AppSettings
throw new NullReferenceException(nameof(appSettings.WorkingDirectoryName));
result = new(
appSettings.Company,
appSettings.DefaultUnknownDirectoryName,
appSettings.ForceIdName.Value,
appSettings.MaxDegreeOfParallelism.Value,
appSettings.MaxMinutesDelta.Value,
@ -48,9 +53,17 @@ public class AppSettings
public static Models.AppSettings Get(IConfigurationRoot configurationRoot)
{
Models.AppSettings result;
#pragma warning disable IL3050, IL2026
AppSettings? appSettings = configurationRoot.Get<AppSettings>();
#pragma warning restore IL3050, IL2026
result = Get(appSettings);
return result;
}
}
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(AppSettings))]
internal partial class BinderAppSettingsSourceGenerationContext : JsonSerializerContext
{
}

View File

@ -12,6 +12,15 @@ namespace View_by_Distance.Rename;
public class Rename
{
private record Record(int Index,
bool IsIgnoreExtension,
bool IsValidImageFormatExtension,
List<FileHolder> FileHolders,
bool FfmpegFilesPresent,
DateTime? DateTimeOriginal,
DateTime?[] DateTimes,
int? Id);
private readonly AppSettings _AppSettings;
private readonly string _WorkingDirectory;
private readonly Configuration _Configuration;
@ -90,44 +99,27 @@ public class Rename
return results;
}
private List<(FileHolder, string, string)> GetToDoCollection(ProgressBar progressBar, string[] files, bool nefPresent)
private List<Record> GetRecords(int offset, ProgressBar progressBar, string[] files)
{
List<(FileHolder, string, string)> results = new();
List<Record> results = new();
int? id;
int season;
string? message;
string checkFile;
DateTime dateTime;
bool? isWrongYear;
string seasonName;
string? directory;
TimeSpan? timeSpan;
string directoryName;
DateTime?[] dateTimes;
FileHolder fileHolder;
string[]? ffmpegFiles;
bool isIgnoreExtension;
string? seasonDirectory;
const string jpg = ".jpg";
DateTime? minimumDateTime;
string checkFileExtension;
DateTime? dateTimeFromName;
DateTime? dateTimeOriginal;
const string jpeg = ".jpeg";
DateTime?[] metadataDateTimes;
List<string> distinct = new();
string[] directoryNameSegments;
bool isValidImageFormatExtension;
DateTime? metadataDateTimeOriginal;
bool nameWithoutExtensionIsIdFormat;
IReadOnlyList<MetadataExtractor.Directory> directories;
foreach (string file in files)
for (int i = 0; i < files.Length; i++)
{
progressBar.Tick();
fileHolder = new(file);
fileHolder = new(files[i]);
if (!fileHolder.Exists)
continue;
directory = Path.GetDirectoryName(file);
directory = Path.GetDirectoryName(files[i]);
if (string.IsNullOrEmpty(directory))
continue;
if (fileHolder.ExtensionLowered == ".id" || fileHolder.ExtensionLowered == ".lsv" || fileHolder.DirectoryName is null)
@ -137,7 +129,6 @@ public class Rename
nameWithoutExtensionIsIdFormat = Shared.Models.Stateless.Methods.IProperty.NameWithoutExtensionIsIdFormat(fileHolder);
if (nameWithoutExtensionIsIdFormat)
continue;
dateTimeFromName = Shared.Models.Stateless.Methods.IProperty.GetDateTimeFromName(fileHolder);
isValidImageFormatExtension = _PropertyConfiguration.ValidImageFormatExtensions.Contains(fileHolder.ExtensionLowered);
isIgnoreExtension = isValidImageFormatExtension && _PropertyConfiguration.IgnoreExtensions.Contains(fileHolder.ExtensionLowered);
if (!isIgnoreExtension && isValidImageFormatExtension)
@ -145,11 +136,11 @@ public class Rename
else
{
try
{ directories = MetadataExtractor.ImageMetadataReader.ReadMetadata(file); }
{ directories = MetadataExtractor.ImageMetadataReader.ReadMetadata(files[i]); }
catch (Exception) { continue; }
CommandTask<CommandResult> result = Cli.Wrap("ffmpeg.exe")
// .WithArguments(new[] { "-ss", "00:00:00", "-t", "00:00:00", "-i", file, "-qscale:v", "2", "-r", "0.01", $"{fileHolder.Name}-%4d.jpg" })
.WithArguments(new[] { "-i", file, "-vframes", "1", $"{fileHolder.Name}-%4d.jpg" })
// .WithArguments(new[] { "-ss", "00:00:00", "-t", "00:00:00", "-i", files[i], "-qscale:v", "2", "-r", "0.01", $"{fileHolder.Name}-%4d.jpg" })
.WithArguments(new[] { "-i", files[i], "-vframes", "1", $"{fileHolder.Name}-%4d.jpg" })
.WithWorkingDirectory(fileHolder.DirectoryName)
.ExecuteAsync();
result.Task.Wait();
@ -166,9 +157,57 @@ public class Rename
if (fileHolder.DirectoryName is null)
continue;
}
(dateTimeOriginal, dateTimes, id, message) = Shared.Models.Stateless.Methods.IProperty.Get(fileHolder, isIgnoreExtension, isValidImageFormatExtension, _PropertyConfiguration.PopulatePropertyId);
if (ffmpegFiles is not null)
{
fileHolder = new(files[i]);
foreach (string ffmpegFile in ffmpegFiles)
File.Delete(ffmpegFile);
}
if (message is not null)
throw new Exception(message);
results.Add(new(i + offset, isIgnoreExtension, isValidImageFormatExtension, new() { fileHolder }, ffmpegFiles is null, dateTimeOriginal, dateTimes, id));
}
return results;
}
private List<(FileHolder, string, string)> GetToDoCollection(ProgressBar progressBar, bool nefPresent, List<Record> records, int length)
{
List<(FileHolder, string, string)> results = new();
string id;
int season;
string checkFile;
bool? isWrongYear;
DateTime dateTime;
string seasonName;
TimeSpan? timeSpan;
string directoryName;
FileHolder fileHolder;
string? seasonDirectory;
const string jpg = ".jpg";
DateTime? minimumDateTime;
string checkFileExtension;
DateTime? dateTimeFromName;
const string jpeg = ".jpeg";
DateTime?[] metadataDateTimes;
List<string> distinct = new();
string[] directoryNameSegments;
DateTime? dateTimeOriginalByLogic;
DateTime? metadataDateTimeOriginal;
IReadOnlyList<MetadataExtractor.Directory> directories;
foreach (Record record in records)
{
progressBar.Tick();
if (record.FileHolders.Count != 1)
continue;
fileHolder = record.FileHolders.First();
if (!fileHolder.Exists)
continue;
if (string.IsNullOrEmpty(fileHolder.DirectoryName))
continue;
if (fileHolder.ExtensionLowered == jpeg)
{
if (!isIgnoreExtension && isValidImageFormatExtension)
if (!record.IsIgnoreExtension && record.IsValidImageFormatExtension)
{
if (File.Exists($"{fileHolder.FullName}.id"))
{
@ -178,7 +217,7 @@ public class Rename
if (distinct.Contains(checkFile))
continue;
distinct.Add(checkFile);
results.Add(new(new($"{fileHolder.FullName}.id"), directory, checkFile));
results.Add(new(new($"{fileHolder.FullName}.id"), fileHolder.DirectoryName, checkFile));
}
checkFile = Path.Combine(fileHolder.DirectoryName, $"{fileHolder.NameWithoutExtension}{jpg}");
if (File.Exists(checkFile))
@ -186,11 +225,11 @@ public class Rename
if (distinct.Contains(checkFile))
continue;
distinct.Add(checkFile);
results.Add(new(fileHolder, directory, checkFile));
results.Add(new(fileHolder, fileHolder.DirectoryName, checkFile));
if (nefPresent)
results.Add(new(new($"{fileHolder.FullName[..^4]}.tif"), directory, $"{checkFile[..^4]}.tif"));
results.Add(new(new($"{fileHolder.FullName[..^4]}.tif"), fileHolder.DirectoryName, $"{checkFile[..^4]}.tif"));
if (nefPresent)
results.Add(new(new($"{fileHolder.FullName[..^4]}.nef"), directory, $"{checkFile[..^4]}.nef"));
results.Add(new(new($"{fileHolder.FullName[..^4]}.nef"), fileHolder.DirectoryName, $"{checkFile[..^4]}.nef"));
if (File.Exists(checkFile))
continue;
File.Move(fileHolder.FullName, checkFile);
@ -199,24 +238,16 @@ public class Rename
continue;
}
}
(dateTimeOriginal, dateTimes, id, message) = Shared.Models.Stateless.Methods.IProperty.Get(fileHolder, isIgnoreExtension, isValidImageFormatExtension, _PropertyConfiguration.PopulatePropertyId);
if (ffmpegFiles is not null)
{
fileHolder = new(file);
foreach (string ffmpegFile in ffmpegFiles)
File.Delete(ffmpegFile);
}
if (fileHolder.DirectoryName is null)
continue;
minimumDateTime = dateTimes.Min();
dateTimeFromName = Shared.Models.Stateless.Methods.IProperty.GetDateTimeFromName(fileHolder);
minimumDateTime = record.DateTimes.Min();
if (minimumDateTime is null)
throw new NotSupportedException();
if (dateTimeOriginal is null)
if (record.DateTimeOriginal is null)
timeSpan = null;
else
timeSpan = new(Math.Abs(minimumDateTime.Value.Ticks - dateTimeOriginal.Value.Ticks));
timeSpan = new(Math.Abs(minimumDateTime.Value.Ticks - record.DateTimeOriginal.Value.Ticks));
if (timeSpan is not null && timeSpan.Value.TotalMinutes < _AppSettings.MaxMinutesDelta)
(metadataDateTimeOriginal, metadataDateTimes) = (null, Array.Empty<DateTime?>());
(dateTimeOriginalByLogic, metadataDateTimeOriginal, metadataDateTimes) = (record.DateTimeOriginal, null, Array.Empty<DateTime?>());
else
{
if (_PropertyConfiguration.IgnoreExtensions.Contains(fileHolder.ExtensionLowered))
@ -225,16 +256,21 @@ public class Rename
{ directories = MetadataExtractor.ImageMetadataReader.ReadMetadata(fileHolder.FullName); }
catch (Exception) { continue; }
(metadataDateTimeOriginal, metadataDateTimes) = Metadata.Models.Stateless.Methods.IMetadata.GetDateTimes(fileHolder, directories);
dateTimeOriginal ??= metadataDateTimeOriginal;
if (ffmpegFiles is not null && dateTimeOriginal is not null)
minimumDateTime = dateTimeOriginal.Value;
if (dateTimeOriginal is null)
dateTimeOriginalByLogic = record.DateTimeOriginal is not null ? record.DateTimeOriginal : metadataDateTimeOriginal;
if (record.FfmpegFilesPresent && dateTimeOriginalByLogic is not null)
minimumDateTime = dateTimeOriginalByLogic.Value;
if (dateTimeOriginalByLogic is null)
timeSpan = null;
else
timeSpan = new(Math.Abs(minimumDateTime.Value.Ticks - dateTimeOriginal.Value.Ticks));
timeSpan = new(Math.Abs(minimumDateTime.Value.Ticks - dateTimeOriginalByLogic.Value.Ticks));
}
if (timeSpan is null || timeSpan.Value.TotalMinutes > _AppSettings.MaxMinutesDelta)
(isWrongYear, seasonDirectory) = (null, !_AppSettings.ForceIdName ? null : Path.Combine(fileHolder.DirectoryName, "Unknown"));
{
if (string.IsNullOrEmpty(_AppSettings.DefaultUnknownDirectoryName))
(isWrongYear, seasonDirectory) = (null, !_AppSettings.ForceIdName ? null : fileHolder.DirectoryName);
else
(isWrongYear, seasonDirectory) = (null, !_AppSettings.ForceIdName ? null : Path.Combine(fileHolder.DirectoryName, _AppSettings.DefaultUnknownDirectoryName));
}
else
{
directoryName = Path.GetFileName(fileHolder.DirectoryName);
@ -251,12 +287,10 @@ public class Rename
{
if (dateTimeFromName is not null && isWrongYear is not null && isWrongYear.Value)
minimumDateTime = dateTimeFromName.Value;
else if (dateTimeOriginal is not null)
minimumDateTime = dateTimeOriginal.Value;
else if (metadataDateTimeOriginal is not null)
minimumDateTime = metadataDateTimeOriginal.Value;
else if (dateTimeOriginalByLogic is not null)
minimumDateTime = dateTimeOriginalByLogic.Value;
else
minimumDateTime = new DateTime?[] { dateTimes.Where(l => l is not null).Min(), metadataDateTimes.Where(l => l is not null).Min() }.Min();
minimumDateTime = new DateTime?[] { record.DateTimes.Where(l => l is not null).Min(), metadataDateTimes.Where(l => l is not null).Min() }.Min();
if (minimumDateTime is null)
continue;
checkFileExtension = fileHolder.ExtensionLowered == jpeg ? jpg : fileHolder.ExtensionLowered;
@ -266,18 +300,19 @@ public class Rename
if (distinct.Contains(checkFile))
continue;
distinct.Add(checkFile);
results.Add(new(fileHolder, directory, checkFile));
results.Add(new(fileHolder, fileHolder.DirectoryName, checkFile));
if (nefPresent)
results.Add(new(new($"{fileHolder.FullName[..^4]}.tif"), directory, $"{checkFile[..^4]}.tif"));
results.Add(new(new($"{fileHolder.FullName[..^4]}.tif"), fileHolder.DirectoryName, $"{checkFile[..^4]}.tif"));
if (nefPresent)
results.Add(new(new($"{fileHolder.FullName[..^4]}.nef"), directory, $"{checkFile[..^4]}.nef"));
results.Add(new(new($"{fileHolder.FullName[..^4]}.nef"), fileHolder.DirectoryName, $"{checkFile[..^4]}.nef"));
}
else
{
if (id is null)
if (record.Id is null)
continue;
id = Shared.Models.Stateless.Methods.IDirectory.GetPaddedId(length, record.Index, record.Id.Value);
checkFileExtension = fileHolder.ExtensionLowered == jpeg ? jpg : fileHolder.ExtensionLowered;
checkFile = Path.Combine(seasonDirectory, $"{id.Value}{checkFileExtension}");
checkFile = Path.Combine(seasonDirectory, $"{id}{checkFileExtension}");
if (checkFile == fileHolder.FullName)
continue;
if (File.Exists(checkFile))
@ -317,13 +352,16 @@ public class Rename
{
List<string> results = new();
string message;
bool nefPresent;
bool nefPresentCheck;
bool nefPresent = false;
ProgressBar progressBar;
List<Record> records = new();
const string fileSearchFilter = "*";
const string directorySearchFilter = "*";
List<string> distinctDirectories = new();
List<(FileHolder, string)> verifiedToDoCollection = new();
List<(FileHolder, string, string)> toDoCollection = new();
int offset = Shared.Models.Stateless.Methods.IDirectory.GetOffset();
ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true };
List<string[]> filesCollection = Shared.Models.Stateless.Methods.IDirectory.GetFilesCollection(_PropertyConfiguration.RootDirectory, directorySearchFilter, fileSearchFilter);
int count = filesCollection.Select(l => l.Length).Sum();
@ -331,28 +369,52 @@ public class Rename
{
if (!files.Any())
continue;
// foreach (string file in files)
// foreach (string files[i] in files)
// {
// if (!file.EndsWith(".del"))
// if (!files[i].EndsWith(".del"))
// continue;
// File.Move(file, file[..^4]);
// File.Move(files[i], files[i][..^4]);
// }
// continue;
distinctDirectories.Clear();
message = $") Renaming files for <{files.FirstOrDefault()}>";
progressBar = new(files.Length, message, options);
if (_AppSettings.RenameUndo)
{
message = $") Undo renaming files for <{files.FirstOrDefault()}>";
progressBar = new(files.Length, message, options);
toDoCollection.AddRange(GetRenameUndoToDoCollection(progressBar, files));
}
else
{
nefPresent = files.Any(l => l.EndsWith(".NEF"));
if (!nefPresent)
toDoCollection.AddRange(GetToDoCollection(progressBar, files, nefPresent));
message = $"{records.Count:00000}) Gathering records for files next to <{files.FirstOrDefault()}>";
progressBar = new(files.Length, message, options);
nefPresentCheck = files.Any(l => l.EndsWith(".NEF"));
if (!nefPresentCheck)
records.AddRange(GetRecords(offset + records.Count, progressBar, files));
else
toDoCollection.AddRange(GetToDoCollection(progressBar, (from l in files where l.EndsWith(".JPG") select l).ToArray(), nefPresent));
{
if (!nefPresent)
nefPresent = true;
records.AddRange(GetRecords(offset + records.Count, progressBar, (from l in files where l.EndsWith(".JPG") select l).ToArray()));
}
}
progressBar.Dispose();
}
if (records.Any())
{
int length = 0;
foreach (Record record in records)
{
if (record.Id is null)
continue;
if (length > record.Id.Value.ToString().Length)
continue;
length = record.Id.Value.ToString().Length;
}
message = $"{length}) comparing records";
progressBar = new(records.Count, message, options);
toDoCollection.AddRange(GetToDoCollection(progressBar, nefPresent, records, length));
progressBar.Dispose();
}
foreach ((FileHolder fileHolder, string directory, string to) in toDoCollection)
{
if (distinctDirectories.Contains(directory))
@ -372,10 +434,10 @@ public class Rename
File.WriteAllText($"{to}.id", $"{to}{Environment.NewLine}{fileHolder.FullName}");
}
ConsoleKey? consoleKey = null;
log.Information($"Ready to Move {verifiedToDoCollection.Count} file(s)?");
log.Information($"Ready to Move {verifiedToDoCollection.Count} files[i](s)?");
for (int y = 0; y < int.MaxValue; y++)
{
log.Information("Press \"Y\" key to move file(s), \"N\" key to log file(s) or close console to not move files");
log.Information("Press \"Y\" key to move files[i](s), \"N\" key to log files[i](s) or close console to not move files");
consoleKey = System.Console.ReadKey().Key;
if (consoleKey is ConsoleKey.Y or ConsoleKey.N)
break;