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

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.CA2253.severity = none # Question - Named placeholders should not be numeric values
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.CS0612.severity = none # Question - is obsolete
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.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.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.IDE0005.severity = none # Question - Remove unnecessary using directives
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.IDE0058.severity = none # Question - Remove unnecessary expression value
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.IDE0065.severity = none # Question -
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) {
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 searchPattern = args[2];
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) {
RecordB? result = null;
string line;
RecordB record;
LineCheck lineCheck;
List<string> collection = [];
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) {
Status status;
Record? record;
List<string[]> messages;
logger.LogInformation(args[0]);
logger.LogInformation(args[1]);
logger.LogInformation(args[2]);

View File

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

View File

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

View File

@ -1,4 +1,3 @@
using System.Drawing;
using Microsoft.Extensions.Logging;
@ -94,19 +93,23 @@ internal static partial class Helper20250705 {
height: rectangleFHeight,
left: rectangleFLeft,
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) {
#if SystemDrawingCommon
RectangleF rectangle = new((float)left, (float)top, width, height);
using (Bitmap source = new(file)) {
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);
}
bitmap.Save($"{file}{suffix}");
}
}
#endif
}
}

View File

@ -154,7 +154,6 @@ internal static partial class Helper20250709 {
logger.LogInformation(args[3]);
logger.LogInformation(args[4]);
logger.LogInformation(args[5]);
string[] segments;
string extension = args[4];
string searchPattern = args[2];
int sizeFilter = int.Parse(args[3]);
@ -162,13 +161,12 @@ internal static partial class Helper20250709 {
string sourceDirectory = Path.GetFullPath(args[0].Split('~')[0]);
string destinationDirectory = Path.GetFullPath(args[6].Split('~')[0]);
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);
}
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;
string text;
string json;
string[] files;
string checkFile;

View File

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