Compare commits

..

No commits in common. "04-06-c" and "master" have entirely different histories.

41 changed files with 1800 additions and 2218 deletions

2
.gitignore vendored
View File

@ -475,4 +475,4 @@ Rename/.vscode/.UserSecrets/secrets.json
Rename/.vscode/.UserSecretsOld/rename.json
Rename/.vscode/.UserSecretsOld/secrets.json
Windows/.vscode/.UserSecrets/secrets.json
Windows/.vscode/.7-Question
Windows/.vscode/.iCloudPhotos2025

14
.vscode/tasks.json vendored
View File

@ -628,19 +628,5 @@
],
"problemMatcher": "$msCompile"
},
{
"label": "File-Folder-Helper AOT s X Day-Helper-2025-03-20",
"type": "shell",
"command": "L:/DevOps/Mesa_FI/File-Folder-Helper/bin/Release/net8.0/win-x64/publish/File-Folder-Helper.exe",
"args": [
"s",
"X",
"L:/Git/AA",
"Day-Helper-2025-03-20",
"false",
"4"
],
"problemMatcher": []
}
]
}

View File

@ -7,12 +7,9 @@
<TargetFramework>net9.0</TargetFramework>
<UserSecretsId>770b6ae3-266e-4d5f-970a-173709b064de</UserSecretsId>
</PropertyGroup>
<PropertyGroup>
<HoursSinceNovember122024>$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1731369600)), 3600))))</HoursSinceNovember122024>
</PropertyGroup>
<PropertyGroup>
<PackageId>Phares.View.by.Distance.Compare</PackageId>
<Version>9.0.104.$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1731369600)), 3600))))</Version>
<Version>8.0.112.0</Version>
<Company>Phares</Company>
<Authors>Mike Phares</Authors>
<IncludeSymbols>true</IncludeSymbols>
@ -39,10 +36,10 @@
<SupportedPlatform Include="browser" />
</ItemGroup>
<ItemGroup>
<None Include=".vscode\read-me.md" Pack="true" PackagePath="\" />
<None Include=".vscode\read-me.md" Pack="true" PackagePath="\"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="runtime.win-x64.Microsoft.DotNet.ILCompiler" Version="8.0.14" />
<PackageReference Include="runtime.win-x64.Microsoft.DotNet.ILCompiler" Version="8.0.12" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.1" />

View File

@ -17,21 +17,6 @@ public partial class Compare : ICompare, IDisposable
private ProgressBar? _ProgressBar;
private readonly ProgressBarOptions _ProgressBarOptions;
void ICompare.Tick() =>
_ProgressBar?.Tick();
void IDisposable.Dispose()
{
_ProgressBar?.Dispose();
GC.SuppressFinalize(this);
}
void ICompare.ConstructProgressBar(int maxTicks, string message)
{
_ProgressBar?.Dispose();
_ProgressBar = new(maxTicks, message, _ProgressBarOptions);
}
public Compare(List<string> args, ILogger<Program>? logger, AppSettings appSettings, bool isSilent, IConsole console)
{
if (isSilent)
@ -46,46 +31,19 @@ public partial class Compare : ICompare, IDisposable
CompareWork(logger, appSettings, compare, ticks);
}
private void CompareWork(ILogger<Program>? logger, AppSettings appSettings, ICompare compare, long ticks)
void ICompare.Tick() =>
_ProgressBar?.Tick();
void ICompare.ConstructProgressBar(int maxTicks, string message)
{
const int updated = 0;
DistanceLimits? distanceLimits;
logger?.LogInformation("{Ticks}", ticks);
ReadOnlyCollection<LocationContainer> matrix;
ReadOnlyCollection<SaveContainer> saveContainers;
ReadOnlyCollection<ExifDirectory> exifDirectories;
ReadOnlyCollection<LocationContainer> preFiltered;
ReadOnlyCollection<LocationContainer> postFiltered;
ReadOnlyDictionary<string, LocationContainer> onlyOne;
bool runToDoCollectionFirst = GetRunToDoCollectionFirst(appSettings, ticks);
ReadOnlyCollections readOnlyCollections = GetReadOnlyCollections(appSettings);
ReadOnlyCollection<ExifDirectory> mappedExifDirectoryWithEncoding = GetMappedExifDirectoryWithEncoding(appSettings, compare, ticks, readOnlyCollections);
ReadOnlyDictionary<int, ReadOnlyDictionary<int, FilePath>> keyValuePairs = IDistance.Extract(appSettings.CompareSettings, mappedExifDirectoryWithEncoding);
foreach (string outputResolution in appSettings.CompareSettings.OutputResolutions)
{
if (runToDoCollectionFirst || outputResolution.Any(char.IsNumber))
continue;
_ProgressBar?.Dispose();
logger?.LogInformation("{outputResolution}", outputResolution);
exifDirectories = IFace.GetExifDirectories(appSettings.ResultSettings, appSettings.MetadataSettings, appSettings.DistanceSettings, appSettings.CompareSettings, compare, ticks, outputResolution);
preFiltered = IDistance.GetPreFilterLocationContainer(appSettings.DistanceSettings, appSettings.CompareSettings, compare, ticks, readOnlyCollections, keyValuePairs, exifDirectories);
if (preFiltered.Count == 0)
continue;
distanceLimits = new(appSettings.DistanceSettings);
postFiltered = IDistance.GetPostFilterLocationContainer(preFiltered, distanceLimits);
if (postFiltered.Count == 0)
continue;
matrix = IDistance.GetMatrixLocationContainers(appSettings.DistanceSettings, appSettings.CompareSettings, compare, ticks, mappedExifDirectoryWithEncoding, distanceLimits, postFiltered);
if (matrix.Count == 0)
continue;
onlyOne = IDistance.GetOnlyOne(appSettings.DistanceSettings, matrix);
if (onlyOne.Count == 0)
continue;
saveContainers = IDistance.GetSaveContainers(appSettings.ResultSettings, appSettings.DistanceSettings, appSettings.CompareSettings, compare, ticks, outputResolution, onlyOne);
if (saveContainers.Count == 0)
continue;
IDistance.SaveContainers(appSettings.DistanceSettings, appSettings.CompareSettings, compare, ticks, updated, saveContainers);
}
_ProgressBar?.Dispose();
_ProgressBar = new(maxTicks, message, _ProgressBarOptions);
}
void IDisposable.Dispose()
{
_ProgressBar?.Dispose();
GC.SuppressFinalize(this);
}
private static bool GetRunToDoCollectionFirst(AppSettings appSettings, long ticks)
@ -165,4 +123,46 @@ public partial class Compare : ICompare, IDisposable
return results;
}
private void CompareWork(ILogger<Program>? logger, AppSettings appSettings, ICompare compare, long ticks)
{
const int updated = 0;
DistanceLimits? distanceLimits;
logger?.LogInformation("{Ticks}", ticks);
ReadOnlyCollection<LocationContainer> matrix;
ReadOnlyCollection<SaveContainer> saveContainers;
ReadOnlyCollection<ExifDirectory> exifDirectories;
ReadOnlyCollection<LocationContainer> preFiltered;
ReadOnlyCollection<LocationContainer> postFiltered;
ReadOnlyDictionary<string, LocationContainer> onlyOne;
bool runToDoCollectionFirst = GetRunToDoCollectionFirst(appSettings, ticks);
ReadOnlyCollections readOnlyCollections = GetReadOnlyCollections(appSettings);
ReadOnlyCollection<ExifDirectory> mappedExifDirectoryWithEncoding = GetMappedExifDirectoryWithEncoding(appSettings, compare, ticks, readOnlyCollections);
ReadOnlyDictionary<int, ReadOnlyDictionary<int, FilePath>> keyValuePairs = IDistance.Extract(appSettings.CompareSettings, mappedExifDirectoryWithEncoding);
foreach (string outputResolution in appSettings.CompareSettings.OutputResolutions)
{
if (runToDoCollectionFirst || outputResolution.Any(char.IsNumber))
continue;
_ProgressBar?.Dispose();
logger?.LogInformation("{outputResolution}", outputResolution);
exifDirectories = IFace.GetExifDirectories(appSettings.ResultSettings, appSettings.MetadataSettings, appSettings.DistanceSettings, appSettings.CompareSettings, compare, ticks, outputResolution);
preFiltered = IDistance.GetPreFilterLocationContainer(appSettings.DistanceSettings, appSettings.CompareSettings, compare, ticks, readOnlyCollections, keyValuePairs, exifDirectories);
if (preFiltered.Count == 0)
continue;
distanceLimits = new(appSettings.DistanceSettings);
postFiltered = IDistance.GetPostFilterLocationContainer(preFiltered, distanceLimits);
if (postFiltered.Count == 0)
continue;
matrix = IDistance.GetMatrixLocationContainers(appSettings.DistanceSettings, appSettings.CompareSettings, compare, ticks, mappedExifDirectoryWithEncoding, distanceLimits, postFiltered);
if (matrix.Count == 0)
continue;
onlyOne = IDistance.GetOnlyOne(appSettings.DistanceSettings, matrix);
if (onlyOne.Count == 0)
continue;
saveContainers = IDistance.GetSaveContainers(appSettings.ResultSettings, appSettings.DistanceSettings, appSettings.CompareSettings, compare, ticks, outputResolution, onlyOne);
if (saveContainers.Count == 0)
continue;
IDistance.SaveContainers(appSettings.DistanceSettings, appSettings.CompareSettings, compare, ticks, updated, saveContainers);
}
}
}

View File

@ -20,16 +20,16 @@ public record AppSettings(ResultSettings ResultSettings,
private static void Verify(AppSettings appSettings)
{
if (appSettings.DistanceSettings.RangeDaysDeltaTolerance.Length is not 3 and not 6)
if (appSettings.DistanceSettings.RangeDaysDeltaTolerance.Length != 3)
throw new NullReferenceException(nameof(appSettings.DistanceSettings.RangeDaysDeltaTolerance));
if (appSettings.DistanceSettings.RangeDistanceTolerance.Length is not 3 and not 6)
if (appSettings.DistanceSettings.RangeDistanceTolerance.Length != 3)
throw new NullReferenceException(nameof(appSettings.DistanceSettings.RangeDistanceTolerance));
if (appSettings.DistanceSettings.RangeFaceAreaTolerance.Length is not 3 and not 6)
if (appSettings.DistanceSettings.RangeFaceAreaTolerance.Length != 3)
throw new NullReferenceException(nameof(appSettings.DistanceSettings.RangeFaceAreaTolerance));
if (appSettings.DistanceSettings.RangeFaceConfidence.Length is not 3 and not 6)
if (appSettings.DistanceSettings.RangeFaceConfidence.Length != 3)
throw new NullReferenceException(nameof(appSettings.DistanceSettings.RangeFaceConfidence));
_ = DateTime.Now.AddDays(-appSettings.DistanceSettings.RangeDaysDeltaTolerance[1]);
if (appSettings.DistanceSettings.SaveSortingWithoutPerson && appSettings.PeopleSettings.JLinks.Where(l => !string.IsNullOrEmpty(l)).Any())
if (appSettings.DistanceSettings.SaveSortingWithoutPerson && appSettings.PeopleSettings.JLinks.Length > 0)
throw new Exception("Settings has SaveSortingWithoutPerson and JLinks!");
if (appSettings.DistanceSettings.SaveSortingWithoutPerson && !string.IsNullOrEmpty(appSettings.DistanceSettings.FocusModel))
throw new Exception("Settings has SaveSortingWithoutPerson and FocusModel!");

View File

@ -8,8 +8,11 @@ public record CompareSettings(string Company,
string FacesFileNameExtension,
string FacesHiddenFileNameExtension,
string FacesPartsFileNameExtension,
string[] IgnoreExtensions,
int MaxDegreeOfParallelism,
string[] OutputResolutions) : Shared.Models.Properties.ICompareSettings
string[] OutputResolutions,
string[] ValidImageFormatExtensions,
string[] ValidVideoFormatExtensions) : Shared.Models.Properties.ICompareSettings
{
public override string ToString()

View File

@ -6,12 +6,9 @@
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<TargetFramework>net9.0</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<HoursSinceNovember122024>$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1731369600)), 3600))))</HoursSinceNovember122024>
</PropertyGroup>
<PropertyGroup>
<PackageId>Phares.AA.Distance</PackageId>
<Version>9.0.104.$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1731369600)), 3600))))</Version>
<Version>8.0.112.0</Version>
<Company>Phares</Company>
<Authors>Mike Phares</Authors>
<IncludeSymbols>true</IncludeSymbols>

View File

@ -11,21 +11,11 @@ namespace View_by_Distance.Distance.Models.Stateless;
internal static class FilterLogicD
{
internal static string GetResizeContentDirectory(ResultSettings resultSettings, string cContentDirectory, FilePath filePath)
{
string result;
CombinedEnumAndIndex cei = IPath.GetCombinedEnumAndIndex(resultSettings, filePath);
result = Path.Combine(cContentDirectory, cei.Combined);
return result;
}
internal record RecordA(long? Ticks, string? Directory);
internal static string GetFacePartsDirectoryX(ResultSettings resultSettings, string d2FacePartsContentDirectory, FilePath filePath)
{
string result;
CombinedEnumAndIndex cei = IPath.GetCombinedEnumAndIndex(resultSettings, filePath);
result = Path.Combine(d2FacePartsContentDirectory, cei.Combined, filePath.NameWithoutExtension);
return result;
}
internal record RecordB(string ByValue, bool IsByMapping, bool IsBySorting);
internal record RecordC(string? DebugDirectory, string? Directory, long? Ticks, string? PersonDirectory);
internal static RecordB Get(int? useFiltersCounter, bool saveIndividually, bool sortingContainersAny, string forceSingleImageHumanized, int? distancePermyriad, int? by, string? displayDirectoryName)
{
@ -49,12 +39,141 @@ internal static class FilterLogicD
else if (isBySorting && useFiltersCounter.HasValue)
byValue = $"{nameof(IMapLogic.Sorting)}{(!isDefaultName ? "-A" : "-Z")} Modified Filters - {useFiltersCounter.Value}";
else
byValue = IDistance.Get(saveIndividually, forceSingleImageHumanized, by.Value, isDefaultName);
{
byValue = $"{by.Value switch
{
IMapLogic.Mapping => nameof(IMapLogic.Mapping),
IMapLogic.Sorting => saveIndividually ? nameof(IMapLogic.Individually) : nameof(IMapLogic.Sorting),
IMapLogic.ForceSingleImage => forceSingleImageHumanized,
_ => throw new NotImplementedException()
}}{(!isDefaultName ? "-A" : "-Z")}";
}
}
result = new(byValue, isByMapping, isBySorting);
return result;
}
private static RecordB Get(int? useFiltersCounter, bool saveIndividually, bool sortingContainersAny, string forceSingleImageHumanized, int? distancePermyriad, PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName? personKeyFormattedAndKeyTicksAndDisplayDirectoryName) =>
Get(useFiltersCounter, saveIndividually, sortingContainersAny, forceSingleImageHumanized, distancePermyriad, personKeyFormattedAndKeyTicksAndDisplayDirectoryName is null ? null : IMapLogic.Mapping, personKeyFormattedAndKeyTicksAndDisplayDirectoryName?.DisplayDirectoryName);
private static RecordC Get(string eDistanceContentTicksDirectory, RecordB recordB, PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName? personKeyFormattedAndKeyTicksAndDisplayDirectoryName, string segmentB)
{
RecordC result;
if (string.IsNullOrEmpty(personKeyFormattedAndKeyTicksAndDisplayDirectoryName?.DisplayDirectoryName))
throw new NotImplementedException();
long? ticks = null;
string? debugDirectory = Path.Combine(eDistanceContentTicksDirectory, recordB.ByValue, personKeyFormattedAndKeyTicksAndDisplayDirectoryName.KeyFormatted, personKeyFormattedAndKeyTicksAndDisplayDirectoryName.DisplayDirectoryName);
string? directory = Path.Combine(eDistanceContentTicksDirectory, recordB.ByValue, personKeyFormattedAndKeyTicksAndDisplayDirectoryName.KeyFormatted, segmentB);
string? personDirectory = Path.Combine(directory, personKeyFormattedAndKeyTicksAndDisplayDirectoryName.DisplayDirectoryName, "lnk");
result = new(debugDirectory, directory, ticks, personDirectory);
return result;
}
internal static string GetResizeContentDirectory(ResultSettings resultSettings, string cContentDirectory, FilePath filePath)
{
string result;
(string directoryName, _) = IPath.GetDirectoryNameAndIndex(resultSettings, filePath);
result = Path.Combine(cContentDirectory, directoryName);
return result;
}
internal static string GetFacePartsDirectoryX(ResultSettings resultSettings, string d2FacePartsContentDirectory, FilePath filePath)
{
string result;
(string directoryName, _) = IPath.GetDirectoryNameAndIndex(resultSettings, filePath);
result = Path.Combine(d2FacePartsContentDirectory, directoryName, filePath.NameWithoutExtension);
return result;
}
internal static ReadOnlyCollection<SaveContainer> GetSaveContainers(ResultSettings resultSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, string outputResolution, ReadOnlyDictionary<string, LocationContainer> onlyOne)
{
List<SaveContainer> results = [];
RecordB recordB;
RecordC recordC;
string segmentB;
string checkFile;
string? directory;
string shortcutFile;
string facesDirectory;
bool isCounterPersonYear;
string facePartsDirectory;
FileHolder? faceFileHolder;
SaveContainer? saveContainer;
FileHolder? resizedFileHolder;
int? useFiltersCounter = null;
string resizeContentDirectory;
FileHolder? facePartsFileHolder;
FileHolder? hiddenFaceFileHolder;
LocationContainer locationContainer;
bool sortingContainersAny = onlyOne.Count > 0;
string eResultsFullGroupDirectory = IResult.GetResultsDateGroupDirectory(resultSettings,
nameof(E_Distance),
resultSettings.ResultContent);
string cResultsFullGroupDirectory = IResult.GetResultsFullGroupDirectory(resultSettings,
nameof(C_Resize),
outputResolution,
includeResizeGroup: true,
includeModel: false,
includePredictorModel: false);
string d2ResultsFullGroupDirectory = IResult.GetResultsFullGroupDirectory(resultSettings,
nameof(D2_FaceParts),
outputResolution,
includeResizeGroup: true,
includeModel: true,
includePredictorModel: true);
string cContentDirectory = Path.Combine(cResultsFullGroupDirectory, resultSettings.ResultContent);
if (!Directory.Exists(cContentDirectory))
_ = Directory.CreateDirectory(cContentDirectory);
string eDistanceContentTicksDirectory = Path.Combine(eResultsFullGroupDirectory, ticks.ToString());
if (!Directory.Exists(eDistanceContentTicksDirectory))
_ = Directory.CreateDirectory(eDistanceContentTicksDirectory);
string d2FacePartsContentDirectory = Path.Combine(d2ResultsFullGroupDirectory, resultSettings.ResultContent);
if (!Directory.Exists(d2FacePartsContentDirectory))
_ = Directory.CreateDirectory(d2FacePartsContentDirectory);
string forceSingleImageHumanized = nameof(IMapLogic.ForceSingleImage).Humanize(LetterCasing.Title);
string message = $") Building Save Container Collection - {(int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds)} total second(s)";
compare.ConstructProgressBar(onlyOne.Count, message);
foreach (KeyValuePair<string, LocationContainer> keyValuePair in onlyOne)
{
if (distanceSettings.SaveIndividually)
break;
locationContainer = keyValuePair.Value;
if (locationContainer.LengthPermyriad is null || locationContainer.LengthSource is null)
continue;
segmentB = locationContainer.LengthPermyriad.Value.ToString().PadLeft(2, '0')[..2];
if (locationContainer.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName is null)
continue;
isCounterPersonYear = IPersonBirthday.IsCounterPersonYear(locationContainer.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName.KeyTicks);
recordB = Get(useFiltersCounter, distanceSettings.SaveIndividually, sortingContainersAny, forceSingleImageHumanized, locationContainer.LengthPermyriad, locationContainer.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName);
recordC = Get(eDistanceContentTicksDirectory, recordB, locationContainer.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName, segmentB);
if (string.IsNullOrEmpty(recordC.Directory) || string.IsNullOrEmpty(recordC.PersonDirectory))
continue;
directory = recordC.Directory;
if (!string.IsNullOrEmpty(recordC.DebugDirectory))
results.Add(SaveContainer.Get(recordC.DebugDirectory));
if (locationContainer.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName is null)
{
if (!distanceSettings.SaveSortingWithoutPerson)
throw new NotSupportedException();
if (recordC.Ticks is null)
continue;
}
results.Add(SaveContainer.Get(recordC.PersonDirectory));
facesDirectory = locationContainer.LengthSource.DirectoryFullPath;
faceFileHolder = FileHolder.Get(locationContainer.LengthSource.FullName);
checkFile = Path.Combine(directory, $"{locationContainer.LengthSource.Name}");
shortcutFile = Path.Combine(recordC.PersonDirectory, $"{locationContainer.LengthSource.Name}.lnk");
resizeContentDirectory = GetResizeContentDirectory(resultSettings, cContentDirectory, locationContainer.LengthSource);
facePartsDirectory = GetFacePartsDirectoryX(resultSettings, d2FacePartsContentDirectory, locationContainer.LengthSource);
hiddenFaceFileHolder = FileHolder.Get(Path.Combine(facesDirectory, $"{locationContainer.LengthSource.NameWithoutExtension}{compareSettings.FacesHiddenFileNameExtension}"));
facePartsFileHolder = FileHolder.Get(Path.Combine(facePartsDirectory, $"{locationContainer.LengthSource.NameWithoutExtension}{compareSettings.FacesPartsFileNameExtension}"));
resizedFileHolder = FileHolder.Get(Path.Combine(resizeContentDirectory, $"{locationContainer.LengthSource.FileNameFirstSegment}{Path.GetExtension(locationContainer.LengthSource.NameWithoutExtension)}"));
saveContainer = SaveContainer.Get(checkFile, directory, faceFileHolder, hiddenFaceFileHolder, facePartsFileHolder, resizedFileHolder, shortcutFile);
results.Add(saveContainer);
}
return results.AsReadOnly();
}
internal static void SaveContainers(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, int? updated, ReadOnlyCollection<SaveContainer> saveContainers)
{
string fileName;
@ -166,115 +285,4 @@ internal static class FilterLogicD
}
}
internal static ReadOnlyCollection<SaveContainer> GetSaveContainers(ResultSettings resultSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, string outputResolution, ReadOnlyDictionary<string, LocationContainer> onlyOne)
{
List<SaveContainer> results = [];
RecordB recordB;
RecordC recordC;
string segmentB;
string checkFile;
string? directory;
string shortcutFile;
string facesDirectory;
bool isCounterPersonYear;
string facePartsDirectory;
FileHolder? faceFileHolder;
SaveContainer? saveContainer;
FileHolder? resizedFileHolder;
int? useFiltersCounter = null;
string resizeContentDirectory;
FileHolder? facePartsFileHolder;
FileHolder? hiddenFaceFileHolder;
LocationContainer locationContainer;
bool sortingContainersAny = onlyOne.Count > 0;
string eResultsFullGroupDirectory = IResult.GetResultsDateGroupDirectory(resultSettings,
nameof(E_Distance),
resultSettings.ResultContent);
string cResultsFullGroupDirectory = IResult.GetResultsFullGroupDirectory(resultSettings,
nameof(C_Resize),
outputResolution,
includeResizeGroup: true,
includeModel: false,
includePredictorModel: false);
string d2ResultsFullGroupDirectory = IResult.GetResultsFullGroupDirectory(resultSettings,
nameof(D2_FaceParts),
outputResolution,
includeResizeGroup: true,
includeModel: true,
includePredictorModel: true);
string cContentDirectory = Path.Combine(cResultsFullGroupDirectory, resultSettings.ResultContent);
if (!Directory.Exists(cContentDirectory))
_ = Directory.CreateDirectory(cContentDirectory);
string eDistanceContentTicksDirectory = Path.Combine(eResultsFullGroupDirectory, ticks.ToString());
if (!Directory.Exists(eDistanceContentTicksDirectory))
_ = Directory.CreateDirectory(eDistanceContentTicksDirectory);
string d2FacePartsContentDirectory = Path.Combine(d2ResultsFullGroupDirectory, resultSettings.ResultContent);
if (!Directory.Exists(d2FacePartsContentDirectory))
_ = Directory.CreateDirectory(d2FacePartsContentDirectory);
string forceSingleImageHumanized = nameof(IMapLogic.ForceSingleImage).Humanize(LetterCasing.Title);
string message = $") Building Save Container Collection - {(int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds)} total second(s)";
compare.ConstructProgressBar(onlyOne.Count, message);
foreach (KeyValuePair<string, LocationContainer> keyValuePair in onlyOne)
{
if (distanceSettings.SaveIndividually)
break;
locationContainer = keyValuePair.Value;
if (locationContainer.LengthPermyriad is null || locationContainer.LengthSource is null)
continue;
segmentB = locationContainer.LengthPermyriad.Value.ToString().PadLeft(2, '0')[..2];
if (locationContainer.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName is null)
continue;
isCounterPersonYear = IPersonBirthday.IsCounterPersonYear(locationContainer.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName.KeyTicks);
recordB = Get(useFiltersCounter, distanceSettings.SaveIndividually, sortingContainersAny, forceSingleImageHumanized, locationContainer.LengthPermyriad, locationContainer.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName);
recordC = Get(eDistanceContentTicksDirectory, recordB, locationContainer.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName, segmentB);
if (string.IsNullOrEmpty(recordC.Directory) || string.IsNullOrEmpty(recordC.PersonDirectory))
continue;
directory = recordC.Directory;
if (!string.IsNullOrEmpty(recordC.DebugDirectory))
results.Add(SaveContainer.Get(recordC.DebugDirectory));
if (locationContainer.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName is null)
{
if (!distanceSettings.SaveSortingWithoutPerson)
throw new NotSupportedException();
if (recordC.Ticks is null)
continue;
}
results.Add(SaveContainer.Get(recordC.PersonDirectory));
facesDirectory = locationContainer.LengthSource.DirectoryFullPath;
faceFileHolder = FileHolder.Get(locationContainer.LengthSource.FullName);
checkFile = Path.Combine(directory, $"{locationContainer.LengthSource.Name}");
shortcutFile = Path.Combine(recordC.PersonDirectory, $"{locationContainer.LengthSource.Name}.lnk");
resizeContentDirectory = GetResizeContentDirectory(resultSettings, cContentDirectory, locationContainer.LengthSource);
facePartsDirectory = GetFacePartsDirectoryX(resultSettings, d2FacePartsContentDirectory, locationContainer.LengthSource);
hiddenFaceFileHolder = FileHolder.Get(Path.Combine(facesDirectory, $"{locationContainer.LengthSource.NameWithoutExtension}{compareSettings.FacesHiddenFileNameExtension}"));
facePartsFileHolder = FileHolder.Get(Path.Combine(facePartsDirectory, $"{locationContainer.LengthSource.NameWithoutExtension}{compareSettings.FacesPartsFileNameExtension}"));
resizedFileHolder = FileHolder.Get(Path.Combine(resizeContentDirectory, $"{locationContainer.LengthSource.FileNameFirstSegment}{Path.GetExtension(locationContainer.LengthSource.NameWithoutExtension)}"));
saveContainer = SaveContainer.Get(checkFile, directory, faceFileHolder, hiddenFaceFileHolder, facePartsFileHolder, resizedFileHolder, shortcutFile);
results.Add(saveContainer);
}
return results.AsReadOnly();
}
private static RecordB Get(int? useFiltersCounter, bool saveIndividually, bool sortingContainersAny, string forceSingleImageHumanized, int? distancePermyriad, PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName? personKeyFormattedAndKeyTicksAndDisplayDirectoryName) =>
Get(useFiltersCounter, saveIndividually, sortingContainersAny, forceSingleImageHumanized, distancePermyriad, personKeyFormattedAndKeyTicksAndDisplayDirectoryName is null ? null : IMapLogic.Mapping, personKeyFormattedAndKeyTicksAndDisplayDirectoryName?.DisplayDirectoryName);
private static RecordC Get(string eDistanceContentTicksDirectory, RecordB recordB, PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName? personKeyFormattedAndKeyTicksAndDisplayDirectoryName, string segmentB)
{
RecordC result;
if (string.IsNullOrEmpty(personKeyFormattedAndKeyTicksAndDisplayDirectoryName?.DisplayDirectoryName))
throw new NotImplementedException();
long? ticks = null;
string? debugDirectory = Path.Combine(eDistanceContentTicksDirectory, recordB.ByValue, personKeyFormattedAndKeyTicksAndDisplayDirectoryName.KeyFormatted, personKeyFormattedAndKeyTicksAndDisplayDirectoryName.DisplayDirectoryName);
string? directory = Path.Combine(eDistanceContentTicksDirectory, recordB.ByValue, personKeyFormattedAndKeyTicksAndDisplayDirectoryName.KeyFormatted, segmentB);
string? personDirectory = Path.Combine(directory, personKeyFormattedAndKeyTicksAndDisplayDirectoryName.DisplayDirectoryName, "lnk");
result = new(debugDirectory, directory, ticks, personDirectory);
return result;
}
internal record RecordA(long? Ticks, string? Directory);
internal record RecordB(string ByValue, bool IsByMapping, bool IsBySorting);
internal record RecordC(string? DebugDirectory, string? Directory, long? Ticks, string? PersonDirectory);
}

View File

@ -9,69 +9,49 @@ namespace View_by_Distance.Distance.Models.Stateless;
public interface IDistance
{
static string Get(bool saveIndividually, string forceSingleImageHumanized, int by, bool isDefaultName) =>
$"{by switch
{
IMapLogic.Mapping => nameof(IMapLogic.Mapping),
IMapLogic.Sorting => saveIndividually ?
nameof(IMapLogic.Individually) :
nameof(IMapLogic.Sorting),
IMapLogic.ForceSingleImage => forceSingleImageHumanized,
_ => throw new NotImplementedException()
}}{(!isDefaultName ? "-A" : "-Z")}";
public static ReadOnlyDictionary<string, LocationContainer> GetOnlyOne(DistanceSettings distanceSettings, ReadOnlyCollection<LocationContainer> matrix) =>
FilterLogicC.GetOnlyOne(distanceSettings, matrix);
public static ReadOnlyCollection<ExifDirectory> GetMappedExifDirectoryWithEncoding(ICompare compare, long ticks, ReadOnlyCollection<ExifDirectory> exifDirectories) =>
FaceEncodingLogic.GetMappedExifDirectoryWithEncoding(compare, ticks, exifDirectories);
public static ReadOnlyCollection<LocationContainer> GetPostFilterLocationContainer(ReadOnlyCollection<LocationContainer> preFiltered, DistanceLimits distanceLimits) =>
FilterLogicC.GetPostFilterLocationContainer(preFiltered, distanceLimits);
public static ReadOnlyDictionary<int, ReadOnlyDictionary<int, FilePath>> Extract(ICompareSettings compareSettings, ReadOnlyCollection<ExifDirectory> exifDirectories) =>
MappedLogicA.Extract(compareSettings, exifDirectories);
public static void SaveContainers(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, int? updated, ReadOnlyCollection<SaveContainer> saveContainers) =>
FilterLogicD.SaveContainers(distanceSettings, compareSettings, compare, ticks, updated, saveContainers);
public static ReadOnlyCollection<SaveContainer> GetSaveContainers(ResultSettings resultSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, string outputResolution, ReadOnlyDictionary<string, LocationContainer> onlyOne) =>
FilterLogicD.GetSaveContainers(resultSettings, distanceSettings, compareSettings, compare, ticks, outputResolution, onlyOne);
public static ReadOnlyCollection<ExifDirectory> GetMapped(ResultSettings resultSettings, MetadataSettings metadataSettings, PeopleSettings peopleSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, ReadOnlyCollections readOnlyCollections) =>
static ReadOnlyCollection<ExifDirectory> TestStatic_GetMapped(ResultSettings resultSettings, MetadataSettings metadataSettings, PeopleSettings peopleSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, ReadOnlyCollections readOnlyCollections) =>
GetMapped(resultSettings, metadataSettings, peopleSettings, distanceSettings, compareSettings, compare, ticks, readOnlyCollections);
static ReadOnlyCollection<ExifDirectory> GetMapped(ResultSettings resultSettings, MetadataSettings metadataSettings, PeopleSettings peopleSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, ReadOnlyCollections readOnlyCollections) =>
MappedLogicA.GetMapped(resultSettings, metadataSettings, peopleSettings, distanceSettings, compareSettings, compare, ticks, readOnlyCollections);
public static ReadOnlyCollection<LocationContainer> GetMatrixLocationContainers(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, ReadOnlyCollection<ExifDirectory> mappedExifDirectoryWithEncoding, DistanceLimits distanceLimits, ReadOnlyCollection<LocationContainer> postFiltered) =>
FilterLogicC.GetMatrixLocationContainers(distanceSettings, compareSettings, compare, ticks, mappedExifDirectoryWithEncoding, distanceLimits, postFiltered);
static ReadOnlyDictionary<int, ReadOnlyDictionary<int, FilePath>> TestStatic_Extract(ICompareSettings compareSettings, ReadOnlyCollection<ExifDirectory> exifDirectories) =>
Extract(compareSettings, exifDirectories);
static ReadOnlyDictionary<int, ReadOnlyDictionary<int, FilePath>> Extract(ICompareSettings compareSettings, ReadOnlyCollection<ExifDirectory> exifDirectories) =>
MappedLogicA.Extract(compareSettings, exifDirectories);
public static ReadOnlyCollection<LocationContainer> GetPreFilterLocationContainer(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, ReadOnlyCollections readOnlyCollections, ReadOnlyDictionary<int, ReadOnlyDictionary<int, FilePath>> keyValuePairs, ReadOnlyCollection<ExifDirectory> exifDirectories) =>
static ReadOnlyCollection<ExifDirectory> TestStatic_GetMappedExifDirectoryWithEncoding(ICompare compare, long ticks, ReadOnlyCollection<ExifDirectory> exifDirectories) =>
GetMappedExifDirectoryWithEncoding(compare, ticks, exifDirectories);
static ReadOnlyCollection<ExifDirectory> GetMappedExifDirectoryWithEncoding(ICompare compare, long ticks, ReadOnlyCollection<ExifDirectory> exifDirectories) =>
FaceEncodingLogic.GetMappedExifDirectoryWithEncoding(compare, ticks, exifDirectories);
static ReadOnlyCollection<LocationContainer> TestStatic_GetPreFilterLocationContainer(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, ReadOnlyCollections readOnlyCollections, ReadOnlyDictionary<int, ReadOnlyDictionary<int, FilePath>> keyValuePairs, ReadOnlyCollection<ExifDirectory> exifDirectories) =>
GetPreFilterLocationContainer(distanceSettings, compareSettings, compare, ticks, readOnlyCollections, keyValuePairs, exifDirectories);
static ReadOnlyCollection<LocationContainer> GetPreFilterLocationContainer(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, ReadOnlyCollections readOnlyCollections, ReadOnlyDictionary<int, ReadOnlyDictionary<int, FilePath>> keyValuePairs, ReadOnlyCollection<ExifDirectory> exifDirectories) =>
FilterLogicA.GetPreFilterLocationContainer(distanceSettings, compareSettings, compare, ticks, readOnlyCollections, keyValuePairs, exifDirectories);
internal static ReadOnlyDictionary<string, LocationContainer> TestStatic_GetOnlyOne(DistanceSettings distanceSettings, ReadOnlyCollection<LocationContainer> matrix) =>
GetOnlyOne(distanceSettings, matrix);
internal static ReadOnlyCollection<ExifDirectory> TestStatic_GetMappedExifDirectoryWithEncoding(ICompare compare, long ticks, ReadOnlyCollection<ExifDirectory> exifDirectories) =>
GetMappedExifDirectoryWithEncoding(compare, ticks, exifDirectories);
internal static ReadOnlyCollection<LocationContainer> TestStatic_GetPostFilterLocationContainer(ReadOnlyCollection<LocationContainer> preFiltered, DistanceLimits distanceLimits) =>
static ReadOnlyCollection<LocationContainer> TestStatic_GetPostFilterLocationContainer(ReadOnlyCollection<LocationContainer> preFiltered, DistanceLimits distanceLimits) =>
GetPostFilterLocationContainer(preFiltered, distanceLimits);
static ReadOnlyCollection<LocationContainer> GetPostFilterLocationContainer(ReadOnlyCollection<LocationContainer> preFiltered, DistanceLimits distanceLimits) =>
FilterLogicC.GetPostFilterLocationContainer(preFiltered, distanceLimits);
internal static ReadOnlyDictionary<int, ReadOnlyDictionary<int, FilePath>> TestStatic_Extract(ICompareSettings compareSettings, ReadOnlyCollection<ExifDirectory> exifDirectories) =>
Extract(compareSettings, exifDirectories);
internal static void TestStatic_SaveContainers(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, int? updated, ReadOnlyCollection<SaveContainer> saveContainers) =>
SaveContainers(distanceSettings, compareSettings, compare, ticks, updated, saveContainers);
internal static ReadOnlyCollection<SaveContainer> TestStatic_GetSaveContainers(ResultSettings resultSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, string outputResolution, ReadOnlyDictionary<string, LocationContainer> onlyOne) =>
GetSaveContainers(resultSettings, distanceSettings, compareSettings, compare, ticks, outputResolution, onlyOne);
internal static ReadOnlyCollection<ExifDirectory> TestStatic_GetMapped(ResultSettings resultSettings, MetadataSettings metadataSettings, PeopleSettings peopleSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, ReadOnlyCollections readOnlyCollections) =>
GetMapped(resultSettings, metadataSettings, peopleSettings, distanceSettings, compareSettings, compare, ticks, readOnlyCollections);
internal static ReadOnlyCollection<LocationContainer> TestStatic_GetMatrixLocationContainers(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, ReadOnlyCollection<ExifDirectory> mappedExifDirectoryWithEncoding, DistanceLimits distanceLimits, ReadOnlyCollection<LocationContainer> postFiltered) =>
static ReadOnlyCollection<LocationContainer> TestStatic_GetMatrixLocationContainers(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, ReadOnlyCollection<ExifDirectory> mappedExifDirectoryWithEncoding, DistanceLimits distanceLimits, ReadOnlyCollection<LocationContainer> postFiltered) =>
GetMatrixLocationContainers(distanceSettings, compareSettings, compare, ticks, mappedExifDirectoryWithEncoding, distanceLimits, postFiltered);
static ReadOnlyCollection<LocationContainer> GetMatrixLocationContainers(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, ReadOnlyCollection<ExifDirectory> mappedExifDirectoryWithEncoding, DistanceLimits distanceLimits, ReadOnlyCollection<LocationContainer> postFiltered) =>
FilterLogicC.GetMatrixLocationContainers(distanceSettings, compareSettings, compare, ticks, mappedExifDirectoryWithEncoding, distanceLimits, postFiltered);
internal static ReadOnlyCollection<LocationContainer> TestStatic_GetPreFilterLocationContainer(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, ReadOnlyCollections readOnlyCollections, ReadOnlyDictionary<int, ReadOnlyDictionary<int, FilePath>> keyValuePairs, ReadOnlyCollection<ExifDirectory> exifDirectories) =>
GetPreFilterLocationContainer(distanceSettings, compareSettings, compare, ticks, readOnlyCollections, keyValuePairs, exifDirectories);
static ReadOnlyDictionary<string, LocationContainer> TestStatic_GetOnlyOne(DistanceSettings distanceSettings, ReadOnlyCollection<LocationContainer> matrix) =>
GetOnlyOne(distanceSettings, matrix);
static ReadOnlyDictionary<string, LocationContainer> GetOnlyOne(DistanceSettings distanceSettings, ReadOnlyCollection<LocationContainer> matrix) =>
FilterLogicC.GetOnlyOne(distanceSettings, matrix);
static ReadOnlyCollection<SaveContainer> TestStatic_GetSaveContainers(ResultSettings resultSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, string outputResolution, ReadOnlyDictionary<string, LocationContainer> onlyOne) =>
GetSaveContainers(resultSettings, distanceSettings, compareSettings, compare, ticks, outputResolution, onlyOne);
static ReadOnlyCollection<SaveContainer> GetSaveContainers(ResultSettings resultSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, string outputResolution, ReadOnlyDictionary<string, LocationContainer> onlyOne) =>
FilterLogicD.GetSaveContainers(resultSettings, distanceSettings, compareSettings, compare, ticks, outputResolution, onlyOne);
static void TestStatic_SaveContainers(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, int? updated, ReadOnlyCollection<SaveContainer> saveContainers) =>
SaveContainers(distanceSettings, compareSettings, compare, ticks, updated, saveContainers);
static void SaveContainers(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, int? updated, ReadOnlyCollection<SaveContainer> saveContainers) =>
FilterLogicD.SaveContainers(distanceSettings, compareSettings, compare, ticks, updated, saveContainers);
}

View File

@ -14,53 +14,33 @@ internal static class MappedLogicA
string? PersonDisplayDirectoryName,
FilePath FilePath);
internal static ReadOnlyDictionary<int, ReadOnlyDictionary<int, FilePath>> Extract(ICompareSettings compareSettings, ReadOnlyCollection<ExifDirectory> exifDirectories)
private static List<MappedFile> GetDisplayDirectoryAllFiles(PeopleSettings peopleSettings, ICompareSettings compareSettings, ReadOnlyCollections readOnlyCollections)
{
Dictionary<int, ReadOnlyDictionary<int, FilePath>> results = [];
int? wholePercentages;
Dictionary<int, FilePath>? keyValues;
Dictionary<int, Dictionary<int, FilePath>> keyValuePairs = [];
foreach (ExifDirectory exifDirectory in exifDirectories)
List<MappedFile> results = [];
FilePath filePath;
MappedFile mappedFile;
string personKeyFormatted;
List<string> distinct = [];
PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName personKeyFormattedAndKeyTicksAndDisplayDirectoryName;
foreach (PersonContainer personContainer in readOnlyCollections.PersonContainers)
{
if (exifDirectory.FilePath.Id is null)
if (personContainer.Key is null)
continue;
if (!keyValuePairs.TryGetValue(exifDirectory.FilePath.Id.Value, out keyValues))
for (int i = personContainer.DisplayDirectoryAllFilePaths.Count - 1; i > -1; i--)
{
keyValuePairs.Add(exifDirectory.FilePath.Id.Value, []);
if (!keyValuePairs.TryGetValue(exifDirectory.FilePath.Id.Value, out keyValues))
throw new Exception();
filePath = personContainer.DisplayDirectoryAllFilePaths[i];
if (filePath.ExtensionLowered != compareSettings.FacesFileNameExtension)
continue;
if (distinct.Contains(filePath.Name))
continue;
distinct.Add(filePath.Name);
personKeyFormatted = IPersonBirthday.GetFormatted(peopleSettings.PersonBirthdayFormat, personContainer.Key.Value);
personKeyFormattedAndKeyTicksAndDisplayDirectoryName = new(personKeyFormatted, personContainer.Key.Value, filePath.Name);
mappedFile = new(personKeyFormattedAndKeyTicksAndDisplayDirectoryName, personContainer.DisplayDirectoryName, filePath);
results.Add(mappedFile);
}
wholePercentages = IMapping.GetWholePercentages(compareSettings, exifDirectory.FilePath);
if (wholePercentages is null)
continue;
keyValues.Add(wholePercentages.Value, exifDirectory.FilePath);
}
foreach (KeyValuePair<int, Dictionary<int, FilePath>> keyValuePair in keyValuePairs)
results.Add(keyValuePair.Key, keyValuePair.Value.AsReadOnly());
return results.AsReadOnly();
}
internal static ReadOnlyCollection<ExifDirectory> GetMapped(ResultSettings resultSettings, MetadataSettings metadataSettings, PeopleSettings peopleSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, ReadOnlyCollections readOnlyCollections)
{
List<ExifDirectory> results = [];
string eDistanceContentDirectory = Path.GetFullPath(IResult.GetResultsDateGroupDirectory(resultSettings, nameof(E_Distance), resultSettings.ResultContent));
ReadOnlyCollection<MappedLogicB.Record> records = MappedLogicB.DeleteEmptyDirectoriesAndGetCollection(resultSettings, metadataSettings, peopleSettings, distanceSettings, compareSettings, compare, ticks, eDistanceContentDirectory, readOnlyCollections);
ReadOnlyCollection<MappedFile> mappedFiles = GetMappedFiles(resultSettings, metadataSettings, peopleSettings, compareSettings, readOnlyCollections, records);
if (mappedFiles.Count > 0)
{
int maxDegreeOfParallelism = compareSettings.MaxDegreeOfParallelism;
int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds);
string message = $") Building Mapped Face Files Collection - {totalSeconds} total second(s)";
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism };
ReadOnlyDictionary<int, List<FilePathAndWholePercentages>> skipNotSkipCollection = readOnlyCollections.SkipNotSkipCollection;
compare.ConstructProgressBar(mappedFiles.Count, message);
_ = Parallel.For(0, mappedFiles.Count, parallelOptions, (i, state) =>
{
compare.Tick();
MappedParallelFor(resultSettings, metadataSettings, distanceSettings, compareSettings, results, skipNotSkipCollection, mappedFiles[i]);
});
}
return results.AsReadOnly();
return results;
}
private static ReadOnlyCollection<MappedFile> GetMappedFiles(ResultSettings resultSettings, MetadataSettings metadataSettings, PeopleSettings peopleSettings, ICompareSettings compareSettings, ReadOnlyCollections readOnlyCollections, ReadOnlyCollection<MappedLogicB.Record> records)
@ -122,35 +102,6 @@ internal static class MappedLogicA
return results.AsReadOnly();
}
private static List<MappedFile> GetDisplayDirectoryAllFiles(PeopleSettings peopleSettings, ICompareSettings compareSettings, ReadOnlyCollections readOnlyCollections)
{
List<MappedFile> results = [];
FilePath filePath;
MappedFile mappedFile;
string personKeyFormatted;
List<string> distinct = [];
PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName personKeyFormattedAndKeyTicksAndDisplayDirectoryName;
foreach (PersonContainer personContainer in readOnlyCollections.PersonContainers)
{
if (personContainer.Key is null)
continue;
for (int i = personContainer.DisplayDirectoryAllFilePaths.Count - 1; i > -1; i--)
{
filePath = personContainer.DisplayDirectoryAllFilePaths[i];
if (filePath.ExtensionLowered != compareSettings.FacesFileNameExtension)
continue;
if (distinct.Contains(filePath.Name))
continue;
distinct.Add(filePath.Name);
personKeyFormatted = IPersonBirthday.GetFormatted(peopleSettings.PersonBirthdayFormat, personContainer.Key.Value);
personKeyFormattedAndKeyTicksAndDisplayDirectoryName = new(personKeyFormatted, personContainer.Key.Value, filePath.Name);
mappedFile = new(personKeyFormattedAndKeyTicksAndDisplayDirectoryName, personContainer.DisplayDirectoryName, filePath);
results.Add(mappedFile);
}
}
return results;
}
private static void MappedParallelFor(ResultSettings resultSettings, MetadataSettings metadataSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, List<ExifDirectory> exifDirectories, ReadOnlyDictionary<int, List<FilePathAndWholePercentages>> skipCollection, MappedFile mappedFile)
{
int? id;
@ -201,4 +152,53 @@ internal static class MappedLogicA
exifDirectories.Add(exifDirectory);
}
internal static ReadOnlyCollection<ExifDirectory> GetMapped(ResultSettings resultSettings, MetadataSettings metadataSettings, PeopleSettings peopleSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, ReadOnlyCollections readOnlyCollections)
{
List<ExifDirectory> results = [];
string eDistanceContentDirectory = IResult.GetResultsDateGroupDirectory(resultSettings, nameof(E_Distance), resultSettings.ResultContent);
ReadOnlyCollection<MappedLogicB.Record> records = MappedLogicB.DeleteEmptyDirectoriesAndGetCollection(resultSettings, metadataSettings, peopleSettings, distanceSettings, compareSettings, compare, ticks, eDistanceContentDirectory, readOnlyCollections);
ReadOnlyCollection<MappedFile> mappedFiles = GetMappedFiles(resultSettings, metadataSettings, peopleSettings, compareSettings, readOnlyCollections, records);
if (mappedFiles.Count > 0)
{
int maxDegreeOfParallelism = compareSettings.MaxDegreeOfParallelism;
int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds);
string message = $") Building Mapped Face Files Collection - {totalSeconds} total second(s)";
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism };
ReadOnlyDictionary<int, List<FilePathAndWholePercentages>> skipNotSkipCollection = readOnlyCollections.SkipNotSkipCollection;
compare.ConstructProgressBar(mappedFiles.Count, message);
_ = Parallel.For(0, mappedFiles.Count, parallelOptions, (i, state) =>
{
compare.Tick();
MappedParallelFor(resultSettings, metadataSettings, distanceSettings, compareSettings, results, skipNotSkipCollection, mappedFiles[i]);
});
}
return results.AsReadOnly();
}
internal static ReadOnlyDictionary<int, ReadOnlyDictionary<int, FilePath>> Extract(ICompareSettings compareSettings, ReadOnlyCollection<ExifDirectory> exifDirectories)
{
Dictionary<int, ReadOnlyDictionary<int, FilePath>> results = [];
int? wholePercentages;
Dictionary<int, FilePath>? keyValues;
Dictionary<int, Dictionary<int, FilePath>> keyValuePairs = [];
foreach (ExifDirectory exifDirectory in exifDirectories)
{
if (exifDirectory.FilePath.Id is null)
continue;
if (!keyValuePairs.TryGetValue(exifDirectory.FilePath.Id.Value, out keyValues))
{
keyValuePairs.Add(exifDirectory.FilePath.Id.Value, []);
if (!keyValuePairs.TryGetValue(exifDirectory.FilePath.Id.Value, out keyValues))
throw new Exception();
}
wholePercentages = IMapping.GetWholePercentages(compareSettings, exifDirectory.FilePath);
if (wholePercentages is null)
continue;
keyValues.Add(wholePercentages.Value, exifDirectory.FilePath);
}
foreach (KeyValuePair<int, Dictionary<int, FilePath>> keyValuePair in keyValuePairs)
results.Add(keyValuePair.Key, keyValuePair.Value.AsReadOnly());
return results.AsReadOnly();
}
}

View File

@ -21,188 +21,62 @@ internal static class MappedLogicB
internal record TicksDirectory(DateTime AlternateDirectoryDateTime,
string Directory,
DateTime DirectoryDateTime,
string DirectoryName,
bool? IsLocationContainerDebugDirectory,
float? TotalDays);
internal static ReadOnlyCollection<Record> DeleteEmptyDirectoriesAndGetCollection(ResultSettings resultSettings, MetadataSettings metadataSettings, PeopleSettings peopleSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, string eDistanceContentDirectory, ReadOnlyCollections readOnlyCollections)
private static void MoveTo(string actionDirectory, TicksDirectory ticksDirectory, string directory, string personKeyFormatted, string yearDirectoryName, string alphaDirectoryName, string[] files, string[] facesFileNames)
{
List<Record> results = [];
bool check;
string message;
string[] files;
bool? isDefault;
int? linksCount;
int totalSeconds;
DateTime dateTime;
TimeSpan timeSpan;
int directoryNumber;
List<Record> records;
string? checkDirectory;
string[] yearDirectories;
string personKeyFormatted;
List<string> distinct = [];
string? personFirstInitial;
bool isReservedDirectoryName;
string[] personNameDirectories;
string? newestPersonKeyFormatted;
string? personDisplayDirectoryName;
string[] personNameLinkDirectories;
string? personFirstInitialDirectory;
List<TicksDirectory> ticksDirectories;
string[] personKeyFormattedDirectories;
string manualCopyHumanized = nameof(IMapLogic.ManualCopy).Humanize(LetterCasing.Title);
string forceSingleImageHumanized = nameof(IMapLogic.ForceSingleImage).Humanize(LetterCasing.Title);
for (int i = 1; i < 6; i++)
string checkFile;
string actionDirectoryName = Path.GetFileName(actionDirectory);
string checkDirectory = actionDirectoryName.StartsWith("y", StringComparison.CurrentCultureIgnoreCase) ? Path.Combine(ticksDirectory.Directory, personKeyFormatted, yearDirectoryName, alphaDirectoryName) : Path.Combine(directory, actionDirectoryName);
if (!Directory.Exists(checkDirectory))
_ = Directory.CreateDirectory(checkDirectory);
foreach (string file in files)
{
check = false;
results.Clear();
distinct.Clear();
directoryNumber = 0;
ticksDirectories = UpdateDateVerifyAndGetTicksDirectories(distanceSettings, eDistanceContentDirectory);
totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds);
message = $"{i}) {ticksDirectories.Count:000} compile from and clean ticks Director(ies) - B - {totalSeconds} total second(s)";
compare.ConstructProgressBar(ticksDirectories.Count, message);
foreach (TicksDirectory ticksDirectory in ticksDirectories)
if (facesFileNames.Contains(file))
{
if (i == 1)
compare.Tick();
personKeyFormattedDirectories = Directory.GetDirectories(ticksDirectory.Directory, "*", SearchOption.TopDirectoryOnly);
foreach (string personKeyFormattedDirectory in personKeyFormattedDirectories)
{
personKeyFormatted = Path.GetFileName(personKeyFormattedDirectory);
isReservedDirectoryName = personKeyFormatted.StartsWith(nameof(IMapLogic.Sorting)) || personKeyFormatted.StartsWith(nameof(IMapLogic.Mapping)) || personKeyFormatted.StartsWith(nameof(IMapLogic.ManualCopy));
if (!isReservedDirectoryName && personKeyFormatted.StartsWith(nameof(IMapLogic.Individually)))
{
Individually(compareSettings, ticksDirectory, personKeyFormattedDirectory);
throw new Exception($"B) Move personKey directories up one from {nameof(IMapLogic.Sorting)} and delete {nameof(IMapLogic.Sorting)} directory!");
}
_ = readOnlyCollections.PersonKeyFormattedToNewestPersonKeyFormatted.TryGetValue(personKeyFormatted, out newestPersonKeyFormatted);
if (readOnlyCollections.PersonKeyFormattedToNewestPersonKeyFormatted.Count > 0 && newestPersonKeyFormatted is null)
{
timeSpan = new TimeSpan(DateTime.Now.Ticks - ticksDirectory.DirectoryDateTime.Ticks);
if (timeSpan.TotalDays > 6)
throw new Exception($"{distanceSettings.MappingDefaultName} <{ticksDirectory.DirectoryDateTime}> are only allowed within x days!");
}
yearDirectories = Directory.GetDirectories(personKeyFormattedDirectory, "*", SearchOption.TopDirectoryOnly);
foreach (string yearDirectory in yearDirectories)
{
if (check && !Directory.Exists(yearDirectory))
continue;
if (ticksDirectory.IsLocationContainerDebugDirectory is null || !ticksDirectory.IsLocationContainerDebugDirectory.Value)
linksCount = null;
else
linksCount = GetLinksCount(yearDirectory);
personNameDirectories = Directory.GetDirectories(yearDirectory, "*", SearchOption.TopDirectoryOnly);
if (personNameDirectories.Length > 1)
throw new NotSupportedException("Try deleting *.lnk files!");
foreach (string personNameDirectory in personNameDirectories)
{
directoryNumber++;
personDisplayDirectoryName = Path.GetFileName(personNameDirectory);
isDefault = IPerson.IsDefaultName(personDisplayDirectoryName) && IPersonBirthday.IsCounterPersonYear(personKeyFormatted[..4]);
if (isDefault.Value && personDisplayDirectoryName.Length == 1)
{
if (personKeyFormatted.Length != peopleSettings.PersonBirthdayFormat.Length || !DateTime.TryParseExact(personKeyFormatted, peopleSettings.PersonBirthdayFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime))
continue;
checkDirectory = Path.Combine(yearDirectory, $"X+{dateTime.Ticks}");
if (Directory.Exists(checkDirectory))
{
Directory.Delete(yearDirectory, recursive: true);
continue;
}
Directory.Move(personNameDirectory, checkDirectory);
if (!check)
check = true;
continue;
}
if (isDefault.Value && (ticksDirectory.DirectoryDateTime.Hour != 0 || ticksDirectory.DirectoryDateTime.Minute != 0 || ticksDirectory.DirectoryDateTime.Second != 0))
{
checkDirectory = Path.GetDirectoryName(ticksDirectory.Directory);
if (checkDirectory is null)
continue;
checkDirectory = Path.Combine(checkDirectory, ticksDirectory.AlternateDirectoryDateTime.Ticks.ToString());
if (!Directory.Exists(checkDirectory))
_ = Directory.CreateDirectory(checkDirectory);
checkDirectory = Path.Combine(checkDirectory, personKeyFormatted);
if (!Directory.Exists(checkDirectory))
{
Directory.Move(personKeyFormattedDirectory, checkDirectory);
if (!check)
check = true;
break;
}
}
files = Directory.GetFiles(personNameDirectory, "*", SearchOption.TopDirectoryOnly);
if (isReservedDirectoryName && files.Length > 0)
throw new Exception($"Move personKey directories up one from {nameof(IMapLogic.Sorting)} and delete {nameof(IMapLogic.Sorting)} directory!");
if (personKeyFormatted == manualCopyHumanized && files.Length > 0)
throw new Exception($"Move personKey directories up one from {manualCopyHumanized} and delete {manualCopyHumanized} directory!");
if (personKeyFormatted == forceSingleImageHumanized && files.Length > 0)
throw new Exception($"Move personKey directories up one from {forceSingleImageHumanized} and delete {forceSingleImageHumanized} directory!");
if (!isDefault.Value)
{
if (readOnlyCollections.PersonKeyFormattedToNewestPersonKeyFormatted.Count > 0 && newestPersonKeyFormatted is null)
files = RenameBirth(files);
else if (newestPersonKeyFormatted is not null && personKeyFormatted != newestPersonKeyFormatted)
{
if (!check)
check = true;
MovedToNewestPersonKeyFormatted(personKeyFormatted, newestPersonKeyFormatted, ticksDirectory, personKeyFormattedDirectory);
continue;
}
}
if (personKeyFormatted.Length != peopleSettings.PersonBirthdayFormat.Length)
continue;
if (personDisplayDirectoryName.Length == 1 || isDefault.Value || !readOnlyCollections.PersonKeyFormattedCollection.Contains(personKeyFormatted))
personFirstInitialDirectory = personNameDirectory;
else
{
personFirstInitial = personDisplayDirectoryName[..1];
if (personFirstInitial.All(char.IsDigit))
{
foreach (string file in files)
File.Delete(file);
files = Directory.GetFiles(personNameDirectory, "*", SearchOption.AllDirectories);
foreach (string file in files)
File.Delete(file);
_ = IPath.DeleteEmptyDirectories(personNameDirectory);
continue;
}
personFirstInitialDirectory = Path.Combine(yearDirectory, personFirstInitial.ToString());
if (Directory.Exists(personFirstInitialDirectory))
throw new Exception("Forgot to ...");
Directory.Move(personNameDirectory, personFirstInitialDirectory);
files = Directory.GetFiles(personFirstInitialDirectory, "*", SearchOption.TopDirectoryOnly);
}
records = GetRecords(resultSettings, metadataSettings, compareSettings, ticksDirectory, isDefault, files, directoryNumber, personKeyFormatted, linksCount, distinct, personDisplayDirectoryName);
if (records.Count > 0)
results.AddRange(records);
personNameLinkDirectories = Directory.GetDirectories(personFirstInitialDirectory, "*", SearchOption.TopDirectoryOnly);
foreach (string personNameLinkDirectory in personNameLinkDirectories)
{
files = Directory.GetFiles(personNameLinkDirectory, "*", SearchOption.TopDirectoryOnly);
foreach (string file in files)
{
if (!file.EndsWith(".lnk"))
continue;
File.Delete(file);
}
_ = IPath.DeleteEmptyDirectories(personNameLinkDirectory);
}
_ = IPath.DeleteEmptyDirectories(personFirstInitialDirectory);
}
_ = IPath.DeleteEmptyDirectories(yearDirectory);
}
_ = IPath.DeleteEmptyDirectories(personKeyFormattedDirectory);
}
_ = IPath.DeleteEmptyDirectories(ticksDirectory.Directory);
_ = IPath.DeleteEmptyDirectories(ticksDirectory.Directory);
}
if (check)
checkFile = Path.Combine(checkDirectory, Path.GetFileName(file));
if (File.Exists(checkFile))
continue;
File.Move(file, checkFile);
continue;
break;
}
File.Delete(file);
}
return results.AsReadOnly();
}
private static void MoveFiles(string personKeyFormatted, string personKeyDirectory, string newestPersonKeyFormatted, string newestPersonKeyDirectory)
{
string[] files;
string checkFile;
string? checkDirectory;
string[] directories = Directory.GetDirectories(personKeyDirectory, "*", SearchOption.TopDirectoryOnly);
foreach (string directory in directories)
{
checkDirectory = Path.Combine(newestPersonKeyDirectory, Path.GetFileName(directory));
if (!Directory.Exists(checkDirectory))
Directory.Move(directory, checkDirectory);
else
{
files = Directory.GetFiles(directory, "*", SearchOption.AllDirectories);
foreach (string file in files)
{
if (file.Split(personKeyFormatted).Length != 2 || file.Contains(newestPersonKeyFormatted))
continue;
checkFile = file.Replace(personKeyFormatted, newestPersonKeyFormatted);
checkDirectory = Path.GetDirectoryName(checkFile);
if (checkDirectory is null)
continue;
if (File.Exists(checkFile))
continue;
if (!Directory.Exists(checkDirectory))
_ = Directory.CreateDirectory(checkDirectory);
File.Move(file, checkFile);
}
}
}
_ = IPath.DeleteEmptyDirectories(personKeyDirectory);
}
private static List<TicksDirectory> UpdateDateVerifyAndGetTicksDirectories(DistanceSettings distanceSettings, string eDistanceContentDirectory)
@ -211,13 +85,13 @@ internal static class MappedLogicB
float? totalDays;
long? next = null;
string? checkDirectory;
string ticksDirectoryName;
DateTime directoryDateTime;
DirectoryInfo directoryInfo;
TicksDirectory ticksDirectory;
long? lastDirectoryTicks = null;
DateTime dateTime = DateTime.Now;
DateTime alternateDirectoryDateTime;
string ticksDirectoryNameFirstSegment;
bool? isLocationContainerDebugDirectory;
long month = dateTime.AddMonths(1).Ticks - dateTime.Ticks;
for (int i = 1; i < 5; i++)
@ -227,10 +101,10 @@ internal static class MappedLogicB
string[] ticksFullPaths = Directory.GetDirectories(eDistanceContentDirectory, "*", SearchOption.TopDirectoryOnly);
foreach (string ticksFullPath in ticksFullPaths)
{
ticksDirectoryNameFirstSegment = Path.GetFileName(ticksFullPath).Split('.')[0];
if (ticksDirectoryNameFirstSegment.Length < 3)
ticksDirectoryName = Path.GetFileName(ticksFullPath);
if (ticksDirectoryName.Length < 3)
continue;
if (!long.TryParse(ticksDirectoryNameFirstSegment, out long directoryTicks))
if (!long.TryParse(ticksDirectoryName, out long directoryTicks))
throw new NotSupportedException();
if (next is null)
next = new DateTime(directoryTicks).Ticks;
@ -256,9 +130,9 @@ internal static class MappedLogicB
if (directoryInfo.LastWriteTime.Ticks != directoryTicks)
Directory.SetLastWriteTime(ticksFullPath, new DateTime(directoryTicks));
alternateDirectoryDateTime = new DateTime(directoryDateTime.Year, directoryDateTime.Month, directoryDateTime.Day).AddMonths(1);
isLocationContainerDebugDirectory = distanceSettings.LocationContainerDebugDirectory is null ? null : ticksDirectoryNameFirstSegment.EndsWith(distanceSettings.LocationContainerDebugDirectory);
isLocationContainerDebugDirectory = distanceSettings.LocationContainerDebugDirectory is null ? null : ticksDirectoryName.EndsWith(distanceSettings.LocationContainerDebugDirectory);
totalDays = lastDirectoryTicks is null || new TimeSpan(dateTime.Ticks - directoryTicks).TotalDays < 1 ? null : (float)new TimeSpan(directoryTicks - lastDirectoryTicks.Value).TotalDays;
ticksDirectory = new(alternateDirectoryDateTime, ticksFullPath, new(directoryTicks), isLocationContainerDebugDirectory, totalDays);
ticksDirectory = new(alternateDirectoryDateTime, ticksFullPath, new(directoryTicks), ticksDirectoryName, isLocationContainerDebugDirectory, totalDays);
results.Add(ticksDirectory);
if (directoryDateTime.Hour == 0 && directoryDateTime.Minute == 0 && directoryDateTime.Second == 0)
continue;
@ -343,42 +217,40 @@ internal static class MappedLogicB
}
}
private static void MoveTo(string actionDirectory, TicksDirectory ticksDirectory, string directory, string personKeyFormatted, string yearDirectoryName, string alphaDirectoryName, string[] files, string[] facesFileNames)
private static List<Record> GetRecords(ResultSettings resultSettings, MetadataSettings metadataSettings, ICompareSettings compareSettings, bool? isDefault, string[] files, int directoryNumber, string personKeyFormatted, int? linksCount, List<string> distinct, string? personDisplayDirectoryName)
{
List<Record> results = [];
Record record;
string fileName;
string checkFile;
string actionDirectoryName = Path.GetFileName(actionDirectory);
string checkDirectory = actionDirectoryName.StartsWith("y", StringComparison.CurrentCultureIgnoreCase) ? Path.Combine(ticksDirectory.Directory, personKeyFormatted, yearDirectoryName, alphaDirectoryName) : Path.Combine(directory, actionDirectoryName);
if (!Directory.Exists(checkDirectory))
_ = Directory.CreateDirectory(checkDirectory);
FilePath filePath;
FileHolder fileHolder;
int? wholePercentages;
foreach (string file in files)
{
if (facesFileNames.Contains(file))
if (file.EndsWith(".lnk"))
continue;
fileHolder = FileHolder.Get(file);
filePath = FilePath.Get(resultSettings, metadataSettings, fileHolder, index: null);
if (filePath.Id is null)
continue;
wholePercentages = IMapping.GetWholePercentages(compareSettings, filePath);
if (wholePercentages is null)
continue;
fileName = Path.GetFileName(file);
if (distinct.Contains(fileName))
{
checkFile = Path.Combine(checkDirectory, Path.GetFileName(file));
checkFile = $"{file}.dup";
if (File.Exists(checkFile))
continue;
File.Move(file, checkFile);
continue;
}
File.Delete(file);
distinct.Add(fileName);
record = new(directoryNumber, isDefault, linksCount, filePath, personDisplayDirectoryName, personKeyFormatted);
results.Add(record);
}
}
private static int? GetLinksCount(string yearDirectory)
{
int? result;
string[] yearDirectoryNameSegments = Path.GetFileName(yearDirectory).Split('-');
if (yearDirectoryNameSegments.Length != 3)
result = null;
else
{
string lastSegment = yearDirectoryNameSegments[^1];
if (lastSegment.Length != 3 || !lastSegment.All(l => l == lastSegment[0]))
result = null;
else
result = lastSegment[0] - 65;
}
return result;
return results;
}
private static string[] RenameBirth(string[] files)
@ -413,117 +285,216 @@ internal static class MappedLogicB
Directory.Move(personKeyDirectory, newestPersonKeyDirectory);
}
private static void MoveFiles(string personKeyFormatted, string personKeyDirectory, string newestPersonKeyFormatted, string newestPersonKeyDirectory)
private static int? GetLinksCount(string yearDirectory)
{
string[] files;
string checkFile;
string? checkDirectory;
string[] directories = Directory.GetDirectories(personKeyDirectory, "*", SearchOption.TopDirectoryOnly);
foreach (string directory in directories)
int? result;
string[] yearDirectoryNameSegments = Path.GetFileName(yearDirectory).Split('-');
if (yearDirectoryNameSegments.Length != 3)
result = null;
else
{
checkDirectory = Path.Combine(newestPersonKeyDirectory, Path.GetFileName(directory));
if (!Directory.Exists(checkDirectory))
Directory.Move(directory, checkDirectory);
string lastSegment = yearDirectoryNameSegments[^1];
if (lastSegment.Length != 3 || !lastSegment.All(l => l == lastSegment[0]))
result = null;
else
{
files = Directory.GetFiles(directory, "*", SearchOption.AllDirectories);
foreach (string file in files)
{
if (file.Split(personKeyFormatted).Length != 2 || file.Contains(newestPersonKeyFormatted))
continue;
checkFile = file.Replace(personKeyFormatted, newestPersonKeyFormatted);
checkDirectory = Path.GetDirectoryName(checkFile);
if (checkDirectory is null)
continue;
if (File.Exists(checkFile))
continue;
if (!Directory.Exists(checkDirectory))
_ = Directory.CreateDirectory(checkDirectory);
File.Move(file, checkFile);
}
}
result = lastSegment[0] - 65;
}
_ = IPath.DeleteEmptyDirectories(personKeyDirectory);
}
private static List<Record> GetRecords(ResultSettings resultSettings, MetadataSettings metadataSettings, ICompareSettings compareSettings, TicksDirectory ticksDirectory, bool? isDefault, string[] files, int directoryNumber, string personKeyFormatted, int? linksCount, List<string> distinct, string? personDisplayDirectoryName)
{
List<Record> results = [];
string @enum;
Record record;
string fileName;
string checkFile;
FilePath filePath;
FileHolder fileHolder;
int? wholePercentages;
foreach (string file in files)
{
if (file.EndsWith(".lnk"))
continue;
fileHolder = FileHolder.Get(file);
filePath = FilePath.Get(resultSettings, metadataSettings, fileHolder, index: null);
if (filePath.Id is null)
continue;
wholePercentages = IMapping.GetWholePercentages(compareSettings, filePath);
if (wholePercentages is null)
continue;
fileName = Path.GetFileName(file);
if (distinct.Contains(fileName))
{
checkFile = $"{file}.dup";
if (File.Exists(checkFile))
continue;
File.Move(file, checkFile);
continue;
}
if (file.StartsWith(ticksDirectory.Directory))
{
@enum = IPath.GetEnum(filePath).ToString();
if (!ticksDirectory.Directory.EndsWith(@enum))
{
checkFile = GetCheckFile(ticksDirectory, @enum, fileName, file);
fileHolder = FileHolder.Get(checkFile);
filePath = FilePath.Get(resultSettings, metadataSettings, fileHolder, index: null);
if (filePath.Id is null)
continue;
}
}
distinct.Add(fileName);
record = new(DirectoryNumber: directoryNumber,
IsDefault: isDefault,
LinksCount: linksCount,
MappedFaceFilePath: filePath,
PersonDisplayDirectoryName: personDisplayDirectoryName,
PersonKeyFormatted: personKeyFormatted);
results.Add(record);
}
return results;
}
private static string GetCheckFile(TicksDirectory ticksDirectory, string @enum, string fileName, string file)
{
string result;
string checkDirectory;
string directory = file;
List<string> collection = [];
for (int i = 0; i < file.Length; i++)
{
directory = Path.GetDirectoryName(directory) ?? throw new Exception();
if (directory == ticksDirectory.Directory)
break;
collection.Add(Path.GetFileName(directory));
}
collection.Reverse();
checkDirectory = $"{ticksDirectory.Directory}.{@enum}";
foreach (string directoryName in collection)
checkDirectory = Path.Combine(checkDirectory, directoryName);
if (!Directory.Exists(checkDirectory))
_ = Directory.CreateDirectory(checkDirectory);
result = Path.Combine(checkDirectory, fileName);
if (File.Exists(result))
throw new Exception($"File <{fileName}> already exists!");
File.Move(file, result);
return result;
}
internal static ReadOnlyCollection<Record> DeleteEmptyDirectoriesAndGetCollection(ResultSettings resultSettings, MetadataSettings metadataSettings, PeopleSettings peopleSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, string eDistanceContentDirectory, ReadOnlyCollections readOnlyCollections)
{
List<Record> results = [];
bool check;
string message;
string[] files;
bool? isDefault;
int? linksCount;
int totalSeconds;
DateTime dateTime;
TimeSpan timeSpan;
int directoryNumber;
string? checkDirectory;
string[] yearDirectories;
string personKeyFormatted;
List<string> distinct = [];
string? personFirstInitial;
bool isReservedDirectoryName;
string[] personNameDirectories;
string? newestPersonKeyFormatted;
string? personDisplayDirectoryName;
string[] personNameLinkDirectories;
string? personFirstInitialDirectory;
List<TicksDirectory> ticksDirectories;
string[] personKeyFormattedDirectories;
string manualCopyHumanized = nameof(Shared.Models.Stateless.IMapLogic.ManualCopy).Humanize(LetterCasing.Title);
string forceSingleImageHumanized = nameof(Shared.Models.Stateless.IMapLogic.ForceSingleImage).Humanize(LetterCasing.Title);
for (int i = 1; i < 6; i++)
{
check = false;
results.Clear();
distinct.Clear();
directoryNumber = 0;
ticksDirectories = UpdateDateVerifyAndGetTicksDirectories(distanceSettings, eDistanceContentDirectory);
totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds);
message = $"{i}) {ticksDirectories.Count:000} compile from and clean ticks Director(ies) - B - {totalSeconds} total second(s)";
compare.ConstructProgressBar(ticksDirectories.Count, message);
foreach (TicksDirectory ticksDirectory in ticksDirectories)
{
if (i == 1)
compare.Tick();
personKeyFormattedDirectories = Directory.GetDirectories(ticksDirectory.Directory, "*", SearchOption.TopDirectoryOnly);
foreach (string personKeyFormattedDirectory in personKeyFormattedDirectories)
{
personKeyFormatted = Path.GetFileName(personKeyFormattedDirectory);
isReservedDirectoryName = personKeyFormatted.StartsWith(nameof(Shared.Models.Stateless.IMapLogic.Sorting)) || personKeyFormatted.StartsWith(nameof(Shared.Models.Stateless.IMapLogic.Mapping)) || personKeyFormatted.StartsWith(nameof(Shared.Models.Stateless.IMapLogic.ManualCopy));
if (!isReservedDirectoryName && personKeyFormatted.StartsWith(nameof(Shared.Models.Stateless.IMapLogic.Individually)))
{
Individually(compareSettings, ticksDirectory, personKeyFormattedDirectory);
throw new Exception($"B) Move personKey directories up one from {nameof(Shared.Models.Stateless.IMapLogic.Sorting)} and delete {nameof(Shared.Models.Stateless.IMapLogic.Sorting)} directory!");
}
_ = readOnlyCollections.PersonKeyFormattedToNewestPersonKeyFormatted.TryGetValue(personKeyFormatted, out newestPersonKeyFormatted);
if (readOnlyCollections.PersonKeyFormattedToNewestPersonKeyFormatted.Count > 0 && newestPersonKeyFormatted is null)
{
timeSpan = new TimeSpan(DateTime.Now.Ticks - ticksDirectory.DirectoryDateTime.Ticks);
if (timeSpan.TotalDays > 6)
throw new Exception($"{distanceSettings.MappingDefaultName} <{ticksDirectory.DirectoryDateTime}> are only allowed within x days!");
}
yearDirectories = Directory.GetDirectories(personKeyFormattedDirectory, "*", SearchOption.TopDirectoryOnly);
foreach (string yearDirectory in yearDirectories)
{
if (check && !Directory.Exists(yearDirectory))
continue;
if (ticksDirectory.IsLocationContainerDebugDirectory is null || !ticksDirectory.IsLocationContainerDebugDirectory.Value)
linksCount = null;
else
linksCount = GetLinksCount(yearDirectory);
if (ticksDirectory.DirectoryName != distanceSettings.LocationContainerDebugDirectory)
{
files = Directory.GetFiles(yearDirectory, "*", SearchOption.TopDirectoryOnly);
foreach (string file in files)
File.Delete(file);
}
if (ticksDirectory.DirectoryName == distanceSettings.LocationContainerDebugDirectory)
{
isDefault = null;
personDisplayDirectoryName = null;
files = Directory.GetFiles(yearDirectory, "*", SearchOption.TopDirectoryOnly);
results.AddRange(GetRecords(resultSettings, metadataSettings, compareSettings, isDefault, files, directoryNumber, personKeyFormatted, linksCount, distinct, personDisplayDirectoryName));
files = Directory.GetFiles(yearDirectory, "*.lnk", SearchOption.AllDirectories);
foreach (string file in files)
File.Delete(file);
continue;
}
personNameDirectories = Directory.GetDirectories(yearDirectory, "*", SearchOption.TopDirectoryOnly);
if (personNameDirectories.Length > 1)
throw new NotSupportedException("Try deleting *.lnk files!");
foreach (string personNameDirectory in personNameDirectories)
{
directoryNumber++;
personDisplayDirectoryName = Path.GetFileName(personNameDirectory);
isDefault = IPerson.IsDefaultName(personDisplayDirectoryName) && IPersonBirthday.IsCounterPersonYear(personKeyFormatted[..4]);
if (isDefault.Value && personDisplayDirectoryName.Length == 1)
{
if (personKeyFormatted.Length != peopleSettings.PersonBirthdayFormat.Length || !DateTime.TryParseExact(personKeyFormatted, peopleSettings.PersonBirthdayFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out dateTime))
continue;
checkDirectory = Path.Combine(yearDirectory, $"X+{dateTime.Ticks}");
if (Directory.Exists(checkDirectory))
{
Directory.Delete(yearDirectory, recursive: true);
continue;
}
Directory.Move(personNameDirectory, checkDirectory);
if (!check)
check = true;
continue;
}
if (isDefault.Value && (ticksDirectory.DirectoryDateTime.Hour != 0 || ticksDirectory.DirectoryDateTime.Minute != 0 || ticksDirectory.DirectoryDateTime.Second != 0))
{
checkDirectory = Path.GetDirectoryName(ticksDirectory.Directory);
if (checkDirectory is null)
continue;
checkDirectory = Path.Combine(checkDirectory, ticksDirectory.AlternateDirectoryDateTime.Ticks.ToString());
if (!Directory.Exists(checkDirectory))
_ = Directory.CreateDirectory(checkDirectory);
checkDirectory = Path.Combine(checkDirectory, personKeyFormatted);
if (!Directory.Exists(checkDirectory))
{
Directory.Move(personKeyFormattedDirectory, checkDirectory);
if (!check)
check = true;
break;
}
}
files = Directory.GetFiles(personNameDirectory, "*", SearchOption.TopDirectoryOnly);
if (isReservedDirectoryName && files.Length > 0)
throw new Exception($"Move personKey directories up one from {nameof(Shared.Models.Stateless.IMapLogic.Sorting)} and delete {nameof(Shared.Models.Stateless.IMapLogic.Sorting)} directory!");
if (personKeyFormatted == manualCopyHumanized && files.Length > 0)
throw new Exception($"Move personKey directories up one from {manualCopyHumanized} and delete {manualCopyHumanized} directory!");
if (personKeyFormatted == forceSingleImageHumanized && files.Length > 0)
throw new Exception($"Move personKey directories up one from {forceSingleImageHumanized} and delete {forceSingleImageHumanized} directory!");
if (!isDefault.Value)
{
if (readOnlyCollections.PersonKeyFormattedToNewestPersonKeyFormatted.Count > 0 && newestPersonKeyFormatted is null)
files = RenameBirth(files);
else if (newestPersonKeyFormatted is not null && personKeyFormatted != newestPersonKeyFormatted)
{
if (!check)
check = true;
MovedToNewestPersonKeyFormatted(personKeyFormatted, newestPersonKeyFormatted, ticksDirectory, personKeyFormattedDirectory);
continue;
}
}
if (personKeyFormatted.Length != peopleSettings.PersonBirthdayFormat.Length)
continue;
if (personDisplayDirectoryName.Length == 1 || isDefault.Value || !readOnlyCollections.PersonKeyFormattedCollection.Contains(personKeyFormatted))
personFirstInitialDirectory = personNameDirectory;
else
{
personFirstInitial = personDisplayDirectoryName[..1];
if (personFirstInitial.All(char.IsDigit))
{
foreach (string file in files)
File.Delete(file);
files = Directory.GetFiles(personNameDirectory, "*", SearchOption.AllDirectories);
foreach (string file in files)
File.Delete(file);
_ = IPath.DeleteEmptyDirectories(personNameDirectory);
continue;
}
personFirstInitialDirectory = Path.Combine(yearDirectory, personFirstInitial.ToString());
if (Directory.Exists(personFirstInitialDirectory))
throw new Exception("Forgot to ...");
Directory.Move(personNameDirectory, personFirstInitialDirectory);
files = Directory.GetFiles(personFirstInitialDirectory, "*", SearchOption.TopDirectoryOnly);
}
results.AddRange(GetRecords(resultSettings, metadataSettings, compareSettings, isDefault, files, directoryNumber, personKeyFormatted, linksCount, distinct, personDisplayDirectoryName));
personNameLinkDirectories = Directory.GetDirectories(personFirstInitialDirectory, "*", SearchOption.TopDirectoryOnly);
foreach (string personNameLinkDirectory in personNameLinkDirectories)
{
files = Directory.GetFiles(personNameLinkDirectory, "*", SearchOption.TopDirectoryOnly);
foreach (string file in files)
{
if (!file.EndsWith(".lnk"))
continue;
File.Delete(file);
}
_ = IPath.DeleteEmptyDirectories(personNameLinkDirectory);
}
_ = IPath.DeleteEmptyDirectories(personFirstInitialDirectory);
}
_ = IPath.DeleteEmptyDirectories(yearDirectory);
}
_ = IPath.DeleteEmptyDirectories(personKeyFormattedDirectory);
}
_ = IPath.DeleteEmptyDirectories(ticksDirectory.Directory);
_ = IPath.DeleteEmptyDirectories(ticksDirectory.Directory);
}
if (check)
continue;
break;
}
return results.AsReadOnly();
}
}

View File

@ -6,12 +6,9 @@
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<TargetFramework>net9.0</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<HoursSinceNovember122024>$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1731369600)), 3600))))</HoursSinceNovember122024>
</PropertyGroup>
<PropertyGroup>
<PackageId>Phares.AA.Face</PackageId>
<Version>9.0.104.$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1731369600)), 3600))))</Version>
<Version>8.0.112.0</Version>
<Company>Phares</Company>
<Authors>Mike Phares</Authors>
<IncludeSymbols>true</IncludeSymbols>

View File

@ -6,13 +6,10 @@
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<TargetFramework>net9.0</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<HoursSinceNovember122024>$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1731369600)), 3600))))</HoursSinceNovember122024>
</PropertyGroup>
<PropertyGroup>
<PackageId>Phares.AA.FaceRecognitionDotNet</PackageId>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<Version>9.0.104.$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1731369600)), 3600))))</Version>
<Version>9.0.100.1</Version>
<Authors>Mike Phares</Authors>
<Company>Phares</Company>
<IncludeSymbols>true</IncludeSymbols>

View File

@ -4,14 +4,11 @@
<Nullable>enable</Nullable>
<OutputType>library</OutputType>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<HoursSinceNovember142023>$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1699920000)), 3600))))</HoursSinceNovember142023>
<TargetFramework>net9.0</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<PackageId>Phares.AA.Metadata</PackageId>
<Version>8.0.114.$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1699920000)), 3600))))</Version>
<Version>8.0.112.0</Version>
<Company>Phares</Company>
<Authors>Mike Phares</Authors>
<IncludeSymbols>true</IncludeSymbols>
@ -38,11 +35,11 @@
<SupportedPlatform Include="browser" />
</ItemGroup>
<ItemGroup>
<None Include=".vscode\read-me.md" Pack="true" PackagePath="\" />
<None Include=".vscode\read-me.md" Pack="true" PackagePath="\"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="MetadataExtractor" Version="2.8.1" />
<PackageReference Include="System.Text.Json" Version="9.0.3" />
<PackageReference Include="System.Text.Json" Version="8.0.5" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.1" />
</ItemGroup>
<ItemGroup>

View File

@ -11,7 +11,7 @@ public class A_Metadata
private readonly ResultSettings _ResultSettings;
private readonly MetadataSettings _MetadataSettings;
private readonly ReadOnlyDictionary<int, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> _ResultSingletonFileGroups;
private readonly ReadOnlyDictionary<int, ReadOnlyDictionary<string, string[]>> _FileGroups;
public A_Metadata(ResultSettings resultSettings, MetadataSettings metadataSettings)
{
@ -23,21 +23,46 @@ public class A_Metadata
includeResizeGroup: false,
includeModel: false,
includePredictorModel: false);
Dictionary<int, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> results = [];
ReadOnlyDictionary<int, ReadOnlyDictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>>> keyValuePairs = IPath.GetKeyValuePairs(resultSettings, aResultsFullGroupDirectory, [resultSettings.ResultSingleton]);
foreach (KeyValuePair<int, ReadOnlyDictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>>> keyValuePair in keyValuePairs)
_FileGroups = IPath.GetKeyValuePairs(resultSettings, aResultsFullGroupDirectory, [resultSettings.ResultSingleton]);
}
private MinimumYearAndPathCombined GetMinimumYearAndPathCombined(ResultSettings resultSettings, FilePath filePath)
{
MinimumYearAndPathCombined result;
(_, int directoryIndex) = IPath.GetDirectoryNameAndIndex(resultSettings, filePath);
DateTime minimumDateTime = new(filePath.CreationTicks < filePath.LastWriteTicks ? filePath.CreationTicks : filePath.LastWriteTicks);
int minimumYear = minimumDateTime.Year < resultSettings.EpicYear ? resultSettings.EpicYear : minimumDateTime.Year;
result = new(minimumYear, Path.Combine(_FileGroups[minimumYear][_ResultSettings.ResultSingleton][directoryIndex], $"{filePath.NameWithoutExtension}{filePath.ExtensionLowered}.json"));
return result;
}
private (int, string) GetJsonFile(ResultSettings resultSettings, FilePath filePath, ExifDirectory exifDirectory)
{
string? result;
DateTime? dateTime;
dateTime = IDate.GetDateTimeOriginal(exifDirectory);
dateTime ??= IDate.GetMinimum(exifDirectory);
(_, int directoryIndex) = IPath.GetDirectoryNameAndIndex(resultSettings, filePath);
int exifYear = dateTime.Value.Year < resultSettings.EpicYear ? resultSettings.EpicYear : dateTime.Value.Year;
result = Path.Combine(_FileGroups[exifYear][_ResultSettings.ResultSingleton][directoryIndex], $"{filePath.NameWithoutExtension}{filePath.ExtensionLowered}.json");
return new(exifYear, result);
}
private static (string, ExifDirectory?) Get(string jsonFile)
{
ExifDirectory? result;
string json = File.ReadAllText(jsonFile);
try
{
foreach (KeyValuePair<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> keyValue in keyValuePair.Value)
{
if (keyValue.Key == resultSettings.ResultSingleton)
results.Add(keyValuePair.Key, keyValue.Value);
else
throw new Exception();
}
result = JsonSerializer.Deserialize(json, ExifDirectorySourceGenerationContext.Default.ExifDirectory);
if (result is null)
throw new Exception();
}
_ResultSingletonFileGroups = new(results);
ReadOnlyCollection<string> directories = new([Path.Combine(aResultsFullGroupDirectory, resultSettings.ResultSingleton)]);
IPath.CreateDirectories(directories);
catch (Exception)
{
result = null;
}
return new(json, result);
}
public (MinimumYearAndPathCombined, ExifDirectory) GetMetadataCollection(ResultSettings resultSettings, MetadataSettings metadataSettings, FilePath filePath)
@ -111,45 +136,19 @@ public class A_Metadata
return new(minimumYearAndPathCombined, result);
}
private MinimumYearAndPathCombined GetMinimumYearAndPathCombined(ResultSettings resultSettings, FilePath filePath)
private static Stream GetStream(HttpClient httpClient, FilePath filePath)
{
MinimumYearAndPathCombined result;
CombinedEnumAndIndex cei = IPath.GetCombinedEnumAndIndex(resultSettings, filePath);
DateTime minimumDateTime = new(filePath.CreationTicks < filePath.LastWriteTicks ? filePath.CreationTicks : filePath.LastWriteTicks);
int minimumYear = minimumDateTime.Year < resultSettings.EpicYear ? resultSettings.EpicYear : minimumDateTime.Year;
result = new(minimumYear, Path.Combine(_ResultSingletonFileGroups[minimumYear][cei.Enum][cei.Index], $"{filePath.NameWithoutExtension}{filePath.ExtensionLowered}.json"));
Stream result;
Task<HttpResponseMessage> httpResponseMessage = httpClient.GetAsync(filePath.FullName);
httpResponseMessage.Wait();
Task task = httpResponseMessage.Result.Content.LoadIntoBufferAsync();
task.Wait();
Task<Stream> stream = httpResponseMessage.Result.Content.ReadAsStreamAsync();
stream.Wait();
result = stream.Result;
return result;
}
private (int, string) GetJsonFile(ResultSettings resultSettings, FilePath filePath, ExifDirectory exifDirectory)
{
string? result;
DateTime? dateTime;
dateTime = IDate.GetDateTimeOriginal(exifDirectory);
dateTime ??= IDate.GetMinimum(exifDirectory);
CombinedEnumAndIndex cei = IPath.GetCombinedEnumAndIndex(resultSettings, filePath);
int exifYear = dateTime.Value.Year < resultSettings.EpicYear ? resultSettings.EpicYear : dateTime.Value.Year;
result = Path.Combine(_ResultSingletonFileGroups[exifYear][cei.Enum][cei.Index], $"{filePath.NameWithoutExtension}{filePath.ExtensionLowered}.json");
return new(exifYear, result);
}
private static (string, ExifDirectory?) Get(string jsonFile)
{
ExifDirectory? result;
string json = File.ReadAllText(jsonFile);
try
{
result = JsonSerializer.Deserialize(json, ExifDirectorySourceGenerationContext.Default.ExifDirectory);
if (result is null)
throw new Exception();
}
catch (Exception)
{
result = null;
}
return new(json, result);
}
public (MinimumYearAndPathCombined, ExifDirectory) GetMetadataCollection(ResultSettings resultSettings, MetadataSettings metadataSettings, HttpClient? httpClient, FilePath filePath)
{
ExifDirectory result;
@ -167,17 +166,4 @@ public class A_Metadata
return new(minimumYearAndPathCombined, result);
}
private static Stream GetStream(HttpClient httpClient, FilePath filePath)
{
Stream result;
Task<HttpResponseMessage> httpResponseMessage = httpClient.GetAsync(filePath.FullName);
httpResponseMessage.Wait();
Task task = httpResponseMessage.Result.Content.LoadIntoBufferAsync();
task.Wait();
Task<Stream> stream = httpResponseMessage.Result.Content.ReadAsStreamAsync();
stream.Wait();
result = stream.Result;
return result;
}
}

View File

@ -54,7 +54,51 @@ internal static class Get
return results.AsReadOnly();
}
internal static Action<string> SetExifDirectoryCollection(IWindows windows, ResultSettings resultSettings, MetadataSettings metadataSettings, A_Metadata metadata, List<string> distinct, List<MetadataGroup> metadataGroups)
internal static Action<string> SetExifDirectoryCollection(IRename rename, ResultSettings resultSettings, MetadataSettings metadataSettings, IRenameSettings renameSettings, A_Metadata metadata, List<string> distinct, List<MetadataGroup> metadataGroups)
{
return file =>
{
rename.Tick();
ExifDirectory exifDirectory;
MetadataGroup metadataGroup;
DeterministicHashCode deterministicHashCode;
FileHolder fileHolder = FileHolder.Get(file);
MinimumYearAndPathCombined minimumYearAndPathCombined;
bool fastForwardMovingPictureExpertsGroupUsed;
FilePath? fastForwardMovingPictureExpertsGroupFilePath;
ReadOnlyCollection<string>? fastForwardMovingPictureExpertsGroupFiles;
FilePath filePath = FilePath.Get(resultSettings, metadataSettings, fileHolder, index: null);
string key = $"{Path.Combine(fileHolder.DirectoryFullPath ?? throw new NotSupportedException(), fileHolder.NameWithoutExtension)}";
if (distinct.Contains(key))
throw new NotSupportedException("Turn off parallelism when sidecar files are present!");
if (renameSettings.SkipIdFiles && filePath.Id is not null && (filePath.IsIntelligentIdFormat || filePath.SortOrder is null))
return;
if (filePath.Id is not null)
{
fastForwardMovingPictureExpertsGroupFiles = null;
deterministicHashCode = new(null, filePath.Id, null);
}
else
{
fastForwardMovingPictureExpertsGroupFiles = rename.ConvertAndGetFastForwardMovingPictureExpertsGroupFiles(renameSettings, filePath);
fastForwardMovingPictureExpertsGroupFilePath = fastForwardMovingPictureExpertsGroupFiles.Count == 0 ? null : FilePath.Get(resultSettings, metadataSettings, FileHolder.Get(fastForwardMovingPictureExpertsGroupFiles[0]), index: null);
deterministicHashCode = fastForwardMovingPictureExpertsGroupFilePath is null ? rename.GetDeterministicHashCode(filePath) : rename.GetDeterministicHashCode(fastForwardMovingPictureExpertsGroupFilePath);
}
filePath = FilePath.Get(filePath, deterministicHashCode);
fastForwardMovingPictureExpertsGroupUsed = fastForwardMovingPictureExpertsGroupFiles is not null && fastForwardMovingPictureExpertsGroupFiles.Count > 0;
(minimumYearAndPathCombined, exifDirectory) = metadata.GetMetadataCollection(resultSettings, metadataSettings, filePath);
metadataGroup = new(fastForwardMovingPictureExpertsGroupUsed, filePath, minimumYearAndPathCombined, exifDirectory, new([]));
lock (metadataGroups)
metadataGroups.Add(metadataGroup);
if (fastForwardMovingPictureExpertsGroupUsed && fastForwardMovingPictureExpertsGroupFiles is not null)
{
foreach (string fastForwardMovingPictureExpertsGroupFile in fastForwardMovingPictureExpertsGroupFiles)
File.Delete(fastForwardMovingPictureExpertsGroupFile);
}
};
}
internal static Action<string> SetExifDirectoryCollection(IWindows windows, ResultSettings resultSettings, MetadataSettings metadataSettings, IWindowsSettings windowsSettings, A_Metadata metadata, List<string> distinct, List<MetadataGroup> metadataGroups)
{
return file =>
{
@ -81,7 +125,7 @@ internal static class Get
}
else
{
fastForwardMovingPictureExpertsGroupFiles = windows.ConvertAndGetFastForwardMovingPictureExpertsGroupFiles(resultSettings, httpClient, filePath);
fastForwardMovingPictureExpertsGroupFiles = windows.ConvertAndGetFastForwardMovingPictureExpertsGroupFiles(windowsSettings, httpClient, filePath);
fastForwardMovingPictureExpertsGroupFilePath = fastForwardMovingPictureExpertsGroupFiles.Count == 0 ? null : FilePath.Get(resultSettings, metadataSettings, FileHolder.Get(fastForwardMovingPictureExpertsGroupFiles[0]), index: null);
deterministicHashCode = fastForwardMovingPictureExpertsGroupFilePath is null ? windows.GetDeterministicHashCode(httpClient, filePath) : windows.GetDeterministicHashCode(httpClient, fastForwardMovingPictureExpertsGroupFilePath);
}
@ -101,48 +145,4 @@ internal static class Get
};
}
internal static Action<string> SetExifDirectoryCollection(IRename rename, ResultSettings resultSettings, MetadataSettings metadataSettings, IRenameSettings renameSettings, A_Metadata metadata, List<string> distinct, List<MetadataGroup> metadataGroups)
{
return file =>
{
rename.Tick();
ExifDirectory exifDirectory;
MetadataGroup metadataGroup;
DeterministicHashCode deterministicHashCode;
FileHolder fileHolder = FileHolder.Get(file);
bool fastForwardMovingPictureExpertsGroupUsed;
MinimumYearAndPathCombined minimumYearAndPathCombined;
FilePath? fastForwardMovingPictureExpertsGroupFilePath;
ReadOnlyCollection<string>? fastForwardMovingPictureExpertsGroupFiles;
FilePath filePath = FilePath.Get(resultSettings, metadataSettings, fileHolder, index: null);
string key = $"{Path.Combine(fileHolder.DirectoryFullPath ?? throw new NotSupportedException(), fileHolder.NameWithoutExtension)}";
if (distinct.Contains(key))
throw new NotSupportedException("Turn off parallelism when sidecar files are present!");
if (renameSettings.SkipIdFiles && filePath.Id is not null && (filePath.IsIntelligentIdFormat || filePath.SortOrder is null))
return;
if (filePath.Id is not null)
{
fastForwardMovingPictureExpertsGroupFiles = null;
deterministicHashCode = new(null, filePath.Id, null);
}
else
{
fastForwardMovingPictureExpertsGroupFiles = rename.ConvertAndGetFastForwardMovingPictureExpertsGroupFiles(resultSettings, filePath);
fastForwardMovingPictureExpertsGroupFilePath = fastForwardMovingPictureExpertsGroupFiles.Count == 0 ? null : FilePath.Get(resultSettings, metadataSettings, FileHolder.Get(fastForwardMovingPictureExpertsGroupFiles[0]), index: null);
deterministicHashCode = fastForwardMovingPictureExpertsGroupFilePath is null ? rename.GetDeterministicHashCode(filePath) : rename.GetDeterministicHashCode(fastForwardMovingPictureExpertsGroupFilePath);
}
filePath = FilePath.Get(filePath, deterministicHashCode);
fastForwardMovingPictureExpertsGroupUsed = fastForwardMovingPictureExpertsGroupFiles is not null && fastForwardMovingPictureExpertsGroupFiles.Count > 0;
(minimumYearAndPathCombined, exifDirectory) = metadata.GetMetadataCollection(resultSettings, metadataSettings, filePath);
metadataGroup = new(fastForwardMovingPictureExpertsGroupUsed, filePath, minimumYearAndPathCombined, exifDirectory, new([]));
lock (metadataGroups)
metadataGroups.Add(metadataGroup);
if (fastForwardMovingPictureExpertsGroupUsed && fastForwardMovingPictureExpertsGroupFiles is not null)
{
foreach (string fastForwardMovingPictureExpertsGroupFile in fastForwardMovingPictureExpertsGroupFiles)
File.Delete(fastForwardMovingPictureExpertsGroupFile);
}
};
}
}

View File

@ -72,9 +72,9 @@ public interface IMetadata
static ReadOnlyDictionary<string, List<FileHolder>> GetKeyValuePairs(IEnumerable<NginxFileSystem> collection) =>
Get.GetKeyValuePairs(collection);
Action<string> TestStatic_SetExifDirectoryCollection(IWindows windows, ResultSettings resultSettings, MetadataSettings metadataSettings, A_Metadata metadata, List<string> distinct, List<MetadataGroup> metadataGroups) =>
SetExifDirectoryCollection(windows, resultSettings, metadataSettings, metadata, distinct, metadataGroups);
static Action<string> SetExifDirectoryCollection(IWindows windows, ResultSettings resultSettings, MetadataSettings metadataSettings, A_Metadata metadata, List<string> distinct, List<MetadataGroup> metadataGroups) =>
Get.SetExifDirectoryCollection(windows, resultSettings, metadataSettings, metadata, distinct, metadataGroups);
Action<string> TestStatic_SetExifDirectoryCollection(IWindows windows, ResultSettings resultSettings, MetadataSettings metadataSettings, IWindowsSettings windowsSettings, A_Metadata metadata, List<string> distinct, List<MetadataGroup> metadataGroups) =>
SetExifDirectoryCollection(windows, resultSettings, metadataSettings, windowsSettings, metadata, distinct, metadataGroups);
static Action<string> SetExifDirectoryCollection(IWindows windows, ResultSettings resultSettings, MetadataSettings metadataSettings, IWindowsSettings windowsSettings, A_Metadata metadata, List<string> distinct, List<MetadataGroup> metadataGroups) =>
Get.SetExifDirectoryCollection(windows, resultSettings, metadataSettings, windowsSettings, metadata, distinct, metadataGroups);
}

View File

@ -6,12 +6,9 @@
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<TargetFramework>net9.0</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<HoursSinceNovember122024>$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1731369600)), 3600))))</HoursSinceNovember122024>
</PropertyGroup>
<PropertyGroup>
<PackageId>Phares.AA.People</PackageId>
<Version>9.0.104.$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1731369600)), 3600))))</Version>
<Version>8.0.112.0</Version>
<Company>Phares</Company>
<Authors>Mike Phares</Authors>
<IncludeSymbols>true</IncludeSymbols>

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
@ -8,12 +8,9 @@
<UserSecretsIdOld>fa0fa59b-afe4-4960-9afc-18fcbc7fb41b</UserSecretsIdOld>
<UserSecretsId>843db3e1-e18f-4cba-8b00-967529a32635</UserSecretsId>
</PropertyGroup>
<PropertyGroup>
<HoursSinceNovember122024>$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1731369600)), 3600))))</HoursSinceNovember122024>
</PropertyGroup>
<PropertyGroup>
<PackageId>Phares.View.by.Distance.Rename</PackageId>
<Version>9.0.104.$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1731369600)), 3600))))</Version>
<Version>8.0.112.0</Version>
<Company>Phares</Company>
<Authors>Mike Phares</Authors>
<IncludeSymbols>true</IncludeSymbols>
@ -40,10 +37,10 @@
<SupportedPlatform Include="browser" />
</ItemGroup>
<ItemGroup>
<None Include=".vscode\read-me.md" Pack="true" PackagePath="\" />
<None Include=".vscode\read-me.md" Pack="true" PackagePath="\"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="CliWrap" Version="3.8.2" />
<PackageReference Include="CliWrap" Version="3.7.1" />
<PackageReference Include="runtime.win-x64.Microsoft.DotNet.ILCompiler" Version="8.0.12" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="8.0.1" />

View File

@ -6,20 +6,21 @@ namespace View_by_Distance.Rename.Models;
public record RenameSettings(string Company,
string DefaultMaker,
Dictionary<string, string?> DirectoryDictionary,
string? FirstPassFile,
bool ForceNewId,
string[] IgnoreExtensions,
bool InPlace,
bool InPlaceMoveDirectory,
bool InPlaceWithOriginalName,
bool JustMediaDate,
int MaxDegreeOfParallelism,
int MaxMilliSecondsPerCall,
bool OnlySaveIdentifiersToDisk,
string RelativePropertyCollectionFile,
bool RequireRootDirectoryExists,
string[] SidecarExtensions,
bool SkipIdFiles) : Shared.Models.Properties.IRenameSettings
bool SkipIdFiles,
string[] ValidImageFormatExtensions,
string[] ValidVideoFormatExtensions) : Shared.Models.Properties.IRenameSettings
{
public override string ToString()

View File

@ -11,6 +11,7 @@ using View_by_Distance.Metadata.Models;
using View_by_Distance.Metadata.Models.Stateless;
using View_by_Distance.Rename.Models;
using View_by_Distance.Shared.Models;
using View_by_Distance.Shared.Models.Properties;
using View_by_Distance.Shared.Models.Stateless;
namespace View_by_Distance.Rename;
@ -34,58 +35,39 @@ public partial class Rename : IRename, IDisposable
private ProgressBar? _ProgressBar;
private readonly ProgressBarOptions _ProgressBarOptions;
public Rename(List<string> args, ILogger<Program>? logger, AppSettings appSettings, bool isSilent, IConsole console)
{
if (isSilent)
{ }
if (args is null)
throw new NullReferenceException(nameof(args));
if (console is null)
throw new NullReferenceException(nameof(console));
IRename rename = this;
long ticks = DateTime.Now.Ticks;
_ProgressBarOptions = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true };
RenameWork(logger, appSettings, rename, ticks);
}
void IRename.Tick() =>
_ProgressBar?.Tick();
void IDisposable.Dispose()
{
_ProgressBar?.Dispose();
GC.SuppressFinalize(this);
}
void IRename.ConstructProgressBar(int maxTicks, string message)
{
_ProgressBar?.Dispose();
_ProgressBar = new(maxTicks, message, _ProgressBarOptions);
}
DeterministicHashCode IRename.GetDeterministicHashCode(FilePath filePath)
void IDisposable.Dispose()
{
DeterministicHashCode result;
int? id;
int? width;
int? height;
try
{
#pragma warning disable CA1416
using Image image = Image.FromFile(filePath.FullName);
width = image.Width;
height = image.Height;
using Bitmap bitmap = new(image);
Rectangle rectangle = new(0, 0, image.Width, image.Height);
BitmapData bitmapData = bitmap.LockBits(rectangle, ImageLockMode.ReadOnly, bitmap.PixelFormat);
IntPtr intPtr = bitmapData.Scan0;
int length = bitmapData.Stride * bitmap.Height;
byte[] bytes = new byte[length];
Marshal.Copy(intPtr, bytes, 0, length);
bitmap.UnlockBits(bitmapData);
#pragma warning restore CA1416
id = IId.GetDeterministicHashCode(bytes);
}
catch (Exception)
{
id = null;
width = null;
height = null;
}
result = new(height, id, width);
return result;
_ProgressBar?.Dispose();
GC.SuppressFinalize(this);
}
ReadOnlyCollection<string> IRename.ConvertAndGetFastForwardMovingPictureExpertsGroupFiles(ResultSettings resultSettings, FilePath filePath)
ReadOnlyCollection<string> IRename.ConvertAndGetFastForwardMovingPictureExpertsGroupFiles(IRenameSettings renameSettings, FilePath filePath)
{
List<string> results = [];
bool isValidVideoFormatExtensions = resultSettings.ValidVideoFormatExtensions.Contains(filePath.ExtensionLowered);
bool isValidVideoFormatExtensions = renameSettings.ValidVideoFormatExtensions.Contains(filePath.ExtensionLowered);
if (isValidVideoFormatExtensions)
{
bool check;
@ -115,259 +97,41 @@ public partial class Rename : IRename, IDisposable
return results.AsReadOnly();
}
public Rename(List<string> args, ILogger<Program>? logger, AppSettings appSettings, bool isSilent, IConsole console)
{
if (isSilent)
{ }
if (args is null)
throw new NullReferenceException(nameof(args));
if (console is null)
throw new NullReferenceException(nameof(console));
IRename rename = this;
LogNetToHoursSince(logger);
long ticks = DateTime.Now.Ticks;
_ProgressBarOptions = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true };
RenameWork(logger, appSettings, rename, ticks);
}
#pragma warning disable CA1416
private static void LogNetToHoursSince(ILogger<Program>? logger)
DeterministicHashCode IRename.GetDeterministicHashCode(FilePath filePath)
{
double secondsInAHour = 3600f;
long epoch = new DateTime(1970, 1, 1).Ticks;
long net8ReleaseDate = new DateTime(2023, 11, 14).Ticks;
long net9ReleaseDate = new DateTime(2024, 11, 12).Ticks;
double net8TotalSeconds = new TimeSpan(net8ReleaseDate - epoch).TotalSeconds;
double net9TotalSeconds = new TimeSpan(net9ReleaseDate - epoch).TotalSeconds;
logger?.LogInformation("It has been {net8TotalSeconds} seconds since net8 was released", net8TotalSeconds);
logger?.LogInformation("It has been {net9TotalSeconds} seconds since net9 was released", net9TotalSeconds);
double net8TotalHours = Math.Floor((DateTimeOffset.UtcNow.ToUnixTimeSeconds() - net8TotalSeconds) / secondsInAHour);
double net9TotalHours = Math.Floor((DateTimeOffset.UtcNow.ToUnixTimeSeconds() - net9TotalSeconds) / secondsInAHour);
logger?.LogInformation("It has been {net8TotalHours} hours since net8 was released", net8TotalHours);
logger?.LogInformation("It has been {net9TotalHours} hours since net9 was released", net9TotalHours);
}
private void RenameWork(ILogger<Program>? logger, AppSettings appSettings, IRename rename, long ticks)
{
ReadOnlyCollection<int> ids = GetIds(appSettings.RenameSettings);
string sourceDirectory = Path.GetFullPath(appSettings.ResultSettings.RootDirectory);
if (!Directory.Exists(sourceDirectory))
_ = Directory.CreateDirectory(sourceDirectory);
logger?.LogInformation("{Ticks} {RootDirectory}", ticks, sourceDirectory);
ReadOnlyCollection<string> files = Directory.GetFiles(sourceDirectory, "*", SearchOption.AllDirectories).ToArray().AsReadOnly();
if (files.Count > 0)
_ = IPath.DeleteEmptyDirectories(appSettings.ResultSettings.RootDirectory);
ReadOnlyCollection<Record> recordCollection = GetRecordCollection(logger, appSettings, rename, ticks, ids, sourceDirectory, files);
SaveIdentifiersToDisk(ticks, appSettings, recordCollection);
if (appSettings.RenameSettings.InPlace || appSettings.RenameSettings.InPlaceWithOriginalName)
{
if (recordCollection.Count > 0)
recordCollection = new([]);
string aMetadataSingletonDirectory = IResult.GetResultsGroupDirectory(appSettings.ResultSettings, nameof(A_Metadata));
_ = IPath.DeleteEmptyDirectories(aMetadataSingletonDirectory);
}
if (!appSettings.RenameSettings.OnlySaveIdentifiersToDisk)
{
DirectoryInfo directoryInfo = new(sourceDirectory);
ReadOnlyCollection<ToDo> toDoCollection = GetToDoCollection(appSettings, directoryInfo, ids, files, recordCollection);
ReadOnlyCollection<string> lines = RenameFilesInDirectories(appSettings.RenameSettings, rename, toDoCollection);
if (lines.Count != 0)
{
File.WriteAllLines($"D:/Tmp/Phares/{DateTime.Now.Ticks}.tsv", lines);
_ = IPath.DeleteEmptyDirectories(directoryInfo.FullName);
}
}
}
private static ReadOnlyCollection<int> GetIds(RenameSettings renameSettings)
{
ReadOnlyCollection<int> results;
string? propertyCollectionFile = string.IsNullOrEmpty(renameSettings.RelativePropertyCollectionFile) ? null : renameSettings.RelativePropertyCollectionFile;
string? json = !File.Exists(propertyCollectionFile) ? null : File.ReadAllText(propertyCollectionFile);
Identifier[]? identifiers = json is null ? null : JsonSerializer.Deserialize(json, IdentifierCollectionSourceGenerationContext.Default.IdentifierArray);
if (identifiers is null && !string.IsNullOrEmpty(renameSettings.RelativePropertyCollectionFile))
throw new Exception($"Invalid {nameof(renameSettings.RelativePropertyCollectionFile)}");
results = identifiers is null ? new([]) : new((from l in identifiers select l.Id).ToArray());
return results;
}
private ReadOnlyCollection<Record> GetRecordCollection(ILogger<Program>? logger, AppSettings appSettings, IRename rename, long ticks, ReadOnlyCollection<int> ids, string sourceDirectory, ReadOnlyCollection<string> files)
{
ReadOnlyCollection<Record> results;
List<FirstPass> collection;
string? checkFile = string.IsNullOrEmpty(appSettings.RenameSettings.FirstPassFile) ? null : Path.Combine(sourceDirectory, appSettings.RenameSettings.FirstPassFile);
if (string.IsNullOrEmpty(checkFile) || !File.Exists(checkFile))
collection = GetRecordCollection(logger, appSettings, rename, ticks, ids, sourceDirectory, files, checkFile);
else
{
string json = File.ReadAllText(checkFile);
collection = JsonSerializer.Deserialize(json, FirstPassCollectionSourceGenerationContext.Default.ListFirstPass) ?? throw new Exception();
}
if (appSettings.RenameSettings.JustMediaDate)
results = new([]);
else
results = GetRecordCollection(appSettings, collection);
return results;
}
private List<FirstPass> GetRecordCollection(ILogger<Program>? logger, AppSettings appSettings, IRename rename, long ticks, ReadOnlyCollection<int> ids, string sourceDirectory, ReadOnlyCollection<string> files, string? checkFile)
{
List<FirstPass> results;
FirstPass firstPass;
A_Metadata metadata = new(appSettings.ResultSettings, appSettings.MetadataSettings);
int appSettingsMaxDegreeOfParallelism = appSettings.RenameSettings.MaxDegreeOfParallelism;
int filesCount = appSettingsMaxDegreeOfParallelism == 1 ? files.Count : 123000;
rename.ConstructProgressBar(filesCount, "EnumerateFiles load");
if (appSettingsMaxDegreeOfParallelism == 1)
{
ReadOnlyDictionary<string, List<FileHolder>> keyValuePairs = IMetadata.GetKeyValuePairs(files);
results = GetFirstPassCollection(logger, appSettings, rename, ticks, ids, metadata, keyValuePairs);
}
else
{
results = [];
List<string> distinct = [];
List<MetadataGroup> metadataGroups = [];
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = appSettingsMaxDegreeOfParallelism };
files.AsParallel().ForAll(IMetadata.SetExifDirectoryCollection(rename, appSettings.ResultSettings, appSettings.MetadataSettings, appSettings.RenameSettings, metadata, distinct, metadataGroups));
Thread.Sleep(500);
foreach (MetadataGroup metadataGroup in metadataGroups)
{
if (metadataGroup.FastForwardMovingPictureExpertsGroupUsed || !appSettings.RenameSettings.InPlaceMoveDirectory || !appSettings.ResultSettings.ValidVideoFormatExtensions.Contains(metadataGroup.FilePath.ExtensionLowered))
firstPass = new(metadataGroup.ExifDirectory, metadataGroup.FastForwardMovingPictureExpertsGroupUsed, metadataGroup.MinimumYearAndPathCombined, metadataGroup.SidecarFiles.ToArray());
else
firstPass = new(metadataGroup.ExifDirectory, FastForwardMovingPictureExpertsGroupUsed: true, metadataGroup.MinimumYearAndPathCombined, metadataGroup.SidecarFiles.ToArray());
results.Add(firstPass);
}
}
if (!string.IsNullOrEmpty(checkFile))
{
string json = JsonSerializer.Serialize(results, FirstPassCollectionSourceGenerationContext.Default.ListFirstPass);
File.WriteAllText(Path.Combine(sourceDirectory, $"{ticks}.json"), json);
}
return results;
}
private List<FirstPass> GetFirstPassCollection(ILogger<Program>? logger, AppSettings appSettings, IRename rename, long ticks, ReadOnlyCollection<int> ids, A_Metadata metadata, ReadOnlyDictionary<string, List<FileHolder>> keyValuePairs)
{
List<FirstPass> results = [];
int index = -1;
TimeSpan timeSpan;
foreach (KeyValuePair<string, List<FileHolder>> keyValuePair in keyValuePairs)
{
index += 1;
rename.Tick();
if (keyValuePair.Value.Count > 1 && !appSettings.RenameSettings.ForceNewId)
{
if (appSettings.RenameSettings.InPlaceMoveDirectory)
continue;
throw new NotSupportedException($"When sidecar files are present {nameof(appSettings.RenameSettings.ForceNewId)} must be true!");
}
if (keyValuePair.Value.Count > 2)
throw new NotSupportedException("Too many sidecar files!");
SetFirstPassCollection(logger, appSettings, rename, ids, metadata, index, keyValuePair, results);
timeSpan = new(DateTime.Now.Ticks - ticks);
if (timeSpan.TotalMilliseconds > appSettings.RenameSettings.MaxMilliSecondsPerCall)
break;
}
return results;
}
private void SetFirstPassCollection(ILogger<Program>? logger, AppSettings appSettings, IRename rename, ReadOnlyCollection<int> ids, A_Metadata metadata, int index, KeyValuePair<string, List<FileHolder>> keyValuePair, List<FirstPass> results)
{
FilePath filePath;
FirstPass firstPass;
string directoryName;
ExifDirectory exifDirectory;
List<FileHolder> sidecarFiles;
DeterministicHashCode deterministicHashCode;
bool fastForwardMovingPictureExpertsGroupUsed;
MinimumYearAndPathCombined minimumYearAndPathCombined;
FilePath? fastForwardMovingPictureExpertsGroupFilePath;
ReadOnlyCollection<string>? fastForwardMovingPictureExpertsGroupFiles;
foreach (FileHolder fileHolder in keyValuePair.Value)
{
if (appSettings.RenameSettings.SidecarExtensions.Contains(fileHolder.ExtensionLowered))
continue;
if (appSettings.ResultSettings.IgnoreExtensions.Contains(fileHolder.ExtensionLowered))
continue;
filePath = FilePath.Get(appSettings.ResultSettings, appSettings.MetadataSettings, fileHolder, index);
if (appSettings.RenameSettings.SkipIdFiles && filePath.Id is not null && (filePath.IsIntelligentIdFormat || filePath.SortOrder is not null))
continue;
if (!appSettings.RenameSettings.ForceNewId && filePath.Id is not null)
{
fastForwardMovingPictureExpertsGroupFiles = null;
deterministicHashCode = new(null, filePath.Id, null);
directoryName = Path.GetFileName(filePath.DirectoryFullPath);
if (appSettings.RenameSettings.InPlaceWithOriginalName || (appSettings.RenameSettings.InPlace && directoryName.EndsWith(filePath.Id.Value.ToString())))
continue;
}
else
{
fastForwardMovingPictureExpertsGroupFiles = rename.ConvertAndGetFastForwardMovingPictureExpertsGroupFiles(appSettings.ResultSettings, filePath);
fastForwardMovingPictureExpertsGroupFilePath = fastForwardMovingPictureExpertsGroupFiles.Count == 0 ? null : FilePath.Get(appSettings.ResultSettings, appSettings.MetadataSettings, FileHolder.Get(fastForwardMovingPictureExpertsGroupFiles[0]), index);
deterministicHashCode = fastForwardMovingPictureExpertsGroupFilePath is null ? rename.GetDeterministicHashCode(filePath) : rename.GetDeterministicHashCode(fastForwardMovingPictureExpertsGroupFilePath);
}
sidecarFiles = [];
filePath = FilePath.Get(filePath, deterministicHashCode);
for (int i = 0; i < keyValuePair.Value.Count; i++)
{
if (appSettings.RenameSettings.JustMediaDate && filePath.HasDateTimeOriginal is not null && filePath.HasDateTimeOriginal.Value)
JustMediaDate(logger, appSettings, metadata, filePath);
if (keyValuePair.Value[i].ExtensionLowered == fileHolder.ExtensionLowered)
continue;
sidecarFiles.Add(keyValuePair.Value[i]);
}
try
{ (minimumYearAndPathCombined, exifDirectory) = metadata.GetMetadataCollection(appSettings.ResultSettings, appSettings.MetadataSettings, filePath); }
catch (Exception)
{
logger?.LogWarning("<{filePath}>", filePath.FullName);
continue;
}
fastForwardMovingPictureExpertsGroupUsed = fastForwardMovingPictureExpertsGroupFiles is not null && fastForwardMovingPictureExpertsGroupFiles.Count > 0;
if (fastForwardMovingPictureExpertsGroupUsed && fastForwardMovingPictureExpertsGroupFiles is not null)
{
foreach (string fastForwardMovingPictureExpertsGroupFile in fastForwardMovingPictureExpertsGroupFiles)
File.Delete(fastForwardMovingPictureExpertsGroupFile);
}
if (appSettings.RenameSettings.InPlace || appSettings.RenameSettings.InPlaceWithOriginalName)
NonParallelismAndInPlace(appSettings, rename, ids, exifDirectory, minimumYearAndPathCombined, fastForwardMovingPictureExpertsGroupUsed, sidecarFiles.ToArray());
if (!fastForwardMovingPictureExpertsGroupUsed && appSettings.RenameSettings.InPlaceMoveDirectory && appSettings.ResultSettings.ValidVideoFormatExtensions.Contains(filePath.ExtensionLowered))
fastForwardMovingPictureExpertsGroupUsed = true;
firstPass = new(exifDirectory, fastForwardMovingPictureExpertsGroupUsed, minimumYearAndPathCombined, sidecarFiles.ToArray());
results.Add(firstPass);
}
}
private void JustMediaDate(ILogger<Program>? logger, AppSettings appSettings, A_Metadata metadata, FilePath filePath)
{
ExifDirectory? exifDirectory;
MinimumYearAndPathCombined? minimumYearAndPathCombined;
DeterministicHashCode result;
int? id;
int? width;
int? height;
try
{ (minimumYearAndPathCombined, exifDirectory) = metadata.GetMetadataCollection(appSettings.ResultSettings, appSettings.MetadataSettings, filePath); }
{
using Image image = Image.FromFile(filePath.FullName);
width = image.Width;
height = image.Height;
using Bitmap bitmap = new(image);
Rectangle rectangle = new(0, 0, image.Width, image.Height);
BitmapData bitmapData = bitmap.LockBits(rectangle, ImageLockMode.ReadOnly, bitmap.PixelFormat);
IntPtr intPtr = bitmapData.Scan0;
int length = bitmapData.Stride * bitmap.Height;
byte[] bytes = new byte[length];
Marshal.Copy(intPtr, bytes, 0, length);
bitmap.UnlockBits(bitmapData);
id = IId.GetDeterministicHashCode(bytes);
}
catch (Exception)
{
exifDirectory = null;
minimumYearAndPathCombined = null;
logger?.LogWarning("<{filePath}>", filePath.FullName);
}
if (exifDirectory is null || minimumYearAndPathCombined is null)
logger?.LogWarning("<{filePath}>", filePath.FullName);
else
{
DateTime? dateTime = IDate.GetDateTimeOriginal(exifDirectory);
if (dateTime is null)
logger?.LogWarning("<{filePath}>", filePath.FullName);
else
{
File.SetCreationTime(filePath.FullName, dateTime.Value);
Thread.Sleep(500);
File.SetLastWriteTime(filePath.FullName, dateTime.Value);
}
id = null;
width = null;
height = null;
}
result = new(height, id, width);
return result;
}
#pragma warning restore CA1416
private void NonParallelismAndInPlace(AppSettings appSettings, IRename rename, ReadOnlyCollection<int> ids, ExifDirectory exifDirectory, MinimumYearAndPathCombined minimumYearAndPathCombined, bool fastForwardMovingPictureExpertsGroupUsed, FileHolder[] sidecarFiles)
{
if (exifDirectory.FilePath.Id is null)
@ -382,7 +146,7 @@ public partial class Rename : IRename, IDisposable
bool hasIgnoreKeyword = appSettings.MetadataSettings.IgnoreRulesKeyWords.Any(keywords.Contains);
string checkFileExtension = exifDirectory.FilePath.ExtensionLowered == jpeg ? jpg : exifDirectory.FilePath.ExtensionLowered;
bool hasDateTimeOriginal = dateTime is not null;
string paddedId = IId.GetPaddedId(appSettings.ResultSettings, appSettings.MetadataSettings, exifDirectory.FilePath.Id.Value, exifDirectory.FilePath.ExtensionLowered, hasIgnoreKeyword, hasDateTimeOriginal, i);
string paddedId = IId.GetPaddedId(appSettings.ResultSettings, appSettings.MetadataSettings, exifDirectory.FilePath.Id.Value, hasIgnoreKeyword, hasDateTimeOriginal, i);
string checkDirectory = appSettings.RenameSettings.InPlaceWithOriginalName ? Path.Combine(exifDirectory.FilePath.DirectoryFullPath, exifDirectory.FilePath.FileNameFirstSegment) : exifDirectory.FilePath.DirectoryFullPath;
string checkFile = Path.Combine(checkDirectory, $"{paddedId}{checkFileExtension}");
if (checkFile != exifDirectory.FilePath.FullName)
@ -412,7 +176,7 @@ public partial class Rename : IRename, IDisposable
JsonFile: minimumYearAndPathCombined.PathCombined);
toDoCollection.AddRange(GetSidecarFiles(appSettings, record, [], checkDirectory, paddedId));
}
_ = RenameFilesInDirectories(appSettings.RenameSettings, rename, toDoCollection.AsReadOnly());
_ = RenameFilesInDirectories(appSettings.RenameSettings, rename, new(toDoCollection));
string jsonFile = Path.Combine(checkDirectory, $"{paddedId}{checkFileExtension}.json");
File.Move(minimumYearAndPathCombined.PathCombined, jsonFile, overwrite: true);
if (appSettings.RenameSettings.InPlaceWithOriginalName && ids.Count > 0)
@ -425,6 +189,92 @@ public partial class Rename : IRename, IDisposable
}
}
private List<FirstPass> GetFirstPassCollection(ILogger<Program>? logger, AppSettings appSettings, IRename rename, long ticks, ReadOnlyCollection<int> ids, IEnumerable<string> files, A_Metadata metadata)
{
List<FirstPass> results = [];
int index = -1;
FilePath filePath;
TimeSpan timeSpan;
FirstPass firstPass;
string directoryName;
ExifDirectory exifDirectory;
List<FileHolder> sidecarFiles;
DeterministicHashCode deterministicHashCode;
bool fastForwardMovingPictureExpertsGroupUsed;
MinimumYearAndPathCombined minimumYearAndPathCombined;
FilePath? fastForwardMovingPictureExpertsGroupFilePath;
ReadOnlyCollection<string>? fastForwardMovingPictureExpertsGroupFiles;
ReadOnlyDictionary<string, List<FileHolder>> keyValuePairs = IMetadata.GetKeyValuePairs(files);
foreach (KeyValuePair<string, List<FileHolder>> keyValuePair in keyValuePairs)
{
index += 1;
rename.Tick();
if (keyValuePair.Value.Count > 1 && !appSettings.RenameSettings.ForceNewId)
{
if (appSettings.RenameSettings.InPlaceMoveDirectory)
continue;
throw new NotSupportedException($"When sidecar files are present {nameof(appSettings.RenameSettings.ForceNewId)} must be true!");
}
if (keyValuePair.Value.Count > 2)
throw new NotSupportedException("Too many sidecar files!");
foreach (FileHolder fileHolder in keyValuePair.Value)
{
if (appSettings.RenameSettings.SidecarExtensions.Contains(fileHolder.ExtensionLowered))
continue;
if (appSettings.RenameSettings.IgnoreExtensions.Contains(fileHolder.ExtensionLowered))
continue;
filePath = FilePath.Get(appSettings.ResultSettings, appSettings.MetadataSettings, fileHolder, index);
if (appSettings.RenameSettings.SkipIdFiles && filePath.Id is not null && (filePath.IsIntelligentIdFormat || filePath.SortOrder is not null))
continue;
if (!appSettings.RenameSettings.ForceNewId && filePath.Id is not null)
{
fastForwardMovingPictureExpertsGroupFiles = null;
deterministicHashCode = new(null, filePath.Id, null);
directoryName = Path.GetFileName(filePath.DirectoryFullPath);
if (appSettings.RenameSettings.InPlaceWithOriginalName || (appSettings.RenameSettings.InPlace && directoryName.EndsWith(filePath.Id.Value.ToString())))
continue;
}
else
{
fastForwardMovingPictureExpertsGroupFiles = rename.ConvertAndGetFastForwardMovingPictureExpertsGroupFiles(appSettings.RenameSettings, filePath);
fastForwardMovingPictureExpertsGroupFilePath = fastForwardMovingPictureExpertsGroupFiles.Count == 0 ? null : FilePath.Get(appSettings.ResultSettings, appSettings.MetadataSettings, FileHolder.Get(fastForwardMovingPictureExpertsGroupFiles[0]), index);
deterministicHashCode = fastForwardMovingPictureExpertsGroupFilePath is null ? rename.GetDeterministicHashCode(filePath) : rename.GetDeterministicHashCode(fastForwardMovingPictureExpertsGroupFilePath);
}
sidecarFiles = [];
filePath = FilePath.Get(filePath, deterministicHashCode);
for (int i = 0; i < keyValuePair.Value.Count; i++)
{
if (keyValuePair.Value[i].ExtensionLowered == fileHolder.ExtensionLowered)
continue;
sidecarFiles.Add(keyValuePair.Value[i]);
}
try
{ (minimumYearAndPathCombined, exifDirectory) = metadata.GetMetadataCollection(appSettings.ResultSettings, appSettings.MetadataSettings, filePath); }
catch (Exception)
{
logger?.LogWarning("<{filePath}>", filePath.FullName);
continue;
}
fastForwardMovingPictureExpertsGroupUsed = fastForwardMovingPictureExpertsGroupFiles is not null && fastForwardMovingPictureExpertsGroupFiles.Count > 0;
if (fastForwardMovingPictureExpertsGroupUsed && fastForwardMovingPictureExpertsGroupFiles is not null)
{
foreach (string fastForwardMovingPictureExpertsGroupFile in fastForwardMovingPictureExpertsGroupFiles)
File.Delete(fastForwardMovingPictureExpertsGroupFile);
}
if (appSettings.RenameSettings.InPlace || appSettings.RenameSettings.InPlaceWithOriginalName)
NonParallelismAndInPlace(appSettings, rename, ids, exifDirectory, minimumYearAndPathCombined, fastForwardMovingPictureExpertsGroupUsed, sidecarFiles.ToArray());
if (!fastForwardMovingPictureExpertsGroupUsed && appSettings.RenameSettings.InPlaceMoveDirectory && appSettings.RenameSettings.ValidVideoFormatExtensions.Contains(filePath.ExtensionLowered))
fastForwardMovingPictureExpertsGroupUsed = true;
firstPass = new(exifDirectory, fastForwardMovingPictureExpertsGroupUsed, minimumYearAndPathCombined, sidecarFiles.ToArray());
results.Add(firstPass);
}
timeSpan = new(DateTime.Now.Ticks - ticks);
if (timeSpan.TotalMilliseconds > appSettings.RenameSettings.MaxMilliSecondsPerCall)
break;
}
return results;
}
private static ReadOnlyCollection<Record> GetRecordCollection(AppSettings appSettings, List<FirstPass> collection)
{
List<Record> results = [];
@ -452,22 +302,152 @@ public partial class Rename : IRename, IDisposable
return results.AsReadOnly();
}
private static void SaveIdentifiersToDisk(long ticks, AppSettings appSettings, ReadOnlyCollection<Record> recordCollection)
private ReadOnlyCollection<Record> GetRecordCollection(ILogger<Program>? logger, AppSettings appSettings, IRename rename, long ticks, ReadOnlyCollection<int> ids, string sourceDirectory, ReadOnlyCollection<string> files)
{
ReadOnlyCollection<Record> results;
FirstPass firstPass;
List<FirstPass> collection;
string? checkFile = string.IsNullOrEmpty(appSettings.RenameSettings.FirstPassFile) ? null : Path.Combine(sourceDirectory, appSettings.RenameSettings.FirstPassFile);
if (!string.IsNullOrEmpty(checkFile) && File.Exists(checkFile))
{
string json = File.ReadAllText(checkFile);
collection = JsonSerializer.Deserialize(json, FirstPassCollectionSourceGenerationContext.Default.ListFirstPass) ?? throw new Exception();
}
else
{
collection = [];
A_Metadata metadata = new(appSettings.ResultSettings, appSettings.MetadataSettings);
int appSettingsMaxDegreeOfParallelism = appSettings.RenameSettings.MaxDegreeOfParallelism;
int filesCount = appSettingsMaxDegreeOfParallelism == 1 ? files.Count : 123000;
rename.ConstructProgressBar(filesCount, "EnumerateFiles load");
if (appSettingsMaxDegreeOfParallelism == 1)
collection.AddRange(GetFirstPassCollection(logger, appSettings, rename, ticks, ids, files, metadata));
else
{
List<string> distinct = [];
List<MetadataGroup> metadataGroups = [];
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = appSettingsMaxDegreeOfParallelism };
files.AsParallel().ForAll(IMetadata.SetExifDirectoryCollection(rename, appSettings.ResultSettings, appSettings.MetadataSettings, appSettings.RenameSettings, metadata, distinct, metadataGroups));
Thread.Sleep(500);
foreach (MetadataGroup metadataGroup in metadataGroups)
{
if (metadataGroup.FastForwardMovingPictureExpertsGroupUsed || !appSettings.RenameSettings.InPlaceMoveDirectory || !appSettings.RenameSettings.ValidVideoFormatExtensions.Contains(metadataGroup.FilePath.ExtensionLowered))
firstPass = new(metadataGroup.ExifDirectory, metadataGroup.FastForwardMovingPictureExpertsGroupUsed, metadataGroup.MinimumYearAndPathCombined, metadataGroup.SidecarFiles.ToArray());
else
firstPass = new(metadataGroup.ExifDirectory, FastForwardMovingPictureExpertsGroupUsed: true, metadataGroup.MinimumYearAndPathCombined, metadataGroup.SidecarFiles.ToArray());
collection.Add(firstPass);
}
}
if (!string.IsNullOrEmpty(checkFile))
{
string json = JsonSerializer.Serialize(collection, FirstPassCollectionSourceGenerationContext.Default.ListFirstPass);
File.WriteAllText(Path.Combine(sourceDirectory, $"{ticks}.json"), json);
}
}
results = GetRecordCollection(appSettings, collection);
return results;
}
private static void VerifyIntMinValueLength(MetadataSettings metadataSettings, ReadOnlyCollection<Record> recordCollection)
{
string paddedId;
Identifier identifier;
List<Identifier> identifiers = [];
string aMetadataCollectionDirectory = IResult.GetResultsDateGroupDirectory(appSettings.ResultSettings, nameof(A_Metadata), appSettings.ResultSettings.ResultCollection);
foreach (Record record in recordCollection)
{
if (record.ExifDirectory.FilePath.Id is null)
continue;
paddedId = IId.GetPaddedId(appSettings.ResultSettings, appSettings.MetadataSettings, record.ExifDirectory.FilePath.Id.Value, record.ExifDirectory.FilePath.ExtensionLowered, record.HasIgnoreKeyword, record.HasDateTimeOriginal, index: null);
identifier = new([], record.HasDateTimeOriginal, record.ExifDirectory.FilePath.Id.Value, record.ExifDirectory.FilePath.Length, paddedId, record.DateTime.Ticks);
identifiers.Add(identifier);
if (metadataSettings.IntMinValueLength < record.ExifDirectory.FilePath.Id.Value.ToString().Length)
throw new NotSupportedException();
}
string json = JsonSerializer.Serialize(identifiers.OrderBy(l => l.PaddedId).ToArray(), IdentifierCollectionSourceGenerationContext.Default.IdentifierArray);
_ = IPath.WriteAllText(Path.Combine(aMetadataCollectionDirectory, $"{ticks}.json"), json, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null);
}
private static string GetTFW(Record record, bool? isWrongYear) =>
string.Concat(record.HasDateTimeOriginal ? "T" : "F", isWrongYear is not null && isWrongYear.Value ? "W" : record.FastForwardMovingPictureExpertsGroupUsed ? "V" : "I");
private static string GetDirectoryName(string year, string tfw, string prefix, string? splat, int seasonValue, string seasonName, string makerSplit) =>
splat is null ? $"{prefix}{year} {tfw}{year}.{seasonValue} {seasonName}{makerSplit}" : $"{prefix}{year} {tfw}{year}{splat}";
private static string? GetCheckDirectory(AppSettings appSettings, DirectoryInfo directoryInfo, Record record, ReadOnlyCollection<int> ids, bool multipleDirectoriesWithFiles, string paddedId)
{
string? result;
string year = record.DateTime.Year.ToString();
string checkDirectoryName = Path.GetFileName(record.ExifDirectory.FilePath.DirectoryFullPath);
if (multipleDirectoriesWithFiles && !checkDirectoryName.Contains(year))
result = null;
else
{
(bool? isWrongYear, string[] years) = IDate.IsWrongYear(directoryInfo, record.ExifDirectory.FilePath, record.ExifDirectory);
if (appSettings.RenameSettings.InPlaceMoveDirectory && !record.ExifDirectory.FilePath.FileNameFirstSegment.Contains(paddedId))
result = null;
else
{
if (record.ExifDirectory.FilePath.FullName[..2] != directoryInfo.FullName[..2])
isWrongYear = null;
string tfw = GetTFW(record, isWrongYear);
string? maker = IMetadata.GetMaker(record.ExifDirectory);
string rootDirectory = appSettings.ResultSettings.RootDirectory;
string[] segments = checkDirectoryName.Split(years, StringSplitOptions.None);
(int seasonValue, string seasonName) = IDate.GetSeason(record.DateTime.DayOfYear);
string? splat = checkDirectoryName.Length > 3 && checkDirectoryName[^3..][1] == '!' ? checkDirectoryName[^3..] : null;
string contains = record.ExifDirectory.FilePath.Id is null || ids.Contains(record.ExifDirectory.FilePath.Id.Value) ? "_ Exists _" : "_ New-Destination _";
string makerSplit = string.IsNullOrEmpty(maker) ? string.IsNullOrEmpty(appSettings.RenameSettings.DefaultMaker) ? string.Empty : appSettings.RenameSettings.DefaultMaker : $" {maker.Split(' ')[0]}";
string directoryName = GetDirectoryName(year, tfw, segments[0], splat, seasonValue, seasonName, makerSplit);
result = Path.GetFullPath(Path.Combine(rootDirectory, contains, directoryName));
}
}
return result;
}
private static List<ToDo> GetSidecarFiles(AppSettings appSettings, Record record, List<string> distinct, string checkDirectory, string paddedId)
{
List<ToDo> results = [];
ToDo toDo;
string checkFile;
FilePath filePath;
string checkFileExtension;
foreach (FileHolder fileHolder in record.SidecarFiles)
{
checkFileExtension = fileHolder.ExtensionLowered;
filePath = FilePath.Get(appSettings.ResultSettings, appSettings.MetadataSettings, fileHolder, index: null);
checkFile = Path.Combine(checkDirectory, $"{paddedId}{checkFileExtension}");
if (checkFile == filePath.FullName)
continue;
if (File.Exists(checkFile))
{
checkFile = string.Concat(checkFile, ".del");
if (File.Exists(checkFile))
continue;
}
if (distinct.Contains(checkFile))
continue;
distinct.Add(checkFile);
toDo = new(Directory: checkDirectory,
FileInfo: new(filePath.FullName),
File: checkFile,
JsonFile: false);
results.Add(toDo);
}
return results;
}
private static bool? GetDirectoryCheck(ResultSettings resultSettings)
{
bool? result = null;
IEnumerable<string> files;
string[] directories = Directory.GetDirectories(resultSettings.RootDirectory, "*", SearchOption.TopDirectoryOnly);
foreach (string directory in directories)
{
files = Directory.EnumerateFiles(directory, "*", SearchOption.AllDirectories);
foreach (string _ in files)
{
if (result is null)
result = false;
else if (result.Value)
result = true;
break;
}
if (result is not null && result.Value)
break;
}
return result;
}
private static ReadOnlyCollection<ToDo> GetToDoCollection(AppSettings appSettings, DirectoryInfo directoryInfo, ReadOnlyCollection<int> ids, ReadOnlyCollection<string> files, ReadOnlyCollection<Record> recordCollection)
@ -479,19 +459,15 @@ public partial class Rename : IRename, IDisposable
string paddedId;
string checkFile;
FilePath filePath;
DateTime? dateTime;
FileInfo[] matches;
string directoryName;
FileHolder fileHolder;
bool? hasIgnoreKeyword;
string? checkDirectory;
CombinedEnumAndIndex cei;
bool? hasDateTimeOriginal;
const string jpg = ".jpg";
string checkFileExtension;
List<string> distinct = [];
const string jpeg = ".jpeg";
string jsonFileSubDirectory;
ReadOnlyCollection<string> keywords;
bool? directoryCheck = GetDirectoryCheck(appSettings.ResultSettings);
VerifyIntMinValueLength(appSettings.MetadataSettings, recordCollection);
bool multipleDirectoriesWithFiles = directoryCheck is not null && directoryCheck.Value;
@ -502,7 +478,7 @@ public partial class Rename : IRename, IDisposable
record = sorted[i];
if (record.ExifDirectory.FilePath.Id is null)
continue;
paddedId = IId.GetPaddedId(appSettings.ResultSettings, appSettings.MetadataSettings, record.ExifDirectory.FilePath.Id.Value, record.ExifDirectory.FilePath.ExtensionLowered, record.HasIgnoreKeyword, record.HasDateTimeOriginal, i);
paddedId = IId.GetPaddedId(appSettings.ResultSettings, appSettings.MetadataSettings, record.ExifDirectory.FilePath.Id.Value, record.HasIgnoreKeyword, record.HasDateTimeOriginal, i);
checkDirectory = GetCheckDirectory(appSettings, directoryInfo, record, ids, multipleDirectoriesWithFiles, paddedId);
if (string.IsNullOrEmpty(checkDirectory))
continue;
@ -517,22 +493,8 @@ public partial class Rename : IRename, IDisposable
if (File.Exists(checkFile))
continue;
}
if (record.ExifDirectory.FilePath.HasDateTimeOriginal is not null && record.ExifDirectory.FilePath.HasIgnoreKeyword is not null)
{
hasIgnoreKeyword = record.ExifDirectory.FilePath.HasIgnoreKeyword;
hasDateTimeOriginal = record.ExifDirectory.FilePath.HasDateTimeOriginal;
}
else
{
dateTime = IDate.GetDateTimeOriginal(record.ExifDirectory);
hasDateTimeOriginal = dateTime is not null;
if (dateTime is null && appSettings.ResultSettings.ValidVideoFormatExtensions.Contains(record.ExifDirectory.FilePath.ExtensionLowered))
continue;
keywords = IMetadata.GetKeywords(record.ExifDirectory);
hasIgnoreKeyword = appSettings.MetadataSettings.IgnoreRulesKeyWords.Any(keywords.Contains);
}
cei = IPath.GetCombinedEnumAndIndex(appSettings.ResultSettings, record.ExifDirectory.FilePath);
jsonFile = Path.Combine(jsonFileSubDirectory, cei.Combined, $"{record.ExifDirectory.FilePath.Id.Value}{checkFileExtension}.json");
(directoryName, _) = IPath.GetDirectoryNameAndIndex(appSettings.ResultSettings, record.ExifDirectory.FilePath);
jsonFile = Path.Combine(jsonFileSubDirectory, directoryName, $"{record.ExifDirectory.FilePath.Id.Value}{checkFileExtension}.json");
if (record.JsonFile != jsonFile)
{
fileHolder = FileHolder.Get(record.JsonFile);
@ -573,116 +535,19 @@ public partial class Rename : IRename, IDisposable
return results.AsReadOnly();
}
private static bool? GetDirectoryCheck(ResultSettings resultSettings)
private static void VerifyDirectories(ReadOnlyCollection<ToDo> toDoCollection)
{
bool? result = null;
IEnumerable<string> files;
string[] directories = Directory.GetDirectories(resultSettings.RootDirectory, "*", SearchOption.TopDirectoryOnly);
foreach (string directory in directories)
List<string> distinct = [];
foreach (ToDo toDo in toDoCollection)
{
files = Directory.EnumerateFiles(directory, "*", SearchOption.AllDirectories);
foreach (string _ in files)
{
if (result is null)
result = false;
else if (result.Value)
result = true;
break;
}
if (result is not null && result.Value)
break;
}
return result;
}
private static void VerifyIntMinValueLength(MetadataSettings metadataSettings, ReadOnlyCollection<Record> recordCollection)
{
foreach (Record record in recordCollection)
{
if (record.ExifDirectory.FilePath.Id is null)
if (toDo.Directory is null || distinct.Contains(toDo.Directory))
continue;
if (metadataSettings.IntMinValueLength < record.ExifDirectory.FilePath.Id.Value.ToString().Length)
throw new NotSupportedException();
if (!Directory.Exists(toDo.Directory))
_ = Directory.CreateDirectory(toDo.Directory);
distinct.Add(toDo.Directory);
}
}
private static string? GetCheckDirectory(AppSettings appSettings, DirectoryInfo directoryInfo, Record record, ReadOnlyCollection<int> ids, bool multipleDirectoriesWithFiles, string paddedId)
{
string? result;
string year = record.DateTime.Year.ToString();
string checkDirectoryName = Path.GetFileName(record.ExifDirectory.FilePath.DirectoryFullPath);
if (multipleDirectoriesWithFiles && !checkDirectoryName.Contains(year))
result = null;
else
{
(bool? isWrongYear, string[] years) = IDate.IsWrongYear(directoryInfo, record.ExifDirectory.FilePath, record.ExifDirectory);
if (appSettings.RenameSettings.InPlaceMoveDirectory && !record.ExifDirectory.FilePath.FileNameFirstSegment.Contains(paddedId))
result = null;
else
{
if (record.ExifDirectory.FilePath.FullName[..2] != directoryInfo.FullName[..2])
isWrongYear = null;
string directoryName;
string tfw = GetTFW(record, isWrongYear);
string? maker = IMetadata.GetMaker(record.ExifDirectory);
string rootDirectory = appSettings.ResultSettings.RootDirectory;
string[] segments = checkDirectoryName.Split(years, StringSplitOptions.None);
(int seasonValue, string seasonName) = IDate.GetSeason(record.DateTime.DayOfYear);
string? splat = checkDirectoryName.Length > 3 && checkDirectoryName[^3..][1] == '!' ? checkDirectoryName[^3..] : null;
string contains = record.ExifDirectory.FilePath.Id is null || ids.Contains(record.ExifDirectory.FilePath.Id.Value) ? "_ Exists _" : "_ New-Destination _";
string makerSplit = string.IsNullOrEmpty(maker) ? string.IsNullOrEmpty(appSettings.RenameSettings.DefaultMaker) ? string.Empty : appSettings.RenameSettings.DefaultMaker : $" {maker.Split(' ')[0]}";
if (!string.IsNullOrEmpty(splat) || isWrongYear is null || isWrongYear.Value || appSettings.RenameSettings.DirectoryDictionary.Count < 2)
directoryName = GetDirectoryName(year, tfw, segments[0], splat, seasonValue, seasonName, makerSplit);
else
{
directoryName = record.DateTime.ToString("yyyy-MM-dd");
if (appSettings.RenameSettings.DirectoryDictionary.TryGetValue(directoryName, out string? value) && !string.IsNullOrEmpty(value))
directoryName = value;
}
result = Path.GetFullPath(Path.Combine(rootDirectory, contains, directoryName));
}
}
return result;
}
private static string GetTFW(Record record, bool? isWrongYear) =>
string.Concat(record.HasDateTimeOriginal ? "T" : "F", isWrongYear is not null && isWrongYear.Value ? "W" : record.FastForwardMovingPictureExpertsGroupUsed ? "V" : "I");
private static string GetDirectoryName(string year, string tfw, string prefix, string? splat, int seasonValue, string seasonName, string makerSplit) =>
splat is null ? $"{prefix}{year} {tfw}{year}.{seasonValue} {seasonName}{makerSplit}" : $"{prefix}{year} {tfw}{year}{splat}";
private static List<ToDo> GetSidecarFiles(AppSettings appSettings, Record record, List<string> distinct, string checkDirectory, string paddedId)
{
List<ToDo> results = [];
ToDo toDo;
string checkFile;
FilePath filePath;
string checkFileExtension;
foreach (FileHolder fileHolder in record.SidecarFiles)
{
checkFileExtension = fileHolder.ExtensionLowered;
filePath = FilePath.Get(appSettings.ResultSettings, appSettings.MetadataSettings, fileHolder, index: null);
checkFile = Path.Combine(checkDirectory, $"{paddedId}{checkFileExtension}");
if (checkFile == filePath.FullName)
continue;
if (File.Exists(checkFile))
{
checkFile = string.Concat(checkFile, ".del");
if (File.Exists(checkFile))
continue;
}
if (distinct.Contains(checkFile))
continue;
distinct.Add(checkFile);
toDo = new(Directory: checkDirectory,
FileInfo: new(filePath.FullName),
File: checkFile,
JsonFile: false);
results.Add(toDo);
}
return results;
}
private ReadOnlyCollection<string> RenameFilesInDirectories(RenameSettings renameSettings, IRename rename, ReadOnlyCollection<ToDo> toDoCollection)
{
List<string> results = [];
@ -723,16 +588,65 @@ public partial class Rename : IRename, IDisposable
return results.AsReadOnly();
}
private static void VerifyDirectories(ReadOnlyCollection<ToDo> toDoCollection)
private static void SaveIdentifiersToDisk(long ticks, AppSettings appSettings, ReadOnlyCollection<Record> recordCollection)
{
List<string> distinct = [];
foreach (ToDo toDo in toDoCollection)
string paddedId;
Identifier identifier;
List<Identifier> identifiers = [];
string aMetadataCollectionDirectory = IResult.GetResultsDateGroupDirectory(appSettings.ResultSettings, nameof(A_Metadata), appSettings.ResultSettings.ResultCollection);
foreach (Record record in recordCollection)
{
if (toDo.Directory is null || distinct.Contains(toDo.Directory))
if (record.ExifDirectory.FilePath.Id is null)
continue;
if (!Directory.Exists(toDo.Directory))
_ = Directory.CreateDirectory(toDo.Directory);
distinct.Add(toDo.Directory);
paddedId = IId.GetPaddedId(appSettings.ResultSettings, appSettings.MetadataSettings, record.ExifDirectory.FilePath.Id.Value, record.HasIgnoreKeyword, record.HasDateTimeOriginal, index: null);
identifier = new([], record.HasDateTimeOriginal, record.ExifDirectory.FilePath.Id.Value, record.ExifDirectory.FilePath.Length, paddedId, record.DateTime.Ticks);
identifiers.Add(identifier);
}
string json = JsonSerializer.Serialize(identifiers.OrderBy(l => l.PaddedId).ToArray(), IdentifierCollectionSourceGenerationContext.Default.IdentifierArray);
_ = IPath.WriteAllText(Path.Combine(aMetadataCollectionDirectory, $"{ticks}.json"), json, updateDateWhenMatches: false, compareBeforeWrite: true, updateToWhenMatches: null);
}
private static ReadOnlyCollection<int> GetIds(RenameSettings renameSettings)
{
ReadOnlyCollection<int> results;
string? propertyCollectionFile = string.IsNullOrEmpty(renameSettings.RelativePropertyCollectionFile) ? null : renameSettings.RelativePropertyCollectionFile;
string? json = !File.Exists(propertyCollectionFile) ? null : File.ReadAllText(propertyCollectionFile);
Identifier[]? identifiers = json is null ? null : JsonSerializer.Deserialize(json, IdentifierCollectionSourceGenerationContext.Default.IdentifierArray);
if (identifiers is null && !string.IsNullOrEmpty(renameSettings.RelativePropertyCollectionFile))
throw new Exception($"Invalid {nameof(renameSettings.RelativePropertyCollectionFile)}");
results = identifiers is null ? new([]) : new((from l in identifiers select l.Id).ToArray());
return results;
}
private void RenameWork(ILogger<Program>? logger, AppSettings appSettings, IRename rename, long ticks)
{
ReadOnlyCollection<int> ids = GetIds(appSettings.RenameSettings);
string sourceDirectory = Path.GetFullPath(appSettings.ResultSettings.RootDirectory);
if (!Directory.Exists(sourceDirectory))
_ = Directory.CreateDirectory(sourceDirectory);
logger?.LogInformation("{Ticks} {RootDirectory}", ticks, sourceDirectory);
ReadOnlyCollection<string> files = Directory.GetFiles(sourceDirectory, "*", SearchOption.AllDirectories).ToArray().AsReadOnly();
if (files.Count > 0)
_ = IPath.DeleteEmptyDirectories(appSettings.ResultSettings.RootDirectory);
ReadOnlyCollection<Record> recordCollection = GetRecordCollection(logger, appSettings, rename, ticks, ids, sourceDirectory, files);
SaveIdentifiersToDisk(ticks, appSettings, recordCollection);
if (appSettings.RenameSettings.InPlace || appSettings.RenameSettings.InPlaceWithOriginalName)
{
if (recordCollection.Count > 0)
recordCollection = new([]);
string aMetadataSingletonDirectory = IResult.GetResultsGroupDirectory(appSettings.ResultSettings, nameof(A_Metadata));
_ = IPath.DeleteEmptyDirectories(aMetadataSingletonDirectory);
}
if (!appSettings.RenameSettings.OnlySaveIdentifiersToDisk)
{
DirectoryInfo directoryInfo = new(sourceDirectory);
ReadOnlyCollection<ToDo> toDoCollection = GetToDoCollection(appSettings, directoryInfo, ids, files, recordCollection);
ReadOnlyCollection<string> lines = RenameFilesInDirectories(appSettings.RenameSettings, rename, toDoCollection);
if (lines.Count != 0)
{
File.WriteAllLines($"D:/Tmp/Phares/{DateTime.Now.Ticks}.tsv", lines);
_ = IPath.DeleteEmptyDirectories(directoryInfo.FullName);
}
}
}

View File

@ -3,14 +3,11 @@
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RuntimeIdentifiers>win-x64;linux-x64</RuntimeIdentifiers>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<HoursSinceNovember142023>$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1699920000)), 3600))))</HoursSinceNovember142023>
<TargetFramework>net9.0</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<PackageId>Phares.AA.Shared</PackageId>
<Version>8.0.114.$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1699920000)), 3600))))</Version>
<Version>8.1.112.0</Version>
<Company>Phares</Company>
<Authors>Mike Phares</Authors>
<IncludeSymbols>true</IncludeSymbols>
@ -37,9 +34,9 @@
<SupportedPlatform Include="browser" />
</ItemGroup>
<ItemGroup>
<None Include=".vscode\read-me.md" Pack="true" PackagePath="\" />
<None Include=".vscode\read-me.md" Pack="true" PackagePath="\"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Text.Json" Version="9.0.3" />
<PackageReference Include="System.Text.Json" Version="8.0.5" />
</ItemGroup>
</Project>

View File

@ -1,23 +0,0 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace View_by_Distance.Shared.Models;
public record CombinedEnumAndIndex(string Combined,
byte Enum,
int Index)
{
public override string ToString()
{
string result = JsonSerializer.Serialize(this, CombinedEnumAndIndexSourceGenerationContext.Default.CombinedEnumAndIndex);
return result;
}
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(CombinedEnumAndIndex))]
internal partial class CombinedEnumAndIndexSourceGenerationContext : JsonSerializerContext
{
}

View File

@ -6,6 +6,9 @@ public interface ICompareSettings
public string FacesFileNameExtension { init; get; }
public string FacesHiddenFileNameExtension { init; get; }
public string FacesPartsFileNameExtension { init; get; }
public string[] IgnoreExtensions { init; get; }
public int MaxDegreeOfParallelism { init; get; }
public string[] ValidImageFormatExtensions { init; get; }
public string[] ValidVideoFormatExtensions { init; get; }
}

View File

@ -3,6 +3,9 @@ namespace View_by_Distance.Shared.Models.Properties;
public interface IRenameSettings
{
public string[] IgnoreExtensions { init; get; }
public bool SkipIdFiles { init; get; }
public string[] ValidImageFormatExtensions { init; get; }
public string[] ValidVideoFormatExtensions { init; get; }
}

View File

@ -3,4 +3,8 @@ namespace View_by_Distance.Shared.Models.Properties;
public interface IWindowsSettings
{
public string[] IgnoreExtensions { init; get; }
public string[] ValidImageFormatExtensions { init; get; }
public string[] ValidVideoFormatExtensions { init; get; }
}

View File

@ -8,15 +8,12 @@ public record ResultSettings(string DateGroup,
string ModelName,
int NumberOfJitters,
int NumberOfTimesToUpsample,
string[] IgnoreExtensions,
string PredictorModelName,
int ResultAllInOneSubdirectoryLength,
string ResultCollection,
string ResultContent,
string ResultSingleton,
string RootDirectory,
string[] ValidImageFormatExtensions,
string[] ValidVideoFormatExtensions)
string RootDirectory)
{
public override string ToString()

View File

@ -3,28 +3,24 @@ namespace View_by_Distance.Shared.Models.Stateless;
public interface IDate
{
public static DateTime GetMinimum(ExifDirectory exifDirectory) =>
XDate.GetMinimum(exifDirectory);
public static (int Season, string seasonName) GetSeason(int dayOfYear) =>
XDate.GetSeason(dayOfYear);
public static DateTime? GetDateTimeOriginal(ExifDirectory exifDirectory) =>
XDate.GetDateTimeOriginal(exifDirectory);
public static (bool?, string[]) IsWrongYear(DirectoryInfo directoryInfo, FilePath filePath, ExifDirectory exifDirectory) =>
(bool?, string[]) TestStatic_IsWrongYear(DirectoryInfo directoryInfo, FilePath filePath, ExifDirectory exifDirectory) =>
IsWrongYear(directoryInfo, filePath, exifDirectory);
static (bool?, string[]) IsWrongYear(DirectoryInfo directoryInfo, FilePath filePath, ExifDirectory exifDirectory) =>
XDate.IsWrongYear(directoryInfo, filePath, exifDirectory);
internal DateTime TestStatic_GetMinimum(ExifDirectory exifDirectory) =>
GetMinimum(exifDirectory);
internal (int Season, string seasonName) TestStatic_GetSeason(int dayOfYear) =>
(int Season, string seasonName) TestStatic_GetSeason(int dayOfYear) =>
GetSeason(dayOfYear);
static (int Season, string seasonName) GetSeason(int dayOfYear) =>
XDate.GetSeason(dayOfYear);
internal DateTime? TestStatic_GetDateTimeOriginal(ExifDirectory exifDirectory) =>
DateTime? TestStatic_GetDateTimeOriginal(ExifDirectory exifDirectory) =>
GetDateTimeOriginal(exifDirectory);
static DateTime? GetDateTimeOriginal(ExifDirectory exifDirectory) =>
XDate.GetDateTimeOriginal(exifDirectory);
internal (bool?, string[]) TestStatic_IsWrongYear(DirectoryInfo directoryInfo, FilePath filePath, ExifDirectory exifDirectory) =>
IsWrongYear(directoryInfo, filePath, exifDirectory);
DateTime TestStatic_GetMinimum(ExifDirectory exifDirectory) =>
GetMinimum(exifDirectory);
static DateTime GetMinimum(ExifDirectory exifDirectory) =>
XDate.GetMinimum(exifDirectory);
}

View File

@ -5,69 +5,57 @@ public interface IId
const int DeterministicHashCode = 9876543;
public static int GetDeterministicHashCode(byte[] value) =>
Id.GetDeterministicHashCode(value);
public static byte GetHasIgnoreKeyword(FilePath filePath) =>
Id.GetHasIgnoreKeyword(filePath);
public static bool IsOffsetDeterministicHashCode(MetadataSettings metadataSettings) =>
static bool IsOffsetDeterministicHashCode(MetadataSettings metadataSettings) =>
metadataSettings.Offset == DeterministicHashCode;
public static byte GetHasDateTimeOriginal(ResultSettings resultSettings, FilePath filePath) =>
Id.GetHasDateTimeOriginal(resultSettings, filePath);
string TestStatic_GetIntelligentId(ResultSettings resultSettings, MetadataSettings metadataSettings, long id, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal) =>
GetIntelligentId(resultSettings, metadataSettings, id, hasIgnoreKeyword, hasDateTimeOriginal);
static string GetIntelligentId(ResultSettings resultSettings, MetadataSettings metadataSettings, long id, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal) =>
Id.GetIntelligentId(resultSettings, metadataSettings, id, hasIgnoreKeyword, hasDateTimeOriginal);
public static byte GetMissingDateTimeOriginal(ResultSettings resultSettings, FilePath filePath) =>
Id.GetMissingDateTimeOriginal(resultSettings, filePath);
public static bool NameWithoutExtensionIsIdFormat(MetadataSettings metadataSettings, FileHolder fileHolder) =>
Id.NameWithoutExtensionIsIdFormat(metadataSettings, fileHolder.NameWithoutExtension.Split('.')[0]);
public static int GetId(ResultSettings resultSettings, MetadataSettings metadataSettings, string intelligentId) =>
int TestStatic_GetId(ResultSettings resultSettings, MetadataSettings metadataSettings, string intelligentId) =>
GetId(resultSettings, metadataSettings, intelligentId);
static int GetId(ResultSettings resultSettings, MetadataSettings metadataSettings, string intelligentId) =>
Id.GetId(resultSettings, metadataSettings, intelligentId);
public static bool NameWithoutExtensionIsIntelligentIdFormat(MetadataSettings metadataSettings, string fileNameFirstSegment) =>
fileNameFirstSegment.Length - 1 == metadataSettings.IntMinValueLength && fileNameFirstSegment[^1] is '0' or '1' or '2' or '3' or '4' or '5' or '6' or '7' or '8' or '9' && fileNameFirstSegment.All(char.IsNumber);
string TestStatic_GetPaddedId(ResultSettings resultSettings, MetadataSettings metadataSettings, int id, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal, int? index) =>
GetPaddedId(resultSettings, metadataSettings, id, hasIgnoreKeyword, hasDateTimeOriginal, index);
static string GetPaddedId(ResultSettings resultSettings, MetadataSettings metadataSettings, int id, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal, int? index) =>
Id.GetPaddedId(resultSettings, metadataSettings, id, hasIgnoreKeyword, hasDateTimeOriginal, index);
public static bool NameWithoutExtensionIsPaddedIntelligentIdFormat(MetadataSettings metadataSettings, int sortOrderOnlyLengthIndex, string fileNameFirstSegment) =>
string TestStatic_GetIgnoreFullPath(FilePath filePath, FileHolder fileHolder) =>
GetIgnoreFullPath(filePath, fileHolder);
static string GetIgnoreFullPath(FilePath filePath, FileHolder fileHolder) =>
fileHolder.DirectoryFullPath is null ?
throw new NotSupportedException() :
filePath.Id > -1 ?
fileHolder.NameWithoutExtension[^1] == '9' ?
Path.Combine(fileHolder.DirectoryFullPath, $"{fileHolder.NameWithoutExtension[..^1]}8{fileHolder.ExtensionLowered}") :
throw new NotSupportedException("High") :
fileHolder.NameWithoutExtension[^1] == '1' ?
Path.Combine(fileHolder.DirectoryFullPath, $"{fileHolder.NameWithoutExtension[..^1]}2{fileHolder.ExtensionLowered}") :
throw new NotSupportedException("Low");
bool TestStatic_NameWithoutExtensionIsIntelligentIdFormat(MetadataSettings metadataSettings, string fileNameFirstSegment) =>
NameWithoutExtensionIsIntelligentIdFormat(metadataSettings, fileNameFirstSegment);
static bool NameWithoutExtensionIsIntelligentIdFormat(MetadataSettings metadataSettings, string fileNameFirstSegment) =>
fileNameFirstSegment.Length - 1 == metadataSettings.IntMinValueLength && fileNameFirstSegment[^1] is '1' or '2' or '8' or '9' && fileNameFirstSegment.All(char.IsNumber);
bool TestStatic_NameWithoutExtensionIsPaddedIntelligentIdFormat(MetadataSettings metadataSettings, int sortOrderOnlyLengthIndex, string fileNameFirstSegment) =>
NameWithoutExtensionIsPaddedIntelligentIdFormat(metadataSettings, sortOrderOnlyLengthIndex, fileNameFirstSegment);
static bool NameWithoutExtensionIsPaddedIntelligentIdFormat(MetadataSettings metadataSettings, int sortOrderOnlyLengthIndex, string fileNameFirstSegment) =>
fileNameFirstSegment.Length == metadataSettings.IntMinValueLength + sortOrderOnlyLengthIndex + 1
&& fileNameFirstSegment[^1] is '0' or '1' or '2' or '3' or '4' or '5' or '6' or '7' or '8' or '9'
&& fileNameFirstSegment[^1] is '1' or '2' or '8' or '9'
&& fileNameFirstSegment.All(char.IsNumber);
public static string GetIntelligentId(ResultSettings resultSettings, MetadataSettings metadataSettings, long id, string extensionLowered, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal) =>
Id.GetIntelligentId(resultSettings, metadataSettings, id, extensionLowered, hasIgnoreKeyword, hasDateTimeOriginal);
public static string GetPaddedId(ResultSettings resultSettings, MetadataSettings metadataSettings, int id, string extensionLowered, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal, int? index) =>
Id.GetPaddedId(resultSettings, metadataSettings, id, extensionLowered, hasIgnoreKeyword, hasDateTimeOriginal, index);
internal int TestStatic_GetDeterministicHashCode(byte[] value) =>
GetDeterministicHashCode(value);
internal byte TestStatic_GetHasIgnoreKeyword(FilePath filePath) =>
GetHasIgnoreKeyword(filePath);
internal byte TestStatic_GetHasDateTimeOriginal(ResultSettings resultSettings, FilePath filePath) =>
GetHasDateTimeOriginal(resultSettings, filePath);
internal byte TestStatic_GetMissingDateTimeOriginal(ResultSettings resultSettings, FilePath filePath) =>
GetMissingDateTimeOriginal(resultSettings, filePath);
internal bool TestStatic_NameWithoutExtensionIsIdFormat(MetadataSettings metadataSettings, FileHolder fileHolder) =>
bool TestStatic_NameWithoutExtensionIsIdFormat(MetadataSettings metadataSettings, FileHolder fileHolder) =>
NameWithoutExtensionIsIdFormat(metadataSettings, fileHolder);
static bool NameWithoutExtensionIsIdFormat(MetadataSettings metadataSettings, FileHolder fileHolder) =>
Id.NameWithoutExtensionIsIdFormat(metadataSettings, fileHolder.NameWithoutExtension.Split('.')[0]);
internal int TestStatic_GetId(ResultSettings resultSettings, MetadataSettings metadataSettings, string intelligentId) =>
GetId(resultSettings, metadataSettings, intelligentId);
internal bool TestStatic_NameWithoutExtensionIsIntelligentIdFormat(MetadataSettings metadataSettings, string fileNameFirstSegment) =>
NameWithoutExtensionIsIntelligentIdFormat(metadataSettings, fileNameFirstSegment);
internal bool TestStatic_NameWithoutExtensionIsPaddedIntelligentIdFormat(MetadataSettings metadataSettings, int sortOrderOnlyLengthIndex, string fileNameFirstSegment) =>
NameWithoutExtensionIsPaddedIntelligentIdFormat(metadataSettings, sortOrderOnlyLengthIndex, fileNameFirstSegment);
internal string TestStatic_GetIntelligentId(ResultSettings resultSettings, MetadataSettings metadataSettings, string extensionLowered, long id, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal) =>
GetIntelligentId(resultSettings, metadataSettings, id, extensionLowered, hasIgnoreKeyword, hasDateTimeOriginal);
internal string TestStatic_GetPaddedId(ResultSettings resultSettings, MetadataSettings metadataSettings, int id, string extensionLowered, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal, int? index) =>
GetPaddedId(resultSettings, metadataSettings, id, extensionLowered, hasIgnoreKeyword, hasDateTimeOriginal, index);
int TestStatic_GetDeterministicHashCode(byte[] value) =>
GetDeterministicHashCode(value);
static int GetDeterministicHashCode(byte[] value) =>
Id.GetDeterministicHashCode(value);
}

View File

@ -3,96 +3,72 @@ using System.Collections.ObjectModel;
namespace View_by_Distance.Shared.Models.Stateless;
public interface IPath
{
{ // ...
public static byte GetEnum(FilePath filePath) =>
XPath.GetEnum(filePath);
public static string[] GetDirectories(string directory) =>
XPath.GetDirectories(directory).ToArray();
public static string[] GetDirectoryNames(string directory) =>
XPath.GetDirectoryNames(directory).ToArray();
public static string GetRelativePath(string path, int length) =>
string TestStatic_GetRelativePath(string path, int length) =>
GetRelativePath(path, length);
static string GetRelativePath(string path, int length) =>
XPath.GetRelativePath(path, length, forceExtensionToLower: false);
public static bool DeleteEmptyDirectories(string rootDirectory) =>
bool TestStatic_DeleteEmptyDirectories(string rootDirectory) =>
DeleteEmptyDirectories(rootDirectory);
static bool DeleteEmptyDirectories(string rootDirectory) =>
XPath.DeleteEmptyDirectories(rootDirectory);
public static void MakeHiddenIfAllItemsAreHidden(string rootDirectory) =>
XPath.MakeHiddenIfAllItemsAreHidden(rootDirectory);
public static void CreateDirectories(ReadOnlyCollection<string> directories) =>
XPath.CreateDirectories(directories);
public static void ChangeDateForEmptyDirectories(string rootDirectory, long ticks) =>
void TestStatic_ChangeDateForEmptyDirectories(string rootDirectory, long ticks) =>
ChangeDateForEmptyDirectories(rootDirectory, ticks);
static void ChangeDateForEmptyDirectories(string rootDirectory, long ticks) =>
XPath.ChangeDateForEmptyDirectories(rootDirectory, ticks);
public static string GetRelativePath(string path, int length, bool forceExtensionToLower) =>
void TestStatic_MakeHiddenIfAllItemsAreHidden(string rootDirectory) =>
MakeHiddenIfAllItemsAreHidden(rootDirectory);
static void MakeHiddenIfAllItemsAreHidden(string rootDirectory) =>
XPath.MakeHiddenIfAllItemsAreHidden(rootDirectory);
void TestStatic_DeleteEmptyDirectories(string rootDirectory, List<string> deletedDirectories) =>
DeleteEmptyDirectories(rootDirectory, deletedDirectories);
static void DeleteEmptyDirectories(string rootDirectory, List<string> deletedDirectories) =>
XPath.DeleteEmptyDirectories(rootDirectory, deletedDirectories);
// $dirs = gci "" -directory -recurse | Where { (gci $_.fullName).count -eq 0 } | select -expandproperty FullName $dirs | Foreach-Object { Remove-Item $_ }
string[] TestStatic_GetDirectoryNames(string directory) =>
GetDirectoryNames(directory);
static string[] GetDirectoryNames(string directory) =>
XPath.GetDirectoryNames(directory).ToArray();
string[] TestStatic_GetDirectories(string directory) =>
GetDirectories(directory);
static string[] GetDirectories(string directory) =>
XPath.GetDirectories(directory).ToArray();
string TestStatic_GetRelativePath(string path, int length, bool forceExtensionToLower) =>
GetRelativePath(path, length, forceExtensionToLower);
static string GetRelativePath(string path, int length, bool forceExtensionToLower) =>
XPath.GetRelativePath(path, length, forceExtensionToLower);
public static string GetDirectory(string sourceDirectory, int level, string directoryName) =>
XPath.GetDirectory(sourceDirectory, level, directoryName);
public static void DeleteEmptyDirectories(string rootDirectory, List<string> deletedDirectories) =>
XPath.DeleteEmptyDirectories(rootDirectory, deletedDirectories);
public static (int level, List<string> directories) Get(string rootDirectory, string sourceDirectory) =>
XPath.Get(rootDirectory, sourceDirectory);
public static CombinedEnumAndIndex GetCombinedEnumAndIndex(ResultSettings resultSettings, FilePath filePath) =>
XPath.GetCombinedEnumAndIndex(resultSettings, filePath);
public static bool WriteAllText(string path, string contents, bool updateDateWhenMatches, bool compareBeforeWrite, DateTime? updateToWhenMatches = null) =>
bool TestStatic_WriteAllText(string path, string contents, bool updateDateWhenMatches, bool compareBeforeWrite, DateTime? updateToWhenMatches = null) =>
WriteAllText(path, contents, updateDateWhenMatches, compareBeforeWrite, updateToWhenMatches);
static bool WriteAllText(string path, string contents, bool updateDateWhenMatches, bool compareBeforeWrite, DateTime? updateToWhenMatches = null) =>
XPath.WriteAllText(path, contents, updateDateWhenMatches, compareBeforeWrite, updateToWhenMatches);
public static ReadOnlyDictionary<int, ReadOnlyDictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>>> GetKeyValuePairs(ResultSettings resultSettings, string? resultsFullGroupDirectory, string[]? jsonGroups) =>
(int level, List<string> directories) TestStatic_Get(string rootDirectory, string sourceDirectory) =>
Get(rootDirectory, sourceDirectory);
static (int level, List<string> directories) Get(string rootDirectory, string sourceDirectory) =>
XPath.Get(rootDirectory, sourceDirectory);
string TestStatic_GetDirectory(string sourceDirectory, int level, string directoryName) =>
GetDirectory(sourceDirectory, level, directoryName);
static string GetDirectory(string sourceDirectory, int level, string directoryName) =>
XPath.GetDirectory(sourceDirectory, level, directoryName);
(string, int) TestStatic_GetDirectoryNameAndIndex(ResultSettings resultSettings, FilePath filePath) =>
GetDirectoryNameAndIndex(resultSettings, filePath);
static (string, int) GetDirectoryNameAndIndex(ResultSettings resultSettings, FilePath filePath) =>
XPath.GetDirectoryNameAndIndex(resultSettings, filePath);
ReadOnlyDictionary<int, ReadOnlyDictionary<string, string[]>> TestStatic_GetKeyValuePairs(ResultSettings resultSettings, string? resultsFullGroupDirectory, string[]? jsonGroups) =>
GetKeyValuePairs(resultSettings, resultsFullGroupDirectory, jsonGroups);
static ReadOnlyDictionary<int, ReadOnlyDictionary<string, string[]>> GetKeyValuePairs(ResultSettings resultSettings, string? resultsFullGroupDirectory, string[]? jsonGroups) =>
XPath.GetKeyValuePairs(resultSettings, resultsFullGroupDirectory, jsonGroups);
internal byte TestStatic_GetEnum(FilePath filePath) =>
GetEnum(filePath);
internal string[] TestStatic_GetDirectories(string directory) =>
GetDirectories(directory);
internal string[] TestStatic_GetDirectoryNames(string directory) =>
GetDirectoryNames(directory);
internal string TestStatic_GetRelativePath(string path, int length) =>
GetRelativePath(path, length);
internal bool TestStatic_DeleteEmptyDirectories(string rootDirectory) =>
DeleteEmptyDirectories(rootDirectory);
internal void TestStatic_MakeHiddenIfAllItemsAreHidden(string rootDirectory) =>
MakeHiddenIfAllItemsAreHidden(rootDirectory);
internal void TestStatic_CreateDirectories(ReadOnlyCollection<string> directories) =>
CreateDirectories(directories);
internal void TestStatic_ChangeDateForEmptyDirectories(string rootDirectory, long ticks) =>
ChangeDateForEmptyDirectories(rootDirectory, ticks);
internal string TestStatic_GetRelativePath(string path, int length, bool forceExtensionToLower) =>
GetRelativePath(path, length, forceExtensionToLower);
internal string TestStatic_GetDirectory(string sourceDirectory, int level, string directoryName) =>
GetDirectory(sourceDirectory, level, directoryName);
internal void TestStatic_DeleteEmptyDirectories(string rootDirectory, List<string> deletedDirectories) =>
DeleteEmptyDirectories(rootDirectory, deletedDirectories);
internal (int level, List<string> directories) TestStatic_Get(string rootDirectory, string sourceDirectory) =>
Get(rootDirectory, sourceDirectory);
internal CombinedEnumAndIndex TestStatic_GetCombinedEnumAndIndex(ResultSettings resultSettings, FilePath filePath) =>
GetCombinedEnumAndIndex(resultSettings, filePath);
internal bool TestStatic_WriteAllText(string path, string contents, bool updateDateWhenMatches, bool compareBeforeWrite, DateTime? updateToWhenMatches = null) =>
WriteAllText(path, contents, updateDateWhenMatches, compareBeforeWrite, updateToWhenMatches);
internal ReadOnlyDictionary<int, ReadOnlyDictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>>> TestStatic_GetKeyValuePairs(ResultSettings resultSettings, string? resultsFullGroupDirectory, string[]? jsonGroups) =>
GetKeyValuePairs(resultSettings, resultsFullGroupDirectory, jsonGroups);
}

View File

@ -1,11 +1,12 @@
using System.Collections.ObjectModel;
using View_by_Distance.Shared.Models.Properties;
namespace View_by_Distance.Shared.Models.Stateless;
public interface IRename
{
ReadOnlyCollection<string> ConvertAndGetFastForwardMovingPictureExpertsGroupFiles(ResultSettings resultSettings, FilePath filePath);
ReadOnlyCollection<string> ConvertAndGetFastForwardMovingPictureExpertsGroupFiles(IRenameSettings renameSettings, FilePath filePath);
DeterministicHashCode GetDeterministicHashCode(FilePath filePath);
void ConstructProgressBar(int maxTicks, string message);
void Tick();

View File

@ -1,11 +1,12 @@
using System.Collections.ObjectModel;
using View_by_Distance.Shared.Models.Properties;
namespace View_by_Distance.Shared.Models.Stateless;
public interface IWindows
{
ReadOnlyCollection<string> ConvertAndGetFastForwardMovingPictureExpertsGroupFiles(ResultSettings resultSettings, HttpClient? httpClient, FilePath filePath);
ReadOnlyCollection<string> ConvertAndGetFastForwardMovingPictureExpertsGroupFiles(IWindowsSettings windowsSettings, HttpClient? httpClient, FilePath filePath);
DeterministicHashCode GetDeterministicHashCode(HttpClient? httpClient, FilePath filePath);
DeterministicHashCode GetDeterministicHashCode(HttpClient httpClient, Uri uri);
void ConstructProgressBar(int maxTicks, string message);

View File

@ -5,6 +5,81 @@ namespace View_by_Distance.Shared.Models.Stateless;
internal abstract class Id
{
internal static bool NameWithoutExtensionIsIdFormat(MetadataSettings metadataSettings, string fileNameFirstSegment)
{
bool result;
if (fileNameFirstSegment.Length < 5 || fileNameFirstSegment.Length > metadataSettings.IntMinValueLength)
result = false;
else
{
bool skipOneAllAreNumbers = fileNameFirstSegment[1..].All(char.IsNumber);
result = (skipOneAllAreNumbers && fileNameFirstSegment[0] == '-') || (skipOneAllAreNumbers && char.IsNumber(fileNameFirstSegment[0]));
}
return result;
}
internal static int GetId(ResultSettings resultSettings, MetadataSettings metadataSettings, string intelligentId)
{
int result;
StringBuilder results = new();
if (metadataSettings.IntMinValueLength < (resultSettings.ResultAllInOneSubdirectoryLength + 2))
throw new NotSupportedException();
for (int i = intelligentId.Length - (resultSettings.ResultAllInOneSubdirectoryLength + 2); i > -1; i--)
_ = results.Append(intelligentId[i]);
_ = results.Append(intelligentId[^3]).Append(intelligentId[^2]);
result = int.Parse(results.ToString());
if (intelligentId[^1] is '1' or '2')
result *= -1;
else if (intelligentId[^1] is not '9' and not '8')
throw new NotSupportedException();
return result;
}
#pragma warning disable IDE0060
internal static string GetIntelligentId(ResultSettings resultSettings, MetadataSettings metadataSettings, long id, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal)
#pragma warning restore IDE0060
{
string result;
StringBuilder stringBuilder = new();
if (metadataSettings.IntMinValueLength < (resultSettings.ResultAllInOneSubdirectoryLength + 2))
throw new NotSupportedException();
int key;
string value;
List<char> resultAllInOneSubdirectoryChars = [];
if (id > -1)
{
key = hasIgnoreKeyword is not null && hasIgnoreKeyword.Value ? 8 : 9;
value = id.ToString().PadLeft(metadataSettings.IntMinValueLength, '0');
}
else
{
key = hasIgnoreKeyword is not null && hasIgnoreKeyword.Value ? 2 : 1;
value = id.ToString()[1..].PadLeft(metadataSettings.IntMinValueLength, '0');
}
for (int i = value.Length - resultSettings.ResultAllInOneSubdirectoryLength - 1; i > -1; i--)
_ = stringBuilder.Append(value[i]);
for (int i = value.Length - resultSettings.ResultAllInOneSubdirectoryLength; i < value.Length; i++)
resultAllInOneSubdirectoryChars.Add(value[i]);
result = $"{stringBuilder}{string.Join(string.Empty, resultAllInOneSubdirectoryChars)}{key}";
return result;
}
internal static string GetPaddedId(ResultSettings resultSettings, MetadataSettings metadataSettings, int id, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal, int? index)
{
string result;
if (metadataSettings.Offset < 0)
result = Guid.NewGuid().ToString();
else
{
string intelligentId = GetIntelligentId(resultSettings, metadataSettings, id, hasIgnoreKeyword, hasDateTimeOriginal);
int check = GetId(resultSettings, metadataSettings, intelligentId);
if (check != id)
throw new NotSupportedException();
result = index is null || metadataSettings.Offset == IId.DeterministicHashCode ? intelligentId : $"{metadataSettings.Offset + index}{intelligentId}";
}
return result;
}
internal static int GetDeterministicHashCode(byte[] value)
{
int result;
@ -24,99 +99,4 @@ internal abstract class Id
return result;
}
internal static byte GetHasIgnoreKeyword(FilePath filePath) =>
(byte)(filePath.Id > -1 ? 8 : 2);
internal static byte GetHasDateTimeOriginal(ResultSettings resultSettings, FilePath filePath) =>
(byte)(!resultSettings.ValidVideoFormatExtensions.Contains(filePath.ExtensionLowered) ? filePath.Id > -1 ? 9 : 1 : filePath.Id > -1 ? 6 : 4);
internal static byte GetMissingDateTimeOriginal(ResultSettings resultSettings, FilePath filePath) =>
(byte)(!resultSettings.ValidVideoFormatExtensions.Contains(filePath.ExtensionLowered) ? filePath.Id > -1 ? 7 : 3 : filePath.Id > -1 ? 5 : 0);
internal static int GetId(ResultSettings resultSettings, MetadataSettings metadataSettings, string intelligentId)
{
int result;
StringBuilder results = new();
if (metadataSettings.IntMinValueLength < (resultSettings.ResultAllInOneSubdirectoryLength + 2))
throw new NotSupportedException();
for (int i = intelligentId.Length - (resultSettings.ResultAllInOneSubdirectoryLength + 2); i > -1; i--)
_ = results.Append(intelligentId[i]);
_ = results.Append(intelligentId[^3]).Append(intelligentId[^2]);
result = int.Parse(results.ToString());
if (intelligentId[^1] is '0' or '1' or '2' or '3' or '4')
result *= -1;
else if (intelligentId[^1] is not '9' and not '8' and not '7' and not '6' and not '5')
throw new NotSupportedException();
return result;
}
internal static bool NameWithoutExtensionIsIdFormat(MetadataSettings metadataSettings, string fileNameFirstSegment)
{
bool result;
if (fileNameFirstSegment.Length < 5 || fileNameFirstSegment.Length > metadataSettings.IntMinValueLength)
result = false;
else
{
bool skipOneAllAreNumbers = fileNameFirstSegment[1..].All(char.IsNumber);
result = (skipOneAllAreNumbers && fileNameFirstSegment[0] == '-') || (skipOneAllAreNumbers && char.IsNumber(fileNameFirstSegment[0]));
}
return result;
}
#pragma warning disable IDE0060
internal static string GetIntelligentId(ResultSettings resultSettings, MetadataSettings metadataSettings, long id, string extensionLowered, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal)
#pragma warning restore IDE0060
{
string result;
StringBuilder stringBuilder = new();
if (metadataSettings.IntMinValueLength < (resultSettings.ResultAllInOneSubdirectoryLength + 2))
throw new NotSupportedException();
int key;
string value;
List<char> resultAllInOneSubdirectoryChars = [];
if (hasDateTimeOriginal is null)
{
key = 0;
value = id.ToString().PadLeft(metadataSettings.IntMinValueLength, '0');
}
else if (id > -1)
{
if (!resultSettings.ValidVideoFormatExtensions.Contains(extensionLowered))
key = hasIgnoreKeyword is not null && hasIgnoreKeyword.Value ? 8 : hasDateTimeOriginal.Value ? 9 : 7;
else
key = hasIgnoreKeyword is not null && hasIgnoreKeyword.Value ? throw new NotImplementedException() : hasDateTimeOriginal.Value ? 6 : 5;
value = id.ToString().PadLeft(metadataSettings.IntMinValueLength, '0');
}
else
{
if (!resultSettings.ValidVideoFormatExtensions.Contains(extensionLowered))
key = hasIgnoreKeyword is not null && hasIgnoreKeyword.Value ? 2 : hasDateTimeOriginal.Value ? 1 : 3;
else
key = hasIgnoreKeyword is not null && hasIgnoreKeyword.Value ? throw new NotImplementedException() : hasDateTimeOriginal.Value ? 4 : 0;
value = id.ToString()[1..].PadLeft(metadataSettings.IntMinValueLength, '0');
}
for (int i = value.Length - resultSettings.ResultAllInOneSubdirectoryLength - 1; i > -1; i--)
_ = stringBuilder.Append(value[i]);
for (int i = value.Length - resultSettings.ResultAllInOneSubdirectoryLength; i < value.Length; i++)
resultAllInOneSubdirectoryChars.Add(value[i]);
result = $"{stringBuilder}{string.Join(string.Empty, resultAllInOneSubdirectoryChars)}{key}";
return result;
}
internal static string GetPaddedId(ResultSettings resultSettings, MetadataSettings metadataSettings, int id, string extensionLowered, bool? hasIgnoreKeyword, bool? hasDateTimeOriginal, int? index)
{
string result;
if (metadataSettings.Offset < 0)
result = Guid.NewGuid().ToString();
else
{
string intelligentId = GetIntelligentId(resultSettings, metadataSettings, id, extensionLowered, hasIgnoreKeyword, hasDateTimeOriginal);
int check = GetId(resultSettings, metadataSettings, intelligentId);
if (check != id)
throw new NotSupportedException();
result = index is null || metadataSettings.Offset == IId.DeterministicHashCode ? intelligentId : $"{metadataSettings.Offset + index}{intelligentId}";
}
return result;
}
}

View File

@ -7,11 +7,179 @@ namespace View_by_Distance.Shared.Models.Stateless;
internal abstract class XDate
{
internal static DateTime GetMinimum(ExifDirectory exifDirectory)
private record Record(bool? IsWrongYear, string[] Years);
internal static (int Season, string seasonName) GetSeason(int dayOfYear)
{
DateTime result;
ReadOnlyCollection<DateTime> results = GetDateTimes(exifDirectory);
result = results.Count == 0 ? DateTime.MinValue : results.Min();
(int Season, string seasonName) result = dayOfYear switch
{
< 78 => new(0, "Winter"),
< 124 => new(1, "Spring"),
< 171 => new(2, "Spring"),
< 217 => new(3, "Summer"),
< 264 => new(4, "Summer"),
< 309 => new(5, "Fall"),
< 354 => new(6, "Fall"),
_ => new(7, "Winter")
};
return result;
}
private static Record IsWrongYear(string[] segments, string year)
{
Record result;
bool? check;
string[] results = (
from l
in segments
where l?.Length > 2
&& (
l[..2] is "18" or "19" or "20"
|| (l.Length == 5 && l.Substring(1, 2) is "18" or "19" or "20" && (l[0] is '~' or '=' or '-' or '^' or '#'))
|| (l.Length == 6 && l[..2] is "18" or "19" or "20" && l[4] == '.')
|| (l.Length == 7 && l.Substring(1, 2) is "18" or "19" or "20" && l[5] == '.')
)
select l
).ToArray();
string[] matches = (
from l
in results
where l == year
|| (l.Length == 5 && l.Substring(1, 4) == year && (l[0] is '~' or '=' or '-' or '^' or '#'))
|| (l.Length == 6 && l[..4] == year && l[4] == '.')
|| (l.Length == 7 && l.Substring(1, 4) == year && l[5] == '.')
select l
).ToArray();
if (results.Length == 0)
check = null;
else
check = matches.Length == 0;
result = new(check, results);
return result;
}
internal static (bool?, string[]) IsWrongYear(DirectoryInfo directoryInfo, FilePath filePath, ExifDirectory exifDirectory)
{
string[] results = [];
bool? result = null;
string year;
string directoryName;
string[] directorySegments;
List<DateTime> collection = [];
string? check = Path.GetFullPath(filePath.FullName);
DateTime? dateTimeOriginal = GetDateTimeOriginal(exifDirectory);
if (dateTimeOriginal is not null)
collection.Add(dateTimeOriginal.Value);
else
{
ReadOnlyCollection<DateTime> dateTimes = GetDateTimes(exifDirectory);
foreach (DateTime dateTime in dateTimes)
collection.Add(dateTime);
}
foreach (DateTime dateTime in collection)
{
year = dateTime.ToString("yyyy");
for (int i = 0; i < int.MaxValue; i++)
{
check = Path.GetDirectoryName(check);
if (string.IsNullOrEmpty(check) || check == directoryInfo.FullName)
break;
directoryName = Path.GetFileName(check);
directorySegments = directoryName.Split(' ');
(result, results) = IsWrongYear(directorySegments, year);
if (result is not null)
break;
}
if (result is not null && !result.Value)
break;
}
return new(result, results);
}
internal static DateTime? GetDateTimeOriginal(ExifDirectory exifDirectory)
{
DateTime? result;
List<DateTime> results = [];
foreach (ExifDirectoryBase exifDirectoryBase in exifDirectory.ExifBaseDirectories)
{
if (exifDirectoryBase.DateTimeOriginal is not null)
results.Add(exifDirectoryBase.DateTimeOriginal.Value);
}
foreach (AviDirectory aviDirectory in exifDirectory.AviDirectories)
{
if (aviDirectory.DateTimeOriginal is not null)
results.Add(aviDirectory.DateTimeOriginal.Value);
}
foreach (QuickTimeMovieHeaderDirectory quickTimeMovieHeaderDirectory in exifDirectory.QuickTimeMovieHeaderDirectories)
{
if (quickTimeMovieHeaderDirectory.Created is not null)
{
if (quickTimeMovieHeaderDirectory.Created.Value.Year == 1904 && quickTimeMovieHeaderDirectory.Created.Value.Month == 1 && quickTimeMovieHeaderDirectory.Created.Value.Day == 1)
continue;
results.Add(quickTimeMovieHeaderDirectory.Created.Value);
}
}
foreach (QuickTimeTrackHeaderDirectory quickTimeTrackHeaderDirectory in exifDirectory.QuickTimeTrackHeaderDirectories)
{
if (quickTimeTrackHeaderDirectory.Created is not null)
{
if ((quickTimeTrackHeaderDirectory.Created.Value.Year is 1904 or 1970) && quickTimeTrackHeaderDirectory.Created.Value.Month == 1 && quickTimeTrackHeaderDirectory.Created.Value.Day == 1)
continue;
results.Add(quickTimeTrackHeaderDirectory.Created.Value);
}
}
result = results.Count == 0 ? null : results.Min();
return result;
}
private static DateTime? GetDateTimeFromName(string fileNameWithoutExtension)
{
DateTime? result = null;
int length;
string format;
string fullFormat;
StringBuilder value = new();
const string ticksExample = "##################";
string[][] dateFormats =
[
[string.Empty, "yyyyMMdd_HHmmss", string.Empty],
[string.Empty, "yyyyMMddHHmmssfff", string.Empty],
[string.Empty, "yyyyMMdd_", ticksExample],
[string.Empty, "yyyy-MM-dd_", ticksExample],
[string.Empty, "yyyy-MM-dd.", ticksExample],
// [string.Empty, "yyyy-MM-dd.", $"{ticksExample}.{fileHolder.Length}"],
[string.Empty, "yyyy-MM-dd HH.mm.ss", string.Empty],
[string.Empty, "yyyyMMdd_HHmmss", "_LLS"],
[string.Empty, "yyyyMMdd_HHmmss", "_HDR"],
["WIN_", "yyyyMMdd_HH_mm_ss", "_Pro"],
["IMG_", "yyyyMMdd_HHmmss", string.Empty],
["IMG#####-", "yyyyMMdd-HHmm", string.Empty],
["CameraZOOM-", "yyyyMMddHHmmss", string.Empty],
["VideoCapture_", "yyyyMMdd-HHmmss ", string.Empty]
];
foreach (string[] dateFormat in dateFormats)
{
_ = value.Clear();
if (dateFormat.Length != 3)
throw new Exception();
fullFormat = string.Join(string.Empty, dateFormat);
if (fileNameWithoutExtension.Length != fullFormat.Length)
continue;
format = dateFormat[1];
length = dateFormat[0].Length + dateFormat[1].Length;
for (int i = dateFormat[0].Length; i < length; i++)
_ = value.Append(fileNameWithoutExtension[i]);
if (value.Length != format.Length)
continue;
if (DateTime.TryParseExact(value.ToString(), format, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime checkDateTime))
{
if (fileNameWithoutExtension.Length < ticksExample.Length || !long.TryParse(fileNameWithoutExtension[^ticksExample.Length..], out long ticks))
result = checkDateTime;
else
result = new DateTime(ticks);
break;
}
}
return result;
}
@ -71,182 +239,12 @@ internal abstract class XDate
return results.AsReadOnly();
}
private static DateTime? GetDateTimeFromName(string fileNameWithoutExtension)
internal static DateTime GetMinimum(ExifDirectory exifDirectory)
{
DateTime? result = null;
int length;
string format;
string fullFormat;
StringBuilder value = new();
const string ticksExample = "##################";
string[][] dateFormats =
[
[string.Empty, "yyyyMMdd_HHmmss", string.Empty],
[string.Empty, "yyyyMMddHHmmssfff", string.Empty],
[string.Empty, "yyyyMMdd_", ticksExample],
[string.Empty, "yyyy-MM-dd_", ticksExample],
[string.Empty, "yyyy-MM-dd.", ticksExample],
// [string.Empty, "yyyy-MM-dd.", $"{ticksExample}.{fileHolder.Length}"],
[string.Empty, "yyyy-MM-dd HH.mm.ss", string.Empty],
[string.Empty, "yyyyMMdd_HHmmss", "_LLS"],
[string.Empty, "yyyyMMdd_HHmmss", "_HDR"],
["WIN_", "yyyyMMdd_HH_mm_ss", "_Pro"],
["IMG_", "yyyyMMdd_HHmmss", string.Empty],
["IMG#####-", "yyyyMMdd-HHmm", string.Empty],
["CameraZOOM-", "yyyyMMddHHmmss", string.Empty],
["VideoCapture_", "yyyyMMdd-HHmmss ", string.Empty]
];
foreach (string[] dateFormat in dateFormats)
{
_ = value.Clear();
if (dateFormat.Length != 3)
throw new Exception();
fullFormat = string.Join(string.Empty, dateFormat);
if (fileNameWithoutExtension.Length != fullFormat.Length)
continue;
format = dateFormat[1];
length = dateFormat[0].Length + dateFormat[1].Length;
for (int i = dateFormat[0].Length; i < length; i++)
_ = value.Append(fileNameWithoutExtension[i]);
if (value.Length != format.Length)
continue;
if (DateTime.TryParseExact(value.ToString(), format, CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime checkDateTime))
{
if (fileNameWithoutExtension.Length < ticksExample.Length || !long.TryParse(fileNameWithoutExtension[^ticksExample.Length..], out long ticks))
result = checkDateTime;
else
result = new DateTime(ticks);
break;
}
}
DateTime result;
ReadOnlyCollection<DateTime> results = GetDateTimes(exifDirectory);
result = results.Count == 0 ? DateTime.MinValue : results.Min();
return result;
}
internal static (int Season, string seasonName) GetSeason(int dayOfYear)
{
(int Season, string seasonName) result = dayOfYear switch
{
< 78 => new(0, "Winter"),
< 124 => new(1, "Spring"),
< 171 => new(2, "Spring"),
< 217 => new(3, "Summer"),
< 264 => new(4, "Summer"),
< 309 => new(5, "Fall"),
< 354 => new(6, "Fall"),
_ => new(7, "Winter")
};
return result;
}
internal static DateTime? GetDateTimeOriginal(ExifDirectory exifDirectory)
{
DateTime? result;
List<DateTime> results = [];
foreach (ExifDirectoryBase exifDirectoryBase in exifDirectory.ExifBaseDirectories)
{
if (exifDirectoryBase.DateTimeOriginal is not null)
results.Add(exifDirectoryBase.DateTimeOriginal.Value);
}
foreach (AviDirectory aviDirectory in exifDirectory.AviDirectories)
{
if (aviDirectory.DateTimeOriginal is not null)
results.Add(aviDirectory.DateTimeOriginal.Value);
}
foreach (QuickTimeMovieHeaderDirectory quickTimeMovieHeaderDirectory in exifDirectory.QuickTimeMovieHeaderDirectories)
{
if (quickTimeMovieHeaderDirectory.Created is not null)
{
if (quickTimeMovieHeaderDirectory.Created.Value.Year == 1904 && quickTimeMovieHeaderDirectory.Created.Value.Month == 1 && quickTimeMovieHeaderDirectory.Created.Value.Day == 1)
continue;
results.Add(quickTimeMovieHeaderDirectory.Created.Value);
}
}
foreach (QuickTimeTrackHeaderDirectory quickTimeTrackHeaderDirectory in exifDirectory.QuickTimeTrackHeaderDirectories)
{
if (quickTimeTrackHeaderDirectory.Created is not null)
{
if ((quickTimeTrackHeaderDirectory.Created.Value.Year is 1904 or 1970) && quickTimeTrackHeaderDirectory.Created.Value.Month == 1 && quickTimeTrackHeaderDirectory.Created.Value.Day == 1)
continue;
results.Add(quickTimeTrackHeaderDirectory.Created.Value);
}
}
result = results.Count == 0 ? null : results.Min();
return result;
}
internal static (bool?, string[]) IsWrongYear(DirectoryInfo directoryInfo, FilePath filePath, ExifDirectory exifDirectory)
{
string[] results = [];
bool? result = null;
string year;
string directoryName;
string[] directorySegments;
List<DateTime> collection = [];
string? check = Path.GetFullPath(filePath.FullName);
DateTime? dateTimeOriginal = GetDateTimeOriginal(exifDirectory);
if (dateTimeOriginal is not null)
collection.Add(dateTimeOriginal.Value);
else
{
ReadOnlyCollection<DateTime> dateTimes = GetDateTimes(exifDirectory);
foreach (DateTime dateTime in dateTimes)
collection.Add(dateTime);
}
foreach (DateTime dateTime in collection)
{
year = dateTime.ToString("yyyy");
for (int i = 0; i < int.MaxValue; i++)
{
check = Path.GetDirectoryName(check);
if (string.IsNullOrEmpty(check))
break;
directoryName = Path.GetFileName(check);
directorySegments = directoryName.Split(' ');
(result, results) = IsWrongYear(directorySegments, year);
if (result is not null)
break;
if (check == directoryInfo.FullName)
break;
}
if (result is not null && !result.Value)
break;
}
return new(result, results);
}
private static Record IsWrongYear(string[] segments, string year)
{
Record result;
bool? check;
string[] results = (
from l
in segments
where l?.Length > 2
&& (
l[..2] is "18" or "19" or "20"
|| (l.Length == 5 && l.Substring(1, 2) is "18" or "19" or "20" && (l[0] is '~' or '=' or '-' or '^' or '#'))
|| (l.Length == 6 && l[..2] is "18" or "19" or "20" && l[4] == '.')
|| (l.Length == 7 && l.Substring(1, 2) is "18" or "19" or "20" && l[5] == '.')
)
select l
).ToArray();
string[] matches = (
from l
in results
where l == year
|| (l.Length == 5 && l.Substring(1, 4) == year && (l[0] is '~' or '=' or '-' or '^' or '#'))
|| (l.Length == 6 && l[..4] == year && l[4] == '.')
|| (l.Length == 7 && l.Substring(1, 4) == year && l[5] == '.')
select l
).ToArray();
if (results.Length == 0)
check = null;
else
check = matches.Length == 0;
result = new(check, results);
return result;
}
private record Record(bool? IsWrongYear, string[] Years);
}

View File

@ -5,29 +5,35 @@ namespace View_by_Distance.Shared.Models.Stateless;
internal abstract class XPath
{
private static ReadOnlyDictionary<byte, ReadOnlyCollection<string>> Convert(List<CombinedEnumAndIndex> collection)
internal static string GetRelativePath(string path, int length, bool forceExtensionToLower)
{
Dictionary<byte, List<string>> results = [];
List<string>? c;
foreach (CombinedEnumAndIndex cei in collection)
string result;
if (forceExtensionToLower)
{
if (!results.TryGetValue(cei.Enum, out c))
string extension = Path.GetExtension(path);
string extensionLowered = Path.GetExtension(path).ToLower();
if (extension != extensionLowered)
{
results.Add(cei.Enum, []);
if (!results.TryGetValue(cei.Enum, out c))
throw new Exception();
string? directoryName = Path.GetDirectoryName(path);
if (string.IsNullOrEmpty(directoryName))
throw new NullReferenceException(directoryName);
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(path);
if (string.IsNullOrEmpty(fileNameWithoutExtension))
throw new NullReferenceException(fileNameWithoutExtension);
path = Path.Combine(directoryName, $"{fileNameWithoutExtension}{extensionLowered}");
}
c.Add(cei.Combined);
}
return Convert(results);
result = path[length..].Replace(@"\", "/");
return result;
}
private static ReadOnlyDictionary<int, ReadOnlyDictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>>> Convert(Dictionary<int, Dictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>>> keyValuePairs)
internal static bool DeleteEmptyDirectories(string rootDirectory)
{
Dictionary<int, ReadOnlyDictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>>> results = [];
foreach (KeyValuePair<int, Dictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>>> keyValuePair in keyValuePairs)
results.Add(keyValuePair.Key, new(keyValuePair.Value));
return results.AsReadOnly();
bool result;
List<string> results = [];
DeleteEmptyDirectories(rootDirectory, results);
result = results.Count > 0;
return result;
}
internal static void DeleteEmptyDirectories(string rootDirectory, List<string> deletedDirectories)
@ -65,54 +71,43 @@ internal abstract class XPath
}
}
internal static byte GetEnum(FilePath filePath) =>
GetEnum(filePath.HasIgnoreKeyword, filePath.HasDateTimeOriginal);
private static byte GetEnum(bool? ik, bool? dto)
internal static bool WriteAllText(string path, string contents, bool updateDateWhenMatches, bool compareBeforeWrite, DateTime? updateToWhenMatches)
{
byte result;
if (ik is not null && ik.Value && dto is not null && dto.Value)
result = 11;
else if (ik is not null && ik.Value && dto is not null && !dto.Value)
result = 15;
else if (ik is not null && ik.Value && dto is null)
result = 19;
else if (ik is not null && !ik.Value && dto is not null && dto.Value)
result = 51;
else if (ik is not null && !ik.Value && dto is not null && !dto.Value)
result = 55;
else if (ik is not null && !ik.Value && dto is null)
result = 59;
else if (ik is null && dto is not null && dto.Value)
result = 91;
else if (ik is null && dto is not null && !dto.Value)
result = 95;
else if (ik is null && dto is null)
result = 99;
bool result;
string text;
if (!compareBeforeWrite)
result = true;
else
throw new Exception();
return result;
}
internal static List<string> GetDirectories(string directory)
{
List<string> results = [];
string? checkDirectory = directory;
string? pathRoot = Path.GetPathRoot(directory);
if (string.IsNullOrEmpty(pathRoot))
throw new NullReferenceException(nameof(pathRoot));
if (Directory.Exists(directory))
results.Add(directory);
for (int i = 0; i < int.MaxValue; i++)
{
checkDirectory = Path.GetDirectoryName(checkDirectory);
if (string.IsNullOrEmpty(checkDirectory) || checkDirectory == pathRoot)
break;
results.Add(checkDirectory);
if (!File.Exists(path))
text = string.Empty;
else
text = File.ReadAllText(path);
result = text != contents;
if (!result && updateDateWhenMatches)
{
if (updateToWhenMatches is null)
File.SetLastWriteTime(path, DateTime.Now);
else
File.SetLastWriteTime(path, updateToWhenMatches.Value);
}
}
results.Add(pathRoot);
results.Reverse();
return results;
if (result)
{
if (path.Contains("()"))
File.WriteAllText(path, contents);
else if (path.Contains("{}") && !path.EndsWith(".json"))
File.WriteAllText(path, contents);
else if (path.Contains("[]") && !path.EndsWith(".json"))
File.WriteAllText(path, contents);
else if (path.Contains("{}") && path.EndsWith(".json") && contents[0] == '{')
File.WriteAllText(path, contents);
else if (path.Contains("[]") && path.EndsWith(".json") && contents[0] == '[')
File.WriteAllText(path, contents);
else
File.WriteAllText(path, contents);
}
return result;
}
internal static List<string> GetDirectoryNames(string directory)
@ -151,15 +146,83 @@ internal abstract class XPath
return results;
}
internal static bool DeleteEmptyDirectories(string rootDirectory)
internal static List<string> GetDirectories(string directory)
{
bool result;
List<string> results = [];
DeleteEmptyDirectories(rootDirectory, results);
result = results.Count > 0;
string? checkDirectory = directory;
string? pathRoot = Path.GetPathRoot(directory);
if (string.IsNullOrEmpty(pathRoot))
throw new NullReferenceException(nameof(pathRoot));
if (Directory.Exists(directory))
results.Add(directory);
for (int i = 0; i < int.MaxValue; i++)
{
checkDirectory = Path.GetDirectoryName(checkDirectory);
if (string.IsNullOrEmpty(checkDirectory) || checkDirectory == pathRoot)
break;
results.Add(checkDirectory);
}
results.Add(pathRoot);
results.Reverse();
return results;
}
internal static (int level, List<string> directories) Get(string rootDirectory, string sourceDirectory)
{
int result = 0;
string? directory;
string? checkDirectory;
List<string> results = [];
checkDirectory = sourceDirectory;
for (int i = 0; i < int.MaxValue; i++)
{
result += 1;
directory = Path.GetFileName(checkDirectory);
if (string.IsNullOrEmpty(directory))
break;
results.Add(directory);
checkDirectory = Path.GetDirectoryName(checkDirectory);
if (checkDirectory == rootDirectory)
break;
}
results.Reverse();
return new(result, results);
}
internal static string GetDirectory(string sourceDirectory, int level, string directoryName)
{
string result;
string? checkDirectory;
checkDirectory = Path.GetDirectoryName(sourceDirectory);
for (int i = 0; i < level; i++)
checkDirectory = Path.GetDirectoryName(checkDirectory);
if (string.IsNullOrEmpty(checkDirectory))
throw new Exception();
checkDirectory = Path.Combine(checkDirectory, directoryName);
if (!Directory.Exists(checkDirectory))
_ = Directory.CreateDirectory(checkDirectory);
result = checkDirectory;
return result;
}
internal static void ChangeDateForEmptyDirectories(string rootDirectory, long ticks)
{
DateTime dateTime = new(ticks);
IEnumerable<string> fileSystemEntries;
string[] directories;
if (!Directory.Exists(rootDirectory))
directories = [];
else
directories = Directory.GetDirectories(rootDirectory, "*", SearchOption.AllDirectories);
foreach (string directory in directories)
{
fileSystemEntries = Directory.EnumerateFileSystemEntries(directory, "*", SearchOption.TopDirectoryOnly);
if (fileSystemEntries.Any())
continue;
Directory.SetLastWriteTime(directory, dateTime);
}
}
internal static void MakeHiddenIfAllItemsAreHidden(string rootDirectory)
{
bool check;
@ -202,204 +265,40 @@ internal abstract class XPath
}
}
internal static void CreateDirectories(ReadOnlyCollection<string> directories)
private static (string, int) GetDirectoryNameAndIndex(int resultAllInOneSubdirectoryLength, string fileNameWithoutExtension)
{
string checkDirectory;
foreach (string directory in directories)
{
for (int i = 0; i < 101; i++)
{
checkDirectory = Path.Combine(directory, i.ToString("000"));
if (!Directory.Exists(checkDirectory))
_ = Directory.CreateDirectory(checkDirectory);
}
}
}
internal static void ChangeDateForEmptyDirectories(string rootDirectory, long ticks)
{
DateTime dateTime = new(ticks);
IEnumerable<string> fileSystemEntries;
string[] directories;
if (!Directory.Exists(rootDirectory))
directories = [];
else
directories = Directory.GetDirectories(rootDirectory, "*", SearchOption.AllDirectories);
foreach (string directory in directories)
{
fileSystemEntries = Directory.EnumerateFileSystemEntries(directory, "*", SearchOption.TopDirectoryOnly);
if (fileSystemEntries.Any())
continue;
Directory.SetLastWriteTime(directory, dateTime);
}
}
internal static string GetRelativePath(string path, int length, bool forceExtensionToLower)
{
string result;
if (forceExtensionToLower)
{
string extension = Path.GetExtension(path);
string extensionLowered = Path.GetExtension(path).ToLower();
if (extension != extensionLowered)
{
string? directoryName = Path.GetDirectoryName(path);
if (string.IsNullOrEmpty(directoryName))
throw new NullReferenceException(directoryName);
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(path);
if (string.IsNullOrEmpty(fileNameWithoutExtension))
throw new NullReferenceException(fileNameWithoutExtension);
path = Path.Combine(directoryName, $"{fileNameWithoutExtension}{extensionLowered}");
}
}
result = path[length..].Replace(@"\", "/");
return result;
}
internal static string GetDirectory(string sourceDirectory, int level, string directoryName)
{
string result;
string? checkDirectory;
checkDirectory = Path.GetDirectoryName(sourceDirectory);
for (int i = 0; i < level; i++)
checkDirectory = Path.GetDirectoryName(checkDirectory);
if (string.IsNullOrEmpty(checkDirectory))
throw new Exception();
checkDirectory = Path.Combine(checkDirectory, directoryName);
if (!Directory.Exists(checkDirectory))
_ = Directory.CreateDirectory(checkDirectory);
result = checkDirectory;
return result;
}
internal static (int level, List<string> directories) Get(string rootDirectory, string sourceDirectory)
{
int result = 0;
string? directory;
string? checkDirectory;
List<string> results = [];
checkDirectory = sourceDirectory;
for (int i = 0; i < int.MaxValue; i++)
{
result += 1;
directory = Path.GetFileName(checkDirectory);
if (string.IsNullOrEmpty(directory))
break;
results.Add(directory);
checkDirectory = Path.GetDirectoryName(checkDirectory);
if (checkDirectory == rootDirectory)
break;
}
results.Reverse();
return new(result, results);
}
internal static CombinedEnumAndIndex GetCombinedEnumAndIndex(ResultSettings resultSettings, FilePath filePath)
{
CombinedEnumAndIndex result;
if (filePath.Id is not null)
result = GetCombinedEnumAndIndex(resultSettings, filePath, filePath.Id.Value.ToString());
else
result = GetCombinedEnumAndIndex(resultSettings, filePath, filePath.NameWithoutExtension);
return result;
}
private static CombinedEnumAndIndex GetCombinedEnumAndIndex(ResultSettings resultSettings, FilePath filePath, string fileNameWithoutExtension)
{
CombinedEnumAndIndex result;
byte @enum;
int converted;
string combined;
byte missingDateTimeOriginal = IId.GetMissingDateTimeOriginal(resultSettings, filePath);
if (!filePath.IsIntelligentIdFormat)
@enum = missingDateTimeOriginal;
else
{
if (filePath.HasIgnoreKeyword is null || filePath.HasDateTimeOriginal is null)
throw new NotImplementedException("Chicken and Egg!");
if (filePath.HasIgnoreKeyword.Value)
@enum = IId.GetHasIgnoreKeyword(filePath);
else if (!filePath.HasDateTimeOriginal.Value)
@enum = missingDateTimeOriginal;
else
@enum = IId.GetHasDateTimeOriginal(resultSettings, filePath);
}
string result;
string fileNameBeforeFirst = fileNameWithoutExtension.Split('.')[0];
string check = fileNameBeforeFirst.Length < resultSettings.ResultAllInOneSubdirectoryLength ?
new('-', resultSettings.ResultAllInOneSubdirectoryLength) :
fileNameBeforeFirst[^resultSettings.ResultAllInOneSubdirectoryLength..];
string check = fileNameBeforeFirst.Length < resultAllInOneSubdirectoryLength ? new('-', resultAllInOneSubdirectoryLength) : fileNameBeforeFirst[^resultAllInOneSubdirectoryLength..];
if (check.Any(l => !char.IsNumber(l)))
{
combined = $"{@enum}{new('-', resultSettings.ResultAllInOneSubdirectoryLength)}";
converted = int.Parse($"1{new string('0', resultSettings.ResultAllInOneSubdirectoryLength)}");
result = new('-', resultAllInOneSubdirectoryLength);
converted = int.Parse($"1{new string('0', resultAllInOneSubdirectoryLength)}");
}
else
{
combined = $"{@enum}{check}";
result = check;
converted = int.Parse(check);
}
result = new(combined, @enum, converted);
return result;
return new(result, converted);
}
internal static bool WriteAllText(string path, string contents, bool updateDateWhenMatches, bool compareBeforeWrite, DateTime? updateToWhenMatches)
internal static (string, int) GetDirectoryNameAndIndex(ResultSettings resultSettings, int id)
{
bool result;
string text;
if (!compareBeforeWrite)
result = true;
(string result, int converted) = GetDirectoryNameAndIndex(resultSettings.ResultAllInOneSubdirectoryLength, id.ToString());
return new(result, converted);
}
internal static (string, int) GetDirectoryNameAndIndex(ResultSettings resultSettings, FilePath filePath)
{
string result;
int converted;
if (filePath.Id is not null)
(result, converted) = GetDirectoryNameAndIndex(resultSettings.ResultAllInOneSubdirectoryLength, filePath.Id.Value.ToString());
else
{
if (!File.Exists(path))
text = string.Empty;
else
text = File.ReadAllText(path);
result = text != contents;
if (!result && updateDateWhenMatches)
{
if (updateToWhenMatches is null)
File.SetLastWriteTime(path, DateTime.Now);
else
File.SetLastWriteTime(path, updateToWhenMatches.Value);
}
}
if (result)
{
if (path.Contains("()"))
File.WriteAllText(path, contents);
else if (path.Contains("{}") && !path.EndsWith(".json"))
File.WriteAllText(path, contents);
else if (path.Contains("[]") && !path.EndsWith(".json"))
File.WriteAllText(path, contents);
else if (path.Contains("{}") && path.EndsWith(".json") && contents[0] == '{')
File.WriteAllText(path, contents);
else if (path.Contains("[]") && path.EndsWith(".json") && contents[0] == '[')
File.WriteAllText(path, contents);
else
File.WriteAllText(path, contents);
}
return result;
}
internal static ReadOnlyDictionary<int, ReadOnlyDictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>>> GetKeyValuePairs(ResultSettings resultSettings, string? resultsFullGroupDirectory, string[]? jsonGroups)
{
Dictionary<int, Dictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>>> results = [];
if (jsonGroups is not null)
{
DateTime dateTime = DateTime.Now;
ReadOnlyCollection<int> years = GetYears(resultSettings);
Dictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>>? k;
ReadOnlyDictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> keyValuePairs = GetKeyValuePairs(resultSettings, resultsFullGroupDirectory, jsonGroups, dateTime);
foreach (int year in years)
{
results.Add(year, []);
if (!results.TryGetValue(year, out k))
throw new NullReferenceException(nameof(k));
foreach (KeyValuePair<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> keyValuePair in keyValuePairs)
k.Add(keyValuePair.Key, keyValuePair.Value);
}
}
return Convert(results);
(result, converted) = GetDirectoryNameAndIndex(resultSettings.ResultAllInOneSubdirectoryLength, filePath.NameWithoutExtension);
return new(result, converted);
}
private static ReadOnlyCollection<int> GetYears(ResultSettings resultSettings)
@ -411,79 +310,65 @@ internal abstract class XPath
return results.AsReadOnly();
}
private static ReadOnlyDictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> GetKeyValuePairs(ResultSettings resultSettings, string? resultsFullGroupDirectory, string[]? jsonGroups, DateTime dateTime)
private static ReadOnlyDictionary<int, ReadOnlyDictionary<string, string[]>> Convert(Dictionary<int, Dictionary<string, string[]>> collection)
{
Dictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> results = [];
int plusOne;
Dictionary<int, ReadOnlyDictionary<string, string[]>> results = [];
foreach (KeyValuePair<int, Dictionary<string, string[]>> keyValuePair in collection)
results.Add(keyValuePair.Key, new(keyValuePair.Value));
return results.AsReadOnly();
}
internal static ReadOnlyDictionary<int, ReadOnlyDictionary<string, string[]>> GetKeyValuePairs(ResultSettings resultSettings, string? resultsFullGroupDirectory, string[]? jsonGroups)
{
Dictionary<int, Dictionary<string, string[]>> results = [];
string directory;
string checkDirectory;
CombinedEnumAndIndex cei;
byte[] bytes = GetBytes();
List<CombinedEnumAndIndex> collection = [];
ReadOnlyDictionary<byte, ReadOnlyCollection<string>> keyValuePairs;
Dictionary<string, string[]>? keyValuePairs;
ReadOnlyCollection<int> years = GetYears(resultSettings);
int converted = int.Parse($"1{new string('0', resultSettings.ResultAllInOneSubdirectoryLength)}");
if (jsonGroups is not null)
int plusOne = converted + 1;
List<string> collection = [];
foreach (int year in years)
{
plusOne = converted + 1;
foreach (string jsonGroup in jsonGroups)
results.Add(year, []);
if (!results.TryGetValue(year, out keyValuePairs))
throw new NullReferenceException(nameof(keyValuePairs));
if (jsonGroups is not null)
{
if (resultsFullGroupDirectory is null)
continue;
foreach (byte @enum in bytes)
foreach (string jsonGroup in jsonGroups)
{
if (resultsFullGroupDirectory is null)
continue;
collection.Clear();
for (int i = 0; i < plusOne; i++)
{
if (string.IsNullOrEmpty(jsonGroup))
{
if (i == converted)
checkDirectory = Path.GetFullPath(Path.Combine(resultsFullGroupDirectory, $"{@enum}{new('-', resultSettings.ResultAllInOneSubdirectoryLength)}"));
checkDirectory = Path.GetFullPath(Path.Combine(resultsFullGroupDirectory, new('-', resultSettings.ResultAllInOneSubdirectoryLength)));
else
checkDirectory = Path.GetFullPath(Path.Combine(resultsFullGroupDirectory, $"{@enum}{i.ToString().PadLeft(resultSettings.ResultAllInOneSubdirectoryLength, '0')}"));
checkDirectory = Path.GetFullPath(Path.Combine(resultsFullGroupDirectory, i.ToString().PadLeft(resultSettings.ResultAllInOneSubdirectoryLength, '0')));
}
else
{
directory = Path.Combine(resultsFullGroupDirectory, jsonGroup);
if (i == converted)
checkDirectory = Path.GetFullPath(Path.Combine(directory, $"{@enum}{new('-', resultSettings.ResultAllInOneSubdirectoryLength)}"));
checkDirectory = Path.GetFullPath(Path.Combine(directory, new('-', resultSettings.ResultAllInOneSubdirectoryLength)));
else
checkDirectory = Path.GetFullPath(Path.Combine(directory, $"{@enum}{i.ToString().PadLeft(resultSettings.ResultAllInOneSubdirectoryLength, '0')}"));
checkDirectory = Path.GetFullPath(Path.Combine(directory, i.ToString().PadLeft(resultSettings.ResultAllInOneSubdirectoryLength, '0')));
}
if (!Directory.Exists(checkDirectory))
_ = Directory.CreateDirectory(checkDirectory);
cei = new(Combined: checkDirectory, Enum: @enum, Index: -1);
collection.Add(cei);
collection.Add(checkDirectory);
}
if (!string.IsNullOrEmpty(jsonGroup))
keyValuePairs.Add(jsonGroup, collection.ToArray());
else
keyValuePairs.Add(year.ToString(), collection.ToArray());
}
keyValuePairs = Convert(collection);
if (!string.IsNullOrEmpty(jsonGroup))
results.Add(jsonGroup, keyValuePairs);
else
results.Add(dateTime.Ticks.ToString(), keyValuePairs);
}
}
return results.AsReadOnly();
}
private static byte[] GetBytes() =>
[
0,
1,
2,
3,
4,
5,
6,
7,
8,
9
];
private static ReadOnlyDictionary<byte, ReadOnlyCollection<string>> Convert(Dictionary<byte, List<string>> keyValuePairs)
{
Dictionary<byte, ReadOnlyCollection<string>> results = [];
foreach (KeyValuePair<byte, List<string>> keyValuePair in keyValuePairs)
results.Add(keyValuePair.Key, new(keyValuePair.Value));
return results.AsReadOnly();
return Convert(results);
}
}

View File

@ -1,5 +0,0 @@
@host = http://192.168.0.11:8080
GET {{host}}/iCloud%20Photos%202025
###

View File

@ -1,5 +0,0 @@
# mklink
```bash 1741014465915 = 638766112659150000 = 2025-0.Winter = Mon Mar 03 2025 08:07:45 GMT-0700 (Mountain Standard Time)
mklink /J "L:\Git\AA\Windows\.vscode\.7-Question" "V:\7-Question"
````

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
@ -7,12 +7,9 @@
<TargetFramework>net9.0</TargetFramework>
<UserSecretsId>076c87e8-c7f0-40a3-aba3-73eb7f9ea892</UserSecretsId>
</PropertyGroup>
<PropertyGroup>
<HoursSinceNovember122024>$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1731369600)), 3600))))</HoursSinceNovember122024>
</PropertyGroup>
<PropertyGroup>
<PackageId>Phares.View.by.Distance.Windows</PackageId>
<Version>9.0.104.$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1731369600)), 3600))))</Version>
<Version>8.0.112.0</Version>
<Company>Phares</Company>
<Authors>Mike Phares</Authors>
<IncludeSymbols>true</IncludeSymbols>
@ -42,13 +39,13 @@
<None Include=".vscode\read-me.md" Pack="true" PackagePath="\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="CliWrap" Version="3.8.2" />
<PackageReference Include="runtime.win-x64.Microsoft.DotNet.ILCompiler" Version="8.0.14" />
<PackageReference Include="CliWrap" Version="3.7.1" />
<PackageReference Include="runtime.win-x64.Microsoft.DotNet.ILCompiler" Version="8.0.12" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.1" />
<PackageReference Include="ShellProgressBar" Version="5.2.0" />
<PackageReference Include="System.Drawing.Common" Version="8.0.14" />
<PackageReference Include="System.Drawing.Common" Version="8.0.12" />
</ItemGroup>
<ItemGroup>
<RuntimeHostConfigurationOption Include="System.Drawing.EnableUnixSupport" Value="true" />

View File

@ -6,9 +6,12 @@ namespace View_by_Distance.Windows.Models;
public record WindowsSettings(string Company,
string? Host,
string[] IgnoreExtensions,
int MaxDegreeOfParallelism,
string? Page,
string[] SidecarExtensions,
string[] ValidImageFormatExtensions,
string[] ValidVideoFormatExtensions,
bool VerifyOnly) : Shared.Models.Properties.IWindowsSettings
{

View File

@ -9,6 +9,7 @@ using System.Text.Json;
using View_by_Distance.Metadata.Models;
using View_by_Distance.Metadata.Models.Stateless;
using View_by_Distance.Shared.Models;
using View_by_Distance.Shared.Models.Properties;
using View_by_Distance.Shared.Models.Stateless;
using View_by_Distance.Windows.Models;
@ -20,98 +21,42 @@ public partial class Windows : IWindows, IDisposable
private ProgressBar? _ProgressBar;
private readonly ProgressBarOptions _ProgressBarOptions;
DeterministicHashCode IWindows.GetDeterministicHashCode(HttpClient httpClient, Uri uri) =>
GetDeterministicHashCode(httpClient, uri);
DeterministicHashCode IWindows.GetDeterministicHashCode(HttpClient? httpClient, FilePath filePath)
public Windows(List<string> args, ILogger<Program>? logger, AppSettings appSettings, bool isSilent, IConsole console)
{
DeterministicHashCode result;
if (httpClient is not null)
result = GetDeterministicHashCode(httpClient, new Uri(filePath.FullName));
else
{
Stream stream = File.OpenRead(filePath.FullName);
result = GetDeterministicHashCode(stream);
stream.Dispose();
}
return result;
}
private static DeterministicHashCode GetDeterministicHashCode(HttpClient httpClient, Uri uri)
{
DeterministicHashCode result;
Stream stream = GetStream(httpClient, uri);
result = GetDeterministicHashCode(stream);
stream.Dispose();
return result;
}
private static Stream GetStream(HttpClient httpClient, Uri uri)
{
Stream result;
Task<Stream> task = httpClient.GetStreamAsync(uri);
task.Wait();
result = task.Result;
return result;
}
private static DeterministicHashCode GetDeterministicHashCode(Stream stream)
{
DeterministicHashCode result;
int? id;
int? width;
int? height;
try
{
#pragma warning disable CA1416
using Image image = Image.FromStream(stream);
width = image.Width;
height = image.Height;
using Bitmap bitmap = new(image);
Rectangle rectangle = new(0, 0, image.Width, image.Height);
BitmapData bitmapData = bitmap.LockBits(rectangle, ImageLockMode.ReadOnly, bitmap.PixelFormat);
IntPtr intPtr = bitmapData.Scan0;
int length = bitmapData.Stride * bitmap.Height;
byte[] bytes = new byte[length];
Marshal.Copy(intPtr, bytes, 0, length);
bitmap.UnlockBits(bitmapData);
#pragma warning restore CA1416
id = IId.GetDeterministicHashCode(bytes);
}
catch (Exception)
{
id = null;
width = null;
height = null;
}
result = new(height, id, width);
return result;
if (isSilent)
{ }
if (args is null)
throw new NullReferenceException(nameof(args));
if (console is null)
throw new NullReferenceException(nameof(console));
IWindows windows = this;
long ticks = DateTime.Now.Ticks;
_ProgressBarOptions = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true };
WindowsWork(logger, appSettings, windows, ticks);
}
void IWindows.Tick() =>
_ProgressBar?.Tick();
void IDisposable.Dispose()
{
_ProgressBar?.Dispose();
GC.SuppressFinalize(this);
}
void IWindows.ConstructProgressBar(int maxTicks, string message)
{
_ProgressBar?.Dispose();
_ProgressBar = new(maxTicks, message, _ProgressBarOptions);
}
ReadOnlyCollection<string> IWindows.ConvertAndGetFastForwardMovingPictureExpertsGroupFiles(ResultSettings resultSettings, HttpClient? httpClient, FilePath filePath)
void IDisposable.Dispose()
{
_ProgressBar?.Dispose();
GC.SuppressFinalize(this);
}
ReadOnlyCollection<string> IWindows.ConvertAndGetFastForwardMovingPictureExpertsGroupFiles(IWindowsSettings WindowsSettings, HttpClient? httpClient, FilePath filePath)
{
List<string> results = [];
bool isValidVideoFormatExtensions = resultSettings.ValidVideoFormatExtensions.Contains(filePath.ExtensionLowered);
bool isValidVideoFormatExtensions = WindowsSettings.ValidVideoFormatExtensions.Contains(filePath.ExtensionLowered);
if (isValidVideoFormatExtensions)
{
bool check;
if (httpClient is not null)
DownloadFile(httpClient, filePath);
try
{
CommandTask<CommandResult> commandTask = Cli.Wrap("L:/Git/ffmpeg-2024-10-02-git-358fdf3083-full_build/bin/ffmpeg.exe")
@ -138,74 +83,75 @@ public partial class Windows : IWindows, IDisposable
return results.AsReadOnly();
}
private static void DownloadFile(HttpClient httpClient, FilePath filePath)
#pragma warning disable CA1416
private static DeterministicHashCode GetDeterministicHashCode(Stream stream)
{
FileStream fileStream = new(filePath.FullName, FileMode.Truncate);
Task<HttpResponseMessage> httpResponseMessage = httpClient.GetAsync(filePath.FullName);
httpResponseMessage.Wait();
Task task = httpResponseMessage.Result.Content.CopyToAsync(fileStream);
DeterministicHashCode result;
int? id;
int? width;
int? height;
try
{
using Image image = Image.FromStream(stream);
width = image.Width;
height = image.Height;
using Bitmap bitmap = new(image);
Rectangle rectangle = new(0, 0, image.Width, image.Height);
BitmapData bitmapData = bitmap.LockBits(rectangle, ImageLockMode.ReadOnly, bitmap.PixelFormat);
IntPtr intPtr = bitmapData.Scan0;
int length = bitmapData.Stride * bitmap.Height;
byte[] bytes = new byte[length];
Marshal.Copy(intPtr, bytes, 0, length);
bitmap.UnlockBits(bitmapData);
id = IId.GetDeterministicHashCode(bytes);
}
catch (Exception)
{
id = null;
width = null;
height = null;
}
result = new(height, id, width);
return result;
}
private static Stream GetStream(HttpClient httpClient, Uri uri)
{
Stream result;
Task<Stream> task = httpClient.GetStreamAsync(uri);
task.Wait();
result = task.Result;
return result;
}
public Windows(List<string> args, ILogger<Program>? logger, AppSettings appSettings, bool isSilent, IConsole console)
private static DeterministicHashCode GetDeterministicHashCode(HttpClient httpClient, Uri uri)
{
if (isSilent)
{ }
if (args is null)
throw new NullReferenceException(nameof(args));
if (console is null)
throw new NullReferenceException(nameof(console));
IWindows windows = this;
LogNetToHoursSince(logger);
long ticks = DateTime.Now.Ticks;
_ProgressBarOptions = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true };
WindowsWork(logger, appSettings, windows, ticks);
DeterministicHashCode result;
Stream stream = GetStream(httpClient, uri);
result = GetDeterministicHashCode(stream);
stream.Dispose();
return result;
}
private static void LogNetToHoursSince(ILogger<Program>? logger)
{
double secondsInAHour = 3600f;
long epoch = new DateTime(1970, 1, 1).Ticks;
long net8ReleaseDate = new DateTime(2023, 11, 14).Ticks;
long net9ReleaseDate = new DateTime(2024, 11, 12).Ticks;
double net8TotalSeconds = new TimeSpan(net8ReleaseDate - epoch).TotalSeconds;
double net9TotalSeconds = new TimeSpan(net9ReleaseDate - epoch).TotalSeconds;
logger?.LogInformation("It has been {net8TotalSeconds} seconds since net8 was released", net8TotalSeconds);
logger?.LogInformation("It has been {net9TotalSeconds} seconds since net9 was released", net9TotalSeconds);
double net8TotalHours = Math.Floor((DateTimeOffset.UtcNow.ToUnixTimeSeconds() - net8TotalSeconds) / secondsInAHour);
double net9TotalHours = Math.Floor((DateTimeOffset.UtcNow.ToUnixTimeSeconds() - net9TotalSeconds) / secondsInAHour);
logger?.LogInformation("It has been {net8TotalHours} hours since net8 was released", net8TotalHours);
logger?.LogInformation("It has been {net9TotalHours} hours since net9 was released", net9TotalHours);
}
DeterministicHashCode IWindows.GetDeterministicHashCode(HttpClient httpClient, Uri uri) =>
GetDeterministicHashCode(httpClient, uri);
private void WindowsWork(ILogger<Program>? logger, AppSettings appSettings, IWindows windows, long ticks)
DeterministicHashCode IWindows.GetDeterministicHashCode(HttpClient? httpClient, FilePath filePath)
{
if (appSettings.WindowsSettings.VerifyOnly && !string.IsNullOrEmpty(appSettings.WindowsSettings.Host) && !string.IsNullOrEmpty(appSettings.WindowsSettings.Page))
Verify(logger, appSettings, windows, appSettings.WindowsSettings.Host, appSettings.WindowsSettings.Page);
DeterministicHashCode result;
if (httpClient is not null)
result = GetDeterministicHashCode(httpClient, new Uri(filePath.FullName));
else
{
string sourceDirectory = Path.GetFullPath(appSettings.ResultSettings.RootDirectory);
if (!Directory.Exists(sourceDirectory))
_ = Directory.CreateDirectory(sourceDirectory);
logger?.LogInformation("{Ticks} {RootDirectory}", ticks, sourceDirectory);
WindowsWork(logger, appSettings, windows, ticks, sourceDirectory);
Stream stream = File.OpenRead(filePath.FullName);
result = GetDeterministicHashCode(stream);
stream.Dispose();
}
return result;
}
private static void Verify(ILogger<Program>? logger, AppSettings appSettings, IWindows windows, string host, string page)
{
List<string> messages = [];
HttpClient httpClient = new();
int appSettingsMaxDegreeOfParallelism = appSettings.WindowsSettings.MaxDegreeOfParallelism;
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = appSettingsMaxDegreeOfParallelism };
ReadOnlyCollection<NginxFileSystem> collection = GetRecursiveCollection(httpClient, host, page) ?? throw new Exception();
windows.ConstructProgressBar(collection.Count, nameof(Verify));
_ = Parallel.For(0, collection.Count, parallelOptions, (i, state) =>
VerifyParallelFor(appSettings, windows, httpClient, collection[i], messages));
httpClient.Dispose();
foreach (string message in messages)
logger?.LogWarning("{message}", message);
}
#pragma warning restore CA1416
private static ReadOnlyCollection<NginxFileSystem>? GetRecursiveCollection(HttpClient httpClient, string host, string page)
{
@ -263,7 +209,6 @@ public partial class Windows : IWindows, IDisposable
string paddedId = IId.GetPaddedId(resultSettings: appSettings.ResultSettings,
metadataSettings: appSettings.MetadataSettings,
id: deterministicHashCode.Id.Value,
extensionLowered: appSettings.ResultSettings.ValidImageFormatExtensions[0],
hasIgnoreKeyword: null,
hasDateTimeOriginal: null,
index: null);
@ -271,43 +216,19 @@ public partial class Windows : IWindows, IDisposable
messages.Add($"!{nginxFileSystem.Name}.StartsWith({paddedId})");
}
private void WindowsWork(ILogger<Program>? logger, AppSettings appSettings, IWindows windows, long ticks, string sourceDirectory)
private static void Verify(ILogger<Program>? logger, AppSettings appSettings, IWindows windows, string host, string page)
{
ReadOnlyCollection<FirstPass> results;
ReadOnlyCollection<string> files = Directory.GetFiles(sourceDirectory, "*", SearchOption.AllDirectories).AsReadOnly();
if (files.Count > 0)
_ = IPath.DeleteEmptyDirectories(sourceDirectory);
A_Metadata metadata = new(appSettings.ResultSettings, appSettings.MetadataSettings);
List<string> messages = [];
HttpClient httpClient = new();
int appSettingsMaxDegreeOfParallelism = appSettings.WindowsSettings.MaxDegreeOfParallelism;
int filesCount = appSettingsMaxDegreeOfParallelism == 1 ? files.Count : 123000;
windows.ConstructProgressBar(filesCount, "EnumerateFiles load");
if (appSettingsMaxDegreeOfParallelism == 1)
results = WindowsSynchronousWork(logger, appSettings, windows, files, metadata);
else
results = WindowsAsynchronousWork(appSettings, windows, files, metadata, appSettingsMaxDegreeOfParallelism);
string json = JsonSerializer.Serialize(results, FirstPassCollectionSourceGenerationContext.Default.ListFirstPass);
File.WriteAllText(Path.Combine(sourceDirectory, $"{ticks}.json"), json);
}
private static ReadOnlyCollection<FirstPass> WindowsSynchronousWork(ILogger<Program>? logger, AppSettings appSettings, IWindows windows, IEnumerable<string> files, A_Metadata metadata)
{
List<FirstPass> results = [];
int index = -1;
ReadOnlyDictionary<string, List<FileHolder>> keyValuePairs;
if (string.IsNullOrEmpty(appSettings.WindowsSettings.Host) || string.IsNullOrEmpty(appSettings.WindowsSettings.Page))
keyValuePairs = IMetadata.GetKeyValuePairs(files);
else
{
ReadOnlyCollection<NginxFileSystem> collection = GetRecursiveCollection(appSettings.WindowsSettings.Host, appSettings.WindowsSettings.Page);
keyValuePairs = IMetadata.GetKeyValuePairs(collection);
}
foreach (KeyValuePair<string, List<FileHolder>> keyValuePair in keyValuePairs)
{
if (keyValuePair.Value.Count > 2)
throw new NotSupportedException("Too many sidecar files!");
index = WindowsSynchronousWork(logger, appSettings, windows, metadata, results, index, keyValuePair);
}
return results.AsReadOnly();
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = appSettingsMaxDegreeOfParallelism };
ReadOnlyCollection<NginxFileSystem> collection = GetRecursiveCollection(httpClient, host, page) ?? throw new Exception();
windows.ConstructProgressBar(collection.Count, nameof(Verify));
_ = Parallel.For(0, collection.Count, parallelOptions, (i, state) =>
VerifyParallelFor(appSettings, windows, httpClient, collection[i], messages));
httpClient.Dispose();
foreach (string message in messages)
logger?.LogWarning("{message}", message);
}
private static ReadOnlyCollection<NginxFileSystem> GetRecursiveCollection(string host, string page)
@ -319,10 +240,10 @@ public partial class Windows : IWindows, IDisposable
return results;
}
private static int WindowsSynchronousWork(ILogger<Program>? logger, AppSettings appSettings, IWindows windows, A_Metadata metadata, List<FirstPass> results, int index, KeyValuePair<string, List<FileHolder>> keyValuePair)
private List<FirstPass> GetCollection(ILogger<Program>? logger, AppSettings appSettings, IWindows windows, IEnumerable<string> files, A_Metadata metadata)
{
int result = index + 1;
windows.Tick();
List<FirstPass> results = [];
int index = -1;
FilePath filePath;
FirstPass firstPass;
string directoryName;
@ -332,79 +253,116 @@ public partial class Windows : IWindows, IDisposable
bool fastForwardMovingPictureExpertsGroupUsed;
MinimumYearAndPathCombined minimumYearAndPathCombined;
FilePath? fastForwardMovingPictureExpertsGroupFilePath;
ReadOnlyDictionary<string, List<FileHolder>> keyValuePairs;
ReadOnlyCollection<string>? fastForwardMovingPictureExpertsGroupFiles;
HttpClient? httpClient = string.IsNullOrEmpty(appSettings.WindowsSettings.Host) || string.IsNullOrEmpty(appSettings.WindowsSettings.Page) ? null : new();
foreach (FileHolder fileHolder in keyValuePair.Value)
if (string.IsNullOrEmpty(appSettings.WindowsSettings.Host) || string.IsNullOrEmpty(appSettings.WindowsSettings.Page))
keyValuePairs = IMetadata.GetKeyValuePairs(files);
else
{
if (appSettings.WindowsSettings.SidecarExtensions.Contains(fileHolder.ExtensionLowered))
continue;
if (appSettings.ResultSettings.IgnoreExtensions.Contains(fileHolder.ExtensionLowered))
continue;
filePath = FilePath.Get(appSettings.ResultSettings, appSettings.MetadataSettings, fileHolder, result);
if (filePath.Id is not null && (filePath.IsIntelligentIdFormat || filePath.SortOrder is not null))
continue;
if (filePath.Id is not null)
{
fastForwardMovingPictureExpertsGroupFiles = null;
deterministicHashCode = new(null, filePath.Id, null);
directoryName = Path.GetFileName(filePath.DirectoryFullPath);
if (directoryName.EndsWith(filePath.Id.Value.ToString()))
continue;
}
else
{
fastForwardMovingPictureExpertsGroupFiles = windows.ConvertAndGetFastForwardMovingPictureExpertsGroupFiles(appSettings.ResultSettings, httpClient, filePath);
fastForwardMovingPictureExpertsGroupFilePath = fastForwardMovingPictureExpertsGroupFiles.Count == 0 ? null : FilePath.Get(appSettings.ResultSettings, appSettings.MetadataSettings, FileHolder.Get(fastForwardMovingPictureExpertsGroupFiles[0]), result);
deterministicHashCode = fastForwardMovingPictureExpertsGroupFilePath is null ? windows.GetDeterministicHashCode(httpClient, filePath) : windows.GetDeterministicHashCode(httpClient, fastForwardMovingPictureExpertsGroupFilePath);
}
sidecarFiles = [];
filePath = FilePath.Get(filePath, deterministicHashCode);
for (int i = 0; i < keyValuePair.Value.Count; i++)
{
if (keyValuePair.Value[i].ExtensionLowered == fileHolder.ExtensionLowered)
continue;
sidecarFiles.Add(keyValuePair.Value[i]);
}
try
{ (minimumYearAndPathCombined, exifDirectory) = metadata.GetMetadataCollection(appSettings.ResultSettings, appSettings.MetadataSettings, httpClient, filePath); }
catch (Exception)
{
logger?.LogWarning("<{filePath}>", filePath.FullName);
continue;
}
fastForwardMovingPictureExpertsGroupUsed = fastForwardMovingPictureExpertsGroupFiles is not null && fastForwardMovingPictureExpertsGroupFiles.Count > 0;
if (fastForwardMovingPictureExpertsGroupUsed && fastForwardMovingPictureExpertsGroupFiles is not null)
{
foreach (string fastForwardMovingPictureExpertsGroupFile in fastForwardMovingPictureExpertsGroupFiles)
File.Delete(fastForwardMovingPictureExpertsGroupFile);
}
if (!fastForwardMovingPictureExpertsGroupUsed && appSettings.ResultSettings.ValidVideoFormatExtensions.Contains(filePath.ExtensionLowered))
fastForwardMovingPictureExpertsGroupUsed = true;
firstPass = new(exifDirectory, fastForwardMovingPictureExpertsGroupUsed, minimumYearAndPathCombined, sidecarFiles.ToArray());
results.Add(firstPass);
ReadOnlyCollection<NginxFileSystem> collection = GetRecursiveCollection(appSettings.WindowsSettings.Host, appSettings.WindowsSettings.Page);
keyValuePairs = IMetadata.GetKeyValuePairs(collection);
}
return result;
foreach (KeyValuePair<string, List<FileHolder>> keyValuePair in keyValuePairs)
{
index += 1;
windows.Tick();
if (keyValuePair.Value.Count > 2)
throw new NotSupportedException("Too many sidecar files!");
foreach (FileHolder fileHolder in keyValuePair.Value)
{
if (appSettings.WindowsSettings.SidecarExtensions.Contains(fileHolder.ExtensionLowered))
continue;
if (appSettings.WindowsSettings.IgnoreExtensions.Contains(fileHolder.ExtensionLowered))
continue;
filePath = FilePath.Get(appSettings.ResultSettings, appSettings.MetadataSettings, fileHolder, index);
if (filePath.Id is not null && (filePath.IsIntelligentIdFormat || filePath.SortOrder is not null))
continue;
if (filePath.Id is not null)
{
fastForwardMovingPictureExpertsGroupFiles = null;
deterministicHashCode = new(null, filePath.Id, null);
directoryName = Path.GetFileName(filePath.DirectoryFullPath);
if (directoryName.EndsWith(filePath.Id.Value.ToString()))
continue;
}
else
{
fastForwardMovingPictureExpertsGroupFiles = windows.ConvertAndGetFastForwardMovingPictureExpertsGroupFiles(appSettings.WindowsSettings, httpClient, filePath);
fastForwardMovingPictureExpertsGroupFilePath = fastForwardMovingPictureExpertsGroupFiles.Count == 0 ? null : FilePath.Get(appSettings.ResultSettings, appSettings.MetadataSettings, FileHolder.Get(fastForwardMovingPictureExpertsGroupFiles[0]), index);
deterministicHashCode = fastForwardMovingPictureExpertsGroupFilePath is null ? windows.GetDeterministicHashCode(httpClient, filePath) : windows.GetDeterministicHashCode(httpClient, fastForwardMovingPictureExpertsGroupFilePath);
}
sidecarFiles = [];
filePath = FilePath.Get(filePath, deterministicHashCode);
for (int i = 0; i < keyValuePair.Value.Count; i++)
{
if (keyValuePair.Value[i].ExtensionLowered == fileHolder.ExtensionLowered)
continue;
sidecarFiles.Add(keyValuePair.Value[i]);
}
try
{ (minimumYearAndPathCombined, exifDirectory) = metadata.GetMetadataCollection(appSettings.ResultSettings, appSettings.MetadataSettings, httpClient, filePath); }
catch (Exception)
{
logger?.LogWarning("<{filePath}>", filePath.FullName);
continue;
}
fastForwardMovingPictureExpertsGroupUsed = fastForwardMovingPictureExpertsGroupFiles is not null && fastForwardMovingPictureExpertsGroupFiles.Count > 0;
if (fastForwardMovingPictureExpertsGroupUsed && fastForwardMovingPictureExpertsGroupFiles is not null)
{
foreach (string fastForwardMovingPictureExpertsGroupFile in fastForwardMovingPictureExpertsGroupFiles)
File.Delete(fastForwardMovingPictureExpertsGroupFile);
}
if (!fastForwardMovingPictureExpertsGroupUsed && appSettings.WindowsSettings.ValidVideoFormatExtensions.Contains(filePath.ExtensionLowered))
fastForwardMovingPictureExpertsGroupUsed = true;
firstPass = new(exifDirectory, fastForwardMovingPictureExpertsGroupUsed, minimumYearAndPathCombined, sidecarFiles.ToArray());
results.Add(firstPass);
}
}
return results;
}
private ReadOnlyCollection<FirstPass> WindowsAsynchronousWork(AppSettings appSettings, IWindows windows, ReadOnlyCollection<string> files, A_Metadata metadata, int appSettingsMaxDegreeOfParallelism)
private void WindowsWork(ILogger<Program>? logger, AppSettings appSettings, IWindows windows, long ticks)
{
List<FirstPass> results = [];
FirstPass firstPass;
List<string> distinct = [];
List<MetadataGroup> metadataGroups = [];
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = appSettingsMaxDegreeOfParallelism };
files.AsParallel().ForAll(IMetadata.SetExifDirectoryCollection(windows, appSettings.ResultSettings, appSettings.MetadataSettings, metadata, distinct, metadataGroups));
if (_ProgressBar?.CurrentTick != results.Count)
throw new NotSupportedException();
foreach (MetadataGroup metadataGroup in metadataGroups)
if (appSettings.WindowsSettings.VerifyOnly && !string.IsNullOrEmpty(appSettings.WindowsSettings.Host) && !string.IsNullOrEmpty(appSettings.WindowsSettings.Page))
Verify(logger, appSettings, windows, appSettings.WindowsSettings.Host, appSettings.WindowsSettings.Page);
else
{
if (metadataGroup.FastForwardMovingPictureExpertsGroupUsed || !appSettings.ResultSettings.ValidVideoFormatExtensions.Contains(metadataGroup.FilePath.ExtensionLowered))
firstPass = new(metadataGroup.ExifDirectory, metadataGroup.FastForwardMovingPictureExpertsGroupUsed, metadataGroup.MinimumYearAndPathCombined, metadataGroup.SidecarFiles.ToArray());
FirstPass firstPass;
List<FirstPass> results = [];
string sourceDirectory = Path.GetFullPath(appSettings.ResultSettings.RootDirectory);
if (!Directory.Exists(sourceDirectory))
_ = Directory.CreateDirectory(sourceDirectory);
logger?.LogInformation("{Ticks} {RootDirectory}", ticks, sourceDirectory);
ReadOnlyCollection<string> files = Directory.GetFiles(sourceDirectory, "*", SearchOption.AllDirectories).ToArray().AsReadOnly();
if (files.Count > 0)
_ = IPath.DeleteEmptyDirectories(appSettings.ResultSettings.RootDirectory);
A_Metadata metadata = new(appSettings.ResultSettings, appSettings.MetadataSettings);
int appSettingsMaxDegreeOfParallelism = appSettings.WindowsSettings.MaxDegreeOfParallelism;
int filesCount = appSettingsMaxDegreeOfParallelism == 1 ? files.Count : 123000;
windows.ConstructProgressBar(filesCount, "EnumerateFiles load");
if (appSettingsMaxDegreeOfParallelism == 1)
results.AddRange(GetCollection(logger, appSettings, windows, files, metadata));
else
firstPass = new(metadataGroup.ExifDirectory, FastForwardMovingPictureExpertsGroupUsed: true, metadataGroup.MinimumYearAndPathCombined, metadataGroup.SidecarFiles.ToArray());
results.Add(firstPass);
{
List<string> distinct = [];
List<MetadataGroup> metadataGroups = [];
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = appSettingsMaxDegreeOfParallelism };
files.AsParallel().ForAll(IMetadata.SetExifDirectoryCollection(windows, appSettings.ResultSettings, appSettings.MetadataSettings, appSettings.WindowsSettings, metadata, distinct, metadataGroups));
if (_ProgressBar?.CurrentTick != results.Count)
throw new NotSupportedException();
foreach (MetadataGroup metadataGroup in metadataGroups)
{
if (metadataGroup.FastForwardMovingPictureExpertsGroupUsed || !appSettings.WindowsSettings.ValidVideoFormatExtensions.Contains(metadataGroup.FilePath.ExtensionLowered))
firstPass = new(metadataGroup.ExifDirectory, metadataGroup.FastForwardMovingPictureExpertsGroupUsed, metadataGroup.MinimumYearAndPathCombined, metadataGroup.SidecarFiles.ToArray());
else
firstPass = new(metadataGroup.ExifDirectory, FastForwardMovingPictureExpertsGroupUsed: true, metadataGroup.MinimumYearAndPathCombined, metadataGroup.SidecarFiles.ToArray());
results.Add(firstPass);
}
}
string json = JsonSerializer.Serialize(results, FirstPassCollectionSourceGenerationContext.Default.ListFirstPass);
File.WriteAllText(Path.Combine(sourceDirectory, $"{ticks}.json"), json);
}
return results.AsReadOnly();
}
}

View File

@ -1,6 +1,6 @@
{
"sdk": {
"rollForward": "latestMinor",
"version": "9.0.0"
"version": "9.0.103"
}
}