OriginalFileName

This commit is contained in:
Mike Phares 2023-11-11 19:33:20 -07:00
parent 5a511a38e1
commit bf8b1c010e
13 changed files with 300 additions and 261 deletions

View File

@ -1,4 +1,5 @@
using MetadataExtractor; using MetadataExtractor;
using System.Collections.ObjectModel;
using System.Text.Json; using System.Text.Json;
using View_by_Distance.Metadata.Models.Stateless; using View_by_Distance.Metadata.Models.Stateless;
using View_by_Distance.Metadata.Models.Stateless.Methods; using View_by_Distance.Metadata.Models.Stateless.Methods;
@ -30,11 +31,17 @@ public class A_Metadata
_FileGroups = IPath.GetKeyValuePairs(aAConfiguration, bResultsFullGroupDirectory, [aAConfiguration.ResultSingleton]); _FileGroups = IPath.GetKeyValuePairs(aAConfiguration, bResultsFullGroupDirectory, [aAConfiguration.ResultSingleton]);
} }
public ExifDirectory GetMetadataCollection(IMetadataConfiguration metadataConfiguration, FilePath filePath, DeterministicHashCode deterministicHashCode) public FileInfo GetFileInfo(FilePath filePath)
{
FileInfo result;
(_, int directoryIndex) = IPath.GetDirectoryNameAndIndex(_AAConfiguration.ResultAllInOneSubdirectoryLength, filePath.Name);
result = new(Path.Combine(_FileGroups[_AAConfiguration.ResultSingleton][directoryIndex], $"{filePath.NameWithoutExtension}{filePath.ExtensionLowered}.json"));
return result;
}
public ExifDirectory GetMetadataCollection(IMetadataConfiguration metadataConfiguration, FilePath filePath, FileInfo fileInfo, DeterministicHashCode deterministicHashCode)
{ {
ExifDirectory? results; ExifDirectory? results;
(_, int directoryIndex) = IPath.GetDirectoryNameAndIndex(_AAConfiguration.ResultAllInOneSubdirectoryLength, filePath.Name);
FileInfo fileInfo = new(Path.Combine(_FileGroups[_AAConfiguration.ResultSingleton][directoryIndex], $"{filePath.NameWithoutExtension}{filePath.ExtensionLowered}.json"));
if (_ForceMetadataLastWriteTimeToCreationTime && !fileInfo.Exists && File.Exists(Path.ChangeExtension(fileInfo.FullName, ".delete"))) if (_ForceMetadataLastWriteTimeToCreationTime && !fileInfo.Exists && File.Exists(Path.ChangeExtension(fileInfo.FullName, ".delete")))
{ {
File.Move(Path.ChangeExtension(fileInfo.FullName, ".delete"), fileInfo.FullName); File.Move(Path.ChangeExtension(fileInfo.FullName, ".delete"), fileInfo.FullName);
@ -71,9 +78,8 @@ public class A_Metadata
try try
{ size = Dimensions.GetDimensions(filePath.FullName); } { size = Dimensions.GetDimensions(filePath.FullName); }
catch (Exception) { size = null; } catch (Exception) { size = null; }
IReadOnlyList<MetadataExtractor.Directory> directories = ImageMetadataReader.ReadMetadata(filePath.FullName); IReadOnlyList<MetadataExtractor.Directory> directories = ImageMetadataReader.ReadMetadata(filePath.FullName);
results = Exif.Covert(filePath, deterministicHashCode, fileInfo, size, directories); results = Exif.Covert(filePath, deterministicHashCode, size, directories);
string json = JsonSerializer.Serialize(results, ExifDirectorySourceGenerationContext.Default.ExifDirectory); string json = JsonSerializer.Serialize(results, ExifDirectorySourceGenerationContext.Default.ExifDirectory);
if (IPath.WriteAllText(fileInfo.FullName, json, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null) && _ForceMetadataLastWriteTimeToCreationTime) if (IPath.WriteAllText(fileInfo.FullName, json, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null) && _ForceMetadataLastWriteTimeToCreationTime)
{ {
@ -84,26 +90,31 @@ public class A_Metadata
return results; return results;
} }
public static Action<string> GetResultCollection(IRename rename, IMetadataConfiguration metadataConfiguration, A_Metadata metadata, List<ExifDirectory> exifDirectories, Action tick) public static Action<string> SetExifDirectoryCollection(IRename rename, IMetadataConfiguration metadataConfiguration, A_Metadata metadata, List<(string, FileInfo, ExifDirectory)> exifDirectories, Action tick)
{ {
return file => return file =>
{ {
tick.Invoke(); tick.Invoke();
FileInfo fileInfo;
FilePath? ffmpegFilePath;
ExifDirectory exifDirectory;
ReadOnlyCollection<string> ffmpegFiles;
DeterministicHashCode deterministicHashCode;
FilePath filePath = IId.GetFilePath(metadataConfiguration, file); FilePath filePath = IId.GetFilePath(metadataConfiguration, file);
if (filePath.ExtensionLowered is not ".paddedId" and not ".lsv") if (filePath.ExtensionLowered is not ".paddedId" and not ".lsv")
{ {
if (filePath.Id is null || (!filePath.IsIdFormat && !filePath.IsPaddedIdFormat)) if (filePath.Id is null || (!filePath.IsIdFormat && !filePath.IsPaddedIdFormat))
{ {
string[]? ffmpegFiles = rename.ConvertAndGetFfmpegFiles(filePath); (ffmpegFiles, ffmpegFilePath) = rename.ConvertAndGetFfmpegFiles(filePath);
filePath = ffmpegFiles is null || ffmpegFiles.Length < 0 ? filePath : IId.GetFilePath(filePath, ffmpegFiles[0]); if (ffmpegFilePath is not null)
DeterministicHashCode deterministicHashCode = filePath.Id is not null ? deterministicHashCode = new(null, filePath.Id, null) : deterministicHashCode = rename.GetDeterministicHashCode(filePath); filePath = ffmpegFilePath;
if (ffmpegFiles is not null) fileInfo = metadata.GetFileInfo(filePath);
{ deterministicHashCode = filePath.Id is not null ? deterministicHashCode = new(null, filePath.Id, null) : deterministicHashCode = rename.GetDeterministicHashCode(filePath);
foreach (string ffmpegFile in ffmpegFiles) exifDirectory = metadata.GetMetadataCollection(metadataConfiguration, filePath, fileInfo, deterministicHashCode);
File.Delete(ffmpegFile);
}
lock (exifDirectories) lock (exifDirectories)
exifDirectories.Add(metadata.GetMetadataCollection(metadataConfiguration, filePath, deterministicHashCode)); exifDirectories.Add(new(file, fileInfo, exifDirectory));
foreach (string ffmpegFile in ffmpegFiles)
File.Delete(ffmpegFile);
} }
} }
}; };

View File

@ -81,15 +81,7 @@ public class Configuration
public static MetadataConfiguration Get(IConfigurationRoot configurationRoot) public static MetadataConfiguration Get(IConfigurationRoot configurationRoot)
{ {
MetadataConfiguration result; MetadataConfiguration result;
#if Linux IConfigurationSection configurationSection = configurationRoot.GetSection(nameof(Configuration));
string environmentName = "Linux";
#elif OSX
string environmentName = "OSX";
#elif Windows
string environmentName = "Windows";
#endif
string section = string.Concat(environmentName, ":", nameof(Configuration));
IConfigurationSection configurationSection = configurationRoot.GetSection(section);
#pragma warning disable IL3050, IL2026 #pragma warning disable IL3050, IL2026
Configuration? configuration = configurationSection.Get<Configuration>(); Configuration? configuration = configurationSection.Get<Configuration>();
#pragma warning restore IL3050, IL2026 #pragma warning restore IL3050, IL2026

View File

@ -1,127 +0,0 @@
// using System.Text.Json;
// using System.Text.Json.Serialization;
// namespace View_by_Distance.Metadata.Models;
// public class ZMetadataConfiguration : Shared.Models.Properties.IAAConfiguration
// {
// protected string _RootDirectory;
// public string RootDirectory => _RootDirectory;
// public string DateGroup { init; get; }
// public string FileNameDirectorySeparator { init; get; }
// public bool ForcePropertyLastWriteTimeToCreationTime { init; get; }
// public string[] IgnoreExtensions { init; get; }
// public string[] IgnoreRulesKeyWords { init; get; }
// public int MaxImagesInDirectoryForTopLevelFirstPass { init; get; }
// public string? ModelName { init; get; }
// public int? NumberOfJitters { init; get; }
// public int? NumberOfTimesToUpsample { init; get; }
// public int Offset { init; get; }
// public string Pattern { init; get; }
// public string PersonBirthdayFormat { init; get; }
// public bool PopulatePropertyId { init; get; }
// public string? PredictorModelName { init; get; }
// public bool PropertiesChangedForProperty { init; get; }
// public string[] PropertyContentCollectionFiles { init; get; }
// public string ResultAllInOne { init; get; }
// public int ResultAllInOneSubdirectoryLength { init; get; }
// public string ResultCollection { init; get; }
// public string ResultContent { init; get; }
// public string ResultSingleton { init; get; }
// public string[] ValidImageFormatExtensions { init; get; }
// [JsonConstructor]
// public MetadataConfiguration(string dateGroup,
// string fileNameDirectorySeparator,
// bool forcePropertyLastWriteTimeToCreationTime,
// string[] ignoreExtensions,
// string[] ignoreRulesKeyWords,
// int maxImagesInDirectoryForTopLevelFirstPass,
// string? modelName,
// int? numberOfJitters,
// int? numberOfTimesToUpsample,
// int offset,
// string pattern,
// string personBirthdayFormat,
// bool populatePropertyId,
// string? predictorModelName,
// bool propertiesChangedForProperty,
// string[] propertyContentCollectionFiles,
// string resultAllInOne,
// int resultAllInOneSubdirectoryLength,
// string resultCollection,
// string resultContent,
// string resultSingleton,
// string rootDirectory,
// string[] validImageFormatExtensions)
// {
// DateGroup = dateGroup;
// FileNameDirectorySeparator = fileNameDirectorySeparator;
// ForcePropertyLastWriteTimeToCreationTime = forcePropertyLastWriteTimeToCreationTime;
// IgnoreExtensions = ignoreExtensions;
// IgnoreRulesKeyWords = ignoreRulesKeyWords;
// MaxImagesInDirectoryForTopLevelFirstPass = maxImagesInDirectoryForTopLevelFirstPass;
// ModelName = modelName;
// NumberOfJitters = numberOfJitters;
// NumberOfTimesToUpsample = numberOfTimesToUpsample;
// Offset = offset;
// Pattern = pattern;
// PersonBirthdayFormat = personBirthdayFormat;
// PredictorModelName = predictorModelName;
// PopulatePropertyId = populatePropertyId;
// PropertiesChangedForProperty = propertiesChangedForProperty;
// PropertyContentCollectionFiles = propertyContentCollectionFiles;
// ResultAllInOne = resultAllInOne;
// ResultAllInOneSubdirectoryLength = resultAllInOneSubdirectoryLength;
// ResultCollection = resultCollection;
// ResultContent = resultContent;
// ResultSingleton = resultSingleton;
// _RootDirectory = rootDirectory;
// ValidImageFormatExtensions = validImageFormatExtensions;
// }
// public override string ToString()
// {
// string result = JsonSerializer.Serialize(this, MetadataConfigurationSourceGenerationContext.Default.MetadataConfiguration);
// return result;
// }
// public void ChangeRootDirectory(string rootDirectory) =>
// _RootDirectory = Path.GetFullPath(rootDirectory);
// public static void Verify(MetadataConfiguration propertyConfiguration, bool requireExist)
// {
// if (propertyConfiguration is null)
// throw new NullReferenceException(nameof(propertyConfiguration));
// if (propertyConfiguration.IgnoreExtensions is null || propertyConfiguration.IgnoreExtensions.Length == 0)
// throw new NullReferenceException(nameof(propertyConfiguration.IgnoreExtensions));
// if (propertyConfiguration.IgnoreRulesKeyWords is null || propertyConfiguration.IgnoreRulesKeyWords.Length == 0)
// throw new NullReferenceException(nameof(propertyConfiguration.IgnoreRulesKeyWords));
// if (propertyConfiguration.PropertyContentCollectionFiles is null)
// throw new NullReferenceException(nameof(propertyConfiguration.PropertyContentCollectionFiles));
// if (propertyConfiguration.ValidImageFormatExtensions is null || propertyConfiguration.ValidImageFormatExtensions.Length == 0)
// throw new NullReferenceException(nameof(propertyConfiguration.ValidImageFormatExtensions));
// if (propertyConfiguration is null)
// throw new NullReferenceException(nameof(propertyConfiguration));
// if (string.IsNullOrEmpty(propertyConfiguration.DateGroup))
// throw new NullReferenceException(nameof(propertyConfiguration.DateGroup));
// if (string.IsNullOrEmpty(propertyConfiguration.FileNameDirectorySeparator))
// throw new NullReferenceException(nameof(propertyConfiguration.FileNameDirectorySeparator));
// if (string.IsNullOrEmpty(propertyConfiguration.Pattern))
// throw new NullReferenceException(nameof(propertyConfiguration.Pattern));
// if (string.IsNullOrEmpty(propertyConfiguration.RootDirectory) || (requireExist && !Directory.Exists(propertyConfiguration.RootDirectory)))
// throw new NullReferenceException(nameof(propertyConfiguration.RootDirectory));
// if (propertyConfiguration.RootDirectory != Path.GetFullPath(propertyConfiguration.RootDirectory))
// throw new Exception();
// }
// }
// [JsonSourceGenerationOptions(WriteIndented = true)]
// [JsonSerializable(typeof(MetadataConfiguration))]
// internal partial class MetadataConfigurationSourceGenerationContext : JsonSerializerContext
// {
// }

View File

@ -122,7 +122,7 @@ internal static class Dimensions
/// <param name="path">The path of the image to get the dimensions of.</param> /// <param name="path">The path of the image to get the dimensions of.</param>
/// <returns>The dimensions of the specified image.</returns> /// <returns>The dimensions of the specified image.</returns>
/// <exception cref="ArgumentException">The image was of an unrecognized format.</exception> /// <exception cref="ArgumentException">The image was of an unrecognized format.</exception>
public static Size GetDimensions(BinaryReader binaryReader) internal static Size GetDimensions(BinaryReader binaryReader)
{ {
int maxMagicBytesLength = _ImageFormatDecoders.Keys.OrderByDescending(x => x.Length).First().Length; int maxMagicBytesLength = _ImageFormatDecoders.Keys.OrderByDescending(x => x.Length).First().Length;
@ -146,11 +146,11 @@ internal static class Dimensions
/// <summary> /// <summary>
/// Gets the dimensions of an image. /// Gets the dimensions of an image.
/// </summary> ///internal </summary>
/// <param name="path">The path of the image to get the dimensions of.</param> /// <param name="path">The path of the image to get the dimensions of.</param>
/// <returns>The dimensions of the specified image.</returns> /// <returns>The dimensions of the specified image.</returns>
/// <exception cref="ArgumentException">The image was of an unrecognized format.</exception> /// <exception cref="ArgumentException">The image was of an unrecognized format.</exception>
public static Size GetDimensions(string path) internal static Size GetDimensions(string path)
{ {
using BinaryReader binaryReader = new(File.OpenRead(path)); using BinaryReader binaryReader = new(File.OpenRead(path));
try try

View File

@ -322,7 +322,7 @@ internal abstract class Exif
return result; return result;
} }
internal static Shared.Models.ExifDirectory Covert(Shared.Models.FilePath filePath, Shared.Models.DeterministicHashCode deterministicHashCode, FileInfo fileInfo, System.Drawing.Size? size, IReadOnlyList<MetadataExtractor.Directory> directories) internal static Shared.Models.ExifDirectory Covert(Shared.Models.FilePath filePath, Shared.Models.DeterministicHashCode deterministicHashCode, System.Drawing.Size? size, IReadOnlyList<MetadataExtractor.Directory> directories)
{ {
Shared.Models.ExifDirectory results; Shared.Models.ExifDirectory results;
Shared.Models.AviDirectory aviDirectory = GetAviDirectory(directories); Shared.Models.AviDirectory aviDirectory = GetAviDirectory(directories);
@ -338,14 +338,13 @@ internal abstract class Exif
Shared.Models.QuickTimeTrackHeaderDirectory quickTimeTrackHeaderDirectory = GetQuickTimeTrackHeaderDirectoryDirectory(directories); Shared.Models.QuickTimeTrackHeaderDirectory quickTimeTrackHeaderDirectory = GetQuickTimeTrackHeaderDirectoryDirectory(directories);
results = new(aviDirectory, results = new(aviDirectory,
exifDirectoryBase, exifDirectoryBase,
filePath.FullName,
fileMetadataDirectory, fileMetadataDirectory,
gifHeaderDirectory, gifHeaderDirectory,
gpsDirectory, gpsDirectory,
size?.Height, size?.Height,
deterministicHashCode.Id ?? filePath.Id, deterministicHashCode.Id ?? filePath.Id,
fileInfo.FullName,
jpegDirectory, jpegDirectory,
filePath.Name,
photoshopDirectory, photoshopDirectory,
pngDirectory, pngDirectory,
quickTimeMovieHeaderDirectory, quickTimeMovieHeaderDirectory,

View File

@ -4,11 +4,7 @@ using System.Text.Json.Serialization;
namespace View_by_Distance.Rename.Models; namespace View_by_Distance.Rename.Models;
public record AppSettings(string Company, public record AppSettings(string Company,
string DefaultUnknownDirectoryName, int MaxDegreeOfParallelism)
bool ForceIdName,
int MaxDegreeOfParallelism,
int MaxMinutesDelta,
bool RenameUndo)
{ {
public override string ToString() public override string ToString()

View File

@ -8,11 +8,7 @@ public class AppSettings
{ {
public string? Company { get; set; } public string? Company { get; set; }
public string? DefaultUnknownDirectoryName { get; set; }
public bool? ForceIdName { get; set; }
public int? MaxDegreeOfParallelism { get; set; } public int? MaxDegreeOfParallelism { get; set; }
public int? MaxMinutesDelta { get; set; }
public bool? RenameUndo { get; set; }
public override string ToString() public override string ToString()
{ {
@ -24,19 +20,10 @@ public class AppSettings
{ {
Models.AppSettings result; Models.AppSettings result;
if (appSettings?.Company is null) throw new NullReferenceException(nameof(appSettings.Company)); 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) throw new NullReferenceException(nameof(appSettings.MaxDegreeOfParallelism)); if (appSettings?.MaxDegreeOfParallelism is null) throw new NullReferenceException(nameof(appSettings.MaxDegreeOfParallelism));
if (appSettings?.MaxMinutesDelta is null) throw new NullReferenceException(nameof(appSettings.MaxMinutesDelta));
if (appSettings?.RenameUndo is null) throw new NullReferenceException(nameof(appSettings.RenameUndo));
result = new( result = new(
appSettings.Company, appSettings.Company,
appSettings.DefaultUnknownDirectoryName, appSettings.MaxDegreeOfParallelism.Value);
appSettings.ForceIdName.Value,
appSettings.MaxDegreeOfParallelism.Value,
appSettings.MaxMinutesDelta.Value,
appSettings.RenameUndo.Value
);
return result; return result;
} }

View File

@ -37,15 +37,7 @@ public class Configuration
public static Models.Configuration Get(IConfigurationRoot configurationRoot, Metadata.Models.MetadataConfiguration metadataConfiguration) public static Models.Configuration Get(IConfigurationRoot configurationRoot, Metadata.Models.MetadataConfiguration metadataConfiguration)
{ {
Models.Configuration result; Models.Configuration result;
#if Linux IConfigurationSection configurationSection = configurationRoot.GetSection(nameof(Configuration));
string environmentName = "Linux";
#elif OSX
string environmentName = "OSX";
#elif Windows
string environmentName = "Windows";
#endif
string section = string.Concat(environmentName, ":", nameof(Configuration));
IConfigurationSection configurationSection = configurationRoot.GetSection(section);
#pragma warning disable IL3050, IL2026 #pragma warning disable IL3050, IL2026
Configuration? configuration = configurationSection.Get<Configuration>(); Configuration? configuration = configurationSection.Get<Configuration>();
#pragma warning restore IL3050, IL2026 #pragma warning restore IL3050, IL2026

View File

@ -2,6 +2,7 @@ using CliWrap;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using ShellProgressBar; using ShellProgressBar;
using System.Collections.ObjectModel;
using System.Drawing; using System.Drawing;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@ -15,6 +16,9 @@ namespace View_by_Distance.Rename;
public class Rename : IRename 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 AppSettings _AppSettings;
private readonly Configuration _Configuration; private readonly Configuration _Configuration;
private readonly IConfigurationRoot _ConfigurationRoot; private readonly IConfigurationRoot _ConfigurationRoot;
@ -39,10 +43,12 @@ public class Rename : IRename
logger?.LogInformation("{RootDirectory}", directoryInfo.FullName); logger?.LogInformation("{RootDirectory}", directoryInfo.FullName);
MetadataConfiguration.Verify(metadataConfiguration, requireExist: false); MetadataConfiguration.Verify(metadataConfiguration, requireExist: false);
Verify(); Verify();
List<string> linesB = RenameFilesInDirectories(logger, ticks, directoryInfo); ReadOnlyCollection<Record> exifDirectories = GetExifDirectoryCollection(directoryInfo);
if (linesB.Count != 0) ReadOnlyCollection<ToDo> toDoCollection = GetToDoCollection(logger, ticks, exifDirectories);
ReadOnlyCollection<string> lines = RenameFilesInDirectories(toDoCollection);
if (lines.Count != 0)
{ {
File.WriteAllLines($"D:/Tmp/Phares/{DateTime.Now.Ticks}.tsv", linesB); File.WriteAllLines($"D:/Tmp/Phares/{DateTime.Now.Ticks}.tsv", lines);
_ = IPath.DeleteEmptyDirectories(directoryInfo.FullName); _ = IPath.DeleteEmptyDirectories(directoryInfo.FullName);
} }
} }
@ -59,53 +65,15 @@ public class Rename : IRename
throw new NullReferenceException(nameof(_MetadataConfiguration)); throw new NullReferenceException(nameof(_MetadataConfiguration));
} }
private bool GetRunToDoCollectionFirst(long ticks, DirectoryInfo directoryInfo) (ReadOnlyCollection<string>, FilePath?) IRename.ConvertAndGetFfmpegFiles(FilePath filePath)
{ {
bool result = false; List<string> results = [];
string[] directories; FilePath? result;
string seasonDirectory;
DateTime dateTime = new(ticks);
(int season, string seasonName) = IDate.GetSeason(dateTime.DayOfYear);
string eDistanceContentDirectory = IResult.GetResultsDateGroupDirectory(_MetadataConfiguration, nameof(A_Metadata), _MetadataConfiguration.ResultContent);
FileSystemInfo fileSystemInfo = new DirectoryInfo(eDistanceContentDirectory);
string[] checkDirectories =
[
Path.Combine(directoryInfo.FullName, "Ancestry"),
Path.Combine(directoryInfo.FullName, "Facebook"),
Path.Combine(directoryInfo.FullName, "LinkedIn"),
directoryInfo.FullName,
];
foreach (string checkDirectory in checkDirectories)
{
if (checkDirectory == directoryInfo.FullName)
seasonDirectory = Path.Combine(checkDirectory, $"{dateTime.Year}.{season} {seasonName}");
else
seasonDirectory = Path.Combine(checkDirectory, $"{dateTime.Year}.{season} {seasonName} {Path.GetFileName(checkDirectory)}");
if (!Directory.Exists(seasonDirectory))
_ = Directory.CreateDirectory(seasonDirectory);
if (result)
continue;
directories = Directory.GetDirectories(checkDirectory, "*", SearchOption.TopDirectoryOnly);
foreach (string directory in directories)
{
if (new DirectoryInfo(directory).LastWriteTime > fileSystemInfo.LastWriteTime)
{
result = true;
break;
}
}
}
return result;
}
string[]? IRename.ConvertAndGetFfmpegFiles(FilePath filePath)
{
string[]? results;
bool isIgnoreExtension; bool isIgnoreExtension;
bool isValidImageFormatExtension = _MetadataConfiguration.ValidImageFormatExtensions.Contains(filePath.ExtensionLowered); bool isValidImageFormatExtension = _MetadataConfiguration.ValidImageFormatExtensions.Contains(filePath.ExtensionLowered);
isIgnoreExtension = isValidImageFormatExtension && _MetadataConfiguration.IgnoreExtensions.Contains(filePath.ExtensionLowered); isIgnoreExtension = isValidImageFormatExtension && _MetadataConfiguration.IgnoreExtensions.Contains(filePath.ExtensionLowered);
if (!isIgnoreExtension && isValidImageFormatExtension) if (!isIgnoreExtension && isValidImageFormatExtension)
results = null; result = null;
else else
{ {
CommandTask<CommandResult> commandTask = Cli.Wrap("ffmpeg.exe") CommandTask<CommandResult> commandTask = Cli.Wrap("ffmpeg.exe")
@ -114,19 +82,20 @@ public class Rename : IRename
.WithWorkingDirectory(filePath.DirectoryName) .WithWorkingDirectory(filePath.DirectoryName)
.ExecuteAsync(); .ExecuteAsync();
commandTask.Task.Wait(); commandTask.Task.Wait();
results = Directory.GetFiles(filePath.DirectoryName, $"{filePath.Name}-*.jpg", SearchOption.TopDirectoryOnly); results.AddRange(Directory.GetFiles(filePath.DirectoryName, $"{filePath.Name}-*.jpg", SearchOption.TopDirectoryOnly));
if (results.Length == 0) if (results.Count == 0)
throw new Exception(); throw new Exception();
if (!filePath.Name.EndsWith("-0001.jpg")) result = IId.GetFilePath(_MetadataConfiguration, results[0]);
if (!result.Name.EndsWith("-0001.jpg"))
throw new Exception(); throw new Exception();
isValidImageFormatExtension = _MetadataConfiguration.ValidImageFormatExtensions.Contains(filePath.ExtensionLowered); isValidImageFormatExtension = _MetadataConfiguration.ValidImageFormatExtensions.Contains(result.ExtensionLowered);
isIgnoreExtension = isValidImageFormatExtension && _MetadataConfiguration.IgnoreExtensions.Contains(filePath.ExtensionLowered); isIgnoreExtension = isValidImageFormatExtension && _MetadataConfiguration.IgnoreExtensions.Contains(result.ExtensionLowered);
if (isIgnoreExtension || !isValidImageFormatExtension) if (isIgnoreExtension || !isValidImageFormatExtension)
throw new Exception(); throw new Exception();
if (filePath.DirectoryName is null) if (result.DirectoryName is null)
throw new NullReferenceException(nameof(filePath.DirectoryName)); throw new NullReferenceException(nameof(result.DirectoryName));
} }
return results; return new(new(results), result);
} }
#pragma warning disable CA1416 #pragma warning disable CA1416
@ -164,53 +133,163 @@ public class Rename : IRename
#pragma warning restore CA1416 #pragma warning restore CA1416
private void GetResultCollection(IRename rename, List<ExifDirectory> exifDirectories, IEnumerable<string> files, A_Metadata metadata) private void GetExifDirectoryCollection(IRename rename, List<(string, FileInfo, ExifDirectory)> exifDirectories, IEnumerable<string> files, A_Metadata metadata)
{ {
string[]? ffmpegFiles; FileInfo fileInfo;
FilePath filePath;
FilePath? ffmpegFilePath;
ExifDirectory exifDirectory;
ReadOnlyCollection<string> ffmpegFiles;
DeterministicHashCode deterministicHashCode; DeterministicHashCode deterministicHashCode;
foreach (string file in files) foreach (string file in files)
{ {
FilePath filePath = IId.GetFilePath(_MetadataConfiguration, file); filePath = IId.GetFilePath(_MetadataConfiguration, file);
if (filePath.ExtensionLowered is ".paddedId" or ".lsv") if (filePath.ExtensionLowered is ".paddedId" or ".lsv")
continue; continue;
if (files.Contains($"{filePath.FullName}.paddedId")) if (files.Contains($"{filePath.FullName}.paddedId"))
continue; continue;
if (filePath.Id is not null && (filePath.IsIdFormat || filePath.IsPaddedIdFormat)) if (filePath.Id is not null && (filePath.IsIdFormat || filePath.IsPaddedIdFormat))
continue; continue;
ffmpegFiles = rename.ConvertAndGetFfmpegFiles(filePath); (ffmpegFiles, ffmpegFilePath) = rename.ConvertAndGetFfmpegFiles(filePath);
filePath = ffmpegFiles is null || ffmpegFiles.Length < 0 ? filePath : IId.GetFilePath(filePath, ffmpegFiles[0]); if (ffmpegFilePath is not null)
filePath = ffmpegFilePath;
fileInfo = metadata.GetFileInfo(filePath);
if (filePath.Id is not null) if (filePath.Id is not null)
deterministicHashCode = new(null, filePath.Id, null); deterministicHashCode = new(null, filePath.Id, null);
else else
deterministicHashCode = rename.GetDeterministicHashCode(filePath); deterministicHashCode = rename.GetDeterministicHashCode(filePath);
if (ffmpegFiles is not null) exifDirectory = metadata.GetMetadataCollection(_MetadataConfiguration, filePath, fileInfo, deterministicHashCode);
{ exifDirectories.Add(new(file, fileInfo, exifDirectory));
foreach (string ffmpegFile in ffmpegFiles) foreach (string ffmpegFile in ffmpegFiles)
File.Delete(ffmpegFile); File.Delete(ffmpegFile);
}
exifDirectories.Add(metadata.GetMetadataCollection(_MetadataConfiguration, filePath, deterministicHashCode));
} }
} }
private List<string> RenameFilesInDirectories(ILogger? logger, long ticks, DirectoryInfo directoryInfo) private static ReadOnlyCollection<Record> GetExifDirectoryCollection(List<(string, FileInfo, ExifDirectory)> exifDirectories)
{ {
List<string> old = []; List<Record> results = [];
DateTime? dateTime;
foreach ((string file, FileInfo fileInfo, ExifDirectory exifDirectory) in exifDirectories)
{
dateTime = IDate.GetDateTimeOriginal(exifDirectory);
dateTime ??= IDate.GetMinimum(exifDirectory);
results.Add(new(dateTime.Value, exifDirectory, file, fileInfo.FullName));
}
return new(results);
}
private ReadOnlyCollection<Record> GetExifDirectoryCollection(DirectoryInfo directoryInfo)
{
ReadOnlyCollection<Record> results;
IRename rename = this; IRename rename = this;
List<ExifDirectory> exifDirectories = []; List<(string, FileInfo, ExifDirectory)> exifDirectories = [];
bool runToDoCollectionFirst = GetRunToDoCollectionFirst(ticks, directoryInfo); int appSettingsMaxDegreeOfParallelism = _AppSettings.MaxDegreeOfParallelism;
IEnumerable<string> files = Directory.EnumerateFiles(directoryInfo.FullName, "*", SearchOption.AllDirectories); IEnumerable<string> files = Directory.EnumerateFiles(directoryInfo.FullName, "*", SearchOption.AllDirectories);
A_Metadata metadata = new(_MetadataConfiguration, _Configuration.ForceMetadataLastWriteTimeToCreationTime, _Configuration.PropertiesChangedForMetadata); A_Metadata metadata = new(_MetadataConfiguration, _Configuration.ForceMetadataLastWriteTimeToCreationTime, _Configuration.PropertiesChangedForMetadata);
if (runToDoCollectionFirst) if (appSettingsMaxDegreeOfParallelism == 1)
GetResultCollection(rename, exifDirectories, files, metadata); GetExifDirectoryCollection(rename, exifDirectories, files, metadata);
else else
{ {
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = _AppSettings.MaxDegreeOfParallelism }; ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = appSettingsMaxDegreeOfParallelism };
ProgressBar progressBar = new(123000, "EnumerateFiles load", new ProgressBarOptions() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }); ProgressBar progressBar = new(123000, "EnumerateFiles load", new ProgressBarOptions() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true });
files.AsParallel().ForAll(A_Metadata.GetResultCollection(rename, _MetadataConfiguration, metadata, exifDirectories, () => progressBar.Tick())); files.AsParallel().ForAll(A_Metadata.SetExifDirectoryCollection(rename, _MetadataConfiguration, metadata, exifDirectories, () => progressBar.Tick()));
if (progressBar.CurrentTick != exifDirectories.Count) if (progressBar.CurrentTick != exifDirectories.Count)
throw new NotSupportedException(); throw new NotSupportedException();
} }
return old; results = GetExifDirectoryCollection(exifDirectories);
return results;
}
private static void VerifyIntMinValueLength(ReadOnlyCollection<Record> exifDirectories, int intMinValueLength)
{
foreach ((DateTime _, ExifDirectory exifDirectory, string _, string _) in exifDirectories)
{
if (exifDirectory.Id is null)
continue;
if (intMinValueLength < exifDirectory.Id.Value.ToString().Length)
throw new NotSupportedException();
}
}
private ReadOnlyCollection<ToDo> GetToDoCollection(ILogger<Program>? logger, long ticks, ReadOnlyCollection<Record> exifDirectories)
{
List<ToDo> results = [];
int season;
Record record;
string paddedId;
string checkFile;
string seasonName;
FileHolder fileHolder;
string? seasonDirectory;
string jsonFileDirectory;
const string jpg = ".jpg";
string checkFileExtension;
List<string> distinct = [];
const string jpeg = ".jpeg";
int intMinValueLength = int.MinValue.ToString().Length;
VerifyIntMinValueLength(exifDirectories, intMinValueLength);
ReadOnlyCollection<Record> records = new((from l in exifDirectories orderby l.DateTime select l).ToArray());
for (int i = 0; i < records.Count; i++)
{
record = records[i];
if (record.ExifDirectory.Id is null)
continue;
fileHolder = new(record.File);
if (fileHolder.DirectoryName is null)
continue;
(season, seasonName) = IDate.GetSeason(record.DateTime.DayOfYear);
jsonFileDirectory = Path.GetDirectoryName(record.JsonFile) ?? throw new Exception();
checkFileExtension = fileHolder.ExtensionLowered == jpeg ? jpg : fileHolder.ExtensionLowered;
seasonDirectory = Path.Combine(fileHolder.DirectoryName, $"{record.DateTime.Year}.{season} {seasonName}");
paddedId = IId.GetPaddedId(intMinValueLength, _MetadataConfiguration.Offset + i, record.ExifDirectory.Id.Value);
checkFile = Path.Combine(seasonDirectory, $"{paddedId}{checkFileExtension}");
if (checkFile == fileHolder.FullName)
continue;
if (File.Exists(checkFile))
{
checkFile = string.Concat(checkFile, ".del");
if (File.Exists(checkFile))
continue;
}
results.Add(new(null, new(record.JsonFile), Path.Combine(jsonFileDirectory, $"{record.ExifDirectory.Id.Value}{checkFileExtension}.json"), JsonFile: true));
if (distinct.Contains(checkFile))
continue;
distinct.Add(checkFile);
results.Add(new(seasonDirectory, fileHolder, checkFile, JsonFile: false));
}
return new(results);
}
private static void VerifyDirectories(ReadOnlyCollection<ToDo> toDoCollection)
{
List<string> 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<string> RenameFilesInDirectories(ReadOnlyCollection<ToDo> toDoCollection)
{
List<string> results = [];
VerifyDirectories(toDoCollection);
foreach (ToDo toDo in toDoCollection)
{
if (toDo.JsonFile)
File.Move(toDo.FileHolder.FullName, toDo.File);
else if (toDo.Directory is null)
throw new NotSupportedException();
else
{
File.Move(toDo.FileHolder.FullName, toDo.File);
results.Add($"{toDo.FileHolder.FullName}\t{toDo.File}");
}
}
return new(results);
} }
} }

View File

@ -5,14 +5,13 @@ namespace View_by_Distance.Shared.Models;
public record ExifDirectory(AviDirectory AviDirectory, public record ExifDirectory(AviDirectory AviDirectory,
ExifDirectoryBase ExifDirectoryBase, ExifDirectoryBase ExifDirectoryBase,
string File,
FileMetadataDirectory FileMetadataDirectory, FileMetadataDirectory FileMetadataDirectory,
GifHeaderDirectory GifHeaderDirectory, GifHeaderDirectory GifHeaderDirectory,
GpsDirectory GpsDirectory, GpsDirectory GpsDirectory,
int? Height, int? Height,
int? Id, int? Id,
string JsonFile,
JpegDirectory JpegDirectory, JpegDirectory JpegDirectory,
string OriginalFileName,
PhotoshopDirectory PhotoshopDirectory, PhotoshopDirectory PhotoshopDirectory,
PngDirectory PngDirectory, PngDirectory PngDirectory,
QuickTimeMovieHeaderDirectory QuickTimeMovieHeaderDirectory, QuickTimeMovieHeaderDirectory QuickTimeMovieHeaderDirectory,

View File

@ -13,4 +13,14 @@ public interface IDate
static (int Season, string seasonName) GetSeason(int dayOfYear) => static (int Season, string seasonName) GetSeason(int dayOfYear) =>
XDate.GetSeason(dayOfYear); XDate.GetSeason(dayOfYear);
DateTime? TestStatic_GetDateTimeOriginal(ExifDirectory exifDirectory) =>
GetDateTimeOriginal(exifDirectory);
static DateTime? GetDateTimeOriginal(ExifDirectory exifDirectory) =>
XDate.GetDateTimeOriginal(exifDirectory);
DateTime TestStatic_GetMinimum(ExifDirectory exifDirectory) =>
GetMinimum(exifDirectory);
static DateTime GetMinimum(ExifDirectory exifDirectory) =>
XDate.GetMinimum(exifDirectory);
} }

View File

@ -1,9 +1,11 @@
using System.Collections.ObjectModel;
namespace View_by_Distance.Shared.Models.Stateless.Methods; namespace View_by_Distance.Shared.Models.Stateless.Methods;
public interface IRename public interface IRename
{ {
string[]? ConvertAndGetFfmpegFiles(FilePath filePath); (ReadOnlyCollection<string>, FilePath?) ConvertAndGetFfmpegFiles(FilePath filePath);
DeterministicHashCode GetDeterministicHashCode(FilePath filePath); DeterministicHashCode GetDeterministicHashCode(FilePath filePath);
} }

View File

@ -1,3 +1,6 @@
using System.Globalization;
using System.Text;
namespace View_by_Distance.Shared.Models.Stateless; namespace View_by_Distance.Shared.Models.Stateless;
internal abstract class XDate internal abstract class XDate
@ -47,4 +50,100 @@ internal abstract class XDate
return new(result, results); return new(result, results);
} }
internal static DateTime? GetDateTimeOriginal(ExifDirectory exifDirectory)
{
DateTime? result;
List<DateTime> results = [];
if (exifDirectory.ExifDirectoryBase.DateTimeOriginal is not null)
results.Add(exifDirectory.ExifDirectoryBase.DateTimeOriginal.Value);
if (exifDirectory.AviDirectory.DateTimeOriginal is not null)
results.Add(exifDirectory.AviDirectory.DateTimeOriginal.Value);
if (exifDirectory.QuickTimeMovieHeaderDirectory.Created is not null)
results.Add(exifDirectory.QuickTimeMovieHeaderDirectory.Created.Value);
if (exifDirectory.QuickTimeTrackHeaderDirectory.Created is not null)
results.Add(exifDirectory.QuickTimeTrackHeaderDirectory.Created.Value);
result = results.Count == 0 ? null : results.Min();
return result;
}
private static DateTime? GetDateTimeFromName(string fileNameWithoutExtension)
{
DateTime? result = null;
int length;
string format;
string fullFormat;
StringBuilder value = new();
const string ticksExample = "##################";
string[][] dateFormats =
[
[string.Empty, "yyyyMMdd_HHmmss", string.Empty],
[string.Empty, "yyyyMMddHHmmssfff", string.Empty],
[string.Empty, "yyyyMMdd_", ticksExample],
[string.Empty, "yyyy-MM-dd_", ticksExample],
[string.Empty, "yyyy-MM-dd.", ticksExample],
// [string.Empty, "yyyy-MM-dd.", $"{ticksExample}.{fileHolder.Length}"],
[string.Empty, "yyyy-MM-dd HH.mm.ss", string.Empty],
[string.Empty, "yyyyMMdd_HHmmss", "_LLS"],
[string.Empty, "yyyyMMdd_HHmmss", "_HDR"],
["WIN_", "yyyyMMdd_HH_mm_ss", "_Pro"],
["IMG_", "yyyyMMdd_HHmmss", string.Empty],
["IMG#####-", "yyyyMMdd-HHmm", string.Empty],
["CameraZOOM-", "yyyyMMddHHmmss", string.Empty],
["VideoCapture_", "yyyyMMdd-HHmmss ", string.Empty]
];
foreach (string[] dateFormat in dateFormats)
{
_ = value.Clear();
if (dateFormat.Length != 3)
throw new Exception();
fullFormat = string.Join(string.Empty, dateFormat);
if (fileNameWithoutExtension.Length != fullFormat.Length)
continue;
format = dateFormat[1];
length = dateFormat[0].Length + dateFormat[1].Length;
for (int i = dateFormat[0].Length; i < length; i++)
_ = value.Append(fileNameWithoutExtension[i]);
if (value.Length != format.Length)
continue;
if (DateTime.TryParseExact(value.ToString(), format, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime checkDateTime))
{
if (fileNameWithoutExtension.Length < ticksExample.Length || !long.TryParse(fileNameWithoutExtension[^ticksExample.Length..], out long ticks))
result = checkDateTime;
else
result = new DateTime(ticks);
break;
}
}
return result;
}
internal static DateTime GetMinimum(ExifDirectory exifDirectory)
{
DateTime result;
List<DateTime> results = [];
if (exifDirectory.ExifDirectoryBase.DateTimeOriginal is not null)
results.Add(exifDirectory.ExifDirectoryBase.DateTimeOriginal.Value);
if (exifDirectory.AviDirectory.DateTimeOriginal is not null)
results.Add(exifDirectory.AviDirectory.DateTimeOriginal.Value);
if (exifDirectory.QuickTimeMovieHeaderDirectory.Created is not null)
results.Add(exifDirectory.QuickTimeMovieHeaderDirectory.Created.Value);
if (exifDirectory.QuickTimeTrackHeaderDirectory.Created is not null)
results.Add(exifDirectory.QuickTimeTrackHeaderDirectory.Created.Value);
if (results.Count == 0)
{
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(exifDirectory.OriginalFileName);
DateTime? dateTime = GetDateTimeFromName(fileNameWithoutExtension);
if (dateTime is not null)
results.Add(dateTime.Value);
if (exifDirectory.ExifDirectoryBase.DateTime is not null)
results.Add(exifDirectory.ExifDirectoryBase.DateTime.Value);
if (exifDirectory.ExifDirectoryBase.DateTimeDigitized is not null)
results.Add(exifDirectory.ExifDirectoryBase.DateTimeDigitized.Value);
}
if (results.Count == 0 && exifDirectory.FileMetadataDirectory.FileModifiedDate is not null)
results.Add(exifDirectory.FileMetadataDirectory.FileModifiedDate.Value);
result = results.Count == 0 ? DateTime.MinValue : results.Min();
return result;
}
} }