Compare commits

...

10 Commits

Author SHA1 Message Date
abbe2feac0 Removed Amazon
IsOffsetDeterministicHashCode
2024-05-18 10:26:36 -07:00
d1557e1d85 RelativePropertyCollectionFile
aMetadataCollectionDirectory
Work with video
FilePath.IsIgnore
Removed IId IsIgnore
Keywords
RootAmazon
SaveAmazon
PhysicalFileProvider Message
Bump
HarFilesDirectory
2024-04-28 17:13:28 -07:00
684ba1f0df Metadata 2023-12-26 18:27:04 -07:00
7ec50dd56e RenameUrl 2023-12-25 20:41:10 -07:00
d830c1d5a4 ValidVideoFormatExtensions 2023-12-25 19:18:54 -07:00
48eea2edbd Removed IntelligentIdRecord 2023-12-24 08:59:45 -07:00
1f00696bf3 Bug 2023-12-22 16:56:40 -07:00
82de27ce61 Videos 2023-12-22 16:22:08 -07:00
96c479e639 IntelligentIdRecord 2023-12-22 15:46:47 -07:00
0310e06f3c Used to re-organized folders 2023-11-12 18:09:22 -07:00
35 changed files with 1272 additions and 589 deletions

2
.gitignore vendored
View File

@ -468,3 +468,5 @@ globalStorage/
[Ll]ib/ [Ll]ib/
Shared/.kanbn Shared/.kanbn
.vscode/Har-Files

0
.vscode/.yml vendored Normal file
View File

View File

@ -22,6 +22,7 @@
"Hmmssfff", "Hmmssfff",
"jfif", "jfif",
"JOSN", "JOSN",
"Makernote",
"mmod", "mmod",
"Nicéphore", "Nicéphore",
"Niépce", "Niépce",

View File

@ -34,8 +34,8 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="MetadataExtractor" Version="2.8.1" /> <PackageReference Include="MetadataExtractor" Version="2.8.1" />
<PackageReference Include="System.Text.Json" Version="7.0.3" /> <PackageReference Include="System.Text.Json" Version="8.0.3" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Shared\AA.Shared.csproj" /> <ProjectReference Include="..\Shared\AA.Shared.csproj" />

View File

@ -1,9 +1,8 @@
using MetadataExtractor;
using System.Collections.ObjectModel; 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.Shared.Models; using View_by_Distance.Shared.Models;
using View_by_Distance.Shared.Models.Properties;
using View_by_Distance.Shared.Models.Stateless.Methods; using View_by_Distance.Shared.Models.Stateless.Methods;
namespace View_by_Distance.Metadata.Models; namespace View_by_Distance.Metadata.Models;
@ -17,30 +16,59 @@ public class A_Metadata
public A_Metadata(MetadataConfiguration metadataConfiguration) public A_Metadata(MetadataConfiguration metadataConfiguration)
{ {
_MetadataConfiguration = metadataConfiguration; _MetadataConfiguration = metadataConfiguration;
string bResultsFullGroupDirectory = IResult.GetResultsFullGroupDirectory(metadataConfiguration.ResultConfiguration, string aResultsFullGroupDirectory = IResult.GetResultsFullGroupDirectory(metadataConfiguration.ResultConfiguration,
nameof(A_Metadata), nameof(A_Metadata),
string.Empty, string.Empty,
includeResizeGroup: false, includeResizeGroup: false,
includeModel: false, includeModel: false,
includePredictorModel: false); includePredictorModel: false);
_FileGroups = IPath.GetKeyValuePairs(metadataConfiguration.ResultConfiguration, bResultsFullGroupDirectory, [metadataConfiguration.ResultConfiguration.ResultSingleton]); _FileGroups = IPath.GetKeyValuePairs(metadataConfiguration.ResultConfiguration, aResultsFullGroupDirectory, [metadataConfiguration.ResultConfiguration.ResultSingleton]);
} }
private FileInfo GetFileInfo(ResultConfiguration resultConfiguration, FilePath filePath) private (int, FileInfo) GetFileInfo(ResultConfiguration resultConfiguration, FilePath filePath)
{ {
FileInfo result; FileInfo result;
FileInfo fileInfo = new(filePath.FullName); FileInfo fileInfo = new(filePath.FullName);
(_, int directoryIndex) = IPath.GetDirectoryNameAndIndex(resultConfiguration, filePath); (_, int directoryIndex) = IPath.GetDirectoryNameAndIndex(resultConfiguration, filePath);
DateTime minimumDateTime = fileInfo.CreationTime < fileInfo.LastWriteTime ? fileInfo.CreationTime : fileInfo.LastWriteTime; DateTime minimumDateTime = fileInfo.CreationTime < fileInfo.LastWriteTime ? fileInfo.CreationTime : fileInfo.LastWriteTime;
int minimumYear = minimumDateTime.Year < resultConfiguration.EpicYear ? resultConfiguration.EpicYear : minimumDateTime.Year; int fileInfoMinimumYear = minimumDateTime.Year < resultConfiguration.EpicYear ? resultConfiguration.EpicYear : minimumDateTime.Year;
result = new(Path.Combine(_FileGroups[minimumYear][_MetadataConfiguration.ResultConfiguration.ResultSingleton][directoryIndex], $"{filePath.NameWithoutExtension}{filePath.ExtensionLowered}.json")); result = new(Path.Combine(_FileGroups[fileInfoMinimumYear][_MetadataConfiguration.ResultConfiguration.ResultSingleton][directoryIndex], $"{filePath.NameWithoutExtension}{filePath.ExtensionLowered}.json"));
return result; 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) public (FileInfo, ExifDirectory) GetMetadataCollection(MetadataConfiguration metadataConfiguration, FilePath filePath, DeterministicHashCode deterministicHashCode)
{ {
ExifDirectory? results; ExifDirectory? result;
FileInfo fileInfo = GetFileInfo(metadataConfiguration.ResultConfiguration, filePath); (int fileInfoMinimumYear, FileInfo fileInfo) = GetFileInfo(metadataConfiguration.ResultConfiguration, filePath);
if (_MetadataConfiguration.ForceMetadataLastWriteTimeToCreationTime && !fileInfo.Exists && File.Exists(Path.ChangeExtension(fileInfo.FullName, ".delete"))) if (_MetadataConfiguration.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);
@ -52,9 +80,9 @@ public class A_Metadata
fileInfo.Refresh(); fileInfo.Refresh();
} }
if (_MetadataConfiguration.PropertiesChangedForMetadata) if (_MetadataConfiguration.PropertiesChangedForMetadata)
results = null; result = null;
else if (!fileInfo.Exists) else if (!fileInfo.Exists)
results = null; result = null;
else if (!fileInfo.FullName.EndsWith(".json") && !fileInfo.FullName.EndsWith(".old")) else if (!fileInfo.FullName.EndsWith(".json") && !fileInfo.FullName.EndsWith(".old"))
throw new ArgumentException("must be a *.json file"); throw new ArgumentException("must be a *.json file");
else else
@ -62,59 +90,83 @@ public class A_Metadata
string json = File.ReadAllText(fileInfo.FullName); string json = File.ReadAllText(fileInfo.FullName);
try try
{ {
results = JsonSerializer.Deserialize(json, ExifDirectorySourceGenerationContext.Default.ExifDirectory); result = JsonSerializer.Deserialize(json, ExifDirectorySourceGenerationContext.Default.ExifDirectory);
if (results is null) if (result is null)
throw new Exception(); throw new Exception();
} }
catch (Exception) catch (Exception)
{ {
results = null; result = null;
} }
} }
if (results is null) if (result is null)
{ {
System.Drawing.Size? size; string json;
try result = Exif.GetExifDirectory(filePath, deterministicHashCode);
{ size = Dimensions.GetDimensions(filePath.FullName); } (int exifYear, string jsonFile) = GetJsonFile(metadataConfiguration.ResultConfiguration, filePath, result);
catch (Exception) { size = null; } if (exifYear == fileInfoMinimumYear)
IReadOnlyList<MetadataExtractor.Directory> directories = ImageMetadataReader.ReadMetadata(filePath.FullName); json = JsonSerializer.Serialize(result, ExifDirectorySourceGenerationContext.Default.ExifDirectory);
results = Exif.Covert(filePath, deterministicHashCode, size, directories); else
string json = JsonSerializer.Serialize(results, ExifDirectorySourceGenerationContext.Default.ExifDirectory); {
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) if (IPath.WriteAllText(fileInfo.FullName, json, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null) && _MetadataConfiguration.ForceMetadataLastWriteTimeToCreationTime)
{ {
File.SetLastWriteTime(fileInfo.FullName, fileInfo.CreationTime); File.SetLastWriteTime(fileInfo.FullName, fileInfo.CreationTime);
fileInfo.Refresh(); fileInfo.Refresh();
} }
} }
return (fileInfo, results); return (fileInfo, result);
} }
public static Action<string> SetExifDirectoryCollection(IRename rename, MetadataConfiguration metadataConfiguration, A_Metadata metadata, List<(string, FileInfo, ExifDirectory)> exifDirectories, Action tick) public static Action<string> SetExifDirectoryCollection(IRename rename, IRenameConfiguration renameConfiguration, A_Metadata metadata, List<(FilePath, FileInfo, ExifDirectory)> exifDirectories)
{ {
return file => return file =>
{ {
tick.Invoke(); rename.Tick();
FileInfo fileInfo; FileInfo fileInfo;
FilePath? ffmpegFilePath; FilePath? ffmpegFilePath;
ExifDirectory exifDirectory; ExifDirectory exifDirectory;
ReadOnlyCollection<string> ffmpegFiles; FileHolder fileHolder = new(file);
ReadOnlyCollection<string>? ffmpegFiles;
DeterministicHashCode deterministicHashCode; DeterministicHashCode deterministicHashCode;
FilePath filePath = IId.GetFilePath(metadataConfiguration, file); FilePath filePath = FilePath.Get(renameConfiguration.MetadataConfiguration, fileHolder, index: null);
if (filePath.ExtensionLowered is not ".paddedId" and not ".lsv") if (!renameConfiguration.SkipIdFiles || filePath.Id is null || !filePath.IsIntelligentIdFormat && filePath.SortOrder is not null)
{ {
if (filePath.Id is null || (!filePath.IsIdFormat && !filePath.IsPaddedIdFormat)) if (filePath.Id is not null)
{
ffmpegFiles = null;
deterministicHashCode = new(null, filePath.Id, null);
}
else
{
ffmpegFiles = rename.ConvertAndGetFfmpegFiles(renameConfiguration, filePath);
ffmpegFilePath = ffmpegFiles.Count == 0 ? null : FilePath.Get(renameConfiguration.MetadataConfiguration, new(ffmpegFiles[0]), index: null);
deterministicHashCode = ffmpegFilePath is null ? rename.GetDeterministicHashCode(filePath) : rename.GetDeterministicHashCode(ffmpegFilePath);
}
(fileInfo, exifDirectory) = metadata.GetMetadataCollection(renameConfiguration.MetadataConfiguration, filePath, deterministicHashCode);
lock (exifDirectories)
exifDirectories.Add(new(filePath, fileInfo, exifDirectory));
if (ffmpegFiles is not null)
{ {
(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) foreach (string ffmpegFile in ffmpegFiles)
File.Delete(ffmpegFile); File.Delete(ffmpegFile);
} }
} }
}; };
} }

View File

@ -9,6 +9,7 @@ public class MetadataConfiguration
public bool? ForceMetadataLastWriteTimeToCreationTime { get; set; } public bool? ForceMetadataLastWriteTimeToCreationTime { get; set; }
public string[]? IgnoreRulesKeyWords { get; set; } public string[]? IgnoreRulesKeyWords { get; set; }
public int? IntMinValueLength { get; set; }
public int? Offset { get; set; } public int? Offset { get; set; }
public bool? PropertiesChangedForMetadata { get; set; } public bool? PropertiesChangedForMetadata { get; set; }
@ -22,21 +23,22 @@ public class MetadataConfiguration
{ {
if (configuration?.ForceMetadataLastWriteTimeToCreationTime is null) if (configuration?.ForceMetadataLastWriteTimeToCreationTime is null)
{ {
List<string> paths = [];
foreach (IConfigurationProvider configurationProvider in configurationRoot.Providers) foreach (IConfigurationProvider configurationProvider in configurationRoot.Providers)
{ {
if (configurationProvider is not Microsoft.Extensions.Configuration.Json.JsonConfigurationProvider jsonConfigurationProvider) if (configurationProvider is not Microsoft.Extensions.Configuration.Json.JsonConfigurationProvider jsonConfigurationProvider)
continue; continue;
if (jsonConfigurationProvider.Source.FileProvider is not Microsoft.Extensions.FileProviders.PhysicalFileProvider physicalFileProvider) if (jsonConfigurationProvider.Source.FileProvider is not Microsoft.Extensions.FileProviders.PhysicalFileProvider physicalFileProvider)
continue; continue;
if (!physicalFileProvider.Root.Contains("UserSecrets")) paths.Add(physicalFileProvider.Root);
continue;
throw new NotSupportedException(physicalFileProvider.Root);
} }
throw new NotSupportedException($"Not found!{Environment.NewLine}{string.Join(Environment.NewLine, paths.Distinct())}");
} }
} }
private static void Verify(MetadataConfiguration configuration) private static void Verify(MetadataConfiguration configuration)
{ {
if (configuration.IntMinValueLength is null || configuration.IntMinValueLength != int.MinValue.ToString().Length) throw new NotSupportedException(nameof(configuration.IgnoreRulesKeyWords));
if (configuration.IgnoreRulesKeyWords is null || configuration.IgnoreRulesKeyWords.Length == 0) throw new NullReferenceException(nameof(configuration.IgnoreRulesKeyWords)); if (configuration.IgnoreRulesKeyWords is null || configuration.IgnoreRulesKeyWords.Length == 0) throw new NullReferenceException(nameof(configuration.IgnoreRulesKeyWords));
} }
@ -45,6 +47,7 @@ public class MetadataConfiguration
Shared.Models.MetadataConfiguration result; Shared.Models.MetadataConfiguration result;
if (configuration is null) throw new NullReferenceException(nameof(configuration)); if (configuration is null) throw new NullReferenceException(nameof(configuration));
if (configuration.ForceMetadataLastWriteTimeToCreationTime is null) throw new NullReferenceException(nameof(configuration.ForceMetadataLastWriteTimeToCreationTime)); if (configuration.ForceMetadataLastWriteTimeToCreationTime is null) throw new NullReferenceException(nameof(configuration.ForceMetadataLastWriteTimeToCreationTime));
if (configuration.IntMinValueLength is null) throw new NullReferenceException(nameof(configuration.IntMinValueLength));
if (configuration.IgnoreRulesKeyWords is null) throw new NullReferenceException(nameof(configuration.IgnoreRulesKeyWords)); if (configuration.IgnoreRulesKeyWords is null) throw new NullReferenceException(nameof(configuration.IgnoreRulesKeyWords));
if (configuration.Offset is null) throw new NullReferenceException(nameof(configuration.Offset)); if (configuration.Offset is null) throw new NullReferenceException(nameof(configuration.Offset));
if (configuration.PropertiesChangedForMetadata is null) throw new NullReferenceException(nameof(configuration.PropertiesChangedForMetadata)); if (configuration.PropertiesChangedForMetadata is null) throw new NullReferenceException(nameof(configuration.PropertiesChangedForMetadata));
@ -52,6 +55,7 @@ public class MetadataConfiguration
result = new(resultConfiguration, result = new(resultConfiguration,
configuration.ForceMetadataLastWriteTimeToCreationTime.Value, configuration.ForceMetadataLastWriteTimeToCreationTime.Value,
configuration.IgnoreRulesKeyWords, configuration.IgnoreRulesKeyWords,
configuration.IntMinValueLength.Value,
configuration.Offset.Value, configuration.Offset.Value,
configuration.PropertiesChangedForMetadata.Value); configuration.PropertiesChangedForMetadata.Value);
return result; return result;

View File

@ -29,16 +29,16 @@ public class ResultConfiguration
{ {
if (configuration?.DateGroup is null) if (configuration?.DateGroup is null)
{ {
List<string> paths = [];
foreach (IConfigurationProvider configurationProvider in configurationRoot.Providers) foreach (IConfigurationProvider configurationProvider in configurationRoot.Providers)
{ {
if (configurationProvider is not Microsoft.Extensions.Configuration.Json.JsonConfigurationProvider jsonConfigurationProvider) if (configurationProvider is not Microsoft.Extensions.Configuration.Json.JsonConfigurationProvider jsonConfigurationProvider)
continue; continue;
if (jsonConfigurationProvider.Source.FileProvider is not Microsoft.Extensions.FileProviders.PhysicalFileProvider physicalFileProvider) if (jsonConfigurationProvider.Source.FileProvider is not Microsoft.Extensions.FileProviders.PhysicalFileProvider physicalFileProvider)
continue; continue;
if (!physicalFileProvider.Root.Contains("UserSecrets")) paths.Add(physicalFileProvider.Root);
continue;
throw new NotSupportedException(physicalFileProvider.Root);
} }
throw new NotSupportedException($"Not found!{Environment.NewLine}{string.Join(Environment.NewLine, paths.Distinct())}");
} }
} }

View File

@ -0,0 +1,68 @@
using System.Collections.ObjectModel;
using View_by_Distance.Shared.Models;
namespace View_by_Distance.Metadata.Models.Stateless.Methods;
internal static class Base
{
internal static string? GetMaker(ExifDirectoryBase[]? exifBaseDirectories)
{
string? result = null;
if (exifBaseDirectories is not null)
{
string value;
foreach (ExifDirectoryBase exifDirectoryBase in exifBaseDirectories)
{
value = exifDirectoryBase?.Make is null ? string.Empty : exifDirectoryBase.Make.ToString().Trim();
if (string.IsNullOrEmpty(value))
result = null;
else
{
result = $"{value[0].ToString().ToUpper()}{value[1..].ToLower()}";
break;
}
}
}
return result;
}
internal static string? GetModel(ExifDirectoryBase[]? exifBaseDirectories)
{
string? result = null;
if (exifBaseDirectories is not null)
{
string value;
foreach (ExifDirectoryBase exifDirectoryBase in exifBaseDirectories)
{
value = exifDirectoryBase?.Model is null ? string.Empty : exifDirectoryBase.Model.ToString().Trim();
if (string.IsNullOrEmpty(value))
result = null;
else
{
result = value;
break;
}
}
}
return result;
}
internal static ReadOnlyCollection<string> GetKeywords(ExifDirectoryBase[]? exifBaseDirectories)
{
List<string> results = [];
if (exifBaseDirectories is not null)
{
string value;
foreach (ExifDirectoryBase exifDirectoryBase in exifBaseDirectories)
{
value = exifDirectoryBase?.WinKeywords is null ? string.Empty : exifDirectoryBase.WinKeywords.ToString().Trim();
if (string.IsNullOrEmpty(value))
continue;
results.Add(value);
}
}
return new(results);
}
}

View File

@ -5,10 +5,8 @@ namespace View_by_Distance.Metadata.Models.Stateless.Methods;
internal static class Dimensions internal static class Dimensions
{ {
const string _ErrorMessage = "Could not recognize image format.";
#pragma warning disable IDE0230 #pragma warning disable IDE0230
private static readonly Dictionary<byte[], Func<BinaryReader, Size>> _ImageFormatDecoders = new() private static readonly Dictionary<byte[], Func<BinaryReader, Size?>> _ImageFormatDecoders = new()
{ {
{ new byte[] { 0x42, 0x4D }, DecodeBitmap }, { new byte[] { 0x42, 0x4D }, DecodeBitmap },
{ new byte[] { 0x47, 0x49, 0x46, 0x38, 0x37, 0x61 }, DecodeGif }, { 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) for (int i = 0; i < thatBytes.Length; i += 1)
{ {
if (thisBytes[i] != thatBytes[i]) if (thisBytes[i] == thatBytes[i])
{ continue;
return false; return false;
}
} }
return true; return true;
} }
@ -35,9 +32,7 @@ internal static class Dimensions
{ {
byte[] bytes = new byte[sizeof(short)]; byte[] bytes = new byte[sizeof(short)];
for (int i = 0; i < sizeof(short); i += 1) for (int i = 0; i < sizeof(short); i += 1)
{
bytes[sizeof(short) - 1 - i] = binaryReader.ReadByte(); bytes[sizeof(short) - 1 - i] = binaryReader.ReadByte();
}
return BitConverter.ToInt16(bytes, 0); return BitConverter.ToInt16(bytes, 0);
} }
@ -45,13 +40,11 @@ internal static class Dimensions
{ {
byte[] bytes = new byte[sizeof(int)]; byte[] bytes = new byte[sizeof(int)];
for (int i = 0; i < sizeof(int); i += 1) for (int i = 0; i < sizeof(int); i += 1)
{
bytes[sizeof(int) - 1 - i] = binaryReader.ReadByte(); bytes[sizeof(int) - 1 - i] = binaryReader.ReadByte();
}
return BitConverter.ToInt32(bytes, 0); return BitConverter.ToInt32(bytes, 0);
} }
private static Size DecodeBitmap(BinaryReader binaryReader) private static Size? DecodeBitmap(BinaryReader binaryReader)
{ {
_ = binaryReader.ReadBytes(16); _ = binaryReader.ReadBytes(16);
int width = binaryReader.ReadInt32(); int width = binaryReader.ReadInt32();
@ -59,14 +52,14 @@ internal static class Dimensions
return new Size(width, height); return new Size(width, height);
} }
private static Size DecodeGif(BinaryReader binaryReader) private static Size? DecodeGif(BinaryReader binaryReader)
{ {
int width = binaryReader.ReadInt16(); int width = binaryReader.ReadInt16();
int height = binaryReader.ReadInt16(); int height = binaryReader.ReadInt16();
return new Size(width, height); return new Size(width, height);
} }
private static Size DecodePng(BinaryReader binaryReader) private static Size? DecodePng(BinaryReader binaryReader)
{ {
_ = binaryReader.ReadBytes(8); _ = binaryReader.ReadBytes(8);
int width = ReadLittleEndianInt32(binaryReader); int width = ReadLittleEndianInt32(binaryReader);
@ -74,100 +67,60 @@ internal static class Dimensions
return new Size(width, height); return new Size(width, height);
} }
private static Size DecodeJfif(BinaryReader binaryReader) private static Size? DecodeJfif(BinaryReader binaryReader)
{ {
while (binaryReader.ReadByte() == 0xff) while (binaryReader.ReadByte() == 0xff)
{ {
byte marker = binaryReader.ReadByte(); byte marker = binaryReader.ReadByte();
short chunkLength = ReadLittleEndianInt16(binaryReader); short chunkLength = ReadLittleEndianInt16(binaryReader);
if (marker == 0xc0) if (marker == 0xc0)
{ {
_ = binaryReader.ReadByte(); _ = binaryReader.ReadByte();
int height = ReadLittleEndianInt16(binaryReader); int height = ReadLittleEndianInt16(binaryReader);
int width = ReadLittleEndianInt16(binaryReader); int width = ReadLittleEndianInt16(binaryReader);
return new Size(width, height); return new Size(width, height);
} }
if (chunkLength >= 0)
if (chunkLength < 0) _ = binaryReader.ReadBytes(chunkLength - 2);
else
{ {
ushort uChunkLength = (ushort)chunkLength; ushort uChunkLength = (ushort)chunkLength;
_ = binaryReader.ReadBytes(uChunkLength - 2); _ = binaryReader.ReadBytes(uChunkLength - 2);
} }
else
{
_ = binaryReader.ReadBytes(chunkLength - 2);
}
} }
return null;
throw new ArgumentException(_ErrorMessage);
} }
private static Size DecodeWebP(BinaryReader binaryReader) private static Size? DecodeWebP(BinaryReader binaryReader)
{ {
_ = binaryReader.ReadUInt32(); // Size _ = binaryReader.ReadUInt32(); // Size
_ = binaryReader.ReadBytes(15); // WEBP, VP8 + more _ = binaryReader.ReadBytes(15); // WEBP, VP8 + more
_ = binaryReader.ReadBytes(3); // SYNC _ = binaryReader.ReadBytes(3); // SYNC
int width = binaryReader.ReadUInt16() & 0b00_11111111111111; // 14 bits width int width = binaryReader.ReadUInt16() & 0b00_11111111111111; // 14 bits width
int height = binaryReader.ReadUInt16() & 0b00_11111111111111; // 14 bits height int height = binaryReader.ReadUInt16() & 0b00_11111111111111; // 14 bits height
return new Size(width, height); return new Size(width, height);
} }
/// <summary> internal static Size? GetDimensions(BinaryReader binaryReader)
/// Gets the dimensions of an image.
/// </summary>
/// <param name="path">The path of the image to get the dimensions of.</param>
/// <returns>The dimensions of the specified image.</returns>
/// <exception cref="ArgumentException">The image was of an unrecognized format.</exception>
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;
byte[] magicBytes = new byte[maxMagicBytesLength]; byte[] magicBytes = new byte[maxMagicBytesLength];
for (int i = 0; i < maxMagicBytesLength; i += 1) for (int i = 0; i < maxMagicBytesLength; i += 1)
{ {
magicBytes[i] = binaryReader.ReadByte(); magicBytes[i] = binaryReader.ReadByte();
foreach (KeyValuePair<byte[], Func<BinaryReader, Size?>> kvPair in _ImageFormatDecoders)
foreach (KeyValuePair<byte[], Func<BinaryReader, Size>> kvPair in _ImageFormatDecoders)
{ {
if (StartsWith(magicBytes, kvPair.Key)) if (StartsWith(magicBytes, kvPair.Key))
{
return kvPair.Value(binaryReader); return kvPair.Value(binaryReader);
}
} }
} }
return null;
throw new ArgumentException(_ErrorMessage, nameof(binaryReader));
} }
/// <summary> internal static Size? GetDimensions(string path)
/// Gets the dimensions of an image.
///internal </summary>
/// <param name="path">The path of the image to get the dimensions of.</param>
/// <returns>The dimensions of the specified image.</returns>
/// <exception cref="ArgumentException">The image was of an unrecognized format.</exception>
internal static Size GetDimensions(string path)
{ {
using BinaryReader binaryReader = new(File.OpenRead(path)); using BinaryReader binaryReader = new(File.OpenRead(path));
try return GetDimensions(binaryReader);
{
return GetDimensions(binaryReader);
}
catch (ArgumentException e)
{
if (e.Message.StartsWith(_ErrorMessage))
{
throw new ArgumentException(_ErrorMessage, nameof(path), e);
}
else
{
throw;
}
}
} }
} }

View File

@ -1,6 +1,8 @@
using MetadataExtractor; using MetadataExtractor;
using MetadataExtractor.Formats.Exif; using MetadataExtractor.Formats.Exif;
using MetadataExtractor.Formats.Exif.Makernotes;
using System.Globalization; using System.Globalization;
using View_by_Distance.Metadata.Models.Stateless.Methods;
namespace View_by_Distance.Metadata.Models.Stateless; namespace View_by_Distance.Metadata.Models.Stateless;
@ -23,14 +25,14 @@ internal abstract class Exif
return result; return result;
} }
private static Shared.Models.AviDirectory GetAviDirectory(IReadOnlyList<MetadataExtractor.Directory> directories) private static Shared.Models.AviDirectory[] GetAviDirectories(IReadOnlyList<MetadataExtractor.Directory> directories)
{ {
Shared.Models.AviDirectory result; List<Shared.Models.AviDirectory> results = [];
MetadataExtractor.Formats.Avi.AviDirectory? aviDirectory = directories.OfType<MetadataExtractor.Formats.Avi.AviDirectory>().FirstOrDefault(); IEnumerable<MetadataExtractor.Formats.Avi.AviDirectory> aviDirectories = directories.OfType<MetadataExtractor.Formats.Avi.AviDirectory>();
if (aviDirectory is null) foreach (MetadataExtractor.Formats.Avi.AviDirectory aviDirectory in aviDirectories)
result = new(null, null, null, null);
else
{ {
if (aviDirectory.Tags.Count == 0)
continue;
DateTime? dateTimeOriginal; DateTime? dateTimeOriginal;
string? duration = aviDirectory.GetDescription(MetadataExtractor.Formats.Avi.AviDirectory.TagDuration); string? duration = aviDirectory.GetDescription(MetadataExtractor.Formats.Avi.AviDirectory.TagDuration);
string? height = aviDirectory.GetDescription(MetadataExtractor.Formats.Avi.AviDirectory.TagHeight); string? height = aviDirectory.GetDescription(MetadataExtractor.Formats.Avi.AviDirectory.TagHeight);
@ -39,19 +41,21 @@ internal abstract class Exif
dateTimeOriginal = checkDateTime; dateTimeOriginal = checkDateTime;
else else
dateTimeOriginal = GetDateTime(aviDirectory.GetString(MetadataExtractor.Formats.Avi.AviDirectory.TagDateTimeOriginal)); dateTimeOriginal = GetDateTime(aviDirectory.GetString(MetadataExtractor.Formats.Avi.AviDirectory.TagDateTimeOriginal));
result = new(dateTimeOriginal, duration, height, width); if (dateTimeOriginal is null && duration is null && height is null && width is null)
continue;
results.Add(new(dateTimeOriginal, duration, height, width));
} }
return result; return results.ToArray();
} }
private static Shared.Models.ExifDirectoryBase GetExifDirectoryBase(IReadOnlyList<MetadataExtractor.Directory> directories) private static Shared.Models.ExifDirectoryBase[] GetExifBaseDirectories(IReadOnlyList<MetadataExtractor.Directory> directories)
{ {
Shared.Models.ExifDirectoryBase result; List<Shared.Models.ExifDirectoryBase> results = [];
ExifDirectoryBase? exifDirectoryBase = directories.OfType<ExifDirectoryBase>().FirstOrDefault(); IEnumerable<ExifDirectoryBase> exifBaseDirectories = directories.OfType<ExifDirectoryBase>();
if (exifDirectoryBase is null) foreach (ExifDirectoryBase exifDirectoryBase in exifBaseDirectories)
result = new(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null);
else
{ {
if (exifDirectoryBase.Tags.Count == 0)
continue;
DateTime? dateTime; DateTime? dateTime;
DateTime checkDateTime; DateTime checkDateTime;
DateTime? dateTimeOriginal; DateTime? dateTimeOriginal;
@ -111,64 +115,113 @@ internal abstract class Exif
dateTimeDigitized = checkDateTime; dateTimeDigitized = checkDateTime;
else else
dateTimeDigitized = GetDateTime(exifDirectoryBase.GetString(ExifDirectoryBase.TagDateTimeDigitized)); dateTimeDigitized = GetDateTime(exifDirectoryBase.GetString(ExifDirectoryBase.TagDateTimeDigitized));
result = new(aperture, if (userComment is not null && userComment.Length > 255)
applicationNotes, userComment = "...";
artist, if (aperture is null
bitsPerSample, && applicationNotes is null
bodySerialNumber, && artist is null
cameraOwnerName, && bitsPerSample is null
compressedAverageBitsPerPixel, && bodySerialNumber is null
compression, && cameraOwnerName is null
copyright, && compressedAverageBitsPerPixel is null
dateTime, && compression is null
dateTimeDigitized, && copyright is null
dateTimeOriginal, && dateTime is null
documentName, && dateTimeDigitized is null
exifVersion, && dateTimeOriginal is null
exposureTime, && documentName is null
fileSource, && exifVersion is null
imageDescription, && exposureTime is null
imageHeight, && fileSource is null
imageNumber, && imageDescription is null
imageUniqueId, && imageHeight is null
imageWidth, && imageNumber is null
isoSpeed, && imageUniqueId is null
lensMake, && imageWidth is null
lensModel, && isoSpeed is null
lensSerialNumber, && lensMake is null
make, && lensModel is null
makerNote, && lensSerialNumber is null
model, && make is null
orientation, && makerNote is null
orientationValue, && model is null
rating, && orientation is null
ratingPercent, && orientationValue is null
securityClassification, && rating is null
shutterSpeed, && ratingPercent is null
software, && securityClassification is null
timeZone, && shutterSpeed is null
timeZoneDigitized, && software is null
timeZoneOriginal, && timeZone is null
userComment, && timeZoneDigitized is null
winAuthor, && timeZoneOriginal is null
winComment, && userComment is null
winKeywords, && winAuthor is null
winSubject, && winComment is null
winTitle, && winKeywords is null
xResolution, && winSubject is null
yResolution); && winTitle is null
&& xResolution is not null
&& yResolution is null)
continue;
results.Add(new(aperture,
applicationNotes,
artist,
bitsPerSample,
bodySerialNumber,
cameraOwnerName,
compressedAverageBitsPerPixel,
compression,
copyright,
dateTime,
dateTimeDigitized,
dateTimeOriginal,
documentName,
exifVersion,
exposureTime,
fileSource,
imageDescription,
imageHeight,
imageNumber,
imageUniqueId,
imageWidth,
isoSpeed,
lensMake,
lensModel,
lensSerialNumber,
make,
makerNote,
model,
orientation,
orientationValue,
rating,
ratingPercent,
securityClassification,
shutterSpeed,
software,
timeZone,
timeZoneDigitized,
timeZoneOriginal,
userComment,
winAuthor,
winComment,
winKeywords,
winSubject,
winTitle,
xResolution,
yResolution));
} }
return result; return results.ToArray();
} }
private static Shared.Models.FileMetadataDirectory GetFileMetadataDirectory(string file, IReadOnlyList<MetadataExtractor.Directory> directories) private static Shared.Models.FileMetadataDirectory[] GetFileMetadataDirectories(string file, IReadOnlyList<MetadataExtractor.Directory> directories)
{ {
Shared.Models.FileMetadataDirectory result; List<Shared.Models.FileMetadataDirectory> results = [];
MetadataExtractor.Formats.FileSystem.FileMetadataDirectory? fileMetadataDirectory = directories.OfType<MetadataExtractor.Formats.FileSystem.FileMetadataDirectory>().FirstOrDefault(); IEnumerable<MetadataExtractor.Formats.FileSystem.FileMetadataDirectory> fileMetadataDirectories = directories.OfType<MetadataExtractor.Formats.FileSystem.FileMetadataDirectory>();
if (fileMetadataDirectory is null) foreach (MetadataExtractor.Formats.FileSystem.FileMetadataDirectory fileMetadataDirectory in fileMetadataDirectories)
result = new(null, null, null);
else
{ {
if (fileMetadataDirectory.Tags.Count == 0)
continue;
DateTime? fileModifiedDate; DateTime? fileModifiedDate;
string? fileName = fileMetadataDirectory.GetDescription(MetadataExtractor.Formats.FileSystem.FileMetadataDirectory.TagFileName); string? fileName = fileMetadataDirectory.GetDescription(MetadataExtractor.Formats.FileSystem.FileMetadataDirectory.TagFileName);
string? fileSize = fileMetadataDirectory.GetDescription(MetadataExtractor.Formats.FileSystem.FileMetadataDirectory.TagFileSize); string? fileSize = fileMetadataDirectory.GetDescription(MetadataExtractor.Formats.FileSystem.FileMetadataDirectory.TagFileSize);
@ -178,49 +231,38 @@ internal abstract class Exif
fileModifiedDate = GetDateTime(fileMetadataDirectory.GetString(MetadataExtractor.Formats.FileSystem.FileMetadataDirectory.TagFileModifiedDate)); fileModifiedDate = GetDateTime(fileMetadataDirectory.GetString(MetadataExtractor.Formats.FileSystem.FileMetadataDirectory.TagFileModifiedDate));
if (fileName is null || !file.EndsWith(fileName)) if (fileName is null || !file.EndsWith(fileName))
throw new NotSupportedException($"!{file}.EndsWith({fileName})"); throw new NotSupportedException($"!{file}.EndsWith({fileName})");
result = new(fileModifiedDate, fileName, fileSize); if (fileModifiedDate is null && fileName is null && fileSize is null)
continue;
results.Add(new(fileModifiedDate, fileName, fileSize));
} }
return result; return results.ToArray();
} }
private static Shared.Models.GifHeaderDirectory GetGifHeaderDirectory(IReadOnlyList<MetadataExtractor.Directory> directories) private static Shared.Models.GifHeaderDirectory[] GetGifHeaderDirectories(IReadOnlyList<MetadataExtractor.Directory> directories)
{ {
Shared.Models.GifHeaderDirectory result; List<Shared.Models.GifHeaderDirectory> results = [];
MetadataExtractor.Formats.Gif.GifHeaderDirectory? gifHeaderDirectory = directories.OfType<MetadataExtractor.Formats.Gif.GifHeaderDirectory>().FirstOrDefault(); IEnumerable<MetadataExtractor.Formats.Gif.GifHeaderDirectory> gifHeaderDirectories = directories.OfType<MetadataExtractor.Formats.Gif.GifHeaderDirectory>();
if (gifHeaderDirectory is null) foreach (MetadataExtractor.Formats.Gif.GifHeaderDirectory gifHeaderDirectory in gifHeaderDirectories)
result = new(null, null);
else
{ {
if (gifHeaderDirectory.Tags.Count == 0)
continue;
string? imageHeight = gifHeaderDirectory.GetDescription(MetadataExtractor.Formats.Gif.GifHeaderDirectory.TagImageHeight); string? imageHeight = gifHeaderDirectory.GetDescription(MetadataExtractor.Formats.Gif.GifHeaderDirectory.TagImageHeight);
string? imageWidth = gifHeaderDirectory.GetDescription(MetadataExtractor.Formats.Gif.GifHeaderDirectory.TagImageWidth); string? imageWidth = gifHeaderDirectory.GetDescription(MetadataExtractor.Formats.Gif.GifHeaderDirectory.TagImageWidth);
result = new(imageHeight, imageWidth); if (imageHeight is null && imageWidth is null)
continue;
results.Add(new(imageHeight, imageWidth));
} }
return result; return results.ToArray();
} }
private static Shared.Models.PhotoshopDirectory GetPhotoshopDirectory(IReadOnlyList<MetadataExtractor.Directory> directories) private static Shared.Models.GpsDirectory[] GetGpsDirectories(IReadOnlyList<MetadataExtractor.Directory> directories)
{ {
Shared.Models.PhotoshopDirectory result; List<Shared.Models.GpsDirectory> results = [];
MetadataExtractor.Formats.Photoshop.PhotoshopDirectory? PhotoshopDirectory = directories.OfType<MetadataExtractor.Formats.Photoshop.PhotoshopDirectory>().FirstOrDefault(); IEnumerable<GpsDirectory> gpsDirectories = directories.OfType<GpsDirectory>();
if (PhotoshopDirectory is null) foreach (GpsDirectory gpsDirectory in gpsDirectories)
result = new(null, null);
else
{
string? jpegQuality = PhotoshopDirectory.GetDescription(MetadataExtractor.Formats.Photoshop.PhotoshopDirectory.TagJpegQuality);
string? url = PhotoshopDirectory.GetDescription(MetadataExtractor.Formats.Photoshop.PhotoshopDirectory.TagUrl);
result = new(jpegQuality, url);
}
return result;
}
private static Shared.Models.GpsDirectory GetGpsDirectory(IReadOnlyList<MetadataExtractor.Directory> directories)
{
Shared.Models.GpsDirectory result;
GpsDirectory? gpsDirectory = directories.OfType<GpsDirectory>().FirstOrDefault();
if (gpsDirectory is null)
result = new(null, null, null, null, null, null);
else
{ {
if (gpsDirectory.Tags.Count == 0)
continue;
DateTime? timeStamp; DateTime? timeStamp;
string? altitude = gpsDirectory.GetDescription(GpsDirectory.TagAltitude); string? altitude = gpsDirectory.GetDescription(GpsDirectory.TagAltitude);
string? latitude = gpsDirectory.GetDescription(GpsDirectory.TagLatitude); string? latitude = gpsDirectory.GetDescription(GpsDirectory.TagLatitude);
@ -231,136 +273,257 @@ internal abstract class Exif
timeStamp = checkDateTime; timeStamp = checkDateTime;
else else
timeStamp = GetDateTime(gpsDirectory.GetString(GpsDirectory.TagTimeStamp)); timeStamp = GetDateTime(gpsDirectory.GetString(GpsDirectory.TagTimeStamp));
result = new(altitude, if (altitude is null && latitude is null && latitudeRef is null && longitude is null && longitudeRef is null && timeStamp is null)
latitude, continue;
latitudeRef, results.Add(new(altitude,
longitude, latitude,
longitudeRef, latitudeRef,
timeStamp); longitude,
longitudeRef,
timeStamp));
} }
return result; return results.ToArray();
} }
private static Shared.Models.JpegDirectory GetJpegDirectory(IReadOnlyList<MetadataExtractor.Directory> directories) private static Shared.Models.JpegDirectory[] GetJpegDirectories(IReadOnlyList<MetadataExtractor.Directory> directories)
{ {
Shared.Models.JpegDirectory result; List<Shared.Models.JpegDirectory> results = [];
MetadataExtractor.Formats.Jpeg.JpegDirectory? jpegDirectory = directories.OfType<MetadataExtractor.Formats.Jpeg.JpegDirectory>().FirstOrDefault(); IEnumerable<MetadataExtractor.Formats.Jpeg.JpegDirectory> jpegDirectories = directories.OfType<MetadataExtractor.Formats.Jpeg.JpegDirectory>();
if (jpegDirectory is null) foreach (MetadataExtractor.Formats.Jpeg.JpegDirectory jpegDirectory in jpegDirectories)
result = new(null, null);
else
{ {
if (jpegDirectory.Tags.Count == 0)
continue;
string? imageHeight = jpegDirectory.GetDescription(MetadataExtractor.Formats.Jpeg.JpegDirectory.TagImageHeight); string? imageHeight = jpegDirectory.GetDescription(MetadataExtractor.Formats.Jpeg.JpegDirectory.TagImageHeight);
string? imageWidth = jpegDirectory.GetDescription(MetadataExtractor.Formats.Jpeg.JpegDirectory.TagImageWidth); string? imageWidth = jpegDirectory.GetDescription(MetadataExtractor.Formats.Jpeg.JpegDirectory.TagImageWidth);
result = new(imageHeight, imageWidth); if (imageHeight is null && imageWidth is null)
continue;
results.Add(new(imageHeight, imageWidth));
} }
return result; return results.ToArray();
} }
private static Shared.Models.PngDirectory GetPngDirectory(IReadOnlyList<MetadataExtractor.Directory> directories) private static Shared.Models.MakernoteDirectory[] GetMakernoteDirectories(IReadOnlyList<MetadataExtractor.Directory> directories)
{ {
Shared.Models.PngDirectory result; List<Shared.Models.MakernoteDirectory> results = [];
MetadataExtractor.Formats.Png.PngDirectory? pngDirectory = directories.OfType<MetadataExtractor.Formats.Png.PngDirectory>().FirstOrDefault(); IEnumerable<AppleMakernoteDirectory> appleMakernoteDirectories = directories.OfType<AppleMakernoteDirectory>();
if (pngDirectory is null) foreach (AppleMakernoteDirectory appleMakernoteDirectory in appleMakernoteDirectories)
result = new(null, null);
else
{ {
if (appleMakernoteDirectory.Tags.Count == 0)
continue;
string? cameraSerialNumber = null;
string? firmwareVersion = null;
string? qualityAndFileFormat = null;
if (cameraSerialNumber is null && firmwareVersion is null && qualityAndFileFormat is null)
continue;
results.Add(new(cameraSerialNumber, firmwareVersion, qualityAndFileFormat));
}
IEnumerable<CanonMakernoteDirectory> canonMakernoteDirectories = directories.OfType<CanonMakernoteDirectory>();
foreach (CanonMakernoteDirectory canonMakernoteDirectory in canonMakernoteDirectories)
{
if (canonMakernoteDirectory.Tags.Count == 0)
continue;
string? cameraSerialNumber = canonMakernoteDirectory.GetDescription(CanonMakernoteDirectory.TagModelId);
string? firmwareVersion = canonMakernoteDirectory.GetDescription(CanonMakernoteDirectory.TagCanonFirmwareVersion);
string? qualityAndFileFormat = canonMakernoteDirectory.GetDescription(CanonMakernoteDirectory.CameraSettings.TagQuality);
if (cameraSerialNumber is null && firmwareVersion is null && qualityAndFileFormat is null)
continue;
results.Add(new(cameraSerialNumber, firmwareVersion, qualityAndFileFormat));
}
IEnumerable<NikonType2MakernoteDirectory> nikonType2MakernoteDirectories = directories.OfType<NikonType2MakernoteDirectory>();
foreach (NikonType2MakernoteDirectory nikonType2MakernoteDirectory in nikonType2MakernoteDirectories)
{
if (nikonType2MakernoteDirectory.Tags.Count == 0)
continue;
string? cameraSerialNumber = nikonType2MakernoteDirectory.GetDescription(NikonType2MakernoteDirectory.TagCameraSerialNumber);
string? firmwareVersion = nikonType2MakernoteDirectory.GetDescription(NikonType2MakernoteDirectory.TagFirmwareVersion);
string? qualityAndFileFormat = nikonType2MakernoteDirectory.GetDescription(NikonType2MakernoteDirectory.TagQualityAndFileFormat);
if (cameraSerialNumber is null && firmwareVersion is null && qualityAndFileFormat is null)
continue;
results.Add(new(cameraSerialNumber, firmwareVersion, qualityAndFileFormat));
}
IEnumerable<OlympusMakernoteDirectory> olympusMakernoteDirectories = directories.OfType<OlympusMakernoteDirectory>();
foreach (OlympusMakernoteDirectory olympusMakernoteDirectory in olympusMakernoteDirectories)
{
if (olympusMakernoteDirectory.Tags.Count == 0)
continue;
string? cameraSerialNumber = olympusMakernoteDirectory.GetDescription(OlympusMakernoteDirectory.TagSerialNumber1);
string? firmwareVersion = olympusMakernoteDirectory.GetDescription(OlympusMakernoteDirectory.TagBodyFirmwareVersion);
string? qualityAndFileFormat = olympusMakernoteDirectory.GetDescription(OlympusMakernoteDirectory.TagJpegQuality);
if (cameraSerialNumber is null && firmwareVersion is null && qualityAndFileFormat is null)
continue;
results.Add(new(cameraSerialNumber, firmwareVersion, qualityAndFileFormat));
}
IEnumerable<PanasonicMakernoteDirectory> panasonicMakernoteDirectories = directories.OfType<PanasonicMakernoteDirectory>();
foreach (PanasonicMakernoteDirectory panasonicMakernoteDirectory in panasonicMakernoteDirectories)
{
if (panasonicMakernoteDirectory.Tags.Count == 0)
continue;
string? cameraSerialNumber = panasonicMakernoteDirectory.GetDescription(PanasonicMakernoteDirectory.TagInternalSerialNumber);
string? firmwareVersion = panasonicMakernoteDirectory.GetDescription(PanasonicMakernoteDirectory.TagFirmwareVersion);
string? qualityAndFileFormat = panasonicMakernoteDirectory.GetDescription(PanasonicMakernoteDirectory.TagQualityMode);
if (cameraSerialNumber is null && firmwareVersion is null && qualityAndFileFormat is null)
continue;
results.Add(new(cameraSerialNumber, firmwareVersion, qualityAndFileFormat));
}
IEnumerable<SamsungType2MakernoteDirectory> samsungType2MakernoteDirectories = directories.OfType<SamsungType2MakernoteDirectory>();
foreach (SamsungType2MakernoteDirectory samsungType2MakernoteDirectory in samsungType2MakernoteDirectories)
{
if (samsungType2MakernoteDirectory.Tags.Count == 0)
continue;
string? cameraSerialNumber = samsungType2MakernoteDirectory.GetDescription(SamsungType2MakernoteDirectory.TagSerialNumber);
string? firmwareVersion = samsungType2MakernoteDirectory.GetDescription(SamsungType2MakernoteDirectory.TagFirmwareName);
string? qualityAndFileFormat = null;
if (cameraSerialNumber is null && firmwareVersion is null && qualityAndFileFormat is null)
continue;
results.Add(new(cameraSerialNumber, firmwareVersion, qualityAndFileFormat));
}
IEnumerable<SonyType6MakernoteDirectory> sonyType6MakernoteDirectories = directories.OfType<SonyType6MakernoteDirectory>();
foreach (SonyType6MakernoteDirectory sonyType6MakernoteDirectory in sonyType6MakernoteDirectories)
{
if (sonyType6MakernoteDirectory.Tags.Count == 0)
continue;
string? cameraSerialNumber = null;
string? firmwareVersion = null;
string? qualityAndFileFormat = null;
if (cameraSerialNumber is null && firmwareVersion is null && qualityAndFileFormat is null)
continue;
results.Add(new(cameraSerialNumber, firmwareVersion, qualityAndFileFormat));
}
return results.ToArray();
}
private static Shared.Models.PhotoshopDirectory[] GetPhotoshopDirectories(IReadOnlyList<MetadataExtractor.Directory> directories)
{
List<Shared.Models.PhotoshopDirectory> results = [];
IEnumerable<MetadataExtractor.Formats.Photoshop.PhotoshopDirectory> photoshopDirectories = directories.OfType<MetadataExtractor.Formats.Photoshop.PhotoshopDirectory>();
foreach (MetadataExtractor.Formats.Photoshop.PhotoshopDirectory photoshopDirectory in photoshopDirectories)
{
if (photoshopDirectory.Tags.Count == 0)
continue;
string? jpegQuality = photoshopDirectory.GetDescription(MetadataExtractor.Formats.Photoshop.PhotoshopDirectory.TagJpegQuality);
string? url = photoshopDirectory.GetDescription(MetadataExtractor.Formats.Photoshop.PhotoshopDirectory.TagUrl);
if (jpegQuality is null && url is null)
continue;
results.Add(new(jpegQuality, url));
}
return results.ToArray();
}
private static Shared.Models.PngDirectory[] GetPngDirectories(IReadOnlyList<MetadataExtractor.Directory> directories)
{
List<Shared.Models.PngDirectory> results = [];
IEnumerable<MetadataExtractor.Formats.Png.PngDirectory> pngDirectories = directories.OfType<MetadataExtractor.Formats.Png.PngDirectory>();
foreach (MetadataExtractor.Formats.Png.PngDirectory pngDirectory in pngDirectories)
{
if (pngDirectory.Tags.Count == 0)
continue;
string? imageHeight = pngDirectory.GetDescription(MetadataExtractor.Formats.Png.PngDirectory.TagImageHeight); string? imageHeight = pngDirectory.GetDescription(MetadataExtractor.Formats.Png.PngDirectory.TagImageHeight);
string? imageWidth = pngDirectory.GetDescription(MetadataExtractor.Formats.Png.PngDirectory.TagImageWidth); string? imageWidth = pngDirectory.GetDescription(MetadataExtractor.Formats.Png.PngDirectory.TagImageWidth);
result = new(imageHeight, imageWidth); string? textualData = pngDirectory.GetDescription(MetadataExtractor.Formats.Png.PngDirectory.TagTextualData);
if (imageHeight is null && imageWidth is null && textualData is null)
continue;
results.Add(new(imageHeight, imageWidth, textualData));
} }
return result; return results.ToArray();
} }
private static Shared.Models.QuickTimeMovieHeaderDirectory GetQuickTimeMovieHeaderDirectoryDirectory(IReadOnlyList<MetadataExtractor.Directory> directories) private static Shared.Models.QuickTimeMovieHeaderDirectory[] GetQuickTimeMovieHeaderDirectoryDirectories(IReadOnlyList<MetadataExtractor.Directory> directories)
{ {
Shared.Models.QuickTimeMovieHeaderDirectory result; List<Shared.Models.QuickTimeMovieHeaderDirectory> results = [];
MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory? aviDirectory = directories.OfType<MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory>().FirstOrDefault(); IEnumerable<MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory> quickTimeMovieHeaderDirectories = directories.OfType<MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory>();
if (aviDirectory is null) foreach (MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory quickTimeMovieHeaderDirectory in quickTimeMovieHeaderDirectories)
result = new(null);
else
{ {
if (quickTimeMovieHeaderDirectory.Tags.Count == 0)
continue;
DateTime? created; DateTime? created;
if (aviDirectory.TryGetDateTime(MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory.TagCreated, out DateTime checkDateTime)) if (quickTimeMovieHeaderDirectory.TryGetDateTime(MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory.TagCreated, out DateTime checkDateTime))
created = checkDateTime; created = checkDateTime;
else else
created = GetDateTime(aviDirectory.GetString(MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory.TagCreated)); created = GetDateTime(quickTimeMovieHeaderDirectory.GetString(MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory.TagCreated));
result = new(created); if (created is null)
continue;
results.Add(new(created));
} }
return result; return results.ToArray();
} }
private static Shared.Models.QuickTimeTrackHeaderDirectory GetQuickTimeTrackHeaderDirectoryDirectory(IReadOnlyList<MetadataExtractor.Directory> directories) private static Shared.Models.QuickTimeTrackHeaderDirectory[] GetQuickTimeTrackHeaderDirectoryDirectories(IReadOnlyList<MetadataExtractor.Directory> directories)
{ {
Shared.Models.QuickTimeTrackHeaderDirectory result; List<Shared.Models.QuickTimeTrackHeaderDirectory> results = [];
MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory? aviDirectory = directories.OfType<MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory>().FirstOrDefault(); IEnumerable<MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory> quickTimeTrackHeaderDirectories = directories.OfType<MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory>();
if (aviDirectory is null) foreach (MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory quickTimeTrackHeaderDirectory in quickTimeTrackHeaderDirectories)
result = new(null);
else
{ {
if (quickTimeTrackHeaderDirectory.Tags.Count == 0)
continue;
DateTime? created; DateTime? created;
if (aviDirectory.TryGetDateTime(MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory.TagCreated, out DateTime checkDateTime)) if (quickTimeTrackHeaderDirectory.TryGetDateTime(MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory.TagCreated, out DateTime checkDateTime))
created = checkDateTime; created = checkDateTime;
else else
created = GetDateTime(aviDirectory.GetString(MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory.TagCreated)); created = GetDateTime(quickTimeTrackHeaderDirectory.GetString(MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory.TagCreated));
result = new(created); if (created is null)
continue;
results.Add(new(created));
} }
return result; return results.ToArray();
} }
private static Shared.Models.WebPDirectory GetWebPDirectory(IReadOnlyList<MetadataExtractor.Directory> directories) private static Shared.Models.WebPDirectory[] GetWebPDirectories(IReadOnlyList<MetadataExtractor.Directory> directories)
{ {
Shared.Models.WebPDirectory result; List<Shared.Models.WebPDirectory> results = [];
MetadataExtractor.Formats.WebP.WebPDirectory? WebPDirectory = directories.OfType<MetadataExtractor.Formats.WebP.WebPDirectory>().FirstOrDefault(); IEnumerable<MetadataExtractor.Formats.WebP.WebPDirectory> webPDirectories = directories.OfType<MetadataExtractor.Formats.WebP.WebPDirectory>();
if (WebPDirectory is null) foreach (MetadataExtractor.Formats.WebP.WebPDirectory webPDirectory in webPDirectories)
result = new(null, null);
else
{ {
string? imageHeight = WebPDirectory.GetDescription(MetadataExtractor.Formats.WebP.WebPDirectory.TagImageHeight); if (webPDirectory.Tags.Count == 0)
string? imageWidth = WebPDirectory.GetDescription(MetadataExtractor.Formats.WebP.WebPDirectory.TagImageWidth); continue;
result = new(imageHeight, imageWidth); string? imageHeight = webPDirectory.GetDescription(MetadataExtractor.Formats.WebP.WebPDirectory.TagImageHeight);
string? imageWidth = webPDirectory.GetDescription(MetadataExtractor.Formats.WebP.WebPDirectory.TagImageWidth);
if (imageHeight is null && imageWidth is null)
continue;
results.Add(new(imageHeight, imageWidth));
} }
return results.ToArray();
}
private static Shared.Models.ExifDirectory Covert(Shared.Models.FilePath filePath, Shared.Models.DeterministicHashCode deterministicHashCode, System.Drawing.Size? size, IReadOnlyList<MetadataExtractor.Directory> directories)
{
Shared.Models.ExifDirectory result;
Shared.Models.AviDirectory[] aviDirectories = GetAviDirectories(directories);
Shared.Models.GpsDirectory[] gpsDirectories = GetGpsDirectories(directories);
Shared.Models.PngDirectory[] pngDirectories = GetPngDirectories(directories);
Shared.Models.JpegDirectory[] jpegDirectories = GetJpegDirectories(directories);
Shared.Models.WebPDirectory[] webPDirectories = GetWebPDirectories(directories);
Shared.Models.ExifDirectoryBase[] exifBaseDirectories = GetExifBaseDirectories(directories);
Shared.Models.GifHeaderDirectory[] gifHeaderDirectories = GetGifHeaderDirectories(directories);
Shared.Models.MakernoteDirectory[] MakernoteDirectories = GetMakernoteDirectories(directories);
Shared.Models.PhotoshopDirectory[] photoshopDirectories = GetPhotoshopDirectories(directories);
Shared.Models.FileMetadataDirectory[] fileMetadataDirectories = GetFileMetadataDirectories(filePath.FullName, directories);
Shared.Models.QuickTimeMovieHeaderDirectory[] quickTimeMovieHeaderDirectories = GetQuickTimeMovieHeaderDirectoryDirectories(directories);
Shared.Models.QuickTimeTrackHeaderDirectory[] quickTimeTrackHeaderDirectories = GetQuickTimeTrackHeaderDirectoryDirectories(directories);
result = new(aviDirectories,
exifBaseDirectories,
fileMetadataDirectories,
gifHeaderDirectories,
gpsDirectories,
size?.Height,
deterministicHashCode.Id ?? filePath.Id,
jpegDirectories,
MakernoteDirectories,
filePath.Name,
photoshopDirectories,
pngDirectories,
quickTimeMovieHeaderDirectories,
quickTimeTrackHeaderDirectories,
webPDirectories,
size?.Width);
return result; return result;
} }
internal static Shared.Models.ExifDirectory Covert(Shared.Models.FilePath filePath, Shared.Models.DeterministicHashCode deterministicHashCode, System.Drawing.Size? size, IReadOnlyList<MetadataExtractor.Directory> directories) internal static Shared.Models.ExifDirectory GetExifDirectory(Shared.Models.FilePath filePath, Shared.Models.DeterministicHashCode deterministicHashCode)
{ {
Shared.Models.ExifDirectory results; Shared.Models.ExifDirectory? result;
Shared.Models.AviDirectory aviDirectory = GetAviDirectory(directories); System.Drawing.Size? size = Dimensions.GetDimensions(filePath.FullName);
Shared.Models.GpsDirectory gpsDirectory = GetGpsDirectory(directories); IReadOnlyList<MetadataExtractor.Directory> directories = ImageMetadataReader.ReadMetadata(filePath.FullName);
Shared.Models.PngDirectory pngDirectory = GetPngDirectory(directories); result = Covert(filePath, deterministicHashCode, size, directories);
Shared.Models.JpegDirectory jpegDirectory = GetJpegDirectory(directories);
Shared.Models.WebPDirectory webPDirectory = GetWebPDirectory(directories);
Shared.Models.ExifDirectoryBase exifDirectoryBase = GetExifDirectoryBase(directories);
Shared.Models.GifHeaderDirectory gifHeaderDirectory = GetGifHeaderDirectory(directories);
Shared.Models.PhotoshopDirectory photoshopDirectory = GetPhotoshopDirectory(directories);
Shared.Models.FileMetadataDirectory fileMetadataDirectory = GetFileMetadataDirectory(filePath.FullName, directories);
Shared.Models.QuickTimeMovieHeaderDirectory quickTimeMovieHeaderDirectory = GetQuickTimeMovieHeaderDirectoryDirectory(directories);
Shared.Models.QuickTimeTrackHeaderDirectory quickTimeTrackHeaderDirectory = GetQuickTimeTrackHeaderDirectoryDirectory(directories);
results = new(aviDirectory,
exifDirectoryBase,
fileMetadataDirectory,
gifHeaderDirectory,
gpsDirectory,
size?.Height,
deterministicHashCode.Id ?? filePath.Id,
jpegDirectory,
filePath.Name,
photoshopDirectory,
pngDirectory,
quickTimeMovieHeaderDirectory,
quickTimeTrackHeaderDirectory,
webPDirectory,
size?.Width);
return results;
}
internal static string GetMaker(Shared.Models.ExifDirectoryBase exifDirectoryBase)
{
string result;
if (string.IsNullOrEmpty(exifDirectoryBase.Make))
result = "Unknown";
else
result = $"{exifDirectoryBase.Make[0].ToString().ToUpper()}{exifDirectoryBase.Make[1..]}".Trim();
return result; return result;
} }

View File

@ -0,0 +1,42 @@
using View_by_Distance.Shared.Models;
namespace View_by_Distance.Metadata.Models.Stateless.Methods;
internal static class Face
{
internal static string? GetFaceEncoding(PngDirectory[]? pngDirectories)
{
string? result = null;
if (pngDirectories is not null)
{
const string comment = "Comment:";
foreach (PngDirectory pngDirectory in pngDirectories)
{
if (pngDirectory.TextualData is null || !pngDirectory.TextualData.StartsWith(comment))
continue;
result = pngDirectory.TextualData[comment.Length..];
break;
}
}
return result;
}
internal static string? GetOutputResolution(PngDirectory[]? pngDirectories)
{
string? result = null;
if (pngDirectories is not null)
{
const string artist = "Artist:";
foreach (PngDirectory pngDirectory in pngDirectories)
{
if (pngDirectory.TextualData is null || !pngDirectory.TextualData.StartsWith(artist))
continue;
result = pngDirectory.TextualData[artist.Length..];
break;
}
}
return result;
}
}

View File

@ -1,4 +1,6 @@
using MetadataExtractor;
using View_by_Distance.Metadata.Models.Stateless.Methods; using View_by_Distance.Metadata.Models.Stateless.Methods;
using View_by_Distance.Shared.Models;
namespace View_by_Distance.Metadata.Models.Stateless; namespace View_by_Distance.Metadata.Models.Stateless;
@ -85,34 +87,36 @@ internal abstract class GPS
return result; return result;
} }
// internal static GeoLocation? GeoLocation(ReadOnlyDictionary<string, MetadataExtractorDirectory> metadataExtractorDirectories) internal static GeoLocation? GeoLocation(GpsDirectory[]? gpsDirectories)
// { {
// GeoLocation? result; GeoLocation? result = null;
// if (!metadataExtractorDirectories.TryGetValue("GPS", out MetadataExtractorDirectory? metadataExtractorDirectory)) if (gpsDirectories is not null)
// result = null; {
// else foreach (GpsDirectory gpsDirectory in gpsDirectories)
// { {
// MetadataExtractorTag? metadataExtractorTag; if (string.IsNullOrEmpty(gpsDirectory?.Latitude))
// if (!metadataExtractorDirectory.Tags.TryGetValue((int)Shared.Models.Stateless.IExif.Tags.GPSLatitude, out metadataExtractorTag) || string.IsNullOrEmpty(metadataExtractorTag.Description)) result = null;
// result = null; else
// else {
// { string latitudeDMS = gpsDirectory.Latitude;
// string latitudeDMS = metadataExtractorTag.Description; double latitude = ParseValueFromDmsString(latitudeDMS);
// double latitude = ParseValueFromDmsString(latitudeDMS); if (string.IsNullOrEmpty(gpsDirectory.Longitude))
// if (!metadataExtractorDirectory.Tags.TryGetValue((int)Shared.Models.Stateless.IExif.Tags.GPSLongitude, out metadataExtractorTag) || string.IsNullOrEmpty(metadataExtractorTag.Description)) result = null;
// result = null; else
// else {
// { string longitudeDMS = gpsDirectory.Longitude;
// string longitudeDMS = metadataExtractorTag.Description; double longitude = ParseValueFromDmsString(longitudeDMS);
// double longitude = ParseValueFromDmsString(longitudeDMS); result = new(latitude, longitude);
// result = new(latitude, longitude); string dms = result.ToDmsString();
// string dms = result.ToDmsString(); if ($"{latitudeDMS}, {longitudeDMS}" != dms)
// if ($"{latitudeDMS}, {longitudeDMS}" != dms) result = null;
// result = null; }
// } }
// } if (result is not null)
// } break;
// return result; }
// } }
return result;
}
} }

View File

@ -1,3 +1,7 @@
using MetadataExtractor;
using System.Collections.ObjectModel;
using View_by_Distance.Shared.Models;
namespace View_by_Distance.Metadata.Models.Stateless.Methods; namespace View_by_Distance.Metadata.Models.Stateless.Methods;
public interface IMetadata public interface IMetadata
@ -11,10 +15,40 @@ public interface IMetadata
Meters Meters
} }
string TestStatic_GetMaker(Shared.Models.ExifDirectoryBase exifDirectoryBase) => ExifDirectory TestStatic_GetExifDirectory(FilePath filePath, DeterministicHashCode deterministicHashCode) =>
GetMaker(exifDirectoryBase); GetExifDirectory(filePath, deterministicHashCode);
static string GetMaker(Shared.Models.ExifDirectoryBase exifDirectoryBase) => static ExifDirectory GetExifDirectory(FilePath filePath, DeterministicHashCode deterministicHashCode) =>
Exif.GetMaker(exifDirectoryBase); Exif.GetExifDirectory(filePath, deterministicHashCode);
string? TestStatic_GetMaker(ExifDirectory? exifDirectory) =>
GetMaker(exifDirectory);
static string? GetMaker(ExifDirectory? exifDirectory) =>
Base.GetMaker(exifDirectory?.ExifBaseDirectories);
string? TestStatic_GetModel(ExifDirectory? exifDirectory) =>
GetModel(exifDirectory);
static string? GetModel(ExifDirectory? exifDirectory) =>
Base.GetModel(exifDirectory?.ExifBaseDirectories);
ReadOnlyCollection<string> TestStatic_GetKeywords(ExifDirectory? exifDirectory) =>
GetKeywords(exifDirectory);
static ReadOnlyCollection<string> GetKeywords(ExifDirectory? exifDirectory) =>
Base.GetKeywords(exifDirectory?.ExifBaseDirectories);
string? TestStatic_GetOutputResolution(ExifDirectory? exifDirectory) =>
GetOutputResolution(exifDirectory);
static string? GetOutputResolution(ExifDirectory? exifDirectory) =>
Face.GetOutputResolution(exifDirectory?.PngDirectories);
string? TestStatic_GetFaceEncoding(ExifDirectory? exifDirectory) =>
GetFaceEncoding(exifDirectory);
static string? GetFaceEncoding(ExifDirectory? exifDirectory) =>
Face.GetFaceEncoding(exifDirectory?.PngDirectories);
GeoLocation? TestStatic_GeoLocation(ExifDirectory? exifDirectory) =>
GeoLocation(exifDirectory);
static GeoLocation? GeoLocation(ExifDirectory? exifDirectory) =>
GPS.GeoLocation(exifDirectory?.GpsDirectories);
double? TestStatic_GetDistance(double originLatitude, double originLongitude, double destinationLatitude, double destinationLongitude, int decimalPlaces = 1, DistanceUnit distanceUnit = DistanceUnit.Miles) => double? TestStatic_GetDistance(double originLatitude, double originLongitude, double destinationLatitude, double destinationLongitude, int decimalPlaces = 1, DistanceUnit distanceUnit = DistanceUnit.Miles) =>
GetDistance(originLatitude, originLongitude, destinationLatitude, destinationLongitude, decimalPlaces, distanceUnit); GetDistance(originLatitude, originLongitude, destinationLatitude, destinationLongitude, decimalPlaces, distanceUnit);

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
@ -34,11 +34,11 @@
<SupportedPlatform Include="browser" /> <SupportedPlatform Include="browser" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="CliWrap" Version="3.6.4" /> <PackageReference Include="CliWrap" Version="3.6.6" />
<PackageReference Include="runtime.win-x64.Microsoft.DotNet.ILCompiler" Version="8.0.0-rc.2.23479.6" /> <PackageReference Include="runtime.win-x64.Microsoft.DotNet.ILCompiler" Version="8.0.4" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="ShellProgressBar" Version="5.2.0" /> <PackageReference Include="ShellProgressBar" Version="5.2.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -21,16 +21,16 @@ public class AppSettings
{ {
if (appSettings?.Company is null) if (appSettings?.Company is null)
{ {
List<string> paths = [];
foreach (IConfigurationProvider configurationProvider in configurationRoot.Providers) foreach (IConfigurationProvider configurationProvider in configurationRoot.Providers)
{ {
if (configurationProvider is not Microsoft.Extensions.Configuration.Json.JsonConfigurationProvider jsonConfigurationProvider) if (configurationProvider is not Microsoft.Extensions.Configuration.Json.JsonConfigurationProvider jsonConfigurationProvider)
continue; continue;
if (jsonConfigurationProvider.Source.FileProvider is not Microsoft.Extensions.FileProviders.PhysicalFileProvider physicalFileProvider) if (jsonConfigurationProvider.Source.FileProvider is not Microsoft.Extensions.FileProviders.PhysicalFileProvider physicalFileProvider)
continue; continue;
if (!physicalFileProvider.Root.Contains("UserSecrets")) paths.Add(physicalFileProvider.Root);
continue;
throw new NotSupportedException(physicalFileProvider.Root);
} }
throw new NotSupportedException($"Not found!{Environment.NewLine}{string.Join(Environment.NewLine, paths.Distinct())}");
} }
} }

View File

@ -7,8 +7,13 @@ namespace View_by_Distance.Rename.Models.Binder;
public class RenameConfiguration public class RenameConfiguration
{ {
public string? DefaultMaker { get; set; }
public bool? ForceNewId { get; set; }
public string[]? IgnoreExtensions { get; set; } public string[]? IgnoreExtensions { get; set; }
public string? RelativePropertyCollectionFile { get; set; }
public bool? SkipIdFiles { get; set; }
public string[]? ValidImageFormatExtensions { get; set; } public string[]? ValidImageFormatExtensions { get; set; }
public string[]? ValidVideoFormatExtensions { get; set; }
public override string ToString() public override string ToString()
{ {
@ -18,18 +23,18 @@ public class RenameConfiguration
private static void PreVerify(IConfigurationRoot configurationRoot, RenameConfiguration? configuration) private static void PreVerify(IConfigurationRoot configurationRoot, RenameConfiguration? configuration)
{ {
if (configuration?.IgnoreExtensions is null) if (configuration?.DefaultMaker is null)
{ {
List<string> paths = [];
foreach (IConfigurationProvider configurationProvider in configurationRoot.Providers) foreach (IConfigurationProvider configurationProvider in configurationRoot.Providers)
{ {
if (configurationProvider is not Microsoft.Extensions.Configuration.Json.JsonConfigurationProvider jsonConfigurationProvider) if (configurationProvider is not Microsoft.Extensions.Configuration.Json.JsonConfigurationProvider jsonConfigurationProvider)
continue; continue;
if (jsonConfigurationProvider.Source.FileProvider is not Microsoft.Extensions.FileProviders.PhysicalFileProvider physicalFileProvider) if (jsonConfigurationProvider.Source.FileProvider is not Microsoft.Extensions.FileProviders.PhysicalFileProvider physicalFileProvider)
continue; continue;
if (!physicalFileProvider.Root.Contains("UserSecrets")) paths.Add(physicalFileProvider.Root);
continue;
throw new NotSupportedException(physicalFileProvider.Root);
} }
throw new NotSupportedException($"Not found!{Environment.NewLine}{string.Join(Environment.NewLine, paths.Distinct())}");
} }
} }
@ -37,18 +42,29 @@ public class RenameConfiguration
{ {
if (configuration.IgnoreExtensions is null || configuration.IgnoreExtensions.Length == 0) throw new NullReferenceException(nameof(configuration.IgnoreExtensions)); if (configuration.IgnoreExtensions is null || configuration.IgnoreExtensions.Length == 0) throw new NullReferenceException(nameof(configuration.IgnoreExtensions));
if (configuration.ValidImageFormatExtensions is null || configuration.ValidImageFormatExtensions.Length == 0) throw new NullReferenceException(nameof(configuration.ValidImageFormatExtensions)); if (configuration.ValidImageFormatExtensions is null || configuration.ValidImageFormatExtensions.Length == 0) throw new NullReferenceException(nameof(configuration.ValidImageFormatExtensions));
if (configuration.ValidVideoFormatExtensions is null || configuration.ValidVideoFormatExtensions.Length == 0) throw new NullReferenceException(nameof(configuration.ValidVideoFormatExtensions));
} }
private static Models.RenameConfiguration Get(RenameConfiguration? configuration, Shared.Models.MetadataConfiguration metadataConfiguration) private static Models.RenameConfiguration Get(RenameConfiguration? configuration, Shared.Models.MetadataConfiguration metadataConfiguration)
{ {
Models.RenameConfiguration result; Models.RenameConfiguration result;
if (configuration is null) throw new NullReferenceException(nameof(configuration)); if (configuration is null) throw new NullReferenceException(nameof(configuration));
if (configuration.DefaultMaker is null) throw new NullReferenceException(nameof(configuration.DefaultMaker));
if (configuration.ForceNewId is null) throw new NullReferenceException(nameof(configuration.ForceNewId));
if (configuration.IgnoreExtensions is null) throw new NullReferenceException(nameof(configuration.IgnoreExtensions)); if (configuration.IgnoreExtensions is null) throw new NullReferenceException(nameof(configuration.IgnoreExtensions));
if (configuration.RelativePropertyCollectionFile is null) throw new NullReferenceException(nameof(configuration.RelativePropertyCollectionFile));
if (configuration.SkipIdFiles is null) throw new NullReferenceException(nameof(configuration.SkipIdFiles));
if (configuration.ValidImageFormatExtensions is null) throw new NullReferenceException(nameof(configuration.ValidImageFormatExtensions)); if (configuration.ValidImageFormatExtensions is null) throw new NullReferenceException(nameof(configuration.ValidImageFormatExtensions));
if (configuration.ValidVideoFormatExtensions is null) throw new NullReferenceException(nameof(configuration.ValidVideoFormatExtensions));
Verify(configuration); Verify(configuration);
result = new(metadataConfiguration, result = new(metadataConfiguration,
configuration.DefaultMaker,
configuration.ForceNewId.Value,
configuration.IgnoreExtensions, configuration.IgnoreExtensions,
configuration.ValidImageFormatExtensions); configuration.RelativePropertyCollectionFile,
configuration.SkipIdFiles.Value,
configuration.ValidImageFormatExtensions,
configuration.ValidVideoFormatExtensions);
return result; return result;
} }

View File

@ -0,0 +1,27 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace View_by_Distance.Rename.Models;
internal record Identifier(int Id, string PaddedId)
{
public override string ToString()
{
string result = JsonSerializer.Serialize(this, IdentifierSourceGenerationContext.Default.Identifier);
return result;
}
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Identifier))]
internal partial class IdentifierSourceGenerationContext : JsonSerializerContext
{
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(Identifier[]))]
internal partial class IdentifierCollectionSourceGenerationContext : JsonSerializerContext
{
}

View File

@ -5,8 +5,13 @@ using System.Text.Json.Serialization;
namespace View_by_Distance.Rename.Models; namespace View_by_Distance.Rename.Models;
public record RenameConfiguration(Shared.Models.MetadataConfiguration MetadataConfiguration, public record RenameConfiguration(Shared.Models.MetadataConfiguration MetadataConfiguration,
string DefaultMaker,
bool ForceNewId,
string[] IgnoreExtensions, string[] IgnoreExtensions,
string[] ValidImageFormatExtensions) string RelativePropertyCollectionFile,
bool SkipIdFiles,
string[] ValidImageFormatExtensions,
string[] ValidVideoFormatExtensions) : Shared.Models.Properties.IRenameConfiguration
{ {
public override string ToString() public override string ToString()

View File

@ -3,25 +3,27 @@ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using ShellProgressBar; using ShellProgressBar;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Data;
using System.Drawing; using System.Drawing;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text.Json;
using View_by_Distance.Metadata.Models; using View_by_Distance.Metadata.Models;
using View_by_Distance.Metadata.Models.Stateless.Methods; using View_by_Distance.Metadata.Models.Stateless.Methods;
using View_by_Distance.Rename.Models; using View_by_Distance.Rename.Models;
using View_by_Distance.Shared.Models; using View_by_Distance.Shared.Models;
using View_by_Distance.Shared.Models.Properties;
using View_by_Distance.Shared.Models.Stateless.Methods; using View_by_Distance.Shared.Models.Stateless.Methods;
namespace View_by_Distance.Rename; namespace View_by_Distance.Rename;
public class Rename : IRename public partial class Rename : IRename
{ {
private record ToDo(string? Directory, FileHolder FileHolder, string File, bool JsonFile); private record ToDo(string? Directory, FilePath FilePath, string File, bool JsonFile);
private record Record(DateTime DateTime, ExifDirectory ExifDirectory, string File, string JsonFile); private record Record(DateTime DateTime, ExifDirectory ExifDirectory, FilePath FilePath, bool HasDateTimeOriginal, bool HasIgnoreKeyword, string JsonFile);
private readonly AppSettings _AppSettings; private ProgressBar? _ProgressBar;
private readonly RenameConfiguration _RenameConfiguration;
public Rename(List<string> args, ILogger<Program>? logger, IConfigurationRoot configurationRoot, AppSettings appSettings, bool isSilent, IConsole console) public Rename(List<string> args, ILogger<Program>? logger, IConfigurationRoot configurationRoot, AppSettings appSettings, bool isSilent, IConsole console)
{ {
@ -31,55 +33,48 @@ public class Rename : IRename
throw new NullReferenceException(nameof(args)); throw new NullReferenceException(nameof(args));
if (console is null) if (console is null)
throw new NullReferenceException(nameof(console)); throw new NullReferenceException(nameof(console));
_AppSettings = appSettings; IRename rename = this;
long ticks = DateTime.Now.Ticks; long ticks = DateTime.Now.Ticks;
ResultConfiguration resultConfiguration = Metadata.Models.Binder.ResultConfiguration.Get(configurationRoot, appSettings.RequireRootDirectoryExists); ResultConfiguration resultConfiguration = Metadata.Models.Binder.ResultConfiguration.Get(configurationRoot, appSettings.RequireRootDirectoryExists);
MetadataConfiguration metadataConfiguration = Metadata.Models.Binder.MetadataConfiguration.Get(configurationRoot, resultConfiguration); MetadataConfiguration metadataConfiguration = Metadata.Models.Binder.MetadataConfiguration.Get(configurationRoot, resultConfiguration);
RenameConfiguration renameConfiguration = Models.Binder.RenameConfiguration.Get(configurationRoot, metadataConfiguration); RenameConfiguration renameConfiguration = Models.Binder.RenameConfiguration.Get(configurationRoot, metadataConfiguration);
_RenameConfiguration = renameConfiguration; RenameWork(logger, appSettings, rename, ticks, renameConfiguration);
DirectoryInfo directoryInfo = new(Path.GetFullPath(resultConfiguration.RootDirectory));
logger?.LogInformation("{RootDirectory}", directoryInfo.FullName);
ReadOnlyCollection<Record> exifDirectories = GetExifDirectoryCollection(directoryInfo);
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", lines);
_ = IPath.DeleteEmptyDirectories(directoryInfo.FullName);
}
} }
(ReadOnlyCollection<string>, FilePath?) IRename.ConvertAndGetFfmpegFiles(FilePath filePath) void IRename.Tick() =>
_ProgressBar?.Tick();
ReadOnlyCollection<string> IRename.ConvertAndGetFfmpegFiles(IRenameConfiguration renameConfiguration, FilePath filePath)
{ {
List<string> results = []; List<string> results = [];
FilePath? result; bool isValidVideoFormatExtensions = renameConfiguration.ValidVideoFormatExtensions.Contains(filePath.ExtensionLowered);
bool isIgnoreExtension; if (isValidVideoFormatExtensions)
bool isValidImageFormatExtension = _RenameConfiguration.ValidImageFormatExtensions.Contains(filePath.ExtensionLowered);
isIgnoreExtension = isValidImageFormatExtension && _RenameConfiguration.IgnoreExtensions.Contains(filePath.ExtensionLowered);
if (!isIgnoreExtension && isValidImageFormatExtension)
result = null;
else
{ {
CommandTask<CommandResult> commandTask = Cli.Wrap("ffmpeg.exe") bool check;
// .WithArguments(new[] { "-ss", "00:00:00", "-t", "00:00:00", "-i", files[i], "-qScale:v", "2", "-r", "0.01", $"{fileHolder.Name}-%4d.jpg" }) try
.WithArguments(new[] { "-i", filePath.FullName, "-vFrames", "1", $"{filePath.Name}-%4d.jpg" }) {
.WithWorkingDirectory(filePath.DirectoryName) CommandTask<CommandResult> commandTask = Cli.Wrap("ffmpeg.exe")
.ExecuteAsync(); .WithArguments(new[] { "-i", filePath.FullName, "-vf", "select=eq(n\\,0)", "-q:v", "1", $"{filePath.Name}-%4d.jpg" })
commandTask.Task.Wait(); .WithWorkingDirectory(filePath.DirectoryName)
results.AddRange(Directory.GetFiles(filePath.DirectoryName, $"{filePath.Name}-*.jpg", SearchOption.TopDirectoryOnly)); .ExecuteAsync();
if (results.Count == 0) commandTask.Task.Wait();
throw new Exception(); check = true;
result = IId.GetFilePath(_RenameConfiguration.MetadataConfiguration, results[0]); }
if (!result.Name.EndsWith("-0001.jpg")) catch (Exception)
throw new Exception(); {
isValidImageFormatExtension = _RenameConfiguration.ValidImageFormatExtensions.Contains(result.ExtensionLowered); check = false;
isIgnoreExtension = isValidImageFormatExtension && _RenameConfiguration.IgnoreExtensions.Contains(result.ExtensionLowered); }
if (isIgnoreExtension || !isValidImageFormatExtension) if (check)
throw new Exception(); {
if (result.DirectoryName is null) results.AddRange(Directory.GetFiles(filePath.DirectoryName, $"{filePath.Name}-*.jpg", SearchOption.TopDirectoryOnly));
throw new NullReferenceException(nameof(result.DirectoryName)); 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 new(new(results), result); return new(results);
} }
#pragma warning disable CA1416 #pragma warning disable CA1416
@ -117,120 +112,197 @@ public class Rename : IRename
#pragma warning restore CA1416 #pragma warning restore CA1416
private void GetExifDirectoryCollection(IRename rename, List<(string, FileInfo, ExifDirectory)> exifDirectories, IEnumerable<string> files, A_Metadata metadata) private static void RenameUrl(FilePath filePath)
{ {
FileInfo fileInfo; string[] lines = File.ReadAllLines(filePath.FullName);
FilePath filePath; if (lines.Length == 1)
FilePath? ffmpegFilePath;
ExifDirectory exifDirectory;
ReadOnlyCollection<string> ffmpegFiles;
DeterministicHashCode deterministicHashCode;
foreach (string file in files)
{ {
filePath = IId.GetFilePath(_RenameConfiguration.MetadataConfiguration, file); FileHolder fileHolder = new(lines[0]);
if (filePath.ExtensionLowered is ".paddedId" or ".lsv") if (fileHolder.Exists)
continue; {
if (files.Contains($"{filePath.FullName}.paddedId")) string checkFile;
continue; checkFile = IId.GetIgnoreFullPath(filePath, fileHolder);
if (filePath.Id is not null && (filePath.IsIdFormat || filePath.IsPaddedIdFormat)) if (lines[0] == checkFile || lines[0].Length != checkFile.Length)
continue; throw new NotSupportedException();
(ffmpegFiles, ffmpegFilePath) = rename.ConvertAndGetFfmpegFiles(filePath); File.Move(lines[0], checkFile);
if (ffmpegFilePath is not null) }
filePath = ffmpegFilePath; File.Delete(filePath.FullName);
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);
exifDirectories.Add(new(file, fileInfo, exifDirectory));
foreach (string ffmpegFile in ffmpegFiles)
File.Delete(ffmpegFile);
} }
} }
private static ReadOnlyCollection<Record> GetExifDirectoryCollection(List<(string, FileInfo, ExifDirectory)> exifDirectories) private static List<(FilePath, FileInfo, ExifDirectory)> GetExifDirectoryCollection(IRename rename, RenameConfiguration renameConfiguration, IEnumerable<string> files, A_Metadata metadata)
{
List<(FilePath, FileInfo, ExifDirectory)> results = [];
int index = -1;
FileInfo fileInfo;
FilePath filePath;
FileHolder fileHolder;
FilePath? ffmpegFilePath;
ExifDirectory exifDirectory;
ReadOnlyCollection<string>? ffmpegFiles;
DeterministicHashCode deterministicHashCode;
foreach (string file in files)
{
index += 1;
rename.Tick();
fileHolder = new(file);
if (renameConfiguration.IgnoreExtensions.Contains(fileHolder.ExtensionLowered))
continue;
filePath = FilePath.Get(renameConfiguration.MetadataConfiguration, fileHolder, index);
if (filePath.ExtensionLowered == ".url" && filePath.Id is not null)
{
RenameUrl(filePath);
continue;
}
if (renameConfiguration.SkipIdFiles && filePath.Id is not null && (filePath.IsIntelligentIdFormat || filePath.SortOrder is not null))
continue;
if (!renameConfiguration.ForceNewId && filePath.Id is not null)
{
ffmpegFiles = null;
deterministicHashCode = new(null, filePath.Id, null);
}
else
{
ffmpegFiles = rename.ConvertAndGetFfmpegFiles(renameConfiguration, filePath);
ffmpegFilePath = ffmpegFiles.Count == 0 ? null : FilePath.Get(renameConfiguration.MetadataConfiguration, new(ffmpegFiles[0]), index);
deterministicHashCode = ffmpegFilePath is null ? rename.GetDeterministicHashCode(filePath) : rename.GetDeterministicHashCode(ffmpegFilePath);
}
(fileInfo, exifDirectory) = metadata.GetMetadataCollection(renameConfiguration.MetadataConfiguration, filePath, deterministicHashCode);
results.Add(new(filePath, fileInfo, exifDirectory));
if (ffmpegFiles is not null)
{
foreach (string ffmpegFile in ffmpegFiles)
File.Delete(ffmpegFile);
}
}
return results;
}
private static ReadOnlyCollection<Record> GetExifDirectoryCollection(MetadataConfiguration metadataConfiguration, List<(FilePath, FileInfo, ExifDirectory)> exifDirectories)
{ {
List<Record> results = []; List<Record> results = [];
DateTime? dateTime; DateTime? dateTime;
foreach ((string file, FileInfo fileInfo, ExifDirectory exifDirectory) in exifDirectories) bool hasIgnoreKeyword;
bool hasDateTimeOriginal;
ReadOnlyCollection<string> keywords;
foreach ((FilePath filePath, FileInfo fileInfo, ExifDirectory exifDirectory) in exifDirectories)
{ {
dateTime = IDate.GetDateTimeOriginal(exifDirectory); dateTime = IDate.GetDateTimeOriginal(exifDirectory);
hasDateTimeOriginal = dateTime is not null;
dateTime ??= IDate.GetMinimum(exifDirectory); dateTime ??= IDate.GetMinimum(exifDirectory);
results.Add(new(dateTime.Value, exifDirectory, file, fileInfo.FullName)); keywords = IMetadata.GetKeywords(exifDirectory);
hasIgnoreKeyword = metadataConfiguration.IgnoreRulesKeyWords.Any(l => keywords.Contains(l));
results.Add(new(dateTime.Value, exifDirectory, filePath, hasDateTimeOriginal, hasIgnoreKeyword, fileInfo.FullName));
} }
return new(results); return new(results);
} }
private ReadOnlyCollection<Record> GetExifDirectoryCollection(DirectoryInfo directoryInfo) private ReadOnlyCollection<Record> GetExifDirectoryCollection(IRename rename, AppSettings appSettings, RenameConfiguration renameConfiguration, DirectoryInfo directoryInfo)
{ {
ReadOnlyCollection<Record> results; ReadOnlyCollection<Record> results;
IRename rename = this; List<(FilePath, FileInfo, ExifDirectory)> exifDirectories = [];
List<(string, FileInfo, ExifDirectory)> exifDirectories = []; A_Metadata metadata = new(renameConfiguration.MetadataConfiguration);
int appSettingsMaxDegreeOfParallelism = _AppSettings.MaxDegreeOfParallelism; int appSettingsMaxDegreeOfParallelism = appSettings.MaxDegreeOfParallelism;
IEnumerable<string> files = Directory.EnumerateFiles(directoryInfo.FullName, "*", SearchOption.AllDirectories); IEnumerable<string> files = appSettingsMaxDegreeOfParallelism == 1 ? Directory.GetFiles(directoryInfo.FullName, "*", SearchOption.AllDirectories) : Directory.EnumerateFiles(directoryInfo.FullName, "*", SearchOption.AllDirectories);
A_Metadata metadata = new(_RenameConfiguration.MetadataConfiguration); int filesCount = appSettingsMaxDegreeOfParallelism == 1 ? files.Count() : 123000;
_ProgressBar = new(filesCount, "EnumerateFiles load", new ProgressBarOptions() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true });
if (appSettingsMaxDegreeOfParallelism == 1) if (appSettingsMaxDegreeOfParallelism == 1)
GetExifDirectoryCollection(rename, exifDirectories, files, metadata); exifDirectories.AddRange(GetExifDirectoryCollection(rename, renameConfiguration, files, metadata));
else else
{ {
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = appSettingsMaxDegreeOfParallelism }; 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, metadata, exifDirectories));
files.AsParallel().ForAll(A_Metadata.SetExifDirectoryCollection(rename, _RenameConfiguration.MetadataConfiguration, metadata, exifDirectories, () => progressBar.Tick())); if (_ProgressBar.CurrentTick != exifDirectories.Count)
if (progressBar.CurrentTick != exifDirectories.Count)
throw new NotSupportedException(); throw new NotSupportedException();
} }
results = GetExifDirectoryCollection(exifDirectories); _ProgressBar.Dispose();
results = GetExifDirectoryCollection(renameConfiguration.MetadataConfiguration, exifDirectories);
return results; return results;
} }
private static void VerifyIntMinValueLength(ReadOnlyCollection<Record> exifDirectories, int intMinValueLength) private static void VerifyIntMinValueLength(MetadataConfiguration metadataConfiguration, ReadOnlyCollection<Record> exifDirectories)
{ {
foreach ((DateTime _, ExifDirectory exifDirectory, string _, string _) in exifDirectories) foreach ((DateTime _, ExifDirectory exifDirectory, FilePath _, bool _, bool _, string _) in exifDirectories)
{ {
if (exifDirectory.Id is null) if (exifDirectory.Id is null)
continue; continue;
if (intMinValueLength < exifDirectory.Id.Value.ToString().Length) if (metadataConfiguration.IntMinValueLength < exifDirectory.Id.Value.ToString().Length)
throw new NotSupportedException(); throw new NotSupportedException();
} }
} }
private ReadOnlyCollection<ToDo> GetToDoCollection(ILogger<Program>? logger, long ticks, ReadOnlyCollection<Record> exifDirectories) private static string? GetCheckDirectory(RenameConfiguration renameConfiguration, Record record, FilePath filePath, ReadOnlyCollection<int> ids, bool multipleDirectoriesWithFiles)
{
string? result;
if (filePath.DirectoryName is null)
throw new NullReferenceException(nameof(filePath.DirectoryName));
string year = record.DateTime.Year.ToString();
string checkDirectoryName = Path.GetFileName(filePath.DirectoryName);
if (multipleDirectoriesWithFiles && !checkDirectoryName.Contains(year))
result = null;
else
{
string? maker = IMetadata.GetMaker(record.ExifDirectory);
(int seasonValue, string seasonName) = IDate.GetSeason(record.DateTime.DayOfYear);
string splat = filePath.DirectoryName[^3..][1] == '!' ? filePath.DirectoryName[^3..] : string.Empty;
string makerSplit = string.IsNullOrEmpty(maker) ? string.IsNullOrEmpty(renameConfiguration.DefaultMaker) ? string.Empty : renameConfiguration.DefaultMaker : $" {maker.Split(' ')[0]}";
string directoryName = $"{year}.{seasonValue} {seasonName}{makerSplit}{splat}";
result = Path.Combine(renameConfiguration.MetadataConfiguration.ResultConfiguration.RootDirectory, record.ExifDirectory.Id is null || !ids.Contains(record.ExifDirectory.Id.Value) ? "_ Destination _" : "_ Exists _", record.HasDateTimeOriginal ? "Has" : "Not", directoryName);
}
return result;
}
private static ReadOnlyCollection<ToDo> GetToDoCollection(RenameConfiguration renameConfiguration, Identifier[]? identifiers, ReadOnlyCollection<Record> records)
{ {
List<ToDo> results = []; List<ToDo> results = [];
int season;
string maker;
Record record; Record record;
string jsonFile; string jsonFile;
string paddedId; string paddedId;
string checkFile; string checkFile;
string seasonName; FilePath filePath;
string directoryName; string directoryName;
FileHolder fileHolder; FileHolder fileHolder;
string? checkDirectory; string? checkDirectory;
const string jpg = ".jpg"; const string jpg = ".jpg";
string checkFileExtension; string checkFileExtension;
bool multipleDirectoriesWithFiles;
List<string> distinct = []; List<string> distinct = [];
bool? directoryCheck = null;
const string jpeg = ".jpeg"; const string jpeg = ".jpeg";
string jsonFileSubDirectory; string jsonFileSubDirectory;
int intMinValueLength = int.MinValue.ToString().Length; foreach (string directory in Directory.GetDirectories(renameConfiguration.MetadataConfiguration.ResultConfiguration.RootDirectory, "*", SearchOption.TopDirectoryOnly))
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]; foreach (string _ in Directory.EnumerateFiles(directory, "*", SearchOption.AllDirectories))
{
if (directoryCheck is null)
directoryCheck = false;
else if (directoryCheck.Value)
directoryCheck = true;
break;
}
if (directoryCheck is not null && directoryCheck.Value)
break;
}
VerifyIntMinValueLength(renameConfiguration.MetadataConfiguration, records);
multipleDirectoriesWithFiles = directoryCheck is not null && directoryCheck.Value;
ReadOnlyCollection<Record> collection = new((from l in records orderby l.DateTime select l).ToArray());
ResultConfiguration resultConfiguration = renameConfiguration.MetadataConfiguration.ResultConfiguration;
ReadOnlyCollection<int> ids = identifiers is null ? new([]) : new((from l in identifiers select l.Id).ToArray());
for (int i = 0; i < collection.Count; i++)
{
record = collection[i];
if (record.ExifDirectory.Id is null) if (record.ExifDirectory.Id is null)
continue; continue;
fileHolder = new(record.File); if (record.FilePath.DirectoryName is null)
if (fileHolder.DirectoryName is null)
continue; continue;
maker = IMetadata.GetMaker(record.ExifDirectory.ExifDirectoryBase); checkDirectory = GetCheckDirectory(renameConfiguration, record, record.FilePath, ids, multipleDirectoriesWithFiles);
(season, seasonName) = IDate.GetSeason(record.DateTime.DayOfYear); if (string.IsNullOrEmpty(checkDirectory))
checkFileExtension = fileHolder.ExtensionLowered == jpeg ? jpg : fileHolder.ExtensionLowered; continue;
checkDirectory = Path.Combine(fileHolder.DirectoryName, $"{record.DateTime.Year}.{season} {seasonName}{maker}"); checkFileExtension = record.FilePath.ExtensionLowered == jpeg ? jpg : record.FilePath.ExtensionLowered;
paddedId = IId.GetPaddedId(renameConfiguration.MetadataConfiguration, record.ExifDirectory.Id.Value, record.HasIgnoreKeyword, i);
jsonFileSubDirectory = Path.GetDirectoryName(Path.GetDirectoryName(record.JsonFile)) ?? throw new Exception(); jsonFileSubDirectory = Path.GetDirectoryName(Path.GetDirectoryName(record.JsonFile)) ?? throw new Exception();
paddedId = IId.GetPaddedId(intMinValueLength, _RenameConfiguration.MetadataConfiguration.Offset + i, record.ExifDirectory.Id.Value);
checkFile = Path.Combine(checkDirectory, $"{paddedId}{checkFileExtension}"); checkFile = Path.Combine(checkDirectory, $"{paddedId}{checkFileExtension}");
if (checkFile == fileHolder.FullName) if (checkFile == record.FilePath.FullName)
continue; continue;
if (File.Exists(checkFile)) if (File.Exists(checkFile))
{ {
@ -238,14 +310,18 @@ public class Rename : IRename
if (File.Exists(checkFile)) if (File.Exists(checkFile))
continue; 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"); jsonFile = Path.Combine(jsonFileSubDirectory, directoryName, $"{record.ExifDirectory.Id.Value}{checkFileExtension}.json");
if (record.JsonFile != jsonFile) if (record.JsonFile != jsonFile)
results.Add(new(null, new(record.JsonFile), jsonFile, JsonFile: true)); {
fileHolder = new(record.JsonFile);
filePath = FilePath.Get(renameConfiguration.MetadataConfiguration, fileHolder, index: null);
results.Add(new(null, filePath, jsonFile, JsonFile: true));
}
if (distinct.Contains(checkFile)) if (distinct.Contains(checkFile))
continue; continue;
distinct.Add(checkFile); distinct.Add(checkFile);
results.Add(new(checkDirectory, fileHolder, checkFile, JsonFile: false)); results.Add(new(checkDirectory, record.FilePath, checkFile, JsonFile: false));
} }
return new(results); return new(results);
} }
@ -267,13 +343,15 @@ public class Rename : IRename
{ {
List<string> results = []; List<string> results = [];
VerifyDirectories(toDoCollection); VerifyDirectories(toDoCollection);
_ProgressBar = new(toDoCollection.Count, "Move Files", new ProgressBarOptions() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true });
foreach (ToDo toDo in toDoCollection) foreach (ToDo toDo in toDoCollection)
{ {
_ProgressBar.Tick();
if (toDo.JsonFile) if (toDo.JsonFile)
{ {
if (File.Exists(toDo.File)) if (File.Exists(toDo.File))
File.Delete(toDo.File); File.Delete(toDo.File);
File.Move(toDo.FileHolder.FullName, toDo.File); File.Move(toDo.FilePath.FullName, toDo.File);
} }
else if (toDo.Directory is null) else if (toDo.Directory is null)
throw new NotSupportedException(); throw new NotSupportedException();
@ -281,11 +359,51 @@ public class Rename : IRename
{ {
if (File.Exists(toDo.File)) if (File.Exists(toDo.File))
File.Delete(toDo.File); File.Delete(toDo.File);
File.Move(toDo.FileHolder.FullName, toDo.File); try
results.Add($"{toDo.FileHolder.FullName}\t{toDo.File}"); { File.Move(toDo.FilePath.FullName, toDo.File); }
catch (Exception)
{ continue; }
results.Add($"{toDo.FilePath.FullName}\t{toDo.File}");
} }
} }
_ProgressBar.Dispose();
return new(results); return new(results);
} }
private static void SaveIdentifiersToDisk(long ticks, RenameConfiguration renameConfiguration, string aMetadataCollectionDirectory, ReadOnlyCollection<Record> records)
{
string paddedId;
List<Identifier> identifiers = [];
foreach (Record record in records)
{
if (record.ExifDirectory.Id is null)
continue;
paddedId = IId.GetPaddedId(renameConfiguration.MetadataConfiguration, record.ExifDirectory.Id.Value, record.FilePath.IsIgnore, index: null);
identifiers.Add(new(record.ExifDirectory.Id.Value, paddedId));
}
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 void RenameWork(ILogger<Program>? logger, AppSettings appSettings, IRename rename, long ticks, RenameConfiguration renameConfiguration)
{
string aMetadataCollectionDirectory = IResult.GetResultsDateGroupDirectory(renameConfiguration.MetadataConfiguration.ResultConfiguration, nameof(A_Metadata), renameConfiguration.MetadataConfiguration.ResultConfiguration.ResultCollection);
string? propertyCollectionFile = string.IsNullOrEmpty(renameConfiguration.RelativePropertyCollectionFile) ? null : Path.GetFullPath(Path.Combine(aMetadataCollectionDirectory, renameConfiguration.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(renameConfiguration.RelativePropertyCollectionFile))
throw new Exception($"Invalid {nameof(renameConfiguration.RelativePropertyCollectionFile)}");
DirectoryInfo directoryInfo = new(Path.GetFullPath(renameConfiguration.MetadataConfiguration.ResultConfiguration.RootDirectory));
logger?.LogInformation("{Ticks} {RootDirectory}", ticks, directoryInfo.FullName);
ReadOnlyCollection<Record> records = GetExifDirectoryCollection(rename, appSettings, renameConfiguration, directoryInfo);
SaveIdentifiersToDisk(ticks, renameConfiguration, aMetadataCollectionDirectory, records);
ReadOnlyCollection<ToDo> toDoCollection = GetToDoCollection(renameConfiguration, identifiers, records);
ReadOnlyCollection<string> lines = RenameFilesInDirectories(toDoCollection);
if (lines.Count != 0)
{
File.WriteAllLines($"D:/Tmp/Phares/{DateTime.Now.Ticks}.tsv", lines);
_ = IPath.DeleteEmptyDirectories(directoryInfo.FullName);
}
}
} }

View File

@ -32,7 +32,7 @@
<SupportedPlatform Include="browser" /> <SupportedPlatform Include="browser" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="System.Drawing.Common" Version="7.0.0" /> <PackageReference Include="System.Drawing.Common" Version="8.0.4" />
<PackageReference Include="System.Text.Json" Version="7.0.3" /> <PackageReference Include="System.Text.Json" Version="8.0.3" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -3,20 +3,21 @@ using System.Text.Json.Serialization;
namespace View_by_Distance.Shared.Models; namespace View_by_Distance.Shared.Models;
public record ExifDirectory(AviDirectory AviDirectory, public record ExifDirectory(AviDirectory[] AviDirectories,
ExifDirectoryBase ExifDirectoryBase, ExifDirectoryBase[] ExifBaseDirectories,
FileMetadataDirectory FileMetadataDirectory, FileMetadataDirectory[] FileMetadataDirectories,
GifHeaderDirectory GifHeaderDirectory, GifHeaderDirectory[] GifHeaderDirectories,
GpsDirectory GpsDirectory, GpsDirectory[] GpsDirectories,
int? Height, int? Height,
int? Id, int? Id,
JpegDirectory JpegDirectory, JpegDirectory[] JpegDirectories,
MakernoteDirectory[] MakernoteDirectories,
string OriginalFileName, string OriginalFileName,
PhotoshopDirectory PhotoshopDirectory, PhotoshopDirectory[] PhotoshopDirectories,
PngDirectory PngDirectory, PngDirectory[] PngDirectories,
QuickTimeMovieHeaderDirectory QuickTimeMovieHeaderDirectory, QuickTimeMovieHeaderDirectory[] QuickTimeMovieHeaderDirectories,
QuickTimeTrackHeaderDirectory QuickTimeTrackHeaderDirectory, QuickTimeTrackHeaderDirectory[] QuickTimeTrackHeaderDirectories,
WebPDirectory WebPDirectory, WebPDirectory[] WebPDirectories,
int? Width) int? Width)
{ {
@ -28,7 +29,7 @@ public record ExifDirectory(AviDirectory AviDirectory,
} }
[JsonSourceGenerationOptions(WriteIndented = true)] [JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]
[JsonSerializable(typeof(ExifDirectory))] [JsonSerializable(typeof(ExifDirectory))]
public partial class ExifDirectorySourceGenerationContext : JsonSerializerContext public partial class ExifDirectorySourceGenerationContext : JsonSerializerContext
{ {

View File

@ -45,7 +45,6 @@ public class FileHolder
{ {
if (fileInfo.Exists) if (fileInfo.Exists)
{ {
_CreationTime = fileInfo.CreationTime;
_CreationTime = fileInfo.CreationTime; _CreationTime = fileInfo.CreationTime;
_LastWriteTime = fileInfo.LastWriteTime; _LastWriteTime = fileInfo.LastWriteTime;
_Length = fileInfo.Length; _Length = fileInfo.Length;

View File

@ -1,16 +1,22 @@
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using View_by_Distance.Shared.Models.Stateless.Methods;
namespace View_by_Distance.Shared.Models; namespace View_by_Distance.Shared.Models;
public record FilePath(string DirectoryName, public record FilePath(long CreationTicks,
string DirectoryName,
string ExtensionLowered, string ExtensionLowered,
string FileNameFirstSegment,
string FullName, string FullName,
int? Id, int? Id,
bool IsIdFormat, bool? IsIgnore,
bool IsPaddedIdFormat, bool IsIntelligentIdFormat,
long LastWriteTicks,
long Length,
string Name, string Name,
string NameWithoutExtension) string NameWithoutExtension,
int? SortOrder)
{ {
public override string ToString() public override string ToString()
@ -19,6 +25,63 @@ public record FilePath(string DirectoryName,
return result; return result;
} }
public static FilePath Get(MetadataConfiguration metadataConfiguration, FileHolder fileHolder, int? index)
{
if (fileHolder.CreationTime is null)
fileHolder = new(fileHolder.FullName);
if (fileHolder.CreationTime is null)
throw new NullReferenceException(nameof(fileHolder.CreationTime));
if (fileHolder.LastWriteTime is null)
throw new NullReferenceException(nameof(fileHolder.LastWriteTime));
if (fileHolder.Length is null)
throw new NullReferenceException(nameof(fileHolder.Length));
FilePath result;
int? id;
int? sortOder;
string fileNameFirstSegment = fileHolder.Name.Split('.')[0];
int sortOrderOnlyLengthIndex = metadataConfiguration.Offset.ToString().Length;
string fileDirectoryName = fileHolder.DirectoryName ?? throw new NullReferenceException();
bool isIntelligentIdFormat = IId.NameWithoutExtensionIsIntelligentIdFormat(metadataConfiguration, fileNameFirstSegment);
bool isPaddedIntelligentIdFormat = IId.NameWithoutExtensionIsPaddedIntelligentIdFormat(metadataConfiguration, sortOrderOnlyLengthIndex, fileNameFirstSegment);
bool fileNameFirstSegmentIsIdFormat = !isPaddedIntelligentIdFormat && !isIntelligentIdFormat && IId.NameWithoutExtensionIsIdFormat(metadataConfiguration, fileHolder);
bool? isIgnore = !isIntelligentIdFormat && !isPaddedIntelligentIdFormat ? null : fileNameFirstSegment[^1] is '2' or '8';
if (!fileNameFirstSegmentIsIdFormat && !isIntelligentIdFormat && !isPaddedIntelligentIdFormat)
(id, sortOder) = (null, null);
else if (isIntelligentIdFormat)
(id, sortOder) = (IId.GetId(metadataConfiguration, fileNameFirstSegment), null);
else if (isPaddedIntelligentIdFormat)
{
if (!int.TryParse(fileNameFirstSegment[..sortOrderOnlyLengthIndex], out int absoluteValueOfSortOrder))
(id, sortOder) = (null, null);
else
(id, sortOder) = (IId.GetId(metadataConfiguration, fileNameFirstSegment[sortOrderOnlyLengthIndex..]), absoluteValueOfSortOrder);
}
else if (fileNameFirstSegmentIsIdFormat)
{
if (index is null)
throw new NullReferenceException(nameof(index));
if (!int.TryParse(fileNameFirstSegment, out int valueOfFileNameFirstSegment))
throw new NotSupportedException();
(id, sortOder) = (valueOfFileNameFirstSegment, metadataConfiguration.Offset + index);
}
else
throw new NotSupportedException();
result = new(fileHolder.CreationTime.Value.Ticks,
fileDirectoryName,
fileHolder.ExtensionLowered,
fileNameFirstSegment,
fileHolder.FullName,
id,
isIgnore,
isIntelligentIdFormat,
fileHolder.LastWriteTime.Value.Ticks,
fileHolder.Length.Value,
fileHolder.Name,
fileHolder.NameWithoutExtension,
sortOder);
return result;
}
} }
[JsonSourceGenerationOptions(WriteIndented = true)] [JsonSourceGenerationOptions(WriteIndented = true)]

View File

@ -0,0 +1,23 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace View_by_Distance.Shared.Models;
public record MakernoteDirectory(string? CameraSerialNumber,
string? FirmwareVersion,
string? QualityAndFileFormat)
{
public override string ToString()
{
string result = JsonSerializer.Serialize(this, MakernoteDirectorySourceGenerationContext.Default.MakernoteDirectory);
return result;
}
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(MakernoteDirectory))]
public partial class MakernoteDirectorySourceGenerationContext : JsonSerializerContext
{
}

View File

@ -0,0 +1,25 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace View_by_Distance.Shared.Models;
public record MappingFromFilter(bool? IsFocusModel,
bool? IsFocusPerson,
bool? IsFocusRelativePath,
bool? InSkipCollection,
bool? IsUsed)
{
public override string ToString()
{
string result = JsonSerializer.Serialize(this, MappingFromFilterGenerationContext.Default.MappingFromFilter);
return result;
}
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(MappingFromFilter))]
public partial class MappingFromFilterGenerationContext : JsonSerializerContext
{
}

View File

@ -6,6 +6,7 @@ namespace View_by_Distance.Shared.Models;
public record MetadataConfiguration(ResultConfiguration ResultConfiguration, public record MetadataConfiguration(ResultConfiguration ResultConfiguration,
bool ForceMetadataLastWriteTimeToCreationTime, bool ForceMetadataLastWriteTimeToCreationTime,
string[] IgnoreRulesKeyWords, string[] IgnoreRulesKeyWords,
int IntMinValueLength,
int Offset, int Offset,
bool PropertiesChangedForMetadata) bool PropertiesChangedForMetadata)
{ {

View File

@ -4,7 +4,8 @@ using System.Text.Json.Serialization;
namespace View_by_Distance.Shared.Models; namespace View_by_Distance.Shared.Models;
public record PngDirectory(string? ImageHeight, public record PngDirectory(string? ImageHeight,
string? ImageWidth) string? ImageWidth,
string? TextualData)
{ {
public override string ToString() public override string ToString()

View File

@ -0,0 +1,12 @@
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; }
public string[] ValidVideoFormatExtensions { init; get; }
}

View File

@ -1,3 +1,4 @@
using System.Text;
using View_by_Distance.Shared.Models.Stateless.Methods; using View_by_Distance.Shared.Models.Stateless.Methods;
namespace View_by_Distance.Shared.Models.Stateless; namespace View_by_Distance.Shared.Models.Stateless;
@ -5,71 +6,76 @@ namespace View_by_Distance.Shared.Models.Stateless;
internal abstract class Id internal abstract class Id
{ {
internal static bool NameWithoutExtensionIsIdFormat(string fileNameWithoutExtension) internal static bool NameWithoutExtensionIsIdFormat(MetadataConfiguration metadataConfiguration, string fileNameFirstSegment)
{ {
bool result; bool result;
int intMinValueLength = int.MinValue.ToString().Length; if (fileNameFirstSegment.Length < 5 || fileNameFirstSegment.Length > metadataConfiguration.IntMinValueLength)
if (fileNameWithoutExtension.Length < 5 || fileNameWithoutExtension.Length > intMinValueLength)
result = false; result = false;
else else
{ {
bool skipOneAllAreNumbers = fileNameWithoutExtension[1..].All(l => char.IsNumber(l)); bool skipOneAllAreNumbers = fileNameFirstSegment[1..].All(char.IsNumber);
result = (skipOneAllAreNumbers && fileNameWithoutExtension[0] == '-') || (skipOneAllAreNumbers && char.IsNumber(fileNameWithoutExtension[0])); result = (skipOneAllAreNumbers && fileNameFirstSegment[0] == '-') || (skipOneAllAreNumbers && char.IsNumber(fileNameFirstSegment[0]));
} }
return result; return result;
} }
internal static FilePath GetFilePath(FilePath filePath, string file) internal static int GetId(MetadataConfiguration metadataConfiguration, string intelligentId)
{ {
FilePath result; int result;
string fileName = Path.GetFileName(file); StringBuilder results = new();
string fileExtensionLowered = Path.GetExtension(file).ToLower(); if (metadataConfiguration.IntMinValueLength < (metadataConfiguration.ResultConfiguration.ResultAllInOneSubdirectoryLength + 2))
result = new(filePath.DirectoryName, fileExtensionLowered, file, filePath.Id, filePath.IsIdFormat, filePath.IsPaddedIdFormat, fileName, filePath.NameWithoutExtension); throw new NotSupportedException();
for (int i = intelligentId.Length - (metadataConfiguration.ResultConfiguration.ResultAllInOneSubdirectoryLength + 2); i > -1; i--)
_ = results.Append(intelligentId[i]);
_ = results.Append(intelligentId[^3]).Append(intelligentId[^2]);
result = int.Parse(results.ToString());
if (intelligentId[^1] is '1' or '2')
result *= -1;
else if (intelligentId[^1] is not '9' and not '8')
throw new NotSupportedException();
return result; return result;
} }
internal static FilePath GetFilePath(MetadataConfiguration metadataConfiguration, string file) internal static string GetIntelligentId(MetadataConfiguration metadataConfiguration, long id, bool? ignore)
{ {
FilePath result; string result;
int? id; StringBuilder stringBuilder = new();
short? multiplier; if (metadataConfiguration.IntMinValueLength < (metadataConfiguration.ResultConfiguration.ResultAllInOneSubdirectoryLength + 2))
char negativeMarker; throw new NotSupportedException();
int absoluteValueOfId; int key;
string fileName = Path.GetFileName(file); string value;
string fileExtensionLowered = Path.GetExtension(file).ToLower(); List<char> resultAllInOneSubdirectoryChars = [];
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(file); if (id > -1)
string fileDirectoryName = Path.GetDirectoryName(file) ?? throw new NullReferenceException();
short sortOrderOnlyLengthIndex = IId.GetSortOrderOnlyLengthIndex(metadataConfiguration.Offset);
bool nameWithoutExtensionIsIdFormat = IId.NameWithoutExtensionIsIdFormat(fileNameWithoutExtension);
bool nameWithoutExtensionIsPaddedIdFormat = IId.NameWithoutExtensionIsPaddedIdFormat(fileNameWithoutExtension, sortOrderOnlyLengthIndex);
if (!nameWithoutExtensionIsIdFormat && !nameWithoutExtensionIsPaddedIdFormat)
id = null;
else if (nameWithoutExtensionIsIdFormat)
{ {
if (!int.TryParse(fileNameWithoutExtension, out absoluteValueOfId)) key = ignore is not null && ignore.Value ? 8 : 9;
id = null; value = id.ToString().PadLeft(metadataConfiguration.IntMinValueLength, '0');
else
id = absoluteValueOfId;
} }
else else
{ {
negativeMarker = fileNameWithoutExtension[sortOrderOnlyLengthIndex - 2]; key = ignore is not null && ignore.Value ? 2 : 1;
if (negativeMarker == '7') value = id.ToString()[1..].PadLeft(metadataConfiguration.IntMinValueLength, '0');
multiplier = 1; }
else if (negativeMarker == '3') for (int i = value.Length - metadataConfiguration.ResultConfiguration.ResultAllInOneSubdirectoryLength - 1; i > -1; i--)
multiplier = -1; _ = stringBuilder.Append(value[i]);
else for (int i = value.Length - metadataConfiguration.ResultConfiguration.ResultAllInOneSubdirectoryLength; i < value.Length; i++)
multiplier = null; resultAllInOneSubdirectoryChars.Add(value[i]);
if (!int.TryParse(fileNameWithoutExtension[sortOrderOnlyLengthIndex..], out absoluteValueOfId)) result = $"{stringBuilder}{string.Join(string.Empty, resultAllInOneSubdirectoryChars)}{key}";
id = null; return result;
else }
{
id = absoluteValueOfId * multiplier; internal static string GetPaddedId(MetadataConfiguration metadataConfiguration, int id, bool? ignore, int? index)
if (id is null || !fileNameWithoutExtension.EndsWith(id.Value.ToString()[1..])) {
id = null; string result;
} if (metadataConfiguration.Offset < 0)
result = Guid.NewGuid().ToString();
else
{
string intelligentId = GetIntelligentId(metadataConfiguration, id, ignore);
int check = GetId(metadataConfiguration, intelligentId);
if (check != id)
throw new NotSupportedException();
result = index is null || metadataConfiguration.Offset == IId.DeterministicHashCode ? intelligentId : $"{metadataConfiguration.Offset + index}{intelligentId}";
} }
result = new(fileDirectoryName, fileExtensionLowered, file, id, nameWithoutExtensionIsIdFormat, nameWithoutExtensionIsPaddedIdFormat, fileName, fileNameWithoutExtension);
return result; return result;
} }

View File

@ -3,43 +3,55 @@ namespace View_by_Distance.Shared.Models.Stateless.Methods;
public interface IId public interface IId
{ {
bool TestStatic_NameWithoutExtensionIsIdFormat(string fileNameWithoutExtension) => const int DeterministicHashCode = 9876543;
NameWithoutExtensionIsIdFormat(fileNameWithoutExtension);
static bool NameWithoutExtensionIsIdFormat(string fileNameWithoutExtension) =>
Id.NameWithoutExtensionIsIdFormat(fileNameWithoutExtension);
bool TestStatic_NameWithoutExtensionIsIdFormat(FileHolder fileHolder) => static bool IsOffsetDeterministicHashCode(MetadataConfiguration metadataConfiguration) =>
NameWithoutExtensionIsIdFormat(fileHolder); metadataConfiguration.Offset == DeterministicHashCode;
static bool NameWithoutExtensionIsIdFormat(FileHolder fileHolder) =>
NameWithoutExtensionIsIdFormat(fileHolder.NameWithoutExtension);
string TestStatic_GetPaddedId(int intMinValueLength, int index, int id) => string TestStatic_GetIntelligentId(MetadataConfiguration metadataConfiguration, long id, bool ignore) =>
GetPaddedId(intMinValueLength, index, id); GetIntelligentId(metadataConfiguration, id, ignore);
static string GetPaddedId(int intMinValueLength, int index, int id) => static string GetIntelligentId(MetadataConfiguration metadataConfiguration, long id, bool ignore) =>
id > -1 ? $"{index}070{id.ToString().PadLeft(intMinValueLength, '0')}" : $"{index}030{id.ToString()[1..].PadLeft(intMinValueLength, '0')}"; Id.GetIntelligentId(metadataConfiguration, id, ignore);
bool TestStatic_NameWithoutExtensionIsPaddedIdFormat(string fileNameWithoutExtension, short sortOrderOnlyLengthIndex) => int TestStatic_GetId(MetadataConfiguration metadataConfiguration, string intelligentId) =>
NameWithoutExtensionIsPaddedIdFormat(fileNameWithoutExtension, sortOrderOnlyLengthIndex); GetId(metadataConfiguration, intelligentId);
static bool NameWithoutExtensionIsPaddedIdFormat(string fileNameWithoutExtension, short sortOrderOnlyLengthIndex) => static int GetId(MetadataConfiguration metadataConfiguration, string intelligentId) =>
fileNameWithoutExtension.Length > sortOrderOnlyLengthIndex Id.GetId(metadataConfiguration, intelligentId);
&& fileNameWithoutExtension[sortOrderOnlyLengthIndex] == '0'
&& fileNameWithoutExtension[sortOrderOnlyLengthIndex - 3] == '0'
&& fileNameWithoutExtension.All(l => char.IsNumber(l));
short TestStatic_GetSortOrderOnlyLengthIndex(int offset) => string TestStatic_GetPaddedId(MetadataConfiguration metadataConfiguration, int id, bool? ignore, int? index) =>
GetSortOrderOnlyLengthIndex(offset); GetPaddedId(metadataConfiguration, id, ignore, index);
static short GetSortOrderOnlyLengthIndex(int offset) => static string GetPaddedId(MetadataConfiguration metadataConfiguration, int id, bool? ignore, int? index) =>
(short)(offset.ToString().Length + 3); Id.GetPaddedId(metadataConfiguration, id, ignore, index);
FilePath TestStatic_GetFilePath(FilePath filePath, string file) => string TestStatic_GetIgnoreFullPath(FilePath filePath, FileHolder fileHolder) =>
GetFilePath(filePath, file); GetIgnoreFullPath(filePath, fileHolder);
static FilePath GetFilePath(FilePath filePath, string file) => static string GetIgnoreFullPath(FilePath filePath, FileHolder fileHolder) =>
Id.GetFilePath(filePath, file); fileHolder.DirectoryName is null ?
throw new NotSupportedException() :
filePath.Id > -1 ?
fileHolder.NameWithoutExtension[^1] == '9' ?
Path.Combine(fileHolder.DirectoryName, $"{fileHolder.NameWithoutExtension[..^1]}8{fileHolder.ExtensionLowered}") :
throw new NotSupportedException("High") :
fileHolder.NameWithoutExtension[^1] == '1' ?
Path.Combine(fileHolder.DirectoryName, $"{fileHolder.NameWithoutExtension[..^1]}2{fileHolder.ExtensionLowered}") :
throw new NotSupportedException("Low");
FilePath TestStatic_GetFilePath(MetadataConfiguration metadataConfiguration, string file) => bool TestStatic_NameWithoutExtensionIsIntelligentIdFormat(MetadataConfiguration metadataConfiguration, string fileNameFirstSegment) =>
GetFilePath(metadataConfiguration, file); NameWithoutExtensionIsIntelligentIdFormat(metadataConfiguration, fileNameFirstSegment);
static FilePath GetFilePath(MetadataConfiguration metadataConfiguration, string file) => static bool NameWithoutExtensionIsIntelligentIdFormat(MetadataConfiguration metadataConfiguration, string fileNameFirstSegment) =>
Id.GetFilePath(metadataConfiguration, file); fileNameFirstSegment.Length - 1 == metadataConfiguration.IntMinValueLength && fileNameFirstSegment[^1] is '1' or '2' or '8' or '9' && fileNameFirstSegment.All(char.IsNumber);
bool TestStatic_NameWithoutExtensionIsPaddedIntelligentIdFormat(MetadataConfiguration metadataConfiguration, int sortOrderOnlyLengthIndex, string fileNameFirstSegment) =>
NameWithoutExtensionIsPaddedIntelligentIdFormat(metadataConfiguration, sortOrderOnlyLengthIndex, fileNameFirstSegment);
static bool NameWithoutExtensionIsPaddedIntelligentIdFormat(MetadataConfiguration metadataConfiguration, int sortOrderOnlyLengthIndex, string fileNameFirstSegment) =>
fileNameFirstSegment.Length == metadataConfiguration.IntMinValueLength + sortOrderOnlyLengthIndex + 1
&& fileNameFirstSegment[^1] is '1' or '2' or '8' or '9'
&& fileNameFirstSegment.All(char.IsNumber);
bool TestStatic_NameWithoutExtensionIsIdFormat(MetadataConfiguration metadataConfiguration, FileHolder fileHolder) =>
NameWithoutExtensionIsIdFormat(metadataConfiguration, fileHolder);
static bool NameWithoutExtensionIsIdFormat(MetadataConfiguration metadataConfiguration, FileHolder fileHolder) =>
Id.NameWithoutExtensionIsIdFormat(metadataConfiguration, fileHolder.NameWithoutExtension.Split('.')[0]);
int TestStatic_GetDeterministicHashCode(byte[] value) => int TestStatic_GetDeterministicHashCode(byte[] value) =>
GetDeterministicHashCode(value); GetDeterministicHashCode(value);

View File

@ -76,9 +76,9 @@ public interface IPath
static (string, int) GetDirectoryNameAndIndex(ResultConfiguration resultConfiguration, int id) => static (string, int) GetDirectoryNameAndIndex(ResultConfiguration resultConfiguration, int id) =>
XPath.GetDirectoryNameAndIndex(resultConfiguration, id); XPath.GetDirectoryNameAndIndex(resultConfiguration, id);
ReadOnlyDictionary<int, ReadOnlyDictionary<string, string[]>> TestStatic_GetKeyValuePairs(ResultConfiguration resultConfiguration, string? resultsFullGroupDirectory, string[]? directories) => ReadOnlyDictionary<int, ReadOnlyDictionary<string, string[]>> TestStatic_GetKeyValuePairs(ResultConfiguration resultConfiguration, string? resultsFullGroupDirectory, string[]? jsonGroups) =>
GetKeyValuePairs(resultConfiguration, resultsFullGroupDirectory, directories); GetKeyValuePairs(resultConfiguration, resultsFullGroupDirectory, jsonGroups);
static ReadOnlyDictionary<int, ReadOnlyDictionary<string, string[]>> GetKeyValuePairs(ResultConfiguration resultConfiguration, string? resultsFullGroupDirectory, string[]? directories) => static ReadOnlyDictionary<int, ReadOnlyDictionary<string, string[]>> GetKeyValuePairs(ResultConfiguration resultConfiguration, string? resultsFullGroupDirectory, string[]? jsonGroups) =>
XPath.GetKeyValuePairs(resultConfiguration, resultsFullGroupDirectory, directories); XPath.GetKeyValuePairs(resultConfiguration, resultsFullGroupDirectory, jsonGroups);
} }

View File

@ -1,11 +1,13 @@
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using View_by_Distance.Shared.Models.Properties;
namespace View_by_Distance.Shared.Models.Stateless.Methods; namespace View_by_Distance.Shared.Models.Stateless.Methods;
public interface IRename public interface IRename
{ {
(ReadOnlyCollection<string>, FilePath?) ConvertAndGetFfmpegFiles(FilePath filePath); ReadOnlyCollection<string> ConvertAndGetFfmpegFiles(IRenameConfiguration renameConfiguration, FilePath filePath);
DeterministicHashCode GetDeterministicHashCode(FilePath filePath); DeterministicHashCode GetDeterministicHashCode(FilePath filePath);
void Tick();
} }

View File

@ -54,14 +54,34 @@ internal abstract class XDate
{ {
DateTime? result; DateTime? result;
List<DateTime> results = []; List<DateTime> results = [];
if (exifDirectory.ExifDirectoryBase.DateTimeOriginal is not null) foreach (ExifDirectoryBase exifDirectoryBase in exifDirectory.ExifBaseDirectories)
results.Add(exifDirectory.ExifDirectoryBase.DateTimeOriginal.Value); {
if (exifDirectory.AviDirectory.DateTimeOriginal is not null) if (exifDirectoryBase.DateTimeOriginal is not null)
results.Add(exifDirectory.AviDirectory.DateTimeOriginal.Value); results.Add(exifDirectoryBase.DateTimeOriginal.Value);
if (exifDirectory.QuickTimeMovieHeaderDirectory.Created is not null) }
results.Add(exifDirectory.QuickTimeMovieHeaderDirectory.Created.Value); foreach (AviDirectory aviDirectory in exifDirectory.AviDirectories)
if (exifDirectory.QuickTimeTrackHeaderDirectory.Created is not null) {
results.Add(exifDirectory.QuickTimeTrackHeaderDirectory.Created.Value); if (aviDirectory.DateTimeOriginal is not null)
results.Add(aviDirectory.DateTimeOriginal.Value);
}
foreach (QuickTimeMovieHeaderDirectory quickTimeMovieHeaderDirectory in exifDirectory.QuickTimeMovieHeaderDirectories)
{
if (quickTimeMovieHeaderDirectory.Created is not null)
{
if (quickTimeMovieHeaderDirectory.Created.Value.Year == 1904 && quickTimeMovieHeaderDirectory.Created.Value.Month == 1 && quickTimeMovieHeaderDirectory.Created.Value.Day == 1)
continue;
results.Add(quickTimeMovieHeaderDirectory.Created.Value);
}
}
foreach (QuickTimeTrackHeaderDirectory quickTimeTrackHeaderDirectory in exifDirectory.QuickTimeTrackHeaderDirectories)
{
if (quickTimeTrackHeaderDirectory.Created is not null)
{
if ((quickTimeTrackHeaderDirectory.Created.Value.Year is 1904 or 1970) && quickTimeTrackHeaderDirectory.Created.Value.Month == 1 && quickTimeTrackHeaderDirectory.Created.Value.Day == 1)
continue;
results.Add(quickTimeTrackHeaderDirectory.Created.Value);
}
}
result = results.Count == 0 ? null : results.Min(); result = results.Count == 0 ? null : results.Min();
return result; return result;
} }
@ -121,27 +141,56 @@ internal abstract class XDate
{ {
DateTime result; DateTime result;
List<DateTime> results = []; List<DateTime> results = [];
if (exifDirectory.ExifDirectoryBase.DateTimeOriginal is not null) foreach (ExifDirectoryBase exifDirectoryBase in exifDirectory.ExifBaseDirectories)
results.Add(exifDirectory.ExifDirectoryBase.DateTimeOriginal.Value); {
if (exifDirectory.AviDirectory.DateTimeOriginal is not null) if (exifDirectoryBase.DateTimeOriginal is not null)
results.Add(exifDirectory.AviDirectory.DateTimeOriginal.Value); results.Add(exifDirectoryBase.DateTimeOriginal.Value);
if (exifDirectory.QuickTimeMovieHeaderDirectory.Created is not null) }
results.Add(exifDirectory.QuickTimeMovieHeaderDirectory.Created.Value); foreach (AviDirectory aviDirectory in exifDirectory.AviDirectories)
if (exifDirectory.QuickTimeTrackHeaderDirectory.Created is not null) {
results.Add(exifDirectory.QuickTimeTrackHeaderDirectory.Created.Value); if (aviDirectory.DateTimeOriginal is not null)
results.Add(aviDirectory.DateTimeOriginal.Value);
}
foreach (QuickTimeMovieHeaderDirectory quickTimeMovieHeaderDirectory in exifDirectory.QuickTimeMovieHeaderDirectories)
{
if (quickTimeMovieHeaderDirectory.Created is not null)
{
if (quickTimeMovieHeaderDirectory.Created.Value.Year == 1904 && quickTimeMovieHeaderDirectory.Created.Value.Month == 1 && quickTimeMovieHeaderDirectory.Created.Value.Day == 1)
continue;
results.Add(quickTimeMovieHeaderDirectory.Created.Value);
}
}
foreach (QuickTimeTrackHeaderDirectory quickTimeTrackHeaderDirectory in exifDirectory.QuickTimeTrackHeaderDirectories)
{
if (quickTimeTrackHeaderDirectory.Created is not null)
{
if ((quickTimeTrackHeaderDirectory.Created.Value.Year is 1904 or 1970) && quickTimeTrackHeaderDirectory.Created.Value.Month == 1 && quickTimeTrackHeaderDirectory.Created.Value.Day == 1)
continue;
results.Add(quickTimeTrackHeaderDirectory.Created.Value);
}
}
if (results.Count == 0) if (results.Count == 0)
{ {
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(exifDirectory.OriginalFileName); string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(exifDirectory.OriginalFileName);
DateTime? dateTime = GetDateTimeFromName(fileNameWithoutExtension); DateTime? dateTime = GetDateTimeFromName(fileNameWithoutExtension);
if (dateTime is not null) if (dateTime is not null)
results.Add(dateTime.Value); results.Add(dateTime.Value);
if (exifDirectory.ExifDirectoryBase.DateTime is not null) foreach (ExifDirectoryBase exifDirectoryBase in exifDirectory.ExifBaseDirectories)
results.Add(exifDirectory.ExifDirectoryBase.DateTime.Value); {
if (exifDirectory.ExifDirectoryBase.DateTimeDigitized is not null) if (exifDirectoryBase.DateTime is not null)
results.Add(exifDirectory.ExifDirectoryBase.DateTimeDigitized.Value); results.Add(exifDirectoryBase.DateTime.Value);
if (exifDirectoryBase.DateTimeDigitized is not null)
results.Add(exifDirectoryBase.DateTimeDigitized.Value);
}
}
if (results.Count == 0)
{
foreach (FileMetadataDirectory fileMetadataDirectory in exifDirectory.FileMetadataDirectories)
{
if (fileMetadataDirectory.FileModifiedDate is not null)
results.Add(fileMetadataDirectory.FileModifiedDate.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(); result = results.Count == 0 ? DateTime.MinValue : results.Min();
return result; return result;
} }

View File

@ -324,7 +324,7 @@ internal abstract class XPath
return new(results); return new(results);
} }
internal static ReadOnlyDictionary<int, ReadOnlyDictionary<string, string[]>> GetKeyValuePairs(ResultConfiguration resultConfiguration, string? resultsFullGroupDirectory, string[]? directories) internal static ReadOnlyDictionary<int, ReadOnlyDictionary<string, string[]>> GetKeyValuePairs(ResultConfiguration resultConfiguration, string? resultsFullGroupDirectory, string[]? jsonGroups)
{ {
Dictionary<int, Dictionary<string, string[]>> results = []; Dictionary<int, Dictionary<string, string[]>> results = [];
string directory; string directory;
@ -339,16 +339,16 @@ internal abstract class XPath
results.Add(year, []); results.Add(year, []);
if (!results.TryGetValue(year, out keyValuePairs)) if (!results.TryGetValue(year, out keyValuePairs))
throw new NullReferenceException(nameof(keyValuePairs)); throw new NullReferenceException(nameof(keyValuePairs));
if (directories is not null) if (jsonGroups is not null)
{ {
foreach (string key in directories) foreach (string jsonGroup in jsonGroups)
{ {
if (resultsFullGroupDirectory is null) if (resultsFullGroupDirectory is null)
continue; continue;
collection.Clear(); collection.Clear();
for (int i = 0; i < plusOne; i++) for (int i = 0; i < plusOne; i++)
{ {
if (string.IsNullOrEmpty(key)) if (string.IsNullOrEmpty(jsonGroup))
{ {
if (i == converted) if (i == converted)
checkDirectory = Path.GetFullPath(Path.Combine(resultsFullGroupDirectory, new('-', resultConfiguration.ResultAllInOneSubdirectoryLength))); checkDirectory = Path.GetFullPath(Path.Combine(resultsFullGroupDirectory, new('-', resultConfiguration.ResultAllInOneSubdirectoryLength)));
@ -357,7 +357,7 @@ internal abstract class XPath
} }
else else
{ {
directory = Path.Combine(resultsFullGroupDirectory, key, year.ToString()); directory = Path.Combine(resultsFullGroupDirectory, jsonGroup);
if (i == converted) if (i == converted)
checkDirectory = Path.GetFullPath(Path.Combine(directory, new('-', resultConfiguration.ResultAllInOneSubdirectoryLength))); checkDirectory = Path.GetFullPath(Path.Combine(directory, new('-', resultConfiguration.ResultAllInOneSubdirectoryLength)));
else else
@ -367,8 +367,8 @@ internal abstract class XPath
_ = Directory.CreateDirectory(checkDirectory); _ = Directory.CreateDirectory(checkDirectory);
collection.Add(checkDirectory); collection.Add(checkDirectory);
} }
if (!string.IsNullOrEmpty(key)) if (!string.IsNullOrEmpty(jsonGroup))
keyValuePairs.Add(key, collection.ToArray()); keyValuePairs.Add(jsonGroup, collection.ToArray());
else else
keyValuePairs.Add(year.ToString(), collection.ToArray()); keyValuePairs.Add(year.ToString(), collection.ToArray());
} }

View File

@ -25,13 +25,13 @@ internal abstract class XResult
return result; return result;
} }
private static void VerifyDirectories(ResultConfiguration resultConfiguration, string dateGroupDirectory, string key) private static void VerifyDirectories(ResultConfiguration resultConfiguration, string dateGroupDirectory, string jsonGroup)
{ {
string checkDirectory; string checkDirectory;
int currentYear = DateTime.Now.Year; int currentYear = DateTime.Now.Year;
for (int i = resultConfiguration.EpicYear; i < currentYear + 1; i++) for (int i = resultConfiguration.EpicYear; i < currentYear + 1; i++)
{ {
checkDirectory = Path.Combine(dateGroupDirectory, key, i.ToString()); checkDirectory = Path.Combine(dateGroupDirectory, jsonGroup, i.ToString());
if (!Directory.Exists(checkDirectory)) if (!Directory.Exists(checkDirectory))
_ = Directory.CreateDirectory(checkDirectory); _ = Directory.CreateDirectory(checkDirectory);
} }