xmp from my face-file after using the digiKam maintenance tool to create xmp files

Alignment with Phares 8.0.118.14751 for Shared and Metadata
This commit is contained in:
2025-07-20 18:46:59 -07:00
parent d57be048e7
commit 8ecea05fe8
38 changed files with 524 additions and 1307 deletions

11
.vscode/launch.json vendored
View File

@ -11,6 +11,17 @@
"preLaunchTask": "build", "preLaunchTask": "build",
"program": "${workspaceFolder}/bin/Debug/net8.0/win-x64/File-Folder-Helper.dll", "program": "${workspaceFolder}/bin/Debug/net8.0/win-x64/File-Folder-Helper.dll",
"args": [ "args": [
"s",
"X",
"V:/Tmp/Phares/Helper-2025-07-20",
"Day-Helper-2025-07-20",
"871467010009.jpg",
"L:/Git/AA/Rename/.vscode/.UserSecrets/secrets.json",
"A:/()/638432064000000000.51/1401-08-03_02/2023/X+441981864000000000/871467010009.444090906.jpg.png",
"*.png",
"D:/{}/DisneyWorld 2019/Magic Kingdom/871467010009.jpg.xmp",
"*.xmp",
"{}-output",
"s", "s",
"X", "X",
"D:/ProgramData/EC_Characterization_Si/Dummy/DEP08CEPIEPSILON/Log", "D:/ProgramData/EC_Characterization_Si/Dummy/DEP08CEPIEPSILON/Log",

11
.vscode/settings.json vendored
View File

@ -1,4 +1,12 @@
{ {
"files.associations": {
"*.container": "ini",
"*.org": "ini",
"*.net": "ini",
"*.xmp": "xml",
"podman": "ini",
"default": "ini"
},
"[markdown]": { "[markdown]": {
"editor.wordWrap": "off" "editor.wordWrap": "off"
}, },
@ -17,6 +25,7 @@
"BIRT", "BIRT",
"CHIL", "CHIL",
"DEAT", "DEAT",
"digi",
"endianness", "endianness",
"Exif", "Exif",
"FAMC", "FAMC",
@ -32,8 +41,10 @@
"kanbn", "kanbn",
"Kofax", "Kofax",
"Linc", "Linc",
"Makernote",
"mesfs", "mesfs",
"mestsa", "mestsa",
"mklink",
"netrm", "netrm",
"NpgSql", "NpgSql",
"NSFX", "NSFX",

View File

@ -325,7 +325,7 @@ dotnet_diagnostic.CA2211.severity = none # Question - Non-constant fields should
dotnet_diagnostic.CA2249.severity = none # Question - Use dotnet_diagnostic.CA2249.severity = none # Question - Use
dotnet_diagnostic.CA2253.severity = none # Question - Named placeholders should not be numeric values dotnet_diagnostic.CA2253.severity = none # Question - Named placeholders should not be numeric values
dotnet_diagnostic.CS0103.severity = none # Question - The name dotnet_diagnostic.CS0103.severity = none # Question - The name
dotnet_diagnostic.CS0168.severity = none # Question - The variable dotnet_diagnostic.CS0168.severity = warning # Question - The variable
dotnet_diagnostic.CS0219.severity = none # Question - The variable dotnet_diagnostic.CS0219.severity = none # Question - The variable
dotnet_diagnostic.CS0612.severity = none # Question - is obsolete dotnet_diagnostic.CS0612.severity = none # Question - is obsolete
dotnet_diagnostic.CS0618.severity = none # Question - Compiler Warning (level 2) dotnet_diagnostic.CS0618.severity = none # Question - Compiler Warning (level 2)
@ -337,7 +337,7 @@ dotnet_diagnostic.CS8603.severity = none # Question - Possible null reference re
dotnet_diagnostic.CS8604.severity = none # Question - Possible null reference argument for parameter. dotnet_diagnostic.CS8604.severity = none # Question - Possible null reference argument for parameter.
dotnet_diagnostic.CS8618.severity = none # Question - Non-nullable variable must contain a non-null value when exiting constructor dotnet_diagnostic.CS8618.severity = none # Question - Non-nullable variable must contain a non-null value when exiting constructor
dotnet_diagnostic.CS8625.severity = none # Question - Cannot convert null literal to non-nullable reference type. dotnet_diagnostic.CS8625.severity = none # Question - Cannot convert null literal to non-nullable reference type.
dotnet_diagnostic.CS8629.severity = none # Question - Nullable value type may be null dotnet_diagnostic.CS8629.severity = warning # Question - Nullable value type may be null
dotnet_diagnostic.CS8765.severity = none # Question - Nullability of type of parameter dotnet_diagnostic.CS8765.severity = none # Question - Nullability of type of parameter
dotnet_diagnostic.IDE0005.severity = none # Question - Remove unnecessary using directives dotnet_diagnostic.IDE0005.severity = none # Question - Remove unnecessary using directives
dotnet_diagnostic.IDE0008.severity = warning # Question - Use explicit type instead of dotnet_diagnostic.IDE0008.severity = warning # Question - Use explicit type instead of
@ -361,7 +361,7 @@ dotnet_diagnostic.IDE0055.severity = none # Question - Formatting rule
dotnet_diagnostic.IDE0057.severity = none # Question - Substring can be simplified dotnet_diagnostic.IDE0057.severity = none # Question - Substring can be simplified
dotnet_diagnostic.IDE0058.severity = none # Question - Remove unnecessary expression value dotnet_diagnostic.IDE0058.severity = none # Question - Remove unnecessary expression value
dotnet_diagnostic.IDE0059.severity = none # Question - Unnecessary assignment of a value to dotnet_diagnostic.IDE0059.severity = none # Question - Unnecessary assignment of a value to
dotnet_diagnostic.IDE0060.severity = none # Question - Remove unused parameter dotnet_diagnostic.IDE0060.severity = warning # Question - Remove unused parameter
dotnet_diagnostic.IDE0063.severity = none # Question - Use simple dotnet_diagnostic.IDE0063.severity = none # Question - Use simple
dotnet_diagnostic.IDE0065.severity = none # Question - dotnet_diagnostic.IDE0065.severity = none # Question -
dotnet_diagnostic.IDE0066.severity = none # Question - Use dotnet_diagnostic.IDE0066.severity = none # Question - Use

View File

@ -50,7 +50,12 @@ internal static partial class Helper20250521 {
} }
internal static void MatchDirectory(ILogger<Worker> logger, List<string> args) { internal static void MatchDirectory(ILogger<Worker> logger, List<string> args) {
Record record; logger.LogInformation(args[0]);
logger.LogInformation(args[1]);
logger.LogInformation(args[2]);
logger.LogInformation(args[3]);
logger.LogInformation(args[4]);
logger.LogInformation(args[5]);
string datePattern = args[5]; string datePattern = args[5];
string searchPattern = args[2]; string searchPattern = args[2];
string searchPatternB = args[3]; string searchPatternB = args[3];
@ -119,7 +124,6 @@ internal static partial class Helper20250521 {
private static RecordB? GetRecord(int dateLineSegmentCount, string datePattern, string[] lines, int i, string[] segments, DateTime transactionDate, DateTime effectiveDate) { private static RecordB? GetRecord(int dateLineSegmentCount, string datePattern, string[] lines, int i, string[] segments, DateTime transactionDate, DateTime effectiveDate) {
RecordB? result = null; RecordB? result = null;
string line; string line;
RecordB record;
LineCheck lineCheck; LineCheck lineCheck;
List<string> collection = []; List<string> collection = [];
for (int j = i + 1; j < lines.Length; j++) { for (int j = i + 1; j < lines.Length; j++) {

View File

@ -36,7 +36,6 @@ internal static partial class Helper20250601 {
internal static void EquipmentAutomationFrameworkStatus(ILogger<Worker> logger, List<string> args) { internal static void EquipmentAutomationFrameworkStatus(ILogger<Worker> logger, List<string> args) {
Status status; Status status;
Record? record; Record? record;
List<string[]> messages;
logger.LogInformation(args[0]); logger.LogInformation(args[0]);
logger.LogInformation(args[1]); logger.LogInformation(args[1]);
logger.LogInformation(args[2]); logger.LogInformation(args[2]);

View File

@ -26,7 +26,6 @@ internal static partial class Helper20250628 {
private static ReadOnlyCollection<Record> GetRecords(string[] searchPatternFiles) { private static ReadOnlyCollection<Record> GetRecords(string[] searchPatternFiles) {
List<Record> results = []; List<Record> results = [];
Record record; Record record;
string[] files;
FileInfo fileInfo; FileInfo fileInfo;
foreach (string searchPatternFile in searchPatternFiles) { foreach (string searchPatternFile in searchPatternFiles) {
fileInfo = new(searchPatternFile); fileInfo = new(searchPatternFile);

View File

@ -52,7 +52,6 @@ internal static partial class Helper20250701 {
string[] files; string[] files;
string markdown; string markdown;
string checkFile; string checkFile;
string[] matches;
FileInfo fileInfo; FileInfo fileInfo;
string? pipeTable; string? pipeTable;
string? collections; string? collections;
@ -102,7 +101,7 @@ internal static partial class Helper20250701 {
logger.LogWarning("json is null"); logger.LogWarning("json is null");
continue; continue;
} }
pipeTable = GetPipeTable(logger, json); pipeTable = GetPipeTable(json);
if (string.IsNullOrEmpty(pipeTable)) { if (string.IsNullOrEmpty(pipeTable)) {
logger.LogWarning("pipeTable is null"); logger.LogWarning("pipeTable is null");
continue; continue;
@ -137,7 +136,7 @@ internal static partial class Helper20250701 {
if (lines.Length < columnTitlesLine.Value + 1) { if (lines.Length < columnTitlesLine.Value + 1) {
logger.LogWarning("<{lines}>(s)", lines.Length); logger.LogWarning("<{lines}>(s)", lines.Length);
} else { } else {
result = GetMarkdown(timeColumn, columnMapping, name, lines, columnTitlesLine); result = GetMarkdown(timeColumn, columnMapping, name, lines, columnTitlesLine.Value);
} }
} }
return result; return result;
@ -156,7 +155,6 @@ internal static partial class Helper20250701 {
private static ReadOnlyDictionary<string, ReadOnlyCollection<string>> GetKeyValuePairs(int columnTitlesLine, string[] lines) { private static ReadOnlyDictionary<string, ReadOnlyCollection<string>> GetKeyValuePairs(int columnTitlesLine, string[] lines) {
Dictionary<string, ReadOnlyCollection<string>> results = []; Dictionary<string, ReadOnlyCollection<string>> results = [];
string value;
string[] segments; string[] segments;
List<List<string>> collections = []; List<List<string>> collections = [];
string[] columns = lines[columnTitlesLine].Split('\t'); string[] columns = lines[columnTitlesLine].Split('\t');
@ -189,11 +187,11 @@ internal static partial class Helper20250701 {
return results.AsReadOnly(); return results.AsReadOnly();
} }
private static string? GetMarkdown(string timeColumn, ReadOnlyDictionary<string, string> columnMapping, string name, string[] lines, int? columnTitlesLine) { private static string? GetMarkdown(string timeColumn, ReadOnlyDictionary<string, string> columnMapping, string name, string[] lines, int columnTitlesLine) {
string? result; string? result;
List<string> charts = []; List<string> charts = [];
List<string> results = []; List<string> results = [];
ReadOnlyDictionary<string, ReadOnlyCollection<string>> keyValuePairs = GetKeyValuePairs(columnTitlesLine.Value, lines); ReadOnlyDictionary<string, ReadOnlyCollection<string>> keyValuePairs = GetKeyValuePairs(columnTitlesLine, lines);
string[] columns = keyValuePairs.Keys.OrderBy(l => l).ToArray(); string[] columns = keyValuePairs.Keys.OrderBy(l => l).ToArray();
if (!columns.Contains(timeColumn)) { if (!columns.Contains(timeColumn)) {
result = null; result = null;
@ -227,10 +225,9 @@ internal static partial class Helper20250701 {
if (results.Count == 0 && charts.Count == 0) { if (results.Count == 0 && charts.Count == 0) {
result = null; result = null;
} else { } else {
string[] segments;
results.Add($"## Footer{Environment.NewLine}"); results.Add($"## Footer{Environment.NewLine}");
results.Insert(0, $"# {name}{Environment.NewLine}"); results.Insert(0, $"# {name}{Environment.NewLine}");
for (int i = columnTitlesLine.Value + 1; i < lines.Length; i++) { for (int i = columnTitlesLine + 1; i < lines.Length; i++) {
if (lines[i].StartsWith("NUM_DATA_ROWS")) { if (lines[i].StartsWith("NUM_DATA_ROWS")) {
for (int j = i; j < lines.Length; j++) { for (int j = i; j < lines.Length; j++) {
results.Add($"- {lines[j]}"); results.Add($"- {lines[j]}");
@ -388,7 +385,7 @@ internal static partial class Helper20250701 {
return results.AsReadOnly(); return results.AsReadOnly();
} }
private static string? GetPipeTable(ILogger<Worker> logger, string json) { private static string? GetPipeTable(string json) {
string? result = null; string? result = null;
string? value; string? value;
string[]? columns = null; string[]? columns = null;

View File

@ -1,4 +1,3 @@
using System.Drawing; using System.Drawing;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@ -94,19 +93,23 @@ internal static partial class Helper20250705 {
height: rectangleFHeight, height: rectangleFHeight,
left: rectangleFLeft, left: rectangleFLeft,
top: rectangleFTop, top: rectangleFTop,
suffix: $"-{i}whole-percentages.jpg"); suffix: $"-{i}-whole-percentages.jpg");
} }
} }
} }
private static void Extract(string file, float width, float height, double left, double top, string suffix) { private static void Extract(string file, float width, float height, double left, double top, string suffix) {
#if SystemDrawingCommon
RectangleF rectangle = new((float)left, (float)top, width, height); RectangleF rectangle = new((float)left, (float)top, width, height);
using (Bitmap source = new(file)) { using (Bitmap source = new(file)) {
using (Bitmap bitmap = new((int)width, (int)height)) { using (Bitmap bitmap = new((int)width, (int)height)) {
using (Graphics graphics = Graphics.FromImage(bitmap)) using (Graphics graphics = Graphics.FromImage(bitmap)) {
graphics.DrawImage(source, new RectangleF(0, 0, width, height), rectangle, GraphicsUnit.Pixel); graphics.DrawImage(source, new RectangleF(0, 0, width, height), rectangle, GraphicsUnit.Pixel);
}
bitmap.Save($"{file}{suffix}"); bitmap.Save($"{file}{suffix}");
} }
} }
#endif
} }
} }

View File

@ -154,7 +154,6 @@ internal static partial class Helper20250709 {
logger.LogInformation(args[3]); logger.LogInformation(args[3]);
logger.LogInformation(args[4]); logger.LogInformation(args[4]);
logger.LogInformation(args[5]); logger.LogInformation(args[5]);
string[] segments;
string extension = args[4]; string extension = args[4];
string searchPattern = args[2]; string searchPattern = args[2];
int sizeFilter = int.Parse(args[3]); int sizeFilter = int.Parse(args[3]);
@ -162,13 +161,12 @@ internal static partial class Helper20250709 {
string sourceDirectory = Path.GetFullPath(args[0].Split('~')[0]); string sourceDirectory = Path.GetFullPath(args[0].Split('~')[0]);
string destinationDirectory = Path.GetFullPath(args[6].Split('~')[0]); string destinationDirectory = Path.GetFullPath(args[6].Split('~')[0]);
string[] directories = Directory.GetDirectories(sourceDirectory, "*", SearchOption.TopDirectoryOnly); string[] directories = Directory.GetDirectories(sourceDirectory, "*", SearchOption.TopDirectoryOnly);
JavaScriptObjectNotationTo(logger, sourceDirectory, searchPattern, sizeFilter, extension, columns.AsReadOnly(), destinationDirectory, directories.AsReadOnly()); JavaScriptObjectNotationTo(sourceDirectory, searchPattern, sizeFilter, extension, destinationDirectory, directories.AsReadOnly());
Helpers.HelperDeleteEmptyDirectories.DeleteEmptyDirectories(logger, sourceDirectory); Helpers.HelperDeleteEmptyDirectories.DeleteEmptyDirectories(logger, sourceDirectory);
} }
private static void JavaScriptObjectNotationTo(ILogger<Worker> logger, string sourceDirectory, string searchPattern, int sizeFilter, string extension, ReadOnlyCollection<string> columnMapping, string destinationDirectory, ReadOnlyCollection<string> directories) { private static void JavaScriptObjectNotationTo(string sourceDirectory, string searchPattern, int sizeFilter, string extension, string destinationDirectory, ReadOnlyCollection<string> directories) {
Raw? raw; Raw? raw;
string text;
string json; string json;
string[] files; string[] files;
string checkFile; string checkFile;

View File

@ -15,17 +15,15 @@ internal static partial class Helper20250710 {
logger.LogInformation(args[1]); logger.LogInformation(args[1]);
logger.LogInformation(args[2]); logger.LogInformation(args[2]);
logger.LogInformation(args[3]); logger.LogInformation(args[3]);
string[] segments;
string searchPattern = args[3]; string searchPattern = args[3];
string directoryPattern = args[2]; string directoryPattern = args[2];
string sourceDirectory = Path.GetFullPath(args[0].Split('~')[0]); string sourceDirectory = Path.GetFullPath(args[0].Split('~')[0]);
string[] directories = Directory.GetDirectories(sourceDirectory, directoryPattern, SearchOption.TopDirectoryOnly); string[] directories = Directory.GetDirectories(sourceDirectory, directoryPattern, SearchOption.TopDirectoryOnly);
LogToTrace(logger, sourceDirectory, searchPattern, directories.AsReadOnly()); LogToTrace(logger, searchPattern, directories.AsReadOnly());
Helpers.HelperDeleteEmptyDirectories.DeleteEmptyDirectories(logger, sourceDirectory); Helpers.HelperDeleteEmptyDirectories.DeleteEmptyDirectories(logger, sourceDirectory);
} }
private static void LogToTrace(ILogger<Worker> logger, string sourceDirectory, string searchPattern, ReadOnlyCollection<string> directories) { private static void LogToTrace(ILogger<Worker> logger, string searchPattern, ReadOnlyCollection<string> directories) {
string text;
string[] lines; string[] lines;
string[] files; string[] files;
string checkFile; string checkFile;
@ -55,7 +53,6 @@ internal static partial class Helper20250710 {
Match match; Match match;
string body; string body;
string[] segments; string[] segments;
string previousLine;
List<string> log = []; List<string> log = [];
for (int i = 1; i < lines.Length - 1; i++) { for (int i = 1; i < lines.Length - 1; i++) {
line = lines[i]; line = lines[i];

View File

@ -0,0 +1,427 @@
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Text.Json;
using Microsoft.Extensions.Logging;
using Phares.Metadata.Models.Stateless;
using Phares.Shared.Models;
namespace File_Folder_Helper.ADO2025.PI6;
internal static partial class Helper20250720 {
internal static void WriteFaceData(ILogger<Worker> logger, List<string> args) {
logger.LogInformation(args[0]);
logger.LogInformation(args[1]);
logger.LogInformation(args[2]);
logger.LogInformation(args[3]);
logger.LogInformation(args[4]);
logger.LogInformation(args[5]);
logger.LogInformation(args[6]);
logger.LogInformation(args[6]);
logger.LogInformation(args[7]);
logger.LogInformation(args[8]);
string searchPattern = args[5];
string searchPatternXMP = args[7];
string outputDirectoryName = args[8];
string jsonFile = Path.GetFullPath(args[3]);
string digiKamFile = Path.GetFullPath(args[6]);
FileInfo faceFileInfo = new(Path.GetFullPath(args[4]));
string sourceDirectory = Path.GetFullPath(args[0].Split('~')[0]);
string pathRoot = Path.GetPathRoot(faceFileInfo.FullName) ?? throw new Exception();
string? checkDirectory = Path.GetDirectoryName(digiKamFile.Replace("{}", outputDirectoryName));
string originalFile = Path.Combine(sourceDirectory, args[2]);
string pathRootXMP = digiKamFile[..5];
if (!File.Exists(jsonFile)) {
logger.LogError("json file doesn't exist! <{jsonFile}>", jsonFile);
} else if (!File.Exists(digiKamFile)) {
logger.LogError("digiKam file doesn't exist! <{digiKamFile}>", digiKamFile);
} else if (!File.Exists(originalFile)) {
logger.LogError("Original file doesn't exist! <{checkFile}>", originalFile);
} else if (!Directory.Exists(checkDirectory)) {
logger.LogError("checkDirectory doesn't exist! <{checkDirectory}>", checkDirectory);
} else if (!faceFileInfo.Exists) {
logger.LogError("Face file doesn't exist! <{faceFileInfo}>", faceFileInfo.FullName);
} else {
string json = File.ReadAllText(jsonFile);
ResultSettings? resultSettings = JsonSerializer.Deserialize(json, ResultSettingsSourceGenerationContext.Default.ResultSettings);
MetadataSettings? metadataSettings = JsonSerializer.Deserialize(json, MetadataSettingsSourceGenerationContext.Default.MetadataSettings);
if (resultSettings is null) {
logger.LogError(nameof(ResultSettings));
} else if (metadataSettings is null) {
logger.LogError(nameof(MetadataSettings));
} else {
WriteFaceData(logger, outputDirectoryName, originalFile, faceFileInfo, digiKamFile, resultSettings, metadataSettings);
ReadOnlyDictionary<long, ReadOnlyCollection<string>> keyValuePairsXMP = GetKeyValuePairs(searchPatternXMP, pathRootXMP);
ReadOnlyDictionary<long, ReadOnlyCollection<ExifDirectory>> keyValuePairs = GetKeyValuePairs(searchPattern, pathRoot, resultSettings, metadataSettings);
if (keyValuePairs.Count == 0) {
logger.LogError("Didn't find any valid file(s)!");
} else {
WriteFaceData(logger, outputDirectoryName, keyValuePairs, keyValuePairsXMP);
}
}
}
}
private static void WriteFaceData(ILogger<Worker> logger, string outputDirectoryName, string originalFile, FileInfo faceFileInfo, string digiKamFile, ResultSettings resultSettings, MetadataSettings metadataSettings) {
ExifDirectory? exifDirectory = IMetadata.GetExifDirectory(resultSettings, metadataSettings, faceFileInfo);
if (exifDirectory is null) {
logger.LogError("exifDirectory is null!");
} else {
long id;
string fileNameWithoutExtension;
fileNameWithoutExtension = Path.GetFileNameWithoutExtension(faceFileInfo.FullName);
if (!long.TryParse(fileNameWithoutExtension.Split('.')[0], out id)) {
id = -1;
}
long? fileNameFirstSegment = id == -1 ? null : id;
ReadOnlyCollection<string> digiKamFiles = new([digiKamFile]);
ReadOnlyCollection<ExifDirectory> exifDirectories = new([exifDirectory]);
WriteFaceData(logger, outputDirectoryName, originalFile, fileNameFirstSegment, exifDirectories, digiKamFiles);
}
}
private static void WriteFaceData(ILogger<Worker> logger, string outputDirectoryName, string? originalFile, long? fileNameFirstSegment, ReadOnlyCollection<ExifDirectory> exifDirectories, ReadOnlyCollection<string> digiKamFiles) {
ReadOnlyDictionary<string, FaceFile> keyValuePairs = GetKeyValuePairs(logger, exifDirectories);
if (keyValuePairs.Count > 0) {
WriteFaceData(logger, outputDirectoryName, originalFile, fileNameFirstSegment, digiKamFiles, keyValuePairs);
}
}
private static int? GetMatchingLine(string digiKamLine, ReadOnlyCollection<string> lines) {
int? result = null;
for (int i = 0; i < lines.Count; i++) {
if (lines[i] == digiKamLine) {
result = i;
break;
}
}
return result;
}
private static void WriteFaceData(string outputDirectoryName, string? originalFile, ReadOnlyDictionary<string, FaceFile> keyValuePairs, string digiKamFile, List<string> trimmed, int rdfLine) {
if (keyValuePairs.Count == 0) {
throw new Exception();
}
double h;
double w;
double mpTop;
double width;
double height;
double mpLeft;
double mwgTop;
double mwgLeft;
string personKey;
FaceFile faceFile;
string descriptionLine = "</rdf:Description>";
faceFile = keyValuePairs.ElementAt(0).Value;
List<string> regionLines = [
"<MP:RegionInfo rdf:parseType=\"Resource\">",
"<MPRI:Regions>",
"<rdf:Bag>"
];
List<string> regionsLinesB = [
"<mwg-rs:Regions rdf:parseType=\"Resource\">",
"<mwg-rs:AppliedToDimensions",
$"stDim:w=\"{faceFile.OutputResolution.Width}\"",
$"stDim:h=\"{faceFile.OutputResolution.Height}\"",
"stDim:unit=\"pixel\"/>",
"<mwg-rs:RegionList>",
"<rdf:Bag>"
];
List<string> digiKamLines = ["<digiKam:TagsList>", "<rdf:Seq>"];
List<string> microsoftPhotoLines = ["<MicrosoftPhoto:LastKeywordXMP>", "<rdf:Bag>"];
List<string> hierarchicalSubjectLines = ["<lr:hierarchicalSubject>", "<rdf:Bag>"];
List<string> catalogSetLines = ["<mediapro:CatalogSets>", "<rdf:Bag>"];
List<string> subjectLines = ["<dc:subject>", "<rdf:Bag>"];
foreach (KeyValuePair<string, FaceFile> keyValuePair in keyValuePairs) {
personKey = keyValuePair.Key;
faceFile = keyValuePair.Value;
width = faceFile.Location.Right - faceFile.Location.Left;
height = faceFile.Location.Bottom - faceFile.Location.Top;
if (!string.IsNullOrEmpty(originalFile) && File.Exists(originalFile)) {
Extract(file: originalFile,
width: width,
height: height,
left: faceFile.Location.Left,
top: faceFile.Location.Top,
suffix: $"-{keyValuePair.Key}-whole-percentages.jpg");
}
w = width / faceFile.OutputResolution.Width;
h = height / faceFile.OutputResolution.Height;
if (w == 0 || h == 0)
throw new NotImplementedException();
mpLeft = (double)faceFile.Location.Left / faceFile.OutputResolution.Width;
mpTop = (double)faceFile.Location.Top / faceFile.OutputResolution.Height;
mwgLeft = (faceFile.Location.Left + (width * .5)) / faceFile.OutputResolution.Width;
mwgTop = (faceFile.Location.Top + (height * .5)) / faceFile.OutputResolution.Height;
// <MP:RegionInfo rdf:parseType="Resource">
// <MPRI:Regions>
// <rdf:Bag>
// <rdf:li
// MPReg:PersonDisplayName="{personKey}"
// MPReg:Rectangle="{mpLeft:0.000000}, {mpTop:0.000000}, {w:0.000000}, {h:0.000000}"/>
// </rdf:Bag>
// </MPRI:Regions>
// </MP:RegionInfo>
regionLines.Add("<rdf:li");
regionLines.Add($"MPReg:PersonDisplayName=\"{personKey}\"");
regionLines.Add($"MPReg:Rectangle=\"{mpLeft:0.000000}, {mpTop:0.000000}, {w:0.000000}, {h:0.000000}\"/>");
// <mwg-rs:Regions rdf:parseType="Resource">
// <mwg-rs:AppliedToDimensions
// stDim:w="{faceFile.OutputResolution.Width}"
// stDim:h="{faceFile.OutputResolution.Height}"
// stDim:unit="pixel"/>
// <mwg-rs:RegionList>
// <rdf:Bag>
// <rdf:li>
// <rdf:Description
// mwg-rs:Name="{personKey}"
// mwg-rs:Type="Face">
// <mwg-rs:Area
// stArea:x="{mwgLeft:0.000000}"
// stArea:y="{mwgTop:0.000000}"
// stArea:w="{w:0.000000}"
// stArea:h="{h:0.000000}"
// stArea:unit="normalized"/>
// </rdf:Description>
// </rdf:li>
// </rdf:Bag>
// </mwg-rs:RegionList>
// </mwg-rs:Regions>
regionsLinesB.Add("<rdf:li>");
regionsLinesB.Add("<rdf:Description");
regionsLinesB.Add($"mwg-rs:Name=\"{personKey}\"");
regionsLinesB.Add("mwg-rs:Type=\"Face\">");
regionsLinesB.Add("<mwg-rs:Area");
regionsLinesB.Add($"stArea:x=\"{mwgLeft:0.000000}\"");
regionsLinesB.Add($"stArea:y=\"{mwgTop:0.000000}\"");
regionsLinesB.Add($"stArea:w=\"{w:0.000000}\"");
regionsLinesB.Add($"stArea:h=\"{h:0.000000}\"");
regionsLinesB.Add("stArea:unit=\"normalized\"/>");
regionsLinesB.Add("</rdf:Description>");
regionsLinesB.Add("</rdf:li>");
// <digiKam:TagsList>
// <rdf:Seq>
// <rdf:li>People/{personKey}</rdf:li>
// </rdf:Seq>
// </digiKam:TagsList>
digiKamLines.Add($"<rdf:li>People/{personKey}</rdf:li>");
// <MicrosoftPhoto:LastKeywordXMP>
// <rdf:Bag>
// <rdf:li>People/{personKey}</rdf:li>
// </rdf:Bag>
// </MicrosoftPhoto:LastKeywordXMP>
microsoftPhotoLines.Add($"<rdf:li>People/{personKey}</rdf:li>");
// <lr:hierarchicalSubject>
// <rdf:Bag>
// <rdf:li>People|{personKey}</rdf:li>
// </rdf:Bag>
// </lr:hierarchicalSubject>
hierarchicalSubjectLines.Add($"<rdf:li>People|{personKey}</rdf:li>");
// <mediapro:CatalogSets>
// <rdf:Bag>
// <rdf:li>People|{personKey}</rdf:li>
// </rdf:Bag>
// </mediapro:CatalogSets>
catalogSetLines.Add($"<rdf:li>People|{personKey}</rdf:li>");
// <dc:subject>
// <rdf:Bag>
// <rdf:li>{personKey}</rdf:li>
// </rdf:Bag>
// </dc:subject>
subjectLines.Add($"<rdf:li>{personKey}</rdf:li>");
}
regionLines.AddRange(["</rdf:Bag>","</MPRI:Regions>","</MP:RegionInfo>"]);
regionsLinesB.AddRange(["</rdf:Bag>", "</mwg-rs:RegionList>","</mwg-rs:Regions>"]);
digiKamLines.AddRange(["</rdf:Seq>", "</digiKam:TagsList>"]);
microsoftPhotoLines.AddRange(["</rdf:Bag>", "</MicrosoftPhoto:LastKeywordXMP>"]);
hierarchicalSubjectLines.AddRange(["</rdf:Bag>", "</lr:hierarchicalSubject>"]);
catalogSetLines.AddRange(["</rdf:Bag>", "</mediapro:CatalogSets>"]);
subjectLines.AddRange(["</rdf:Bag>", "</dc:subject>"]);
List<string> lines = [];
lines.AddRange(regionLines);
lines.AddRange(regionsLinesB);
lines.AddRange(digiKamLines);
lines.AddRange(microsoftPhotoLines);
lines.AddRange(hierarchicalSubjectLines);
lines.AddRange(catalogSetLines);
lines.AddRange(subjectLines);
string text = string.Join(Environment.NewLine, lines);
if (trimmed[rdfLine - 1] != descriptionLine) {
trimmed[rdfLine - 1] = $"{trimmed[rdfLine - 1][..^2]}>";
trimmed.Insert(rdfLine, descriptionLine);
rdfLine++;
}
trimmed.Insert(rdfLine - 1, text);
string allText = string.Join(Environment.NewLine, trimmed);
File.WriteAllText(digiKamFile.Replace("{}", outputDirectoryName), allText);
if (!string.IsNullOrEmpty(originalFile)) {
if (Debugger.IsAttached) {
File.WriteAllText(".xml", allText);
}
}
}
private static ReadOnlyDictionary<long, ReadOnlyCollection<string>> GetKeyValuePairs(string searchPattern, string pathRoot) {
Dictionary<long, ReadOnlyCollection<string>> results = [];
long fileNameFirstSegment;
List<string>? collection;
string fileNameWithoutExtension;
Dictionary<long, List<string>> keyValuePairs = [];
string[] files = Directory.GetFiles(pathRoot, searchPattern, SearchOption.AllDirectories);
foreach (string file in files) {
fileNameWithoutExtension = Path.GetFileNameWithoutExtension(file);
if (!long.TryParse(fileNameWithoutExtension.Split('.')[0], out fileNameFirstSegment)) {
continue;
}
if (!keyValuePairs.TryGetValue(fileNameFirstSegment, out collection)) {
keyValuePairs.Add(fileNameFirstSegment, []);
if (!keyValuePairs.TryGetValue(fileNameFirstSegment, out collection)) {
throw new Exception();
}
}
collection.Add(file);
}
foreach (KeyValuePair<long, List<string>> keyValuePair in keyValuePairs) {
results.Add(keyValuePair.Key, keyValuePair.Value.AsReadOnly());
}
return results.AsReadOnly();
}
private static ReadOnlyDictionary<string, FaceFile> GetKeyValuePairs(ILogger<Worker> logger, ReadOnlyCollection<ExifDirectory> exifDirectories) {
Dictionary<string, FaceFile> results = [];
string personKey;
FaceFile? faceFile;
foreach (ExifDirectory exifDirectory in exifDirectories) {
faceFile = IMetadata.GetFaceFile(exifDirectory);
if (faceFile is null) {
logger.LogError("faceFile is null!");
} else if (faceFile.Location is null) {
logger.LogError("faceFile location is null!");
} else if (faceFile.OutputResolution?.Orientation is not 0 and not 1) {
logger.LogWarning("faceFile output-resolution orientation is not aloud!");
} else {
personKey = Path.GetFileName(Path.GetDirectoryName(Path.GetDirectoryName(Path.GetDirectoryName(exifDirectory.FilePath.FullName))));
if (results.ContainsKey(personKey)) {
continue;
}
results.Add(personKey, faceFile);
}
}
return results.AsReadOnly();
}
private static ReadOnlyDictionary<long, ReadOnlyCollection<ExifDirectory>> GetKeyValuePairs(string searchPattern, string pathRoot, ResultSettings resultSettings, MetadataSettings metadataSettings) {
Dictionary<long, ReadOnlyCollection<ExifDirectory>> results = [];
FileInfo faceFileInfo;
long fileNameFirstSegment;
ExifDirectory? exifDirectory;
List<ExifDirectory>? collection;
string fileNameWithoutExtension;
Dictionary<long, List<ExifDirectory>> keyValuePairs = [];
string[] files = Directory.GetFiles(pathRoot, searchPattern, SearchOption.AllDirectories);
foreach (string file in files) {
faceFileInfo = new(file);
fileNameWithoutExtension = Path.GetFileNameWithoutExtension(faceFileInfo.FullName);
if (!long.TryParse(fileNameWithoutExtension.Split('.')[0], out fileNameFirstSegment)) {
continue;
}
exifDirectory = IMetadata.GetExifDirectory(resultSettings, metadataSettings, faceFileInfo);
if (exifDirectory is null)
continue;
if (!keyValuePairs.TryGetValue(fileNameFirstSegment, out collection)) {
keyValuePairs.Add(fileNameFirstSegment, []);
if (!keyValuePairs.TryGetValue(fileNameFirstSegment, out collection))
throw new Exception();
}
collection.Add(exifDirectory);
}
foreach (KeyValuePair<long, List<ExifDirectory>> keyValuePair in keyValuePairs) {
results.Add(keyValuePair.Key, keyValuePair.Value.AsReadOnly());
}
return results.AsReadOnly();
}
private static void WriteFaceData(ILogger<Worker> logger, string outputDirectoryName, ReadOnlyDictionary<long, ReadOnlyCollection<ExifDirectory>> keyValuePairs, ReadOnlyDictionary<long, ReadOnlyCollection<string>> keyValuePairsXMP) {
ReadOnlyCollection<string>? digiKamFiles;
foreach (KeyValuePair<long, ReadOnlyCollection<ExifDirectory>> keyValuePair in keyValuePairs) {
if (!keyValuePairsXMP.TryGetValue(keyValuePair.Key, out digiKamFiles)) {
logger.LogWarning("{fileNameFirstSegment}) Didn't find a matching file!", keyValuePair.Key);
} else {
string? originalFile = null;
WriteFaceData(logger, outputDirectoryName, originalFile, keyValuePair.Key, keyValuePair.Value, digiKamFiles);
}
}
}
private static void WriteFaceData(ILogger<Worker> logger, string outputDirectoryName, string? originalFile, long? fileNameFirstSegment, ReadOnlyCollection<string> digiKamFiles, ReadOnlyDictionary<string, FaceFile> keyValuePairs) {
#if xmp
IXmpMeta xmp;
using FileStream stream = File.OpenRead(digiKamFile);
xmp = XmpMetaFactory.Parse(stream);
foreach (var property in xmp.Properties) {
logger.LogDebug("Path={property.Path} Namespace={property.Namespace} Value={property.Value}", property.Path, property.Namespace, property.Value);
}
xmp.Sort();
SerializeOptions serializeOptions = new(SerializeOptions.EncodeUtf8);
string check = XmpMetaFactory.SerializeToString(xmp, serializeOptions);
File.WriteAllText(".xmp", check);
#endif
string[] requiredLines = [
"xmlns:digiKam=\"http://www.digikam.org/ns/1.0/\"",
"xmlns:xmp=\"http://ns.adobe.com/xap/1.0/\"",
"xmlns:exif=\"http://ns.adobe.com/exif/1.0/\"",
"xmlns:tiff=\"http://ns.adobe.com/tiff/1.0/\"",
"xmlns:dc=\"http://purl.org/dc/elements/1.1/\"",
"xmlns:acdsee=\"http://ns.acdsee.com/iptc/1.0/\"",
"xmlns:lr=\"http://ns.adobe.com/lightroom/1.0/\"",
"xmlns:MP=\"http://ns.microsoft.com/photo/1.2/\"",
"xmlns:stArea=\"http://ns.adobe.com/xmp/sType/Area#\"",
"xmlns:photoshop=\"http://ns.adobe.com/photoshop/1.0/\"",
"xmlns:MicrosoftPhoto=\"http://ns.microsoft.com/photo/1.0/\"",
"xmlns:MPReg=\"http://ns.microsoft.com/photo/1.2/t/Region#\"",
"xmlns:stDim=\"http://ns.adobe.com/xap/1.0/sType/Dimensions#\"",
"xmlns:MPRI=\"http://ns.microsoft.com/photo/1.2/t/RegionInfo#\"",
"xmlns:mediapro=\"http://ns.iview-multimedia.com/mediapro/1.0/\"",
"xmlns:mwg-rs=\"http://www.metadataworkinggroup.com/schemas/regions/\"",
"</rdf:RDF>"
];
foreach (string digiKamFile in digiKamFiles) {
string[] lines = File.ReadAllLines(digiKamFile);
List<string> trimmed = lines.Select(l => l.Trim()).ToList();
int? digiKamLine = GetMatchingLine(requiredLines[0], trimmed.AsReadOnly());
if (digiKamLine is null) {
logger.LogError("{fileNameFirstSegment}) Didn't fine digiKam line!", fileNameFirstSegment);
} else {
foreach (string requiredLine in requiredLines) {
if (!trimmed.Contains(requiredLine)) {
trimmed.Insert(digiKamLine.Value + 1, requiredLine);
}
}
int? rdfLine = GetMatchingLine(requiredLines[^1], trimmed.AsReadOnly());
if (rdfLine is null) {
logger.LogError("{fileNameFirstSegment}) Didn't fine description line!", fileNameFirstSegment);
} else {
WriteFaceData(outputDirectoryName, originalFile, keyValuePairs, digiKamFile, trimmed, rdfLine.Value);
}
}
}
}
private static void Extract(string file, double width, double height, int left, int top, string suffix) {
#if SystemDrawingCommon
Rectangle rectangle = new(left, top, width, height);
using (Bitmap source = new(file)) {
using (Bitmap bitmap = new(width, height)) {
using (Graphics graphics = Graphics.FromImage(bitmap)) {
graphics.DrawImage(source, new Rectangle(0, 0, width, height), rectangle, GraphicsUnit.Pixel);
}
bitmap.Save($"{file}{suffix}");
}
}
#endif
}
}

View File

@ -181,6 +181,8 @@ internal static class HelperDay
ADO2025.PI6.Helper20250709.JavaScriptObjectNotationToReactor(logger, args); ADO2025.PI6.Helper20250709.JavaScriptObjectNotationToReactor(logger, args);
else if (args[1] == "Day-Helper-2025-07-10") else if (args[1] == "Day-Helper-2025-07-10")
ADO2025.PI6.Helper20250710.StripLog(logger, args); ADO2025.PI6.Helper20250710.StripLog(logger, args);
else if (args[1] == "Day-Helper-2025-07-20")
ADO2025.PI6.Helper20250720.WriteFaceData(logger, args);
else else
throw new Exception(appSettings.Company); throw new Exception(appSettings.Company);
} }

View File

@ -14,12 +14,12 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="DiscUtils.Iso9660" Version="0.16.13" /> <PackageReference Include="DiscUtils.Iso9660" Version="0.16.13" />
<PackageReference Include="MetadataExtractor" Version="2.8.1" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.1" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.1" /> <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.1" />
<PackageReference Include="runtime.win-x64.Microsoft.DotNet.ILCompiler" Version="8.0.17" /> <PackageReference Include="Phares.Metadata" Version="8.0.118.14751" />
<PackageReference Include="System.Drawing.Common" Version="8.0.10" /> <PackageReference Include="Phares.Shared" Version="8.0.118.14751" />
<PackageReference Include="System.Text.Json" Version="9.0.5" /> <PackageReference Include="runtime.win-x64.Microsoft.DotNet.ILCompiler" Version="8.0.18" />
<PackageReference Include="System.Text.Json" Version="9.0.7" />
<PackageReference Include="TextCopy" Version="6.2.1" /> <PackageReference Include="TextCopy" Version="6.2.1" />
<PackageReference Include="WindowsShortcutFactory" Version="1.2.0" /> <PackageReference Include="WindowsShortcutFactory" Version="1.2.0" />
<PackageReference Include="YamlDotNet" Version="16.3.0" /> <PackageReference Include="YamlDotNet" Version="16.3.0" />

View File

@ -1,126 +0,0 @@
using System.Drawing;
namespace File_Folder_Helper.Helpers.Exif;
internal static class Dimensions
{
#pragma warning disable IDE0230
private static readonly Dictionary<byte[], Func<BinaryReader, Size?>> _ImageFormatDecoders = new()
{
{ new byte[] { 0x42, 0x4D }, DecodeBitmap },
{ new byte[] { 0x47, 0x49, 0x46, 0x38, 0x37, 0x61 }, DecodeGif },
{ new byte[] { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 }, DecodeGif },
{ new byte[] { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }, DecodePng },
{ new byte[] { 0xff, 0xd8 }, DecodeJfif },
{ new byte[] { 0x52, 0x49, 0x46, 0x46 }, DecodeWebP },
};
#pragma warning restore IDE0230
private static bool StartsWith(byte[] thisBytes, byte[] thatBytes)
{
for (int i = 0; i < thatBytes.Length; i += 1)
{
if (thisBytes[i] == thatBytes[i])
continue;
return false;
}
return true;
}
private static short ReadLittleEndianInt16(BinaryReader binaryReader)
{
byte[] bytes = new byte[sizeof(short)];
for (int i = 0; i < sizeof(short); i += 1)
bytes[sizeof(short) - 1 - i] = binaryReader.ReadByte();
return BitConverter.ToInt16(bytes, 0);
}
private static int ReadLittleEndianInt32(BinaryReader binaryReader)
{
byte[] bytes = new byte[sizeof(int)];
for (int i = 0; i < sizeof(int); i += 1)
bytes[sizeof(int) - 1 - i] = binaryReader.ReadByte();
return BitConverter.ToInt32(bytes, 0);
}
private static Size? DecodeBitmap(BinaryReader binaryReader)
{
_ = binaryReader.ReadBytes(16);
int width = binaryReader.ReadInt32();
int height = binaryReader.ReadInt32();
return new Size(width, height);
}
private static Size? DecodeGif(BinaryReader binaryReader)
{
int width = binaryReader.ReadInt16();
int height = binaryReader.ReadInt16();
return new Size(width, height);
}
private static Size? DecodePng(BinaryReader binaryReader)
{
_ = binaryReader.ReadBytes(8);
int width = ReadLittleEndianInt32(binaryReader);
int height = ReadLittleEndianInt32(binaryReader);
return new Size(width, height);
}
private static Size? DecodeJfif(BinaryReader binaryReader)
{
while (binaryReader.ReadByte() == 0xff)
{
byte marker = binaryReader.ReadByte();
short chunkLength = ReadLittleEndianInt16(binaryReader);
if (marker == 0xc0)
{
_ = binaryReader.ReadByte();
int height = ReadLittleEndianInt16(binaryReader);
int width = ReadLittleEndianInt16(binaryReader);
return new Size(width, height);
}
if (chunkLength >= 0)
_ = binaryReader.ReadBytes(chunkLength - 2);
else
{
ushort uChunkLength = (ushort)chunkLength;
_ = binaryReader.ReadBytes(uChunkLength - 2);
}
}
return null;
}
private static Size? DecodeWebP(BinaryReader binaryReader)
{
_ = binaryReader.ReadUInt32(); // Size
_ = binaryReader.ReadBytes(15); // WEBP, VP8 + more
_ = binaryReader.ReadBytes(3); // SYNC
int width = binaryReader.ReadUInt16() & 0b00_11111111111111; // 14 bits width
int height = binaryReader.ReadUInt16() & 0b00_11111111111111; // 14 bits height
return new Size(width, height);
}
internal static Size? GetDimensions(BinaryReader binaryReader)
{
int maxMagicBytesLength = _ImageFormatDecoders.Keys.OrderByDescending(x => x.Length).First().Length;
byte[] magicBytes = new byte[maxMagicBytesLength];
for (int i = 0; i < maxMagicBytesLength; i += 1)
{
magicBytes[i] = binaryReader.ReadByte();
foreach (KeyValuePair<byte[], Func<BinaryReader, Size?>> kvPair in _ImageFormatDecoders)
{
if (StartsWith(magicBytes, kvPair.Key))
return kvPair.Value(binaryReader);
}
}
return null;
}
internal static Size? GetDimensions(string path)
{
using BinaryReader binaryReader = new(File.OpenRead(path));
return GetDimensions(binaryReader);
}
}

View File

@ -1,534 +0,0 @@
using MetadataExtractor;
using MetadataExtractor.Formats.Exif;
using MetadataExtractor.Formats.Exif.Makernotes;
using System.Globalization;
namespace File_Folder_Helper.Helpers.Exif;
internal abstract class Exif
{
private static DateTime? GetDateTime(string? value)
{
DateTime? result;
string dateTimeFormat = "yyyy:MM:dd HH:mm:ss";
string alternateFormat = "ddd MMM dd HH:mm:ss yyyy";
if (value is not null && DateTime.TryParse(value, out DateTime dateTime))
result = dateTime;
else if (value is not null && value.Length == dateTimeFormat.Length && DateTime.TryParseExact(value, dateTimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime))
result = dateTime;
else if (value is not null && value.Length == alternateFormat.Length && DateTime.TryParseExact(value, alternateFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime))
result = dateTime;
else
result = null;
return result;
}
private static Models.Exif.AviDirectory[] GetAviDirectories(IReadOnlyList<MetadataExtractor.Directory> directories)
{
List<Models.Exif.AviDirectory> results = [];
IEnumerable<MetadataExtractor.Formats.Avi.AviDirectory> aviDirectories = directories.OfType<MetadataExtractor.Formats.Avi.AviDirectory>();
foreach (MetadataExtractor.Formats.Avi.AviDirectory aviDirectory in aviDirectories)
{
if (aviDirectory.Tags.Count == 0)
continue;
DateTime? dateTimeOriginal;
string? duration = aviDirectory.GetDescription(MetadataExtractor.Formats.Avi.AviDirectory.TagDuration);
string? height = aviDirectory.GetDescription(MetadataExtractor.Formats.Avi.AviDirectory.TagHeight);
string? width = aviDirectory.GetDescription(MetadataExtractor.Formats.Avi.AviDirectory.TagWidth);
if (aviDirectory.TryGetDateTime(MetadataExtractor.Formats.Avi.AviDirectory.TagDateTimeOriginal, out DateTime checkDateTime))
dateTimeOriginal = checkDateTime;
else
dateTimeOriginal = GetDateTime(aviDirectory.GetString(MetadataExtractor.Formats.Avi.AviDirectory.TagDateTimeOriginal));
if (dateTimeOriginal is null && duration is null && height is null && width is null)
continue;
results.Add(new(dateTimeOriginal, duration, height, width));
}
return results.ToArray();
}
private static Models.Exif.ExifDirectoryBase[] GetExifBaseDirectories(IReadOnlyList<MetadataExtractor.Directory> directories)
{
List<Models.Exif.ExifDirectoryBase> results = [];
IEnumerable<ExifDirectoryBase> exifBaseDirectories = directories.OfType<ExifDirectoryBase>();
foreach (ExifDirectoryBase exifDirectoryBase in exifBaseDirectories)
{
if (exifDirectoryBase.Tags.Count == 0)
continue;
DateTime? dateTime;
DateTime checkDateTime;
DateTime? dateTimeOriginal;
DateTime? dateTimeDigitized;
string? aperture = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagAperture);
string? applicationNotes = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagApplicationNotes);
string? artist = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagArtist);
string? bitsPerSample = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagBitsPerSample);
string? bodySerialNumber = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagBodySerialNumber);
string? cameraOwnerName = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagCameraOwnerName);
string? compressedAverageBitsPerPixel = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagCompressedAverageBitsPerPixel);
string? compression = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagCompression);
string? copyright = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagCopyright);
string? documentName = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagDocumentName);
string? exifVersion = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagExifVersion);
string? exposureTime = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagExposureTime);
string? fileSource = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagFileSource);
string? imageDescription = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagImageDescription);
string? imageHeight = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagImageHeight);
string? imageNumber = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagImageNumber);
string? imageUniqueId = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagImageUniqueId);
string? imageWidth = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagImageWidth);
string? isoSpeed = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagIsoSpeed);
string? lensMake = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagLensMake);
string? lensModel = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagLensModel);
string? lensSerialNumber = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagLensSerialNumber);
string? make = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagMake);
string? makerNote = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagMakernote);
string? model = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagModel);
string? orientation = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagOrientation);
int? orientationValue = orientation is null ? null : exifDirectoryBase.GetInt32(ExifDirectoryBase.TagOrientation);
string? rating = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagRating);
string? ratingPercent = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagRatingPercent);
string? securityClassification = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagSecurityClassification);
string? shutterSpeed = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagShutterSpeed);
string? software = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagSoftware);
string? timeZone = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagTimeZone);
string? timeZoneDigitized = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagTimeZoneDigitized);
string? timeZoneOriginal = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagTimeZoneOriginal);
string? userComment = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagUserComment);
string? winAuthor = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagWinAuthor);
string? winComment = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagWinComment);
string? winKeywords = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagWinKeywords);
string? winSubject = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagWinSubject);
string? winTitle = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagWinTitle);
string? xResolution = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagXResolution);
string? yResolution = exifDirectoryBase.GetDescription(ExifDirectoryBase.TagYResolution);
if (exifDirectoryBase.TryGetDateTime(ExifDirectoryBase.TagDateTime, out checkDateTime))
dateTime = checkDateTime;
else
dateTime = GetDateTime(exifDirectoryBase.GetString(ExifDirectoryBase.TagDateTime));
if (exifDirectoryBase.TryGetDateTime(ExifDirectoryBase.TagDateTimeOriginal, out checkDateTime))
dateTimeOriginal = checkDateTime;
else
dateTimeOriginal = GetDateTime(exifDirectoryBase.GetString(ExifDirectoryBase.TagDateTimeOriginal));
if (exifDirectoryBase.TryGetDateTime(ExifDirectoryBase.TagDateTimeDigitized, out checkDateTime))
dateTimeDigitized = checkDateTime;
else
dateTimeDigitized = GetDateTime(exifDirectoryBase.GetString(ExifDirectoryBase.TagDateTimeDigitized));
if (userComment is not null && userComment.Length > 255)
userComment = "...";
if (aperture is null
&& applicationNotes is null
&& artist is null
&& bitsPerSample is null
&& bodySerialNumber is null
&& cameraOwnerName is null
&& compressedAverageBitsPerPixel is null
&& compression is null
&& copyright is null
&& dateTime is null
&& dateTimeDigitized is null
&& dateTimeOriginal is null
&& documentName is null
&& exifVersion is null
&& exposureTime is null
&& fileSource is null
&& imageDescription is null
&& imageHeight is null
&& imageNumber is null
&& imageUniqueId is null
&& imageWidth is null
&& isoSpeed is null
&& lensMake is null
&& lensModel is null
&& lensSerialNumber is null
&& make is null
&& makerNote is null
&& model is null
&& orientation is null
&& orientationValue is null
&& rating is null
&& ratingPercent is null
&& securityClassification is null
&& shutterSpeed is null
&& software is null
&& timeZone is null
&& timeZoneDigitized is null
&& timeZoneOriginal is null
&& userComment is null
&& winAuthor is null
&& winComment is null
&& winKeywords is null
&& winSubject is null
&& 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 results.ToArray();
}
private static Models.Exif.FileMetadataDirectory[] GetFileMetadataDirectories(string file, IReadOnlyList<MetadataExtractor.Directory> directories)
{
List<Models.Exif.FileMetadataDirectory> results = [];
IEnumerable<MetadataExtractor.Formats.FileSystem.FileMetadataDirectory> fileMetadataDirectories = directories.OfType<MetadataExtractor.Formats.FileSystem.FileMetadataDirectory>();
foreach (MetadataExtractor.Formats.FileSystem.FileMetadataDirectory fileMetadataDirectory in fileMetadataDirectories)
{
if (fileMetadataDirectory.Tags.Count == 0)
continue;
DateTime? fileModifiedDate;
string? fileName = fileMetadataDirectory.GetDescription(MetadataExtractor.Formats.FileSystem.FileMetadataDirectory.TagFileName);
string? fileSize = fileMetadataDirectory.GetDescription(MetadataExtractor.Formats.FileSystem.FileMetadataDirectory.TagFileSize);
if (fileMetadataDirectory.TryGetDateTime(MetadataExtractor.Formats.FileSystem.FileMetadataDirectory.TagFileModifiedDate, out DateTime checkDateTime))
fileModifiedDate = checkDateTime;
else
fileModifiedDate = GetDateTime(fileMetadataDirectory.GetString(MetadataExtractor.Formats.FileSystem.FileMetadataDirectory.TagFileModifiedDate));
if (fileName is null || !file.EndsWith(fileName))
throw new NotSupportedException($"!{file}.EndsWith({fileName})");
if (fileModifiedDate is null && fileName is null && fileSize is null)
continue;
results.Add(new(fileModifiedDate, fileName, fileSize));
}
return results.ToArray();
}
private static Models.Exif.GifHeaderDirectory[] GetGifHeaderDirectories(IReadOnlyList<MetadataExtractor.Directory> directories)
{
List<Models.Exif.GifHeaderDirectory> results = [];
IEnumerable<MetadataExtractor.Formats.Gif.GifHeaderDirectory> gifHeaderDirectories = directories.OfType<MetadataExtractor.Formats.Gif.GifHeaderDirectory>();
foreach (MetadataExtractor.Formats.Gif.GifHeaderDirectory gifHeaderDirectory in gifHeaderDirectories)
{
if (gifHeaderDirectory.Tags.Count == 0)
continue;
string? imageHeight = gifHeaderDirectory.GetDescription(MetadataExtractor.Formats.Gif.GifHeaderDirectory.TagImageHeight);
string? imageWidth = gifHeaderDirectory.GetDescription(MetadataExtractor.Formats.Gif.GifHeaderDirectory.TagImageWidth);
if (imageHeight is null && imageWidth is null)
continue;
results.Add(new(imageHeight, imageWidth));
}
return results.ToArray();
}
private static Models.Exif.GpsDirectory[] GetGpsDirectories(IReadOnlyList<MetadataExtractor.Directory> directories)
{
List<Models.Exif.GpsDirectory> results = [];
IEnumerable<GpsDirectory> gpsDirectories = directories.OfType<GpsDirectory>();
foreach (GpsDirectory gpsDirectory in gpsDirectories)
{
if (gpsDirectory.Tags.Count == 0)
continue;
DateTime? timeStamp;
string? altitude = gpsDirectory.GetDescription(GpsDirectory.TagAltitude);
string? latitude = gpsDirectory.GetDescription(GpsDirectory.TagLatitude);
string? latitudeRef = gpsDirectory.GetDescription(GpsDirectory.TagLatitudeRef);
string? longitude = gpsDirectory.GetDescription(GpsDirectory.TagLongitude);
string? longitudeRef = gpsDirectory.GetDescription(GpsDirectory.TagLongitudeRef);
if (gpsDirectory.TryGetDateTime(GpsDirectory.TagTimeStamp, out DateTime checkDateTime))
timeStamp = checkDateTime;
else
timeStamp = GetDateTime(gpsDirectory.GetString(GpsDirectory.TagTimeStamp));
if (altitude is null && latitude is null && latitudeRef is null && longitude is null && longitudeRef is null && timeStamp is null)
continue;
results.Add(new(altitude,
latitude,
latitudeRef,
longitude,
longitudeRef,
timeStamp));
}
return results.ToArray();
}
private static Models.Exif.JpegDirectory[] GetJpegDirectories(IReadOnlyList<MetadataExtractor.Directory> directories)
{
List<Models.Exif.JpegDirectory> results = [];
IEnumerable<MetadataExtractor.Formats.Jpeg.JpegDirectory> jpegDirectories = directories.OfType<MetadataExtractor.Formats.Jpeg.JpegDirectory>();
foreach (MetadataExtractor.Formats.Jpeg.JpegDirectory jpegDirectory in jpegDirectories)
{
if (jpegDirectory.Tags.Count == 0)
continue;
string? imageHeight = jpegDirectory.GetDescription(MetadataExtractor.Formats.Jpeg.JpegDirectory.TagImageHeight);
string? imageWidth = jpegDirectory.GetDescription(MetadataExtractor.Formats.Jpeg.JpegDirectory.TagImageWidth);
if (imageHeight is null && imageWidth is null)
continue;
results.Add(new(imageHeight, imageWidth));
}
return results.ToArray();
}
private static Models.Exif.MakernoteDirectory[] GetMakernoteDirectories(IReadOnlyList<MetadataExtractor.Directory> directories)
{
List<Models.Exif.MakernoteDirectory> results = [];
IEnumerable<AppleMakernoteDirectory> appleMakernoteDirectories = directories.OfType<AppleMakernoteDirectory>();
foreach (AppleMakernoteDirectory appleMakernoteDirectory in appleMakernoteDirectories)
{
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 Models.Exif.PhotoshopDirectory[] GetPhotoshopDirectories(IReadOnlyList<MetadataExtractor.Directory> directories)
{
List<Models.Exif.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 Models.Exif.PngDirectory[] GetPngDirectories(IReadOnlyList<MetadataExtractor.Directory> directories)
{
List<Models.Exif.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? imageWidth = pngDirectory.GetDescription(MetadataExtractor.Formats.Png.PngDirectory.TagImageWidth);
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 results.ToArray();
}
private static Models.Exif.QuickTimeMovieHeaderDirectory[] GetQuickTimeMovieHeaderDirectoryDirectories(IReadOnlyList<MetadataExtractor.Directory> directories)
{
List<Models.Exif.QuickTimeMovieHeaderDirectory> results = [];
IEnumerable<MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory> quickTimeMovieHeaderDirectories = directories.OfType<MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory>();
foreach (MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory quickTimeMovieHeaderDirectory in quickTimeMovieHeaderDirectories)
{
if (quickTimeMovieHeaderDirectory.Tags.Count == 0)
continue;
DateTime? created;
if (quickTimeMovieHeaderDirectory.TryGetDateTime(MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory.TagCreated, out DateTime checkDateTime))
created = checkDateTime;
else
created = GetDateTime(quickTimeMovieHeaderDirectory.GetString(MetadataExtractor.Formats.QuickTime.QuickTimeMovieHeaderDirectory.TagCreated));
if (created is null)
continue;
results.Add(new(created));
}
return results.ToArray();
}
private static Models.Exif.QuickTimeTrackHeaderDirectory[] GetQuickTimeTrackHeaderDirectoryDirectories(IReadOnlyList<MetadataExtractor.Directory> directories)
{
List<Models.Exif.QuickTimeTrackHeaderDirectory> results = [];
IEnumerable<MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory> quickTimeTrackHeaderDirectories = directories.OfType<MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory>();
foreach (MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory quickTimeTrackHeaderDirectory in quickTimeTrackHeaderDirectories)
{
if (quickTimeTrackHeaderDirectory.Tags.Count == 0)
continue;
DateTime? created;
if (quickTimeTrackHeaderDirectory.TryGetDateTime(MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory.TagCreated, out DateTime checkDateTime))
created = checkDateTime;
else
created = GetDateTime(quickTimeTrackHeaderDirectory.GetString(MetadataExtractor.Formats.QuickTime.QuickTimeTrackHeaderDirectory.TagCreated));
if (created is null)
continue;
results.Add(new(created));
}
return results.ToArray();
}
private static Models.Exif.WebPDirectory[] GetWebPDirectories(IReadOnlyList<MetadataExtractor.Directory> directories)
{
List<Models.Exif.WebPDirectory> results = [];
IEnumerable<MetadataExtractor.Formats.WebP.WebPDirectory> webPDirectories = directories.OfType<MetadataExtractor.Formats.WebP.WebPDirectory>();
foreach (MetadataExtractor.Formats.WebP.WebPDirectory webPDirectory in webPDirectories)
{
if (webPDirectory.Tags.Count == 0)
continue;
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 Models.Exif.ExifDirectory Covert(FileInfo fileInfo, System.Drawing.Size? size, int id, IReadOnlyList<MetadataExtractor.Directory> directories)
{
Models.Exif.ExifDirectory result;
Models.Exif.AviDirectory[] aviDirectories = GetAviDirectories(directories);
Models.Exif.GpsDirectory[] gpsDirectories = GetGpsDirectories(directories);
Models.Exif.PngDirectory[] pngDirectories = GetPngDirectories(directories);
Models.Exif.JpegDirectory[] jpegDirectories = GetJpegDirectories(directories);
Models.Exif.WebPDirectory[] webPDirectories = GetWebPDirectories(directories);
Models.Exif.ExifDirectoryBase[] exifBaseDirectories = GetExifBaseDirectories(directories);
Models.Exif.GifHeaderDirectory[] gifHeaderDirectories = GetGifHeaderDirectories(directories);
Models.Exif.MakernoteDirectory[] MakernoteDirectories = GetMakernoteDirectories(directories);
Models.Exif.PhotoshopDirectory[] photoshopDirectories = GetPhotoshopDirectories(directories);
Models.Exif.FileMetadataDirectory[] fileMetadataDirectories = GetFileMetadataDirectories(fileInfo.FullName, directories);
Models.Exif.QuickTimeMovieHeaderDirectory[] quickTimeMovieHeaderDirectories = GetQuickTimeMovieHeaderDirectoryDirectories(directories);
Models.Exif.QuickTimeTrackHeaderDirectory[] quickTimeTrackHeaderDirectories = GetQuickTimeTrackHeaderDirectoryDirectories(directories);
result = new(aviDirectories,
exifBaseDirectories,
fileMetadataDirectories,
gifHeaderDirectories,
gpsDirectories,
size?.Height,
id,
jpegDirectories,
MakernoteDirectories,
fileInfo.Name,
photoshopDirectories,
pngDirectories,
quickTimeMovieHeaderDirectories,
quickTimeTrackHeaderDirectories,
webPDirectories,
size?.Width);
return result;
}
internal static Models.Exif.ExifDirectory GetExifDirectory(FileInfo fileInfo)
{
Models.Exif.ExifDirectory result;
int id = 1;
System.Drawing.Size? size;
try
{ size = Dimensions.GetDimensions(fileInfo.FullName); }
catch (Exception)
{ size = null; }
IReadOnlyList<MetadataExtractor.Directory> directories = ImageMetadataReader.ReadMetadata(fileInfo.FullName);
result = Covert(fileInfo, size, id, directories);
return result;
}
}

View File

@ -1,55 +0,0 @@
using File_Folder_Helper.Models;
using File_Folder_Helper.Models.Exif;
using File_Folder_Helper.Models.Face;
using Microsoft.Extensions.Logging;
using System.Collections.ObjectModel;
using System.Text.Json;
namespace File_Folder_Helper.Helpers.Exif;
internal static class HelperExif
{
private static ReadOnlyCollection<FaceFile> GetCollection(ExifDirectoryBase[]? exifDirectoryBases)
{
List<FaceFile> results = [];
if (exifDirectoryBases is not null)
{
string? json;
FaceFile[]? collection;
foreach (ExifDirectoryBase exifDirectoryBase in exifDirectoryBases)
{
json = exifDirectoryBase.Artist;
if (string.IsNullOrEmpty(json))
continue;
collection = JsonSerializer.Deserialize(json, FaceFileCollectionGenerationContext.Default.FaceFileArray);
if (collection is null)
continue;
results.AddRange(collection);
}
}
return new(results);
}
internal static void DragAndDrop(ILogger<Worker> logger, string argZero)
{
FileInfo fileInfo = new(argZero);
logger.LogInformation("<{argZero}> exists", argZero);
ExifDirectory exifDirectory = Exif.GetExifDirectory(fileInfo);
string directory = Path.GetDirectoryName(argZero) ?? throw new Exception();
string[] files = Directory.GetFiles(directory, "*.json", SearchOption.TopDirectoryOnly);
string? json = files.Length != 1 ? string.Empty : File.ReadAllText(files[0]);
ReadOnlyCollection<FaceFile> collection = GetCollection(exifDirectory.ExifBaseDirectories);
logger.LogInformation("<{collection}> value", collection.Count);
Dictionary<long, Person>? people = string.IsNullOrEmpty(json) ? [] : JsonSerializer.Deserialize(json, PeopleSourceGenerationContext.Default.DictionaryInt64Person) ?? throw new NullReferenceException();
logger.LogInformation("<{people}> value", people?.Count);
foreach (FaceFile faceFile in collection)
{
if (faceFile.MappingFromPerson is null)
logger.LogInformation("<{Confidence}> value", faceFile.Location?.Confidence);
else
logger.LogInformation("<{DisplayDirectoryName}> value", faceFile.MappingFromPerson.DisplayDirectoryName);
}
}
}

43
Helpers/ExifHelper.cs Normal file
View File

@ -0,0 +1,43 @@
using File_Folder_Helper.Models;
using Microsoft.Extensions.Logging;
using System.Text.Json;
using Phares.Metadata.Models.Stateless;
using Phares.Shared.Models;
namespace File_Folder_Helper.Helpers;
internal static partial class ExifHelper
{
internal static void DragAndDrop(ILogger<Worker> logger, string argZero)
{
string? json;
string secrets = "L:/Git/AA/Rename/.vscode/.UserSecrets/secrets.json";
json = !File.Exists(secrets) ? null : File.ReadAllText(secrets);
if (string.IsNullOrEmpty(json))
throw new Exception($"Use mklink to map user secrets for rename!");
ResultSettings? resultSettings = JsonSerializer.Deserialize(json, ResultSettingsSourceGenerationContext.Default.ResultSettings) ??
throw new Exception(nameof(ResultSettings));
MetadataSettings? metadataSettings = JsonSerializer.Deserialize(json, MetadataSettingsSourceGenerationContext.Default.MetadataSettings) ??
throw new Exception(nameof(MetadataSettings));
FileInfo fileInfo = new(argZero);
logger.LogInformation("<{argZero}> exists", argZero);
if (!string.IsNullOrEmpty(fileInfo.Directory?.FullName))
{
string[] files = Directory.GetFiles(fileInfo.Directory.FullName, "*.json", SearchOption.TopDirectoryOnly);
json = files.Length != 1 ? string.Empty : File.ReadAllText(files[0]);
Dictionary<long, Person>? people = string.IsNullOrEmpty(json) ? [] : JsonSerializer.Deserialize(json, PeopleSourceGenerationContext.Default.DictionaryInt64Person) ?? throw new NullReferenceException();
logger.LogInformation("<{people}> value", people?.Count);
}
ExifDirectory? exifDirectory = IMetadata.GetExifDirectory(resultSettings, metadataSettings, fileInfo);
FaceFile? faceFile = IMetadata.GetFaceFile(exifDirectory);
if (faceFile is not null)
{
if (faceFile.MappingFromPerson is null)
logger.LogInformation("<{Confidence}> value", faceFile.Location?.Confidence);
else
logger.LogInformation("<{DisplayDirectoryName}> value", faceFile.MappingFromPerson.DisplayDirectoryName);
}
}
}

View File

@ -1,24 +0,0 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace File_Folder_Helper.Models.Exif;
public record AviDirectory(DateTime? DateTimeOriginal,
string? Duration,
string? Height,
string? Width)
{
public override string ToString()
{
string result = JsonSerializer.Serialize(this, AviDirectorySourceGenerationContext.Default.AviDirectory);
return result;
}
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(AviDirectory))]
public partial class AviDirectorySourceGenerationContext : JsonSerializerContext
{
}

View File

@ -1,36 +0,0 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace File_Folder_Helper.Models.Exif;
public record ExifDirectory(AviDirectory[] AviDirectories,
ExifDirectoryBase[] ExifBaseDirectories,
FileMetadataDirectory[] FileMetadataDirectories,
GifHeaderDirectory[] GifHeaderDirectories,
GpsDirectory[] GpsDirectories,
int? Height,
int? Id,
JpegDirectory[] JpegDirectories,
MakernoteDirectory[] MakernoteDirectories,
string OriginalFileName,
PhotoshopDirectory[] PhotoshopDirectories,
PngDirectory[] PngDirectories,
QuickTimeMovieHeaderDirectory[] QuickTimeMovieHeaderDirectories,
QuickTimeTrackHeaderDirectory[] QuickTimeTrackHeaderDirectories,
WebPDirectory[] WebPDirectories,
int? Width)
{
public override string ToString()
{
string result = JsonSerializer.Serialize(this, ExifDirectorySourceGenerationContext.Default.ExifDirectory);
return result;
}
}
[JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]
[JsonSerializable(typeof(ExifDirectory))]
public partial class ExifDirectorySourceGenerationContext : JsonSerializerContext
{
}

View File

@ -1,66 +0,0 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace File_Folder_Helper.Models.Exif;
public record ExifDirectoryBase(string? Aperture,
string? ApplicationNotes,
string? Artist,
string? BitsPerSample,
string? BodySerialNumber,
string? CameraOwnerName,
string? CompressedAverageBitsPerPixel,
string? Compression,
string? Copyright,
DateTime? DateTime,
DateTime? DateTimeDigitized,
DateTime? DateTimeOriginal,
string? DocumentName,
string? ExifVersion,
string? ExposureTime,
string? FileSource,
string? ImageDescription,
string? ImageHeight,
string? ImageNumber,
string? ImageUniqueId,
string? ImageWidth,
string? IsoSpeed,
string? LensMake,
string? LensModel,
string? LensSerialNumber,
string? Make,
string? MakerNote,
string? Model,
string? Orientation,
int? OrientationValue,
string? Rating,
string? RatingPercent,
string? SecurityClassification,
string? ShutterSpeed,
string? Software,
string? TimeZone,
string? TimeZoneDigitized,
string? TimeZoneOriginal,
string? UserComment,
string? WinAuthor,
string? WinComment,
string? WinKeywords,
string? WinSubject,
string? WinTitle,
string? XResolution,
string? YResolution)
{
public override string ToString()
{
string result = JsonSerializer.Serialize(this, ExifDirectoryBaseSourceGenerationContext.Default.ExifDirectoryBase);
return result;
}
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(ExifDirectoryBase))]
public partial class ExifDirectoryBaseSourceGenerationContext : JsonSerializerContext
{
}

View File

@ -1,23 +0,0 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace File_Folder_Helper.Models.Exif;
public record FileMetadataDirectory(DateTime? FileModifiedDate,
string? FileName,
string? FileSize)
{
public override string ToString()
{
string result = JsonSerializer.Serialize(this, FileMetadataDirectorySourceGenerationContext.Default.FileMetadataDirectory);
return result;
}
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(FileMetadataDirectory))]
public partial class FileMetadataDirectorySourceGenerationContext : JsonSerializerContext
{
}

View File

@ -1,22 +0,0 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace File_Folder_Helper.Models.Exif;
public record GifHeaderDirectory(string? ImageHeight,
string? ImageWidth)
{
public override string ToString()
{
string result = JsonSerializer.Serialize(this, GifHeaderDirectorySourceGenerationContext.Default.GifHeaderDirectory);
return result;
}
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(GifHeaderDirectory))]
public partial class GifHeaderDirectorySourceGenerationContext : JsonSerializerContext
{
}

View File

@ -1,26 +0,0 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace File_Folder_Helper.Models.Exif;
public record GpsDirectory(string? Altitude,
string? Latitude,
string? LatitudeRef,
string? Longitude,
string? LongitudeRef,
DateTime? TimeStamp)
{
public override string ToString()
{
string result = JsonSerializer.Serialize(this, GpsDirectorySourceGenerationContext.Default.GpsDirectory);
return result;
}
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(GpsDirectory))]
public partial class GpsDirectorySourceGenerationContext : JsonSerializerContext
{
}

View File

@ -1,22 +0,0 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace File_Folder_Helper.Models.Exif;
public record JpegDirectory(string? ImageHeight,
string? ImageWidth)
{
public override string ToString()
{
string result = JsonSerializer.Serialize(this, JpegDirectorySourceGenerationContext.Default.JpegDirectory);
return result;
}
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(JpegDirectory))]
public partial class JpegDirectorySourceGenerationContext : JsonSerializerContext
{
}

View File

@ -1,23 +0,0 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace File_Folder_Helper.Models.Exif;
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

@ -1,22 +0,0 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace File_Folder_Helper.Models.Exif;
public record PhotoshopDirectory(string? JpegQuality,
string? Url)
{
public override string ToString()
{
string result = JsonSerializer.Serialize(this, PhotoshopDirectorySourceGenerationContext.Default.PhotoshopDirectory);
return result;
}
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(PhotoshopDirectory))]
public partial class PhotoshopDirectorySourceGenerationContext : JsonSerializerContext
{
}

View File

@ -1,23 +0,0 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace File_Folder_Helper.Models.Exif;
public record PngDirectory(string? ImageHeight,
string? ImageWidth,
string? TextualData)
{
public override string ToString()
{
string result = JsonSerializer.Serialize(this, PngDirectorySourceGenerationContext.Default.PngDirectory);
return result;
}
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(PngDirectory))]
public partial class PngDirectorySourceGenerationContext : JsonSerializerContext
{
}

View File

@ -1,21 +0,0 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace File_Folder_Helper.Models.Exif;
public record QuickTimeMovieHeaderDirectory(DateTime? Created)
{
public override string ToString()
{
string result = JsonSerializer.Serialize(this, QuickTimeMovieHeaderDirectorySourceGenerationContext.Default.QuickTimeMovieHeaderDirectory);
return result;
}
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(QuickTimeMovieHeaderDirectory))]
public partial class QuickTimeMovieHeaderDirectorySourceGenerationContext : JsonSerializerContext
{
}

View File

@ -1,21 +0,0 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace File_Folder_Helper.Models.Exif;
public record QuickTimeTrackHeaderDirectory(DateTime? Created)
{
public override string ToString()
{
string result = JsonSerializer.Serialize(this, QuickTimeTrackHeaderDirectorySourceGenerationContext.Default.QuickTimeTrackHeaderDirectory);
return result;
}
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(QuickTimeTrackHeaderDirectory))]
public partial class QuickTimeTrackHeaderDirectorySourceGenerationContext : JsonSerializerContext
{
}

View File

@ -1,22 +0,0 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace File_Folder_Helper.Models.Exif;
public record WebPDirectory(string? ImageHeight,
string? ImageWidth)
{
public override string ToString()
{
string result = JsonSerializer.Serialize(this, WebPDirectorySourceGenerationContext.Default.WebPDirectory);
return result;
}
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(WebPDirectory))]
public partial class WebPDirectorySourceGenerationContext : JsonSerializerContext
{
}

View File

@ -1,11 +0,0 @@
using System.Text.Json.Serialization;
namespace File_Folder_Helper.Models.Face;
public record FaceEncoding(double[] RawEncoding, int Size);
[JsonSourceGenerationOptions(WriteIndented = false)]
[JsonSerializable(typeof(FaceEncoding))]
public partial class FaceEncodingGenerationContext : JsonSerializerContext
{
}

View File

@ -1,33 +0,0 @@
using System.Text.Json.Serialization;
namespace File_Folder_Helper.Models.Face;
public record FaceFile(int? AreaPermyriad,
int? ConfidencePercent,
string? DMS,
DateTime DateTime,
FaceEncoding? FaceEncoding,
Dictionary<FacePart, FacePoint[]>? FaceParts,
Location? Location,
string? Maker,
MappingFromPerson? MappingFromPerson,
string? Model,
OutputResolution? OutputResolution);
[JsonSourceGenerationOptions(WriteIndented = false)]
[JsonSerializable(typeof(FaceFile))]
public partial class FaceFileGenerationContext : JsonSerializerContext
{
}
[JsonSourceGenerationOptions(WriteIndented = false, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]
[JsonSerializable(typeof(FaceFile[]))]
public partial class FaceFileCollectionGenerationContext : JsonSerializerContext
{
}
[JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]
[JsonSerializable(typeof(FaceFile[]))]
public partial class FaceFileCollectionWriteIndentedGenerationContext : JsonSerializerContext
{
}

View File

@ -1,54 +0,0 @@
namespace File_Folder_Helper.Models.Face;
/// <summary>
/// Specifies the part of face.
/// </summary>
public enum FacePart
{
/// <summary>
/// Specifies the chin.
/// </summary>
Chin = 0,
/// <summary>
/// Specifies the left eyebrow.
/// </summary>
LeftEyebrow = 17,
/// <summary>
/// Specifies the right eyebrow.
/// </summary>
RightEyebrow = 22,
/// <summary>
/// Specifies the nose bridge.
/// </summary>
NoseBridge = 27,
/// <summary>
/// Specifies the nose tip.
/// </summary>
NoseTip = 31,
/// <summary>
/// Specifies the left eye.
/// </summary>
LeftEye = 36,
/// <summary>
/// Specifies the right eye.
/// </summary>
RightEye = 42,
/// <summary>
/// Specifies the top lip.
/// </summary>
TopLip = 48,
/// <summary>
/// Specifies the bottom lip.
/// </summary>
BottomLip = 55
}

View File

@ -1,39 +0,0 @@
using System.Drawing;
using System.Text.Json.Serialization;
namespace File_Folder_Helper.Models.Face;
[method: JsonConstructor]
public class FacePoint(int index, int x, int y)
{
public int Index { get; } = index;
public int X { get; } = x;
public int Y { get; } = y;
private readonly Point _Point = new(x, y);
public override bool Equals(object? obj) => obj is FacePoint point && Equals(point);
#pragma warning disable IDE0070
public override int GetHashCode()
#pragma warning restore IDE0070
{
int hashCode = 1861411795;
hashCode = (hashCode * -1521134295) + _Point.GetHashCode();
hashCode = (hashCode * -1521134295) + Index.GetHashCode();
return hashCode;
}
public bool Equals(FacePoint? facePoint)
{
return facePoint is not null
&& X == facePoint.X
&& Y == facePoint.Y
&& Index == facePoint.Index;
}
public static bool operator ==(FacePoint point1, FacePoint point2) => point1.Equals(point2);
public static bool operator !=(FacePoint point1, FacePoint point2) => !(point1 == point2);
}

View File

@ -1,42 +0,0 @@
using System.Text.Json.Serialization;
namespace File_Folder_Helper.Models.Face;
[method: JsonConstructor]
public class Location(int bottom, double confidence, int left, int right, int top) : IEquatable<Location>
{
public int Bottom { init; get; } = bottom;
public double Confidence { init; get; } = confidence;
public int Left { init; get; } = left;
public int Right { init; get; } = right;
public int Top { init; get; } = top;
public override bool Equals(object? obj) => Equals(obj as Location);
#pragma warning disable IDE0070
public override int GetHashCode()
#pragma warning restore IDE0070
{
int hashCode = -773114317;
hashCode = (hashCode * -1521134295) + Bottom.GetHashCode();
hashCode = (hashCode * -1521134295) + Left.GetHashCode();
hashCode = (hashCode * -1521134295) + Right.GetHashCode();
hashCode = (hashCode * -1521134295) + Top.GetHashCode();
return hashCode;
}
public bool Equals(Location? location)
{
return location is not null
&& Bottom == location.Bottom
&& Left == location.Left
&& Right == location.Right
&& Top == location.Top;
}
public static bool operator ==(Location location1, Location location2) => EqualityComparer<Location>.Default.Equals(location1, location2);
public static bool operator !=(Location location1, Location location2) => !(location1 == location2);
}

View File

@ -1,24 +0,0 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace File_Folder_Helper.Models.Face;
public record MappingFromPerson(int? ApproximateYears,
string DisplayDirectoryName,
long PersonKey,
string SegmentB)
{
public override string ToString()
{
string result = JsonSerializer.Serialize(this, MappingFromPersonGenerationContext.Default.MappingFromPerson);
return result;
}
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(MappingFromPerson))]
public partial class MappingFromPersonGenerationContext : JsonSerializerContext
{
}

View File

@ -1,5 +0,0 @@
namespace File_Folder_Helper.Models.Face;
public record OutputResolution(int Height,
int Orientation,
int Width);

View File

@ -235,7 +235,7 @@ public class Worker : BackgroundService
} }
} }
else if (_AppSettings.ValidImageFormatExtensions.Contains(extension) && File.Exists(_Args[0])) else if (_AppSettings.ValidImageFormatExtensions.Contains(extension) && File.Exists(_Args[0]))
Helpers.Exif.HelperExif.DragAndDrop(_Logger, _Args[0]); Helpers.ExifHelper.DragAndDrop(_Logger, _Args[0]);
else else
throw new Exception(_Args[0]); throw new Exception(_Args[0]);
} }