Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
23256c8152 | |||
1bbe583359 | |||
9fb6f0fa05 | |||
ae23e803fa | |||
c15c854481 |
2
.gitignore
vendored
2
.gitignore
vendored
@ -475,4 +475,4 @@ Rename/.vscode/.UserSecrets/secrets.json
|
|||||||
Rename/.vscode/.UserSecretsOld/rename.json
|
Rename/.vscode/.UserSecretsOld/rename.json
|
||||||
Rename/.vscode/.UserSecretsOld/secrets.json
|
Rename/.vscode/.UserSecretsOld/secrets.json
|
||||||
Windows/.vscode/.UserSecrets/secrets.json
|
Windows/.vscode/.UserSecrets/secrets.json
|
||||||
Windows/.vscode/.iCloudPhotos2025
|
Windows/.vscode/.7-Question
|
14
.vscode/tasks.json
vendored
14
.vscode/tasks.json
vendored
@ -628,5 +628,19 @@
|
|||||||
],
|
],
|
||||||
"problemMatcher": "$msCompile"
|
"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": []
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
@ -7,9 +7,12 @@
|
|||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<UserSecretsId>770b6ae3-266e-4d5f-970a-173709b064de</UserSecretsId>
|
<UserSecretsId>770b6ae3-266e-4d5f-970a-173709b064de</UserSecretsId>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<HoursSinceNovember122024>$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1731369600)), 3600))))</HoursSinceNovember122024>
|
||||||
|
</PropertyGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<PackageId>Phares.View.by.Distance.Compare</PackageId>
|
<PackageId>Phares.View.by.Distance.Compare</PackageId>
|
||||||
<Version>8.0.112.0</Version>
|
<Version>9.0.104.$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1731369600)), 3600))))</Version>
|
||||||
<Company>Phares</Company>
|
<Company>Phares</Company>
|
||||||
<Authors>Mike Phares</Authors>
|
<Authors>Mike Phares</Authors>
|
||||||
<IncludeSymbols>true</IncludeSymbols>
|
<IncludeSymbols>true</IncludeSymbols>
|
||||||
@ -39,7 +42,7 @@
|
|||||||
<None Include=".vscode\read-me.md" Pack="true" PackagePath="\" />
|
<None Include=".vscode\read-me.md" Pack="true" PackagePath="\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="runtime.win-x64.Microsoft.DotNet.ILCompiler" Version="8.0.12" />
|
<PackageReference Include="runtime.win-x64.Microsoft.DotNet.ILCompiler" Version="8.0.14" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.1" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.1" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" 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="Microsoft.Extensions.Hosting" Version="8.0.1" />
|
||||||
|
@ -17,6 +17,21 @@ public partial class Compare : ICompare, IDisposable
|
|||||||
private ProgressBar? _ProgressBar;
|
private ProgressBar? _ProgressBar;
|
||||||
private readonly ProgressBarOptions _ProgressBarOptions;
|
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)
|
public Compare(List<string> args, ILogger<Program>? logger, AppSettings appSettings, bool isSilent, IConsole console)
|
||||||
{
|
{
|
||||||
if (isSilent)
|
if (isSilent)
|
||||||
@ -31,19 +46,46 @@ public partial class Compare : ICompare, IDisposable
|
|||||||
CompareWork(logger, appSettings, compare, ticks);
|
CompareWork(logger, appSettings, compare, ticks);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ICompare.Tick() =>
|
private void CompareWork(ILogger<Program>? logger, AppSettings appSettings, ICompare compare, long ticks)
|
||||||
_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();
|
_ProgressBar?.Dispose();
|
||||||
_ProgressBar = new(maxTicks, message, _ProgressBarOptions);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IDisposable.Dispose()
|
|
||||||
{
|
|
||||||
_ProgressBar?.Dispose();
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool GetRunToDoCollectionFirst(AppSettings appSettings, long ticks)
|
private static bool GetRunToDoCollectionFirst(AppSettings appSettings, long ticks)
|
||||||
@ -123,46 +165,4 @@ public partial class Compare : ICompare, IDisposable
|
|||||||
return results;
|
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
@ -20,16 +20,16 @@ public record AppSettings(ResultSettings ResultSettings,
|
|||||||
|
|
||||||
private static void Verify(AppSettings appSettings)
|
private static void Verify(AppSettings appSettings)
|
||||||
{
|
{
|
||||||
if (appSettings.DistanceSettings.RangeDaysDeltaTolerance.Length != 3)
|
if (appSettings.DistanceSettings.RangeDaysDeltaTolerance.Length is not 3 and not 6)
|
||||||
throw new NullReferenceException(nameof(appSettings.DistanceSettings.RangeDaysDeltaTolerance));
|
throw new NullReferenceException(nameof(appSettings.DistanceSettings.RangeDaysDeltaTolerance));
|
||||||
if (appSettings.DistanceSettings.RangeDistanceTolerance.Length != 3)
|
if (appSettings.DistanceSettings.RangeDistanceTolerance.Length is not 3 and not 6)
|
||||||
throw new NullReferenceException(nameof(appSettings.DistanceSettings.RangeDistanceTolerance));
|
throw new NullReferenceException(nameof(appSettings.DistanceSettings.RangeDistanceTolerance));
|
||||||
if (appSettings.DistanceSettings.RangeFaceAreaTolerance.Length != 3)
|
if (appSettings.DistanceSettings.RangeFaceAreaTolerance.Length is not 3 and not 6)
|
||||||
throw new NullReferenceException(nameof(appSettings.DistanceSettings.RangeFaceAreaTolerance));
|
throw new NullReferenceException(nameof(appSettings.DistanceSettings.RangeFaceAreaTolerance));
|
||||||
if (appSettings.DistanceSettings.RangeFaceConfidence.Length != 3)
|
if (appSettings.DistanceSettings.RangeFaceConfidence.Length is not 3 and not 6)
|
||||||
throw new NullReferenceException(nameof(appSettings.DistanceSettings.RangeFaceConfidence));
|
throw new NullReferenceException(nameof(appSettings.DistanceSettings.RangeFaceConfidence));
|
||||||
_ = DateTime.Now.AddDays(-appSettings.DistanceSettings.RangeDaysDeltaTolerance[1]);
|
_ = DateTime.Now.AddDays(-appSettings.DistanceSettings.RangeDaysDeltaTolerance[1]);
|
||||||
if (appSettings.DistanceSettings.SaveSortingWithoutPerson && appSettings.PeopleSettings.JLinks.Length > 0)
|
if (appSettings.DistanceSettings.SaveSortingWithoutPerson && appSettings.PeopleSettings.JLinks.Where(l => !string.IsNullOrEmpty(l)).Any())
|
||||||
throw new Exception("Settings has SaveSortingWithoutPerson and JLinks!");
|
throw new Exception("Settings has SaveSortingWithoutPerson and JLinks!");
|
||||||
if (appSettings.DistanceSettings.SaveSortingWithoutPerson && !string.IsNullOrEmpty(appSettings.DistanceSettings.FocusModel))
|
if (appSettings.DistanceSettings.SaveSortingWithoutPerson && !string.IsNullOrEmpty(appSettings.DistanceSettings.FocusModel))
|
||||||
throw new Exception("Settings has SaveSortingWithoutPerson and FocusModel!");
|
throw new Exception("Settings has SaveSortingWithoutPerson and FocusModel!");
|
||||||
|
@ -8,11 +8,8 @@ public record CompareSettings(string Company,
|
|||||||
string FacesFileNameExtension,
|
string FacesFileNameExtension,
|
||||||
string FacesHiddenFileNameExtension,
|
string FacesHiddenFileNameExtension,
|
||||||
string FacesPartsFileNameExtension,
|
string FacesPartsFileNameExtension,
|
||||||
string[] IgnoreExtensions,
|
|
||||||
int MaxDegreeOfParallelism,
|
int MaxDegreeOfParallelism,
|
||||||
string[] OutputResolutions,
|
string[] OutputResolutions) : Shared.Models.Properties.ICompareSettings
|
||||||
string[] ValidImageFormatExtensions,
|
|
||||||
string[] ValidVideoFormatExtensions) : Shared.Models.Properties.ICompareSettings
|
|
||||||
{
|
{
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
|
@ -6,9 +6,12 @@
|
|||||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<HoursSinceNovember122024>$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1731369600)), 3600))))</HoursSinceNovember122024>
|
||||||
|
</PropertyGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<PackageId>Phares.AA.Distance</PackageId>
|
<PackageId>Phares.AA.Distance</PackageId>
|
||||||
<Version>8.0.112.0</Version>
|
<Version>9.0.104.$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1731369600)), 3600))))</Version>
|
||||||
<Company>Phares</Company>
|
<Company>Phares</Company>
|
||||||
<Authors>Mike Phares</Authors>
|
<Authors>Mike Phares</Authors>
|
||||||
<IncludeSymbols>true</IncludeSymbols>
|
<IncludeSymbols>true</IncludeSymbols>
|
||||||
|
@ -11,11 +11,21 @@ namespace View_by_Distance.Distance.Models.Stateless;
|
|||||||
internal static class FilterLogicD
|
internal static class FilterLogicD
|
||||||
{
|
{
|
||||||
|
|
||||||
internal record RecordA(long? Ticks, string? Directory);
|
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 RecordB(string ByValue, bool IsByMapping, bool IsBySorting);
|
internal static string GetFacePartsDirectoryX(ResultSettings resultSettings, string d2FacePartsContentDirectory, FilePath filePath)
|
||||||
|
{
|
||||||
internal record RecordC(string? DebugDirectory, string? Directory, long? Ticks, string? PersonDirectory);
|
string result;
|
||||||
|
CombinedEnumAndIndex cei = IPath.GetCombinedEnumAndIndex(resultSettings, filePath);
|
||||||
|
result = Path.Combine(d2FacePartsContentDirectory, cei.Combined, filePath.NameWithoutExtension);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
internal static RecordB Get(int? useFiltersCounter, bool saveIndividually, bool sortingContainersAny, string forceSingleImageHumanized, int? distancePermyriad, int? by, string? displayDirectoryName)
|
internal static RecordB Get(int? useFiltersCounter, bool saveIndividually, bool sortingContainersAny, string forceSingleImageHumanized, int? distancePermyriad, int? by, string? displayDirectoryName)
|
||||||
{
|
{
|
||||||
@ -39,141 +49,12 @@ internal static class FilterLogicD
|
|||||||
else if (isBySorting && useFiltersCounter.HasValue)
|
else if (isBySorting && useFiltersCounter.HasValue)
|
||||||
byValue = $"{nameof(IMapLogic.Sorting)}{(!isDefaultName ? "-A" : "-Z")} Modified Filters - {useFiltersCounter.Value}";
|
byValue = $"{nameof(IMapLogic.Sorting)}{(!isDefaultName ? "-A" : "-Z")} Modified Filters - {useFiltersCounter.Value}";
|
||||||
else
|
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);
|
result = new(byValue, isByMapping, isBySorting);
|
||||||
return result;
|
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)
|
internal static void SaveContainers(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, int? updated, ReadOnlyCollection<SaveContainer> saveContainers)
|
||||||
{
|
{
|
||||||
string fileName;
|
string fileName;
|
||||||
@ -285,4 +166,115 @@ 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);
|
||||||
|
|
||||||
}
|
}
|
@ -9,49 +9,69 @@ namespace View_by_Distance.Distance.Models.Stateless;
|
|||||||
public interface IDistance
|
public interface IDistance
|
||||||
{
|
{
|
||||||
|
|
||||||
static ReadOnlyCollection<ExifDirectory> TestStatic_GetMapped(ResultSettings resultSettings, MetadataSettings metadataSettings, PeopleSettings peopleSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, ReadOnlyCollections readOnlyCollections) =>
|
static string Get(bool saveIndividually, string forceSingleImageHumanized, int by, bool isDefaultName) =>
|
||||||
GetMapped(resultSettings, metadataSettings, peopleSettings, distanceSettings, compareSettings, compare, ticks, readOnlyCollections);
|
$"{by switch
|
||||||
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);
|
IMapLogic.Mapping => nameof(IMapLogic.Mapping),
|
||||||
|
IMapLogic.Sorting => saveIndividually ?
|
||||||
|
nameof(IMapLogic.Individually) :
|
||||||
|
nameof(IMapLogic.Sorting),
|
||||||
|
IMapLogic.ForceSingleImage => forceSingleImageHumanized,
|
||||||
|
_ => throw new NotImplementedException()
|
||||||
|
}}{(!isDefaultName ? "-A" : "-Z")}";
|
||||||
|
|
||||||
static ReadOnlyDictionary<int, ReadOnlyDictionary<int, FilePath>> TestStatic_Extract(ICompareSettings compareSettings, ReadOnlyCollection<ExifDirectory> exifDirectories) =>
|
public static ReadOnlyDictionary<string, LocationContainer> GetOnlyOne(DistanceSettings distanceSettings, ReadOnlyCollection<LocationContainer> matrix) =>
|
||||||
Extract(compareSettings, exifDirectories);
|
|
||||||
static ReadOnlyDictionary<int, ReadOnlyDictionary<int, FilePath>> Extract(ICompareSettings compareSettings, ReadOnlyCollection<ExifDirectory> exifDirectories) =>
|
|
||||||
MappedLogicA.Extract(compareSettings, 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);
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
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);
|
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) =>
|
public static ReadOnlyCollection<ExifDirectory> GetMappedExifDirectoryWithEncoding(ICompare compare, long ticks, ReadOnlyCollection<ExifDirectory> exifDirectories) =>
|
||||||
GetSaveContainers(resultSettings, distanceSettings, compareSettings, compare, ticks, outputResolution, onlyOne);
|
FaceEncodingLogic.GetMappedExifDirectoryWithEncoding(compare, ticks, exifDirectories);
|
||||||
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) =>
|
public static ReadOnlyCollection<LocationContainer> GetPostFilterLocationContainer(ReadOnlyCollection<LocationContainer> preFiltered, DistanceLimits distanceLimits) =>
|
||||||
SaveContainers(distanceSettings, compareSettings, compare, ticks, updated, saveContainers);
|
FilterLogicC.GetPostFilterLocationContainer(preFiltered, distanceLimits);
|
||||||
static void SaveContainers(DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, int? updated, ReadOnlyCollection<SaveContainer> saveContainers) =>
|
|
||||||
|
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);
|
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) =>
|
||||||
|
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);
|
||||||
|
|
||||||
|
public 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) =>
|
||||||
|
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) =>
|
||||||
|
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);
|
||||||
|
|
||||||
}
|
}
|
@ -14,33 +14,53 @@ internal static class MappedLogicA
|
|||||||
string? PersonDisplayDirectoryName,
|
string? PersonDisplayDirectoryName,
|
||||||
FilePath FilePath);
|
FilePath FilePath);
|
||||||
|
|
||||||
private static List<MappedFile> GetDisplayDirectoryAllFiles(PeopleSettings peopleSettings, ICompareSettings compareSettings, ReadOnlyCollections readOnlyCollections)
|
internal static ReadOnlyDictionary<int, ReadOnlyDictionary<int, FilePath>> Extract(ICompareSettings compareSettings, ReadOnlyCollection<ExifDirectory> exifDirectories)
|
||||||
{
|
{
|
||||||
List<MappedFile> results = [];
|
Dictionary<int, ReadOnlyDictionary<int, FilePath>> results = [];
|
||||||
FilePath filePath;
|
int? wholePercentages;
|
||||||
MappedFile mappedFile;
|
Dictionary<int, FilePath>? keyValues;
|
||||||
string personKeyFormatted;
|
Dictionary<int, Dictionary<int, FilePath>> keyValuePairs = [];
|
||||||
List<string> distinct = [];
|
foreach (ExifDirectory exifDirectory in exifDirectories)
|
||||||
PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName personKeyFormattedAndKeyTicksAndDisplayDirectoryName;
|
|
||||||
foreach (PersonContainer personContainer in readOnlyCollections.PersonContainers)
|
|
||||||
{
|
{
|
||||||
if (personContainer.Key is null)
|
if (exifDirectory.FilePath.Id is null)
|
||||||
continue;
|
continue;
|
||||||
for (int i = personContainer.DisplayDirectoryAllFilePaths.Count - 1; i > -1; i--)
|
if (!keyValuePairs.TryGetValue(exifDirectory.FilePath.Id.Value, out keyValues))
|
||||||
{
|
{
|
||||||
filePath = personContainer.DisplayDirectoryAllFilePaths[i];
|
keyValuePairs.Add(exifDirectory.FilePath.Id.Value, []);
|
||||||
if (filePath.ExtensionLowered != compareSettings.FacesFileNameExtension)
|
if (!keyValuePairs.TryGetValue(exifDirectory.FilePath.Id.Value, out keyValues))
|
||||||
continue;
|
throw new Exception();
|
||||||
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);
|
||||||
}
|
}
|
||||||
return results;
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ReadOnlyCollection<MappedFile> GetMappedFiles(ResultSettings resultSettings, MetadataSettings metadataSettings, PeopleSettings peopleSettings, ICompareSettings compareSettings, ReadOnlyCollections readOnlyCollections, ReadOnlyCollection<MappedLogicB.Record> records)
|
private static ReadOnlyCollection<MappedFile> GetMappedFiles(ResultSettings resultSettings, MetadataSettings metadataSettings, PeopleSettings peopleSettings, ICompareSettings compareSettings, ReadOnlyCollections readOnlyCollections, ReadOnlyCollection<MappedLogicB.Record> records)
|
||||||
@ -102,6 +122,35 @@ internal static class MappedLogicA
|
|||||||
return results.AsReadOnly();
|
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)
|
private static void MappedParallelFor(ResultSettings resultSettings, MetadataSettings metadataSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, List<ExifDirectory> exifDirectories, ReadOnlyDictionary<int, List<FilePathAndWholePercentages>> skipCollection, MappedFile mappedFile)
|
||||||
{
|
{
|
||||||
int? id;
|
int? id;
|
||||||
@ -152,53 +201,4 @@ internal static class MappedLogicA
|
|||||||
exifDirectories.Add(exifDirectory);
|
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
@ -21,62 +21,188 @@ internal static class MappedLogicB
|
|||||||
internal record TicksDirectory(DateTime AlternateDirectoryDateTime,
|
internal record TicksDirectory(DateTime AlternateDirectoryDateTime,
|
||||||
string Directory,
|
string Directory,
|
||||||
DateTime DirectoryDateTime,
|
DateTime DirectoryDateTime,
|
||||||
string DirectoryName,
|
|
||||||
bool? IsLocationContainerDebugDirectory,
|
bool? IsLocationContainerDebugDirectory,
|
||||||
float? TotalDays);
|
float? TotalDays);
|
||||||
|
|
||||||
private static void MoveTo(string actionDirectory, TicksDirectory ticksDirectory, string directory, string personKeyFormatted, string yearDirectoryName, string alphaDirectoryName, string[] files, string[] facesFileNames)
|
internal static ReadOnlyCollection<Record> DeleteEmptyDirectoriesAndGetCollection(ResultSettings resultSettings, MetadataSettings metadataSettings, PeopleSettings peopleSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, string eDistanceContentDirectory, ReadOnlyCollections readOnlyCollections)
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
if (facesFileNames.Contains(file))
|
|
||||||
{
|
|
||||||
checkFile = Path.Combine(checkDirectory, Path.GetFileName(file));
|
|
||||||
if (File.Exists(checkFile))
|
|
||||||
continue;
|
|
||||||
File.Move(file, checkFile);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
File.Delete(file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void MoveFiles(string personKeyFormatted, string personKeyDirectory, string newestPersonKeyFormatted, string newestPersonKeyDirectory)
|
|
||||||
{
|
{
|
||||||
|
List<Record> results = [];
|
||||||
|
bool check;
|
||||||
|
string message;
|
||||||
string[] files;
|
string[] files;
|
||||||
string checkFile;
|
bool? isDefault;
|
||||||
|
int? linksCount;
|
||||||
|
int totalSeconds;
|
||||||
|
DateTime dateTime;
|
||||||
|
TimeSpan timeSpan;
|
||||||
|
int directoryNumber;
|
||||||
|
List<Record> records;
|
||||||
string? checkDirectory;
|
string? checkDirectory;
|
||||||
string[] directories = Directory.GetDirectories(personKeyDirectory, "*", SearchOption.TopDirectoryOnly);
|
string[] yearDirectories;
|
||||||
foreach (string directory in directories)
|
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++)
|
||||||
{
|
{
|
||||||
checkDirectory = Path.Combine(newestPersonKeyDirectory, Path.GetFileName(directory));
|
check = false;
|
||||||
if (!Directory.Exists(checkDirectory))
|
results.Clear();
|
||||||
Directory.Move(directory, checkDirectory);
|
distinct.Clear();
|
||||||
else
|
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)
|
||||||
{
|
{
|
||||||
files = Directory.GetFiles(directory, "*", SearchOption.AllDirectories);
|
if (i == 1)
|
||||||
foreach (string file in files)
|
compare.Tick();
|
||||||
|
personKeyFormattedDirectories = Directory.GetDirectories(ticksDirectory.Directory, "*", SearchOption.TopDirectoryOnly);
|
||||||
|
foreach (string personKeyFormattedDirectory in personKeyFormattedDirectories)
|
||||||
{
|
{
|
||||||
if (file.Split(personKeyFormatted).Length != 2 || file.Contains(newestPersonKeyFormatted))
|
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;
|
continue;
|
||||||
checkFile = file.Replace(personKeyFormatted, newestPersonKeyFormatted);
|
if (ticksDirectory.IsLocationContainerDebugDirectory is null || !ticksDirectory.IsLocationContainerDebugDirectory.Value)
|
||||||
checkDirectory = Path.GetDirectoryName(checkFile);
|
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)
|
if (checkDirectory is null)
|
||||||
continue;
|
continue;
|
||||||
if (File.Exists(checkFile))
|
checkDirectory = Path.Combine(checkDirectory, ticksDirectory.AlternateDirectoryDateTime.Ticks.ToString());
|
||||||
continue;
|
|
||||||
if (!Directory.Exists(checkDirectory))
|
if (!Directory.Exists(checkDirectory))
|
||||||
_ = Directory.CreateDirectory(checkDirectory);
|
_ = Directory.CreateDirectory(checkDirectory);
|
||||||
File.Move(file, checkFile);
|
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;
|
||||||
}
|
}
|
||||||
_ = IPath.DeleteEmptyDirectories(personKeyDirectory);
|
}
|
||||||
|
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)
|
||||||
|
continue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return results.AsReadOnly();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<TicksDirectory> UpdateDateVerifyAndGetTicksDirectories(DistanceSettings distanceSettings, string eDistanceContentDirectory)
|
private static List<TicksDirectory> UpdateDateVerifyAndGetTicksDirectories(DistanceSettings distanceSettings, string eDistanceContentDirectory)
|
||||||
@ -85,13 +211,13 @@ internal static class MappedLogicB
|
|||||||
float? totalDays;
|
float? totalDays;
|
||||||
long? next = null;
|
long? next = null;
|
||||||
string? checkDirectory;
|
string? checkDirectory;
|
||||||
string ticksDirectoryName;
|
|
||||||
DateTime directoryDateTime;
|
DateTime directoryDateTime;
|
||||||
DirectoryInfo directoryInfo;
|
DirectoryInfo directoryInfo;
|
||||||
TicksDirectory ticksDirectory;
|
TicksDirectory ticksDirectory;
|
||||||
long? lastDirectoryTicks = null;
|
long? lastDirectoryTicks = null;
|
||||||
DateTime dateTime = DateTime.Now;
|
DateTime dateTime = DateTime.Now;
|
||||||
DateTime alternateDirectoryDateTime;
|
DateTime alternateDirectoryDateTime;
|
||||||
|
string ticksDirectoryNameFirstSegment;
|
||||||
bool? isLocationContainerDebugDirectory;
|
bool? isLocationContainerDebugDirectory;
|
||||||
long month = dateTime.AddMonths(1).Ticks - dateTime.Ticks;
|
long month = dateTime.AddMonths(1).Ticks - dateTime.Ticks;
|
||||||
for (int i = 1; i < 5; i++)
|
for (int i = 1; i < 5; i++)
|
||||||
@ -101,10 +227,10 @@ internal static class MappedLogicB
|
|||||||
string[] ticksFullPaths = Directory.GetDirectories(eDistanceContentDirectory, "*", SearchOption.TopDirectoryOnly);
|
string[] ticksFullPaths = Directory.GetDirectories(eDistanceContentDirectory, "*", SearchOption.TopDirectoryOnly);
|
||||||
foreach (string ticksFullPath in ticksFullPaths)
|
foreach (string ticksFullPath in ticksFullPaths)
|
||||||
{
|
{
|
||||||
ticksDirectoryName = Path.GetFileName(ticksFullPath);
|
ticksDirectoryNameFirstSegment = Path.GetFileName(ticksFullPath).Split('.')[0];
|
||||||
if (ticksDirectoryName.Length < 3)
|
if (ticksDirectoryNameFirstSegment.Length < 3)
|
||||||
continue;
|
continue;
|
||||||
if (!long.TryParse(ticksDirectoryName, out long directoryTicks))
|
if (!long.TryParse(ticksDirectoryNameFirstSegment, out long directoryTicks))
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
if (next is null)
|
if (next is null)
|
||||||
next = new DateTime(directoryTicks).Ticks;
|
next = new DateTime(directoryTicks).Ticks;
|
||||||
@ -130,9 +256,9 @@ internal static class MappedLogicB
|
|||||||
if (directoryInfo.LastWriteTime.Ticks != directoryTicks)
|
if (directoryInfo.LastWriteTime.Ticks != directoryTicks)
|
||||||
Directory.SetLastWriteTime(ticksFullPath, new DateTime(directoryTicks));
|
Directory.SetLastWriteTime(ticksFullPath, new DateTime(directoryTicks));
|
||||||
alternateDirectoryDateTime = new DateTime(directoryDateTime.Year, directoryDateTime.Month, directoryDateTime.Day).AddMonths(1);
|
alternateDirectoryDateTime = new DateTime(directoryDateTime.Year, directoryDateTime.Month, directoryDateTime.Day).AddMonths(1);
|
||||||
isLocationContainerDebugDirectory = distanceSettings.LocationContainerDebugDirectory is null ? null : ticksDirectoryName.EndsWith(distanceSettings.LocationContainerDebugDirectory);
|
isLocationContainerDebugDirectory = distanceSettings.LocationContainerDebugDirectory is null ? null : ticksDirectoryNameFirstSegment.EndsWith(distanceSettings.LocationContainerDebugDirectory);
|
||||||
totalDays = lastDirectoryTicks is null || new TimeSpan(dateTime.Ticks - directoryTicks).TotalDays < 1 ? null : (float)new TimeSpan(directoryTicks - lastDirectoryTicks.Value).TotalDays;
|
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), ticksDirectoryName, isLocationContainerDebugDirectory, totalDays);
|
ticksDirectory = new(alternateDirectoryDateTime, ticksFullPath, new(directoryTicks), isLocationContainerDebugDirectory, totalDays);
|
||||||
results.Add(ticksDirectory);
|
results.Add(ticksDirectory);
|
||||||
if (directoryDateTime.Hour == 0 && directoryDateTime.Minute == 0 && directoryDateTime.Second == 0)
|
if (directoryDateTime.Hour == 0 && directoryDateTime.Minute == 0 && directoryDateTime.Second == 0)
|
||||||
continue;
|
continue;
|
||||||
@ -217,40 +343,42 @@ internal static class MappedLogicB
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
private static void MoveTo(string actionDirectory, TicksDirectory ticksDirectory, string directory, string personKeyFormatted, string yearDirectoryName, string alphaDirectoryName, string[] files, string[] facesFileNames)
|
||||||
{
|
{
|
||||||
List<Record> results = [];
|
|
||||||
Record record;
|
|
||||||
string fileName;
|
|
||||||
string checkFile;
|
string checkFile;
|
||||||
FilePath filePath;
|
string actionDirectoryName = Path.GetFileName(actionDirectory);
|
||||||
FileHolder fileHolder;
|
string checkDirectory = actionDirectoryName.StartsWith("y", StringComparison.CurrentCultureIgnoreCase) ? Path.Combine(ticksDirectory.Directory, personKeyFormatted, yearDirectoryName, alphaDirectoryName) : Path.Combine(directory, actionDirectoryName);
|
||||||
int? wholePercentages;
|
if (!Directory.Exists(checkDirectory))
|
||||||
|
_ = Directory.CreateDirectory(checkDirectory);
|
||||||
foreach (string file in files)
|
foreach (string file in files)
|
||||||
{
|
{
|
||||||
if (file.EndsWith(".lnk"))
|
if (facesFileNames.Contains(file))
|
||||||
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";
|
checkFile = Path.Combine(checkDirectory, Path.GetFileName(file));
|
||||||
if (File.Exists(checkFile))
|
if (File.Exists(checkFile))
|
||||||
continue;
|
continue;
|
||||||
File.Move(file, checkFile);
|
File.Move(file, checkFile);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
distinct.Add(fileName);
|
File.Delete(file);
|
||||||
record = new(directoryNumber, isDefault, linksCount, filePath, personDisplayDirectoryName, personKeyFormatted);
|
|
||||||
results.Add(record);
|
|
||||||
}
|
}
|
||||||
return results;
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string[] RenameBirth(string[] files)
|
private static string[] RenameBirth(string[] files)
|
||||||
@ -285,216 +413,117 @@ internal static class MappedLogicB
|
|||||||
Directory.Move(personKeyDirectory, newestPersonKeyDirectory);
|
Directory.Move(personKeyDirectory, newestPersonKeyDirectory);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int? GetLinksCount(string yearDirectory)
|
private static void MoveFiles(string personKeyFormatted, string personKeyDirectory, string newestPersonKeyFormatted, string newestPersonKeyDirectory)
|
||||||
{
|
{
|
||||||
int? result;
|
string[] files;
|
||||||
string[] yearDirectoryNameSegments = Path.GetFileName(yearDirectory).Split('-');
|
string checkFile;
|
||||||
if (yearDirectoryNameSegments.Length != 3)
|
string? checkDirectory;
|
||||||
result = null;
|
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
|
else
|
||||||
{
|
{
|
||||||
string lastSegment = yearDirectoryNameSegments[^1];
|
files = Directory.GetFiles(directory, "*", SearchOption.AllDirectories);
|
||||||
if (lastSegment.Length != 3 || !lastSegment.All(l => l == lastSegment[0]))
|
foreach (string file in files)
|
||||||
result = null;
|
{
|
||||||
else
|
if (file.Split(personKeyFormatted).Length != 2 || file.Contains(newestPersonKeyFormatted))
|
||||||
result = lastSegment[0] - 65;
|
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<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;
|
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
@ -6,9 +6,12 @@
|
|||||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<HoursSinceNovember122024>$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1731369600)), 3600))))</HoursSinceNovember122024>
|
||||||
|
</PropertyGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<PackageId>Phares.AA.Face</PackageId>
|
<PackageId>Phares.AA.Face</PackageId>
|
||||||
<Version>8.0.112.0</Version>
|
<Version>9.0.104.$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1731369600)), 3600))))</Version>
|
||||||
<Company>Phares</Company>
|
<Company>Phares</Company>
|
||||||
<Authors>Mike Phares</Authors>
|
<Authors>Mike Phares</Authors>
|
||||||
<IncludeSymbols>true</IncludeSymbols>
|
<IncludeSymbols>true</IncludeSymbols>
|
||||||
|
@ -6,10 +6,13 @@
|
|||||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<HoursSinceNovember122024>$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1731369600)), 3600))))</HoursSinceNovember122024>
|
||||||
|
</PropertyGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<PackageId>Phares.AA.FaceRecognitionDotNet</PackageId>
|
<PackageId>Phares.AA.FaceRecognitionDotNet</PackageId>
|
||||||
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
|
||||||
<Version>9.0.100.1</Version>
|
<Version>9.0.104.$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1731369600)), 3600))))</Version>
|
||||||
<Authors>Mike Phares</Authors>
|
<Authors>Mike Phares</Authors>
|
||||||
<Company>Phares</Company>
|
<Company>Phares</Company>
|
||||||
<IncludeSymbols>true</IncludeSymbols>
|
<IncludeSymbols>true</IncludeSymbols>
|
||||||
|
@ -4,11 +4,14 @@
|
|||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<OutputType>library</OutputType>
|
<OutputType>library</OutputType>
|
||||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<HoursSinceNovember142023>$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1699920000)), 3600))))</HoursSinceNovember142023>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<PackageId>Phares.AA.Metadata</PackageId>
|
<PackageId>Phares.AA.Metadata</PackageId>
|
||||||
<Version>8.0.112.0</Version>
|
<Version>8.0.114.$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1699920000)), 3600))))</Version>
|
||||||
<Company>Phares</Company>
|
<Company>Phares</Company>
|
||||||
<Authors>Mike Phares</Authors>
|
<Authors>Mike Phares</Authors>
|
||||||
<IncludeSymbols>true</IncludeSymbols>
|
<IncludeSymbols>true</IncludeSymbols>
|
||||||
@ -39,7 +42,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="MetadataExtractor" Version="2.8.1" />
|
<PackageReference Include="MetadataExtractor" Version="2.8.1" />
|
||||||
<PackageReference Include="System.Text.Json" Version="8.0.5" />
|
<PackageReference Include="System.Text.Json" Version="9.0.3" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.1" />
|
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -11,7 +11,7 @@ public class A_Metadata
|
|||||||
|
|
||||||
private readonly ResultSettings _ResultSettings;
|
private readonly ResultSettings _ResultSettings;
|
||||||
private readonly MetadataSettings _MetadataSettings;
|
private readonly MetadataSettings _MetadataSettings;
|
||||||
private readonly ReadOnlyDictionary<int, ReadOnlyDictionary<string, string[]>> _FileGroups;
|
private readonly ReadOnlyDictionary<int, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> _ResultSingletonFileGroups;
|
||||||
|
|
||||||
public A_Metadata(ResultSettings resultSettings, MetadataSettings metadataSettings)
|
public A_Metadata(ResultSettings resultSettings, MetadataSettings metadataSettings)
|
||||||
{
|
{
|
||||||
@ -23,46 +23,21 @@ public class A_Metadata
|
|||||||
includeResizeGroup: false,
|
includeResizeGroup: false,
|
||||||
includeModel: false,
|
includeModel: false,
|
||||||
includePredictorModel: false);
|
includePredictorModel: false);
|
||||||
_FileGroups = IPath.GetKeyValuePairs(resultSettings, aResultsFullGroupDirectory, [resultSettings.ResultSingleton]);
|
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)
|
||||||
private MinimumYearAndPathCombined GetMinimumYearAndPathCombined(ResultSettings resultSettings, FilePath filePath)
|
|
||||||
{
|
{
|
||||||
MinimumYearAndPathCombined result;
|
foreach (KeyValuePair<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> keyValue in keyValuePair.Value)
|
||||||
(_, 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;
|
if (keyValue.Key == resultSettings.ResultSingleton)
|
||||||
DateTime? dateTime;
|
results.Add(keyValuePair.Key, keyValue.Value);
|
||||||
dateTime = IDate.GetDateTimeOriginal(exifDirectory);
|
else
|
||||||
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
|
|
||||||
{
|
|
||||||
result = JsonSerializer.Deserialize(json, ExifDirectorySourceGenerationContext.Default.ExifDirectory);
|
|
||||||
if (result is null)
|
|
||||||
throw new Exception();
|
throw new Exception();
|
||||||
}
|
}
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
result = null;
|
|
||||||
}
|
}
|
||||||
return new(json, result);
|
_ResultSingletonFileGroups = new(results);
|
||||||
|
ReadOnlyCollection<string> directories = new([Path.Combine(aResultsFullGroupDirectory, resultSettings.ResultSingleton)]);
|
||||||
|
IPath.CreateDirectories(directories);
|
||||||
}
|
}
|
||||||
|
|
||||||
public (MinimumYearAndPathCombined, ExifDirectory) GetMetadataCollection(ResultSettings resultSettings, MetadataSettings metadataSettings, FilePath filePath)
|
public (MinimumYearAndPathCombined, ExifDirectory) GetMetadataCollection(ResultSettings resultSettings, MetadataSettings metadataSettings, FilePath filePath)
|
||||||
@ -136,19 +111,45 @@ public class A_Metadata
|
|||||||
return new(minimumYearAndPathCombined, result);
|
return new(minimumYearAndPathCombined, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Stream GetStream(HttpClient httpClient, FilePath filePath)
|
private MinimumYearAndPathCombined GetMinimumYearAndPathCombined(ResultSettings resultSettings, FilePath filePath)
|
||||||
{
|
{
|
||||||
Stream result;
|
MinimumYearAndPathCombined result;
|
||||||
Task<HttpResponseMessage> httpResponseMessage = httpClient.GetAsync(filePath.FullName);
|
CombinedEnumAndIndex cei = IPath.GetCombinedEnumAndIndex(resultSettings, filePath);
|
||||||
httpResponseMessage.Wait();
|
DateTime minimumDateTime = new(filePath.CreationTicks < filePath.LastWriteTicks ? filePath.CreationTicks : filePath.LastWriteTicks);
|
||||||
Task task = httpResponseMessage.Result.Content.LoadIntoBufferAsync();
|
int minimumYear = minimumDateTime.Year < resultSettings.EpicYear ? resultSettings.EpicYear : minimumDateTime.Year;
|
||||||
task.Wait();
|
result = new(minimumYear, Path.Combine(_ResultSingletonFileGroups[minimumYear][cei.Enum][cei.Index], $"{filePath.NameWithoutExtension}{filePath.ExtensionLowered}.json"));
|
||||||
Task<Stream> stream = httpResponseMessage.Result.Content.ReadAsStreamAsync();
|
|
||||||
stream.Wait();
|
|
||||||
result = stream.Result;
|
|
||||||
return 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)
|
public (MinimumYearAndPathCombined, ExifDirectory) GetMetadataCollection(ResultSettings resultSettings, MetadataSettings metadataSettings, HttpClient? httpClient, FilePath filePath)
|
||||||
{
|
{
|
||||||
ExifDirectory result;
|
ExifDirectory result;
|
||||||
@ -166,4 +167,17 @@ public class A_Metadata
|
|||||||
return new(minimumYearAndPathCombined, result);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -54,51 +54,7 @@ internal static class Get
|
|||||||
return results.AsReadOnly();
|
return results.AsReadOnly();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static Action<string> SetExifDirectoryCollection(IRename rename, ResultSettings resultSettings, MetadataSettings metadataSettings, IRenameSettings renameSettings, A_Metadata metadata, List<string> distinct, List<MetadataGroup> metadataGroups)
|
internal static Action<string> SetExifDirectoryCollection(IWindows windows, ResultSettings resultSettings, MetadataSettings metadataSettings, 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 =>
|
return file =>
|
||||||
{
|
{
|
||||||
@ -125,7 +81,7 @@ internal static class Get
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fastForwardMovingPictureExpertsGroupFiles = windows.ConvertAndGetFastForwardMovingPictureExpertsGroupFiles(windowsSettings, httpClient, filePath);
|
fastForwardMovingPictureExpertsGroupFiles = windows.ConvertAndGetFastForwardMovingPictureExpertsGroupFiles(resultSettings, httpClient, filePath);
|
||||||
fastForwardMovingPictureExpertsGroupFilePath = fastForwardMovingPictureExpertsGroupFiles.Count == 0 ? null : FilePath.Get(resultSettings, metadataSettings, FileHolder.Get(fastForwardMovingPictureExpertsGroupFiles[0]), index: null);
|
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);
|
deterministicHashCode = fastForwardMovingPictureExpertsGroupFilePath is null ? windows.GetDeterministicHashCode(httpClient, filePath) : windows.GetDeterministicHashCode(httpClient, fastForwardMovingPictureExpertsGroupFilePath);
|
||||||
}
|
}
|
||||||
@ -145,4 +101,48 @@ 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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -72,9 +72,9 @@ public interface IMetadata
|
|||||||
static ReadOnlyDictionary<string, List<FileHolder>> GetKeyValuePairs(IEnumerable<NginxFileSystem> collection) =>
|
static ReadOnlyDictionary<string, List<FileHolder>> GetKeyValuePairs(IEnumerable<NginxFileSystem> collection) =>
|
||||||
Get.GetKeyValuePairs(collection);
|
Get.GetKeyValuePairs(collection);
|
||||||
|
|
||||||
Action<string> TestStatic_SetExifDirectoryCollection(IWindows windows, ResultSettings resultSettings, MetadataSettings metadataSettings, IWindowsSettings windowsSettings, A_Metadata metadata, List<string> distinct, List<MetadataGroup> metadataGroups) =>
|
Action<string> TestStatic_SetExifDirectoryCollection(IWindows windows, ResultSettings resultSettings, MetadataSettings metadataSettings, A_Metadata metadata, List<string> distinct, List<MetadataGroup> metadataGroups) =>
|
||||||
SetExifDirectoryCollection(windows, resultSettings, metadataSettings, windowsSettings, metadata, distinct, metadataGroups);
|
SetExifDirectoryCollection(windows, resultSettings, metadataSettings, metadata, distinct, metadataGroups);
|
||||||
static Action<string> SetExifDirectoryCollection(IWindows windows, ResultSettings resultSettings, MetadataSettings metadataSettings, IWindowsSettings windowsSettings, A_Metadata metadata, List<string> distinct, List<MetadataGroup> 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, windowsSettings, metadata, distinct, metadataGroups);
|
Get.SetExifDirectoryCollection(windows, resultSettings, metadataSettings, metadata, distinct, metadataGroups);
|
||||||
|
|
||||||
}
|
}
|
@ -6,9 +6,12 @@
|
|||||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<HoursSinceNovember122024>$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1731369600)), 3600))))</HoursSinceNovember122024>
|
||||||
|
</PropertyGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<PackageId>Phares.AA.People</PackageId>
|
<PackageId>Phares.AA.People</PackageId>
|
||||||
<Version>8.0.112.0</Version>
|
<Version>9.0.104.$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1731369600)), 3600))))</Version>
|
||||||
<Company>Phares</Company>
|
<Company>Phares</Company>
|
||||||
<Authors>Mike Phares</Authors>
|
<Authors>Mike Phares</Authors>
|
||||||
<IncludeSymbols>true</IncludeSymbols>
|
<IncludeSymbols>true</IncludeSymbols>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
@ -8,9 +8,12 @@
|
|||||||
<UserSecretsIdOld>fa0fa59b-afe4-4960-9afc-18fcbc7fb41b</UserSecretsIdOld>
|
<UserSecretsIdOld>fa0fa59b-afe4-4960-9afc-18fcbc7fb41b</UserSecretsIdOld>
|
||||||
<UserSecretsId>843db3e1-e18f-4cba-8b00-967529a32635</UserSecretsId>
|
<UserSecretsId>843db3e1-e18f-4cba-8b00-967529a32635</UserSecretsId>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<HoursSinceNovember122024>$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1731369600)), 3600))))</HoursSinceNovember122024>
|
||||||
|
</PropertyGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<PackageId>Phares.View.by.Distance.Rename</PackageId>
|
<PackageId>Phares.View.by.Distance.Rename</PackageId>
|
||||||
<Version>8.0.112.0</Version>
|
<Version>9.0.104.$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1731369600)), 3600))))</Version>
|
||||||
<Company>Phares</Company>
|
<Company>Phares</Company>
|
||||||
<Authors>Mike Phares</Authors>
|
<Authors>Mike Phares</Authors>
|
||||||
<IncludeSymbols>true</IncludeSymbols>
|
<IncludeSymbols>true</IncludeSymbols>
|
||||||
@ -40,7 +43,7 @@
|
|||||||
<None Include=".vscode\read-me.md" Pack="true" PackagePath="\" />
|
<None Include=".vscode\read-me.md" Pack="true" PackagePath="\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="CliWrap" Version="3.7.1" />
|
<PackageReference Include="CliWrap" Version="3.8.2" />
|
||||||
<PackageReference Include="runtime.win-x64.Microsoft.DotNet.ILCompiler" Version="8.0.12" />
|
<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.Json" Version="8.0.1" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="8.0.1" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="8.0.1" />
|
||||||
|
@ -6,21 +6,20 @@ namespace View_by_Distance.Rename.Models;
|
|||||||
|
|
||||||
public record RenameSettings(string Company,
|
public record RenameSettings(string Company,
|
||||||
string DefaultMaker,
|
string DefaultMaker,
|
||||||
|
Dictionary<string, string?> DirectoryDictionary,
|
||||||
string? FirstPassFile,
|
string? FirstPassFile,
|
||||||
bool ForceNewId,
|
bool ForceNewId,
|
||||||
string[] IgnoreExtensions,
|
|
||||||
bool InPlace,
|
bool InPlace,
|
||||||
bool InPlaceMoveDirectory,
|
bool InPlaceMoveDirectory,
|
||||||
bool InPlaceWithOriginalName,
|
bool InPlaceWithOriginalName,
|
||||||
|
bool JustMediaDate,
|
||||||
int MaxDegreeOfParallelism,
|
int MaxDegreeOfParallelism,
|
||||||
int MaxMilliSecondsPerCall,
|
int MaxMilliSecondsPerCall,
|
||||||
bool OnlySaveIdentifiersToDisk,
|
bool OnlySaveIdentifiersToDisk,
|
||||||
string RelativePropertyCollectionFile,
|
string RelativePropertyCollectionFile,
|
||||||
bool RequireRootDirectoryExists,
|
bool RequireRootDirectoryExists,
|
||||||
string[] SidecarExtensions,
|
string[] SidecarExtensions,
|
||||||
bool SkipIdFiles,
|
bool SkipIdFiles) : Shared.Models.Properties.IRenameSettings
|
||||||
string[] ValidImageFormatExtensions,
|
|
||||||
string[] ValidVideoFormatExtensions) : Shared.Models.Properties.IRenameSettings
|
|
||||||
{
|
{
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
|
778
Rename/Rename.cs
778
Rename/Rename.cs
@ -11,7 +11,6 @@ using View_by_Distance.Metadata.Models;
|
|||||||
using View_by_Distance.Metadata.Models.Stateless;
|
using View_by_Distance.Metadata.Models.Stateless;
|
||||||
using View_by_Distance.Rename.Models;
|
using View_by_Distance.Rename.Models;
|
||||||
using View_by_Distance.Shared.Models;
|
using View_by_Distance.Shared.Models;
|
||||||
using View_by_Distance.Shared.Models.Properties;
|
|
||||||
using View_by_Distance.Shared.Models.Stateless;
|
using View_by_Distance.Shared.Models.Stateless;
|
||||||
|
|
||||||
namespace View_by_Distance.Rename;
|
namespace View_by_Distance.Rename;
|
||||||
@ -35,39 +34,58 @@ public partial class Rename : IRename, IDisposable
|
|||||||
private ProgressBar? _ProgressBar;
|
private ProgressBar? _ProgressBar;
|
||||||
private readonly ProgressBarOptions _ProgressBarOptions;
|
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() =>
|
void IRename.Tick() =>
|
||||||
_ProgressBar?.Tick();
|
_ProgressBar?.Tick();
|
||||||
|
|
||||||
void IRename.ConstructProgressBar(int maxTicks, string message)
|
|
||||||
{
|
|
||||||
_ProgressBar?.Dispose();
|
|
||||||
_ProgressBar = new(maxTicks, message, _ProgressBarOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
void IDisposable.Dispose()
|
void IDisposable.Dispose()
|
||||||
{
|
{
|
||||||
_ProgressBar?.Dispose();
|
_ProgressBar?.Dispose();
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
ReadOnlyCollection<string> IRename.ConvertAndGetFastForwardMovingPictureExpertsGroupFiles(IRenameSettings renameSettings, FilePath filePath)
|
void IRename.ConstructProgressBar(int maxTicks, string message)
|
||||||
|
{
|
||||||
|
_ProgressBar?.Dispose();
|
||||||
|
_ProgressBar = new(maxTicks, message, _ProgressBarOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
DeterministicHashCode IRename.GetDeterministicHashCode(FilePath filePath)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadOnlyCollection<string> IRename.ConvertAndGetFastForwardMovingPictureExpertsGroupFiles(ResultSettings resultSettings, FilePath filePath)
|
||||||
{
|
{
|
||||||
List<string> results = [];
|
List<string> results = [];
|
||||||
bool isValidVideoFormatExtensions = renameSettings.ValidVideoFormatExtensions.Contains(filePath.ExtensionLowered);
|
bool isValidVideoFormatExtensions = resultSettings.ValidVideoFormatExtensions.Contains(filePath.ExtensionLowered);
|
||||||
if (isValidVideoFormatExtensions)
|
if (isValidVideoFormatExtensions)
|
||||||
{
|
{
|
||||||
bool check;
|
bool check;
|
||||||
@ -97,40 +115,258 @@ public partial class Rename : IRename, IDisposable
|
|||||||
return results.AsReadOnly();
|
return results.AsReadOnly();
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma warning disable CA1416
|
public Rename(List<string> args, ILogger<Program>? logger, AppSettings appSettings, bool isSilent, IConsole console)
|
||||||
|
|
||||||
DeterministicHashCode IRename.GetDeterministicHashCode(FilePath filePath)
|
|
||||||
{
|
{
|
||||||
DeterministicHashCode result;
|
if (isSilent)
|
||||||
int? id;
|
{ }
|
||||||
int? width;
|
if (args is null)
|
||||||
int? height;
|
throw new NullReferenceException(nameof(args));
|
||||||
try
|
if (console is null)
|
||||||
{
|
throw new NullReferenceException(nameof(console));
|
||||||
using Image image = Image.FromFile(filePath.FullName);
|
IRename rename = this;
|
||||||
width = image.Width;
|
LogNetToHoursSince(logger);
|
||||||
height = image.Height;
|
long ticks = DateTime.Now.Ticks;
|
||||||
using Bitmap bitmap = new(image);
|
_ProgressBarOptions = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true };
|
||||||
Rectangle rectangle = new(0, 0, image.Width, image.Height);
|
RenameWork(logger, appSettings, rename, ticks);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
id = null;
|
logger?.LogWarning("<{filePath}>", filePath.FullName);
|
||||||
width = null;
|
continue;
|
||||||
height = null;
|
}
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
result = new(height, id, width);
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma warning restore CA1416
|
private void JustMediaDate(ILogger<Program>? logger, AppSettings appSettings, A_Metadata metadata, FilePath filePath)
|
||||||
|
{
|
||||||
|
ExifDirectory? exifDirectory;
|
||||||
|
MinimumYearAndPathCombined? minimumYearAndPathCombined;
|
||||||
|
try
|
||||||
|
{ (minimumYearAndPathCombined, exifDirectory) = metadata.GetMetadataCollection(appSettings.ResultSettings, appSettings.MetadataSettings, filePath); }
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void NonParallelismAndInPlace(AppSettings appSettings, IRename rename, ReadOnlyCollection<int> ids, ExifDirectory exifDirectory, MinimumYearAndPathCombined minimumYearAndPathCombined, bool fastForwardMovingPictureExpertsGroupUsed, FileHolder[] sidecarFiles)
|
private void NonParallelismAndInPlace(AppSettings appSettings, IRename rename, ReadOnlyCollection<int> ids, ExifDirectory exifDirectory, MinimumYearAndPathCombined minimumYearAndPathCombined, bool fastForwardMovingPictureExpertsGroupUsed, FileHolder[] sidecarFiles)
|
||||||
{
|
{
|
||||||
@ -146,7 +382,7 @@ public partial class Rename : IRename, IDisposable
|
|||||||
bool hasIgnoreKeyword = appSettings.MetadataSettings.IgnoreRulesKeyWords.Any(keywords.Contains);
|
bool hasIgnoreKeyword = appSettings.MetadataSettings.IgnoreRulesKeyWords.Any(keywords.Contains);
|
||||||
string checkFileExtension = exifDirectory.FilePath.ExtensionLowered == jpeg ? jpg : exifDirectory.FilePath.ExtensionLowered;
|
string checkFileExtension = exifDirectory.FilePath.ExtensionLowered == jpeg ? jpg : exifDirectory.FilePath.ExtensionLowered;
|
||||||
bool hasDateTimeOriginal = dateTime is not null;
|
bool hasDateTimeOriginal = dateTime is not null;
|
||||||
string paddedId = IId.GetPaddedId(appSettings.ResultSettings, appSettings.MetadataSettings, exifDirectory.FilePath.Id.Value, hasIgnoreKeyword, hasDateTimeOriginal, i);
|
string paddedId = IId.GetPaddedId(appSettings.ResultSettings, appSettings.MetadataSettings, exifDirectory.FilePath.Id.Value, exifDirectory.FilePath.ExtensionLowered, hasIgnoreKeyword, hasDateTimeOriginal, i);
|
||||||
string checkDirectory = appSettings.RenameSettings.InPlaceWithOriginalName ? Path.Combine(exifDirectory.FilePath.DirectoryFullPath, exifDirectory.FilePath.FileNameFirstSegment) : exifDirectory.FilePath.DirectoryFullPath;
|
string checkDirectory = appSettings.RenameSettings.InPlaceWithOriginalName ? Path.Combine(exifDirectory.FilePath.DirectoryFullPath, exifDirectory.FilePath.FileNameFirstSegment) : exifDirectory.FilePath.DirectoryFullPath;
|
||||||
string checkFile = Path.Combine(checkDirectory, $"{paddedId}{checkFileExtension}");
|
string checkFile = Path.Combine(checkDirectory, $"{paddedId}{checkFileExtension}");
|
||||||
if (checkFile != exifDirectory.FilePath.FullName)
|
if (checkFile != exifDirectory.FilePath.FullName)
|
||||||
@ -176,7 +412,7 @@ public partial class Rename : IRename, IDisposable
|
|||||||
JsonFile: minimumYearAndPathCombined.PathCombined);
|
JsonFile: minimumYearAndPathCombined.PathCombined);
|
||||||
toDoCollection.AddRange(GetSidecarFiles(appSettings, record, [], checkDirectory, paddedId));
|
toDoCollection.AddRange(GetSidecarFiles(appSettings, record, [], checkDirectory, paddedId));
|
||||||
}
|
}
|
||||||
_ = RenameFilesInDirectories(appSettings.RenameSettings, rename, new(toDoCollection));
|
_ = RenameFilesInDirectories(appSettings.RenameSettings, rename, toDoCollection.AsReadOnly());
|
||||||
string jsonFile = Path.Combine(checkDirectory, $"{paddedId}{checkFileExtension}.json");
|
string jsonFile = Path.Combine(checkDirectory, $"{paddedId}{checkFileExtension}.json");
|
||||||
File.Move(minimumYearAndPathCombined.PathCombined, jsonFile, overwrite: true);
|
File.Move(minimumYearAndPathCombined.PathCombined, jsonFile, overwrite: true);
|
||||||
if (appSettings.RenameSettings.InPlaceWithOriginalName && ids.Count > 0)
|
if (appSettings.RenameSettings.InPlaceWithOriginalName && ids.Count > 0)
|
||||||
@ -189,92 +425,6 @@ 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)
|
private static ReadOnlyCollection<Record> GetRecordCollection(AppSettings appSettings, List<FirstPass> collection)
|
||||||
{
|
{
|
||||||
List<Record> results = [];
|
List<Record> results = [];
|
||||||
@ -302,152 +452,22 @@ public partial class Rename : IRename, IDisposable
|
|||||||
return results.AsReadOnly();
|
return results.AsReadOnly();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ReadOnlyCollection<Record> GetRecordCollection(ILogger<Program>? logger, AppSettings appSettings, IRename rename, long ticks, ReadOnlyCollection<int> ids, string sourceDirectory, ReadOnlyCollection<string> files)
|
private static void SaveIdentifiersToDisk(long ticks, AppSettings appSettings, ReadOnlyCollection<Record> recordCollection)
|
||||||
{
|
|
||||||
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)
|
foreach (Record record in recordCollection)
|
||||||
{
|
{
|
||||||
if (record.ExifDirectory.FilePath.Id is null)
|
if (record.ExifDirectory.FilePath.Id is null)
|
||||||
continue;
|
continue;
|
||||||
if (metadataSettings.IntMinValueLength < record.ExifDirectory.FilePath.Id.Value.ToString().Length)
|
paddedId = IId.GetPaddedId(appSettings.ResultSettings, appSettings.MetadataSettings, record.ExifDirectory.FilePath.Id.Value, record.ExifDirectory.FilePath.ExtensionLowered, record.HasIgnoreKeyword, record.HasDateTimeOriginal, index: null);
|
||||||
throw new NotSupportedException();
|
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 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)
|
private static ReadOnlyCollection<ToDo> GetToDoCollection(AppSettings appSettings, DirectoryInfo directoryInfo, ReadOnlyCollection<int> ids, ReadOnlyCollection<string> files, ReadOnlyCollection<Record> recordCollection)
|
||||||
@ -459,15 +479,19 @@ public partial class Rename : IRename, IDisposable
|
|||||||
string paddedId;
|
string paddedId;
|
||||||
string checkFile;
|
string checkFile;
|
||||||
FilePath filePath;
|
FilePath filePath;
|
||||||
|
DateTime? dateTime;
|
||||||
FileInfo[] matches;
|
FileInfo[] matches;
|
||||||
string directoryName;
|
|
||||||
FileHolder fileHolder;
|
FileHolder fileHolder;
|
||||||
|
bool? hasIgnoreKeyword;
|
||||||
string? checkDirectory;
|
string? checkDirectory;
|
||||||
|
CombinedEnumAndIndex cei;
|
||||||
|
bool? hasDateTimeOriginal;
|
||||||
const string jpg = ".jpg";
|
const string jpg = ".jpg";
|
||||||
string checkFileExtension;
|
string checkFileExtension;
|
||||||
List<string> distinct = [];
|
List<string> distinct = [];
|
||||||
const string jpeg = ".jpeg";
|
const string jpeg = ".jpeg";
|
||||||
string jsonFileSubDirectory;
|
string jsonFileSubDirectory;
|
||||||
|
ReadOnlyCollection<string> keywords;
|
||||||
bool? directoryCheck = GetDirectoryCheck(appSettings.ResultSettings);
|
bool? directoryCheck = GetDirectoryCheck(appSettings.ResultSettings);
|
||||||
VerifyIntMinValueLength(appSettings.MetadataSettings, recordCollection);
|
VerifyIntMinValueLength(appSettings.MetadataSettings, recordCollection);
|
||||||
bool multipleDirectoriesWithFiles = directoryCheck is not null && directoryCheck.Value;
|
bool multipleDirectoriesWithFiles = directoryCheck is not null && directoryCheck.Value;
|
||||||
@ -478,7 +502,7 @@ public partial class Rename : IRename, IDisposable
|
|||||||
record = sorted[i];
|
record = sorted[i];
|
||||||
if (record.ExifDirectory.FilePath.Id is null)
|
if (record.ExifDirectory.FilePath.Id is null)
|
||||||
continue;
|
continue;
|
||||||
paddedId = IId.GetPaddedId(appSettings.ResultSettings, appSettings.MetadataSettings, record.ExifDirectory.FilePath.Id.Value, record.HasIgnoreKeyword, record.HasDateTimeOriginal, i);
|
paddedId = IId.GetPaddedId(appSettings.ResultSettings, appSettings.MetadataSettings, record.ExifDirectory.FilePath.Id.Value, record.ExifDirectory.FilePath.ExtensionLowered, record.HasIgnoreKeyword, record.HasDateTimeOriginal, i);
|
||||||
checkDirectory = GetCheckDirectory(appSettings, directoryInfo, record, ids, multipleDirectoriesWithFiles, paddedId);
|
checkDirectory = GetCheckDirectory(appSettings, directoryInfo, record, ids, multipleDirectoriesWithFiles, paddedId);
|
||||||
if (string.IsNullOrEmpty(checkDirectory))
|
if (string.IsNullOrEmpty(checkDirectory))
|
||||||
continue;
|
continue;
|
||||||
@ -493,8 +517,22 @@ public partial class Rename : IRename, IDisposable
|
|||||||
if (File.Exists(checkFile))
|
if (File.Exists(checkFile))
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
(directoryName, _) = IPath.GetDirectoryNameAndIndex(appSettings.ResultSettings, record.ExifDirectory.FilePath);
|
if (record.ExifDirectory.FilePath.HasDateTimeOriginal is not null && record.ExifDirectory.FilePath.HasIgnoreKeyword is not null)
|
||||||
jsonFile = Path.Combine(jsonFileSubDirectory, directoryName, $"{record.ExifDirectory.FilePath.Id.Value}{checkFileExtension}.json");
|
{
|
||||||
|
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");
|
||||||
if (record.JsonFile != jsonFile)
|
if (record.JsonFile != jsonFile)
|
||||||
{
|
{
|
||||||
fileHolder = FileHolder.Get(record.JsonFile);
|
fileHolder = FileHolder.Get(record.JsonFile);
|
||||||
@ -535,17 +573,114 @@ public partial class Rename : IRename, IDisposable
|
|||||||
return results.AsReadOnly();
|
return results.AsReadOnly();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void VerifyDirectories(ReadOnlyCollection<ToDo> toDoCollection)
|
private static bool? GetDirectoryCheck(ResultSettings resultSettings)
|
||||||
{
|
{
|
||||||
List<string> distinct = [];
|
bool? result = null;
|
||||||
foreach (ToDo toDo in toDoCollection)
|
IEnumerable<string> files;
|
||||||
|
string[] directories = Directory.GetDirectories(resultSettings.RootDirectory, "*", SearchOption.TopDirectoryOnly);
|
||||||
|
foreach (string directory in directories)
|
||||||
{
|
{
|
||||||
if (toDo.Directory is null || distinct.Contains(toDo.Directory))
|
files = Directory.EnumerateFiles(directory, "*", SearchOption.AllDirectories);
|
||||||
continue;
|
foreach (string _ in files)
|
||||||
if (!Directory.Exists(toDo.Directory))
|
{
|
||||||
_ = Directory.CreateDirectory(toDo.Directory);
|
if (result is null)
|
||||||
distinct.Add(toDo.Directory);
|
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)
|
||||||
|
continue;
|
||||||
|
if (metadataSettings.IntMinValueLength < record.ExifDirectory.FilePath.Id.Value.ToString().Length)
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
private ReadOnlyCollection<string> RenameFilesInDirectories(RenameSettings renameSettings, IRename rename, ReadOnlyCollection<ToDo> toDoCollection)
|
||||||
@ -588,65 +723,16 @@ public partial class Rename : IRename, IDisposable
|
|||||||
return results.AsReadOnly();
|
return results.AsReadOnly();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void SaveIdentifiersToDisk(long ticks, AppSettings appSettings, ReadOnlyCollection<Record> recordCollection)
|
private static void VerifyDirectories(ReadOnlyCollection<ToDo> toDoCollection)
|
||||||
{
|
{
|
||||||
string paddedId;
|
List<string> distinct = [];
|
||||||
Identifier identifier;
|
foreach (ToDo toDo in toDoCollection)
|
||||||
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)
|
if (toDo.Directory is null || distinct.Contains(toDo.Directory))
|
||||||
continue;
|
continue;
|
||||||
paddedId = IId.GetPaddedId(appSettings.ResultSettings, appSettings.MetadataSettings, record.ExifDirectory.FilePath.Id.Value, record.HasIgnoreKeyword, record.HasDateTimeOriginal, index: null);
|
if (!Directory.Exists(toDo.Directory))
|
||||||
identifier = new([], record.HasDateTimeOriginal, record.ExifDirectory.FilePath.Id.Value, record.ExifDirectory.FilePath.Length, paddedId, record.DateTime.Ticks);
|
_ = Directory.CreateDirectory(toDo.Directory);
|
||||||
identifiers.Add(identifier);
|
distinct.Add(toDo.Directory);
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,11 +3,14 @@
|
|||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<RuntimeIdentifiers>win-x64;linux-x64</RuntimeIdentifiers>
|
<RuntimeIdentifiers>win-x64;linux-x64</RuntimeIdentifiers>
|
||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<HoursSinceNovember142023>$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1699920000)), 3600))))</HoursSinceNovember142023>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<PackageId>Phares.AA.Shared</PackageId>
|
<PackageId>Phares.AA.Shared</PackageId>
|
||||||
<Version>8.1.112.0</Version>
|
<Version>8.0.114.$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1699920000)), 3600))))</Version>
|
||||||
<Company>Phares</Company>
|
<Company>Phares</Company>
|
||||||
<Authors>Mike Phares</Authors>
|
<Authors>Mike Phares</Authors>
|
||||||
<IncludeSymbols>true</IncludeSymbols>
|
<IncludeSymbols>true</IncludeSymbols>
|
||||||
@ -37,6 +40,6 @@
|
|||||||
<None Include=".vscode\read-me.md" Pack="true" PackagePath="\" />
|
<None Include=".vscode\read-me.md" Pack="true" PackagePath="\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="System.Text.Json" Version="8.0.5" />
|
<PackageReference Include="System.Text.Json" Version="9.0.3" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
23
Shared/Models/CombinedEnumAndIndex.cs
Normal file
23
Shared/Models/CombinedEnumAndIndex.cs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
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
|
||||||
|
{
|
||||||
|
}
|
@ -6,9 +6,6 @@ public interface ICompareSettings
|
|||||||
public string FacesFileNameExtension { init; get; }
|
public string FacesFileNameExtension { init; get; }
|
||||||
public string FacesHiddenFileNameExtension { init; get; }
|
public string FacesHiddenFileNameExtension { init; get; }
|
||||||
public string FacesPartsFileNameExtension { init; get; }
|
public string FacesPartsFileNameExtension { init; get; }
|
||||||
public string[] IgnoreExtensions { init; get; }
|
|
||||||
public int MaxDegreeOfParallelism { init; get; }
|
public int MaxDegreeOfParallelism { init; get; }
|
||||||
public string[] ValidImageFormatExtensions { init; get; }
|
|
||||||
public string[] ValidVideoFormatExtensions { init; get; }
|
|
||||||
|
|
||||||
}
|
}
|
@ -3,9 +3,6 @@ namespace View_by_Distance.Shared.Models.Properties;
|
|||||||
public interface IRenameSettings
|
public interface IRenameSettings
|
||||||
{
|
{
|
||||||
|
|
||||||
public string[] IgnoreExtensions { init; get; }
|
|
||||||
public bool SkipIdFiles { init; get; }
|
public bool SkipIdFiles { init; get; }
|
||||||
public string[] ValidImageFormatExtensions { init; get; }
|
|
||||||
public string[] ValidVideoFormatExtensions { init; get; }
|
|
||||||
|
|
||||||
}
|
}
|
@ -3,8 +3,4 @@ namespace View_by_Distance.Shared.Models.Properties;
|
|||||||
public interface IWindowsSettings
|
public interface IWindowsSettings
|
||||||
{
|
{
|
||||||
|
|
||||||
public string[] IgnoreExtensions { init; get; }
|
|
||||||
public string[] ValidImageFormatExtensions { init; get; }
|
|
||||||
public string[] ValidVideoFormatExtensions { init; get; }
|
|
||||||
|
|
||||||
}
|
}
|
@ -8,12 +8,15 @@ public record ResultSettings(string DateGroup,
|
|||||||
string ModelName,
|
string ModelName,
|
||||||
int NumberOfJitters,
|
int NumberOfJitters,
|
||||||
int NumberOfTimesToUpsample,
|
int NumberOfTimesToUpsample,
|
||||||
|
string[] IgnoreExtensions,
|
||||||
string PredictorModelName,
|
string PredictorModelName,
|
||||||
int ResultAllInOneSubdirectoryLength,
|
int ResultAllInOneSubdirectoryLength,
|
||||||
string ResultCollection,
|
string ResultCollection,
|
||||||
string ResultContent,
|
string ResultContent,
|
||||||
string ResultSingleton,
|
string ResultSingleton,
|
||||||
string RootDirectory)
|
string RootDirectory,
|
||||||
|
string[] ValidImageFormatExtensions,
|
||||||
|
string[] ValidVideoFormatExtensions)
|
||||||
{
|
{
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
|
@ -3,24 +3,28 @@ namespace View_by_Distance.Shared.Models.Stateless;
|
|||||||
public interface IDate
|
public interface IDate
|
||||||
{
|
{
|
||||||
|
|
||||||
(bool?, string[]) TestStatic_IsWrongYear(DirectoryInfo directoryInfo, FilePath filePath, ExifDirectory exifDirectory) =>
|
public static DateTime GetMinimum(ExifDirectory exifDirectory) =>
|
||||||
IsWrongYear(directoryInfo, filePath, exifDirectory);
|
|
||||||
static (bool?, string[]) IsWrongYear(DirectoryInfo directoryInfo, FilePath filePath, ExifDirectory exifDirectory) =>
|
|
||||||
XDate.IsWrongYear(directoryInfo, filePath, exifDirectory);
|
|
||||||
|
|
||||||
(int Season, string seasonName) TestStatic_GetSeason(int dayOfYear) =>
|
|
||||||
GetSeason(dayOfYear);
|
|
||||||
static (int Season, string seasonName) GetSeason(int dayOfYear) =>
|
|
||||||
XDate.GetSeason(dayOfYear);
|
|
||||||
|
|
||||||
DateTime? TestStatic_GetDateTimeOriginal(ExifDirectory exifDirectory) =>
|
|
||||||
GetDateTimeOriginal(exifDirectory);
|
|
||||||
static DateTime? GetDateTimeOriginal(ExifDirectory exifDirectory) =>
|
|
||||||
XDate.GetDateTimeOriginal(exifDirectory);
|
|
||||||
|
|
||||||
DateTime TestStatic_GetMinimum(ExifDirectory exifDirectory) =>
|
|
||||||
GetMinimum(exifDirectory);
|
|
||||||
static DateTime GetMinimum(ExifDirectory exifDirectory) =>
|
|
||||||
XDate.GetMinimum(exifDirectory);
|
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) =>
|
||||||
|
XDate.IsWrongYear(directoryInfo, filePath, exifDirectory);
|
||||||
|
|
||||||
|
internal DateTime TestStatic_GetMinimum(ExifDirectory exifDirectory) =>
|
||||||
|
GetMinimum(exifDirectory);
|
||||||
|
|
||||||
|
internal (int Season, string seasonName) TestStatic_GetSeason(int dayOfYear) =>
|
||||||
|
GetSeason(dayOfYear);
|
||||||
|
|
||||||
|
internal DateTime? TestStatic_GetDateTimeOriginal(ExifDirectory exifDirectory) =>
|
||||||
|
GetDateTimeOriginal(exifDirectory);
|
||||||
|
|
||||||
|
internal (bool?, string[]) TestStatic_IsWrongYear(DirectoryInfo directoryInfo, FilePath filePath, ExifDirectory exifDirectory) =>
|
||||||
|
IsWrongYear(directoryInfo, filePath, exifDirectory);
|
||||||
|
|
||||||
}
|
}
|
@ -5,57 +5,69 @@ public interface IId
|
|||||||
|
|
||||||
const int DeterministicHashCode = 9876543;
|
const int DeterministicHashCode = 9876543;
|
||||||
|
|
||||||
static bool IsOffsetDeterministicHashCode(MetadataSettings metadataSettings) =>
|
public static int GetDeterministicHashCode(byte[] value) =>
|
||||||
metadataSettings.Offset == DeterministicHashCode;
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
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 '1' or '2' or '8' or '9'
|
|
||||||
&& fileNameFirstSegment.All(char.IsNumber);
|
|
||||||
|
|
||||||
bool TestStatic_NameWithoutExtensionIsIdFormat(MetadataSettings metadataSettings, FileHolder fileHolder) =>
|
|
||||||
NameWithoutExtensionIsIdFormat(metadataSettings, fileHolder);
|
|
||||||
static bool NameWithoutExtensionIsIdFormat(MetadataSettings metadataSettings, FileHolder fileHolder) =>
|
|
||||||
Id.NameWithoutExtensionIsIdFormat(metadataSettings, fileHolder.NameWithoutExtension.Split('.')[0]);
|
|
||||||
|
|
||||||
int TestStatic_GetDeterministicHashCode(byte[] value) =>
|
|
||||||
GetDeterministicHashCode(value);
|
|
||||||
static int GetDeterministicHashCode(byte[] value) =>
|
|
||||||
Id.GetDeterministicHashCode(value);
|
Id.GetDeterministicHashCode(value);
|
||||||
|
|
||||||
|
public static byte GetHasIgnoreKeyword(FilePath filePath) =>
|
||||||
|
Id.GetHasIgnoreKeyword(filePath);
|
||||||
|
|
||||||
|
public static bool IsOffsetDeterministicHashCode(MetadataSettings metadataSettings) =>
|
||||||
|
metadataSettings.Offset == DeterministicHashCode;
|
||||||
|
|
||||||
|
public static byte GetHasDateTimeOriginal(ResultSettings resultSettings, FilePath filePath) =>
|
||||||
|
Id.GetHasDateTimeOriginal(resultSettings, filePath);
|
||||||
|
|
||||||
|
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) =>
|
||||||
|
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);
|
||||||
|
|
||||||
|
public 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.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) =>
|
||||||
|
NameWithoutExtensionIsIdFormat(metadataSettings, fileHolder);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
}
|
}
|
@ -3,72 +3,96 @@ using System.Collections.ObjectModel;
|
|||||||
namespace View_by_Distance.Shared.Models.Stateless;
|
namespace View_by_Distance.Shared.Models.Stateless;
|
||||||
|
|
||||||
public interface IPath
|
public interface IPath
|
||||||
{ // ...
|
{
|
||||||
|
|
||||||
string TestStatic_GetRelativePath(string path, int length) =>
|
public static byte GetEnum(FilePath filePath) =>
|
||||||
GetRelativePath(path, length);
|
XPath.GetEnum(filePath);
|
||||||
static string GetRelativePath(string path, int length) =>
|
|
||||||
XPath.GetRelativePath(path, length, forceExtensionToLower: false);
|
|
||||||
|
|
||||||
bool TestStatic_DeleteEmptyDirectories(string rootDirectory) =>
|
public static string[] GetDirectories(string directory) =>
|
||||||
DeleteEmptyDirectories(rootDirectory);
|
|
||||||
static bool DeleteEmptyDirectories(string rootDirectory) =>
|
|
||||||
XPath.DeleteEmptyDirectories(rootDirectory);
|
|
||||||
|
|
||||||
void TestStatic_ChangeDateForEmptyDirectories(string rootDirectory, long ticks) =>
|
|
||||||
ChangeDateForEmptyDirectories(rootDirectory, ticks);
|
|
||||||
static void ChangeDateForEmptyDirectories(string rootDirectory, long ticks) =>
|
|
||||||
XPath.ChangeDateForEmptyDirectories(rootDirectory, ticks);
|
|
||||||
|
|
||||||
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();
|
XPath.GetDirectories(directory).ToArray();
|
||||||
|
|
||||||
string TestStatic_GetRelativePath(string path, int length, bool forceExtensionToLower) =>
|
public static string[] GetDirectoryNames(string directory) =>
|
||||||
GetRelativePath(path, length, forceExtensionToLower);
|
XPath.GetDirectoryNames(directory).ToArray();
|
||||||
static string GetRelativePath(string path, int length, bool forceExtensionToLower) =>
|
|
||||||
|
public static string GetRelativePath(string path, int length) =>
|
||||||
|
XPath.GetRelativePath(path, length, forceExtensionToLower: false);
|
||||||
|
|
||||||
|
public 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) =>
|
||||||
|
XPath.ChangeDateForEmptyDirectories(rootDirectory, ticks);
|
||||||
|
|
||||||
|
public static string GetRelativePath(string path, int length, bool forceExtensionToLower) =>
|
||||||
XPath.GetRelativePath(path, length, forceExtensionToLower);
|
XPath.GetRelativePath(path, length, forceExtensionToLower);
|
||||||
|
|
||||||
bool TestStatic_WriteAllText(string path, string contents, bool updateDateWhenMatches, bool compareBeforeWrite, DateTime? updateToWhenMatches = null) =>
|
public static string GetDirectory(string sourceDirectory, int level, string directoryName) =>
|
||||||
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);
|
|
||||||
|
|
||||||
(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);
|
XPath.GetDirectory(sourceDirectory, level, directoryName);
|
||||||
|
|
||||||
(string, int) TestStatic_GetDirectoryNameAndIndex(ResultSettings resultSettings, FilePath filePath) =>
|
public static void DeleteEmptyDirectories(string rootDirectory, List<string> deletedDirectories) =>
|
||||||
GetDirectoryNameAndIndex(resultSettings, filePath);
|
XPath.DeleteEmptyDirectories(rootDirectory, deletedDirectories);
|
||||||
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) =>
|
public static (int level, List<string> directories) Get(string rootDirectory, string sourceDirectory) =>
|
||||||
GetKeyValuePairs(resultSettings, resultsFullGroupDirectory, jsonGroups);
|
XPath.Get(rootDirectory, sourceDirectory);
|
||||||
static ReadOnlyDictionary<int, ReadOnlyDictionary<string, string[]>> GetKeyValuePairs(ResultSettings resultSettings, string? resultsFullGroupDirectory, string[]? jsonGroups) =>
|
|
||||||
|
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) =>
|
||||||
|
XPath.WriteAllText(path, contents, updateDateWhenMatches, compareBeforeWrite, updateToWhenMatches);
|
||||||
|
|
||||||
|
public static ReadOnlyDictionary<int, ReadOnlyDictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>>> GetKeyValuePairs(ResultSettings resultSettings, string? resultsFullGroupDirectory, string[]? jsonGroups) =>
|
||||||
XPath.GetKeyValuePairs(resultSettings, resultsFullGroupDirectory, 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);
|
||||||
|
|
||||||
}
|
}
|
@ -1,12 +1,11 @@
|
|||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using View_by_Distance.Shared.Models.Properties;
|
|
||||||
|
|
||||||
namespace View_by_Distance.Shared.Models.Stateless;
|
namespace View_by_Distance.Shared.Models.Stateless;
|
||||||
|
|
||||||
public interface IRename
|
public interface IRename
|
||||||
{
|
{
|
||||||
|
|
||||||
ReadOnlyCollection<string> ConvertAndGetFastForwardMovingPictureExpertsGroupFiles(IRenameSettings renameSettings, FilePath filePath);
|
ReadOnlyCollection<string> ConvertAndGetFastForwardMovingPictureExpertsGroupFiles(ResultSettings resultSettings, FilePath filePath);
|
||||||
DeterministicHashCode GetDeterministicHashCode(FilePath filePath);
|
DeterministicHashCode GetDeterministicHashCode(FilePath filePath);
|
||||||
void ConstructProgressBar(int maxTicks, string message);
|
void ConstructProgressBar(int maxTicks, string message);
|
||||||
void Tick();
|
void Tick();
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using View_by_Distance.Shared.Models.Properties;
|
|
||||||
|
|
||||||
namespace View_by_Distance.Shared.Models.Stateless;
|
namespace View_by_Distance.Shared.Models.Stateless;
|
||||||
|
|
||||||
public interface IWindows
|
public interface IWindows
|
||||||
{
|
{
|
||||||
|
|
||||||
ReadOnlyCollection<string> ConvertAndGetFastForwardMovingPictureExpertsGroupFiles(IWindowsSettings windowsSettings, HttpClient? httpClient, FilePath filePath);
|
ReadOnlyCollection<string> ConvertAndGetFastForwardMovingPictureExpertsGroupFiles(ResultSettings resultSettings, HttpClient? httpClient, FilePath filePath);
|
||||||
DeterministicHashCode GetDeterministicHashCode(HttpClient? httpClient, FilePath filePath);
|
DeterministicHashCode GetDeterministicHashCode(HttpClient? httpClient, FilePath filePath);
|
||||||
DeterministicHashCode GetDeterministicHashCode(HttpClient httpClient, Uri uri);
|
DeterministicHashCode GetDeterministicHashCode(HttpClient httpClient, Uri uri);
|
||||||
void ConstructProgressBar(int maxTicks, string message);
|
void ConstructProgressBar(int maxTicks, string message);
|
||||||
|
@ -5,81 +5,6 @@ namespace View_by_Distance.Shared.Models.Stateless;
|
|||||||
internal abstract class Id
|
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)
|
internal static int GetDeterministicHashCode(byte[] value)
|
||||||
{
|
{
|
||||||
int result;
|
int result;
|
||||||
@ -99,4 +24,99 @@ internal abstract class Id
|
|||||||
return result;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -7,179 +7,11 @@ namespace View_by_Distance.Shared.Models.Stateless;
|
|||||||
internal abstract class XDate
|
internal abstract class XDate
|
||||||
{
|
{
|
||||||
|
|
||||||
private record Record(bool? IsWrongYear, string[] Years);
|
internal static DateTime GetMinimum(ExifDirectory exifDirectory)
|
||||||
|
|
||||||
internal static (int Season, string seasonName) GetSeason(int dayOfYear)
|
|
||||||
{
|
{
|
||||||
(int Season, string seasonName) result = dayOfYear switch
|
DateTime result;
|
||||||
{
|
ReadOnlyCollection<DateTime> results = GetDateTimes(exifDirectory);
|
||||||
< 78 => new(0, "Winter"),
|
result = results.Count == 0 ? DateTime.MinValue : results.Min();
|
||||||
< 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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,12 +71,182 @@ internal abstract class XDate
|
|||||||
return results.AsReadOnly();
|
return results.AsReadOnly();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static DateTime GetMinimum(ExifDirectory exifDirectory)
|
private static DateTime? GetDateTimeFromName(string fileNameWithoutExtension)
|
||||||
{
|
{
|
||||||
DateTime result;
|
DateTime? result = null;
|
||||||
ReadOnlyCollection<DateTime> results = GetDateTimes(exifDirectory);
|
int length;
|
||||||
result = results.Count == 0 ? DateTime.MinValue : results.Min();
|
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;
|
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);
|
||||||
|
|
||||||
}
|
}
|
@ -5,35 +5,29 @@ namespace View_by_Distance.Shared.Models.Stateless;
|
|||||||
internal abstract class XPath
|
internal abstract class XPath
|
||||||
{
|
{
|
||||||
|
|
||||||
internal static string GetRelativePath(string path, int length, bool forceExtensionToLower)
|
private static ReadOnlyDictionary<byte, ReadOnlyCollection<string>> Convert(List<CombinedEnumAndIndex> collection)
|
||||||
{
|
{
|
||||||
string result;
|
Dictionary<byte, List<string>> results = [];
|
||||||
if (forceExtensionToLower)
|
List<string>? c;
|
||||||
|
foreach (CombinedEnumAndIndex cei in collection)
|
||||||
{
|
{
|
||||||
string extension = Path.GetExtension(path);
|
if (!results.TryGetValue(cei.Enum, out c))
|
||||||
string extensionLowered = Path.GetExtension(path).ToLower();
|
|
||||||
if (extension != extensionLowered)
|
|
||||||
{
|
{
|
||||||
string? directoryName = Path.GetDirectoryName(path);
|
results.Add(cei.Enum, []);
|
||||||
if (string.IsNullOrEmpty(directoryName))
|
if (!results.TryGetValue(cei.Enum, out c))
|
||||||
throw new NullReferenceException(directoryName);
|
throw new Exception();
|
||||||
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(path);
|
|
||||||
if (string.IsNullOrEmpty(fileNameWithoutExtension))
|
|
||||||
throw new NullReferenceException(fileNameWithoutExtension);
|
|
||||||
path = Path.Combine(directoryName, $"{fileNameWithoutExtension}{extensionLowered}");
|
|
||||||
}
|
}
|
||||||
|
c.Add(cei.Combined);
|
||||||
}
|
}
|
||||||
result = path[length..].Replace(@"\", "/");
|
return Convert(results);
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static bool DeleteEmptyDirectories(string rootDirectory)
|
private static ReadOnlyDictionary<int, ReadOnlyDictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>>> Convert(Dictionary<int, Dictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>>> keyValuePairs)
|
||||||
{
|
{
|
||||||
bool result;
|
Dictionary<int, ReadOnlyDictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>>> results = [];
|
||||||
List<string> results = [];
|
foreach (KeyValuePair<int, Dictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>>> keyValuePair in keyValuePairs)
|
||||||
DeleteEmptyDirectories(rootDirectory, results);
|
results.Add(keyValuePair.Key, new(keyValuePair.Value));
|
||||||
result = results.Count > 0;
|
return results.AsReadOnly();
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void DeleteEmptyDirectories(string rootDirectory, List<string> deletedDirectories)
|
internal static void DeleteEmptyDirectories(string rootDirectory, List<string> deletedDirectories)
|
||||||
@ -71,45 +65,56 @@ internal abstract class XPath
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static bool WriteAllText(string path, string contents, bool updateDateWhenMatches, bool compareBeforeWrite, DateTime? updateToWhenMatches)
|
internal static byte GetEnum(FilePath filePath) =>
|
||||||
|
GetEnum(filePath.HasIgnoreKeyword, filePath.HasDateTimeOriginal);
|
||||||
|
|
||||||
|
private static byte GetEnum(bool? ik, bool? dto)
|
||||||
{
|
{
|
||||||
bool result;
|
byte result;
|
||||||
string text;
|
if (ik is not null && ik.Value && dto is not null && dto.Value)
|
||||||
if (!compareBeforeWrite)
|
result = 11;
|
||||||
result = true;
|
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;
|
||||||
else
|
else
|
||||||
{
|
throw new Exception();
|
||||||
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;
|
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);
|
||||||
|
}
|
||||||
|
results.Add(pathRoot);
|
||||||
|
results.Reverse();
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
internal static List<string> GetDirectoryNames(string directory)
|
internal static List<string> GetDirectoryNames(string directory)
|
||||||
{
|
{
|
||||||
List<string> results = [];
|
List<string> results = [];
|
||||||
@ -146,83 +151,15 @@ internal abstract class XPath
|
|||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static List<string> GetDirectories(string directory)
|
internal static bool DeleteEmptyDirectories(string rootDirectory)
|
||||||
{
|
{
|
||||||
|
bool result;
|
||||||
List<string> results = [];
|
List<string> results = [];
|
||||||
string? checkDirectory = directory;
|
DeleteEmptyDirectories(rootDirectory, results);
|
||||||
string? pathRoot = Path.GetPathRoot(directory);
|
result = results.Count > 0;
|
||||||
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;
|
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)
|
internal static void MakeHiddenIfAllItemsAreHidden(string rootDirectory)
|
||||||
{
|
{
|
||||||
bool check;
|
bool check;
|
||||||
@ -265,40 +202,204 @@ internal abstract class XPath
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static (string, int) GetDirectoryNameAndIndex(int resultAllInOneSubdirectoryLength, string fileNameWithoutExtension)
|
internal static void CreateDirectories(ReadOnlyCollection<string> directories)
|
||||||
|
{
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
int converted;
|
|
||||||
string result;
|
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 fileNameBeforeFirst = fileNameWithoutExtension.Split('.')[0];
|
string fileNameBeforeFirst = fileNameWithoutExtension.Split('.')[0];
|
||||||
string check = fileNameBeforeFirst.Length < resultAllInOneSubdirectoryLength ? new('-', resultAllInOneSubdirectoryLength) : fileNameBeforeFirst[^resultAllInOneSubdirectoryLength..];
|
string check = fileNameBeforeFirst.Length < resultSettings.ResultAllInOneSubdirectoryLength ?
|
||||||
|
new('-', resultSettings.ResultAllInOneSubdirectoryLength) :
|
||||||
|
fileNameBeforeFirst[^resultSettings.ResultAllInOneSubdirectoryLength..];
|
||||||
if (check.Any(l => !char.IsNumber(l)))
|
if (check.Any(l => !char.IsNumber(l)))
|
||||||
{
|
{
|
||||||
result = new('-', resultAllInOneSubdirectoryLength);
|
combined = $"{@enum}{new('-', resultSettings.ResultAllInOneSubdirectoryLength)}";
|
||||||
converted = int.Parse($"1{new string('0', resultAllInOneSubdirectoryLength)}");
|
converted = int.Parse($"1{new string('0', resultSettings.ResultAllInOneSubdirectoryLength)}");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
result = check;
|
combined = $"{@enum}{check}";
|
||||||
converted = int.Parse(check);
|
converted = int.Parse(check);
|
||||||
}
|
}
|
||||||
return new(result, converted);
|
result = new(combined, @enum, converted);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static (string, int) GetDirectoryNameAndIndex(ResultSettings resultSettings, int id)
|
internal static bool WriteAllText(string path, string contents, bool updateDateWhenMatches, bool compareBeforeWrite, DateTime? updateToWhenMatches)
|
||||||
{
|
{
|
||||||
(string result, int converted) = GetDirectoryNameAndIndex(resultSettings.ResultAllInOneSubdirectoryLength, id.ToString());
|
bool result;
|
||||||
return new(result, converted);
|
string text;
|
||||||
}
|
if (!compareBeforeWrite)
|
||||||
|
result = true;
|
||||||
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
|
else
|
||||||
(result, converted) = GetDirectoryNameAndIndex(resultSettings.ResultAllInOneSubdirectoryLength, filePath.NameWithoutExtension);
|
{
|
||||||
return new(result, converted);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ReadOnlyCollection<int> GetYears(ResultSettings resultSettings)
|
private static ReadOnlyCollection<int> GetYears(ResultSettings resultSettings)
|
||||||
@ -310,65 +411,79 @@ internal abstract class XPath
|
|||||||
return results.AsReadOnly();
|
return results.AsReadOnly();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ReadOnlyDictionary<int, ReadOnlyDictionary<string, string[]>> Convert(Dictionary<int, Dictionary<string, string[]>> collection)
|
private static ReadOnlyDictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> GetKeyValuePairs(ResultSettings resultSettings, string? resultsFullGroupDirectory, string[]? jsonGroups, DateTime dateTime)
|
||||||
{
|
{
|
||||||
Dictionary<int, ReadOnlyDictionary<string, string[]>> results = [];
|
Dictionary<string, ReadOnlyDictionary<byte, ReadOnlyCollection<string>>> results = [];
|
||||||
foreach (KeyValuePair<int, Dictionary<string, string[]>> keyValuePair in collection)
|
int plusOne;
|
||||||
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 directory;
|
||||||
string checkDirectory;
|
string checkDirectory;
|
||||||
Dictionary<string, string[]>? keyValuePairs;
|
CombinedEnumAndIndex cei;
|
||||||
ReadOnlyCollection<int> years = GetYears(resultSettings);
|
byte[] bytes = GetBytes();
|
||||||
|
List<CombinedEnumAndIndex> collection = [];
|
||||||
|
ReadOnlyDictionary<byte, ReadOnlyCollection<string>> keyValuePairs;
|
||||||
int converted = int.Parse($"1{new string('0', resultSettings.ResultAllInOneSubdirectoryLength)}");
|
int converted = int.Parse($"1{new string('0', resultSettings.ResultAllInOneSubdirectoryLength)}");
|
||||||
int plusOne = converted + 1;
|
|
||||||
List<string> collection = [];
|
|
||||||
foreach (int year in years)
|
|
||||||
{
|
|
||||||
results.Add(year, []);
|
|
||||||
if (!results.TryGetValue(year, out keyValuePairs))
|
|
||||||
throw new NullReferenceException(nameof(keyValuePairs));
|
|
||||||
if (jsonGroups is not null)
|
if (jsonGroups is not null)
|
||||||
{
|
{
|
||||||
|
plusOne = converted + 1;
|
||||||
foreach (string jsonGroup in jsonGroups)
|
foreach (string jsonGroup in jsonGroups)
|
||||||
{
|
{
|
||||||
if (resultsFullGroupDirectory is null)
|
if (resultsFullGroupDirectory is null)
|
||||||
continue;
|
continue;
|
||||||
collection.Clear();
|
foreach (byte @enum in bytes)
|
||||||
|
{
|
||||||
for (int i = 0; i < plusOne; i++)
|
for (int i = 0; i < plusOne; i++)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(jsonGroup))
|
if (string.IsNullOrEmpty(jsonGroup))
|
||||||
{
|
{
|
||||||
if (i == converted)
|
if (i == converted)
|
||||||
checkDirectory = Path.GetFullPath(Path.Combine(resultsFullGroupDirectory, new('-', resultSettings.ResultAllInOneSubdirectoryLength)));
|
checkDirectory = Path.GetFullPath(Path.Combine(resultsFullGroupDirectory, $"{@enum}{new('-', resultSettings.ResultAllInOneSubdirectoryLength)}"));
|
||||||
else
|
else
|
||||||
checkDirectory = Path.GetFullPath(Path.Combine(resultsFullGroupDirectory, i.ToString().PadLeft(resultSettings.ResultAllInOneSubdirectoryLength, '0')));
|
checkDirectory = Path.GetFullPath(Path.Combine(resultsFullGroupDirectory, $"{@enum}{i.ToString().PadLeft(resultSettings.ResultAllInOneSubdirectoryLength, '0')}"));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
directory = Path.Combine(resultsFullGroupDirectory, jsonGroup);
|
directory = Path.Combine(resultsFullGroupDirectory, jsonGroup);
|
||||||
if (i == converted)
|
if (i == converted)
|
||||||
checkDirectory = Path.GetFullPath(Path.Combine(directory, new('-', resultSettings.ResultAllInOneSubdirectoryLength)));
|
checkDirectory = Path.GetFullPath(Path.Combine(directory, $"{@enum}{new('-', resultSettings.ResultAllInOneSubdirectoryLength)}"));
|
||||||
else
|
else
|
||||||
checkDirectory = Path.GetFullPath(Path.Combine(directory, i.ToString().PadLeft(resultSettings.ResultAllInOneSubdirectoryLength, '0')));
|
checkDirectory = Path.GetFullPath(Path.Combine(directory, $"{@enum}{i.ToString().PadLeft(resultSettings.ResultAllInOneSubdirectoryLength, '0')}"));
|
||||||
}
|
}
|
||||||
if (!Directory.Exists(checkDirectory))
|
if (!Directory.Exists(checkDirectory))
|
||||||
_ = Directory.CreateDirectory(checkDirectory);
|
_ = Directory.CreateDirectory(checkDirectory);
|
||||||
collection.Add(checkDirectory);
|
cei = new(Combined: checkDirectory, Enum: @enum, Index: -1);
|
||||||
|
collection.Add(cei);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
keyValuePairs = Convert(collection);
|
||||||
if (!string.IsNullOrEmpty(jsonGroup))
|
if (!string.IsNullOrEmpty(jsonGroup))
|
||||||
keyValuePairs.Add(jsonGroup, collection.ToArray());
|
results.Add(jsonGroup, keyValuePairs);
|
||||||
else
|
else
|
||||||
keyValuePairs.Add(year.ToString(), collection.ToArray());
|
results.Add(dateTime.Ticks.ToString(), keyValuePairs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return results.AsReadOnly();
|
||||||
}
|
}
|
||||||
return Convert(results);
|
|
||||||
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
5
Windows/.vscode/11.http
vendored
Normal file
5
Windows/.vscode/11.http
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
@host = http://192.168.0.11:8080
|
||||||
|
|
||||||
|
GET {{host}}/iCloud%20Photos%202025
|
||||||
|
|
||||||
|
###
|
5
Windows/.vscode/mklink.md
vendored
Normal file
5
Windows/.vscode/mklink.md
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# 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"
|
||||||
|
````
|
@ -1,4 +1,4 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
@ -7,9 +7,12 @@
|
|||||||
<TargetFramework>net9.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<UserSecretsId>076c87e8-c7f0-40a3-aba3-73eb7f9ea892</UserSecretsId>
|
<UserSecretsId>076c87e8-c7f0-40a3-aba3-73eb7f9ea892</UserSecretsId>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<HoursSinceNovember122024>$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1731369600)), 3600))))</HoursSinceNovember122024>
|
||||||
|
</PropertyGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<PackageId>Phares.View.by.Distance.Windows</PackageId>
|
<PackageId>Phares.View.by.Distance.Windows</PackageId>
|
||||||
<Version>8.0.112.0</Version>
|
<Version>9.0.104.$([System.Math]::Floor($([MSBuild]::Divide($([MSBuild]::Subtract($([System.DateTimeOffset]::UtcNow.ToUnixTimeSeconds()), 1731369600)), 3600))))</Version>
|
||||||
<Company>Phares</Company>
|
<Company>Phares</Company>
|
||||||
<Authors>Mike Phares</Authors>
|
<Authors>Mike Phares</Authors>
|
||||||
<IncludeSymbols>true</IncludeSymbols>
|
<IncludeSymbols>true</IncludeSymbols>
|
||||||
@ -39,13 +42,13 @@
|
|||||||
<None Include=".vscode\read-me.md" Pack="true" PackagePath="\" />
|
<None Include=".vscode\read-me.md" Pack="true" PackagePath="\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="CliWrap" Version="3.7.1" />
|
<PackageReference Include="CliWrap" Version="3.8.2" />
|
||||||
<PackageReference Include="runtime.win-x64.Microsoft.DotNet.ILCompiler" Version="8.0.12" />
|
<PackageReference Include="runtime.win-x64.Microsoft.DotNet.ILCompiler" Version="8.0.14" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.1" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.1" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" 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="Microsoft.Extensions.Hosting" Version="8.0.1" />
|
||||||
<PackageReference Include="ShellProgressBar" Version="5.2.0" />
|
<PackageReference Include="ShellProgressBar" Version="5.2.0" />
|
||||||
<PackageReference Include="System.Drawing.Common" Version="8.0.12" />
|
<PackageReference Include="System.Drawing.Common" Version="8.0.14" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<RuntimeHostConfigurationOption Include="System.Drawing.EnableUnixSupport" Value="true" />
|
<RuntimeHostConfigurationOption Include="System.Drawing.EnableUnixSupport" Value="true" />
|
||||||
|
@ -6,12 +6,9 @@ namespace View_by_Distance.Windows.Models;
|
|||||||
|
|
||||||
public record WindowsSettings(string Company,
|
public record WindowsSettings(string Company,
|
||||||
string? Host,
|
string? Host,
|
||||||
string[] IgnoreExtensions,
|
|
||||||
int MaxDegreeOfParallelism,
|
int MaxDegreeOfParallelism,
|
||||||
string? Page,
|
string? Page,
|
||||||
string[] SidecarExtensions,
|
string[] SidecarExtensions,
|
||||||
string[] ValidImageFormatExtensions,
|
|
||||||
string[] ValidVideoFormatExtensions,
|
|
||||||
bool VerifyOnly) : Shared.Models.Properties.IWindowsSettings
|
bool VerifyOnly) : Shared.Models.Properties.IWindowsSettings
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -9,7 +9,6 @@ using System.Text.Json;
|
|||||||
using View_by_Distance.Metadata.Models;
|
using View_by_Distance.Metadata.Models;
|
||||||
using View_by_Distance.Metadata.Models.Stateless;
|
using View_by_Distance.Metadata.Models.Stateless;
|
||||||
using View_by_Distance.Shared.Models;
|
using View_by_Distance.Shared.Models;
|
||||||
using View_by_Distance.Shared.Models.Properties;
|
|
||||||
using View_by_Distance.Shared.Models.Stateless;
|
using View_by_Distance.Shared.Models.Stateless;
|
||||||
using View_by_Distance.Windows.Models;
|
using View_by_Distance.Windows.Models;
|
||||||
|
|
||||||
@ -21,42 +20,98 @@ public partial class Windows : IWindows, IDisposable
|
|||||||
private ProgressBar? _ProgressBar;
|
private ProgressBar? _ProgressBar;
|
||||||
private readonly ProgressBarOptions _ProgressBarOptions;
|
private readonly ProgressBarOptions _ProgressBarOptions;
|
||||||
|
|
||||||
public Windows(List<string> args, ILogger<Program>? logger, AppSettings appSettings, bool isSilent, IConsole console)
|
DeterministicHashCode IWindows.GetDeterministicHashCode(HttpClient httpClient, Uri uri) =>
|
||||||
|
GetDeterministicHashCode(httpClient, uri);
|
||||||
|
|
||||||
|
DeterministicHashCode IWindows.GetDeterministicHashCode(HttpClient? httpClient, FilePath filePath)
|
||||||
{
|
{
|
||||||
if (isSilent)
|
DeterministicHashCode result;
|
||||||
{ }
|
if (httpClient is not null)
|
||||||
if (args is null)
|
result = GetDeterministicHashCode(httpClient, new Uri(filePath.FullName));
|
||||||
throw new NullReferenceException(nameof(args));
|
else
|
||||||
if (console is null)
|
{
|
||||||
throw new NullReferenceException(nameof(console));
|
Stream stream = File.OpenRead(filePath.FullName);
|
||||||
IWindows windows = this;
|
result = GetDeterministicHashCode(stream);
|
||||||
long ticks = DateTime.Now.Ticks;
|
stream.Dispose();
|
||||||
_ProgressBarOptions = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true };
|
}
|
||||||
WindowsWork(logger, appSettings, windows, ticks);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
void IWindows.Tick() =>
|
void IWindows.Tick() =>
|
||||||
_ProgressBar?.Tick();
|
_ProgressBar?.Tick();
|
||||||
|
|
||||||
void IWindows.ConstructProgressBar(int maxTicks, string message)
|
|
||||||
{
|
|
||||||
_ProgressBar?.Dispose();
|
|
||||||
_ProgressBar = new(maxTicks, message, _ProgressBarOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
void IDisposable.Dispose()
|
void IDisposable.Dispose()
|
||||||
{
|
{
|
||||||
_ProgressBar?.Dispose();
|
_ProgressBar?.Dispose();
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
ReadOnlyCollection<string> IWindows.ConvertAndGetFastForwardMovingPictureExpertsGroupFiles(IWindowsSettings WindowsSettings, HttpClient? httpClient, FilePath filePath)
|
void IWindows.ConstructProgressBar(int maxTicks, string message)
|
||||||
|
{
|
||||||
|
_ProgressBar?.Dispose();
|
||||||
|
_ProgressBar = new(maxTicks, message, _ProgressBarOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadOnlyCollection<string> IWindows.ConvertAndGetFastForwardMovingPictureExpertsGroupFiles(ResultSettings resultSettings, HttpClient? httpClient, FilePath filePath)
|
||||||
{
|
{
|
||||||
List<string> results = [];
|
List<string> results = [];
|
||||||
bool isValidVideoFormatExtensions = WindowsSettings.ValidVideoFormatExtensions.Contains(filePath.ExtensionLowered);
|
bool isValidVideoFormatExtensions = resultSettings.ValidVideoFormatExtensions.Contains(filePath.ExtensionLowered);
|
||||||
if (isValidVideoFormatExtensions)
|
if (isValidVideoFormatExtensions)
|
||||||
{
|
{
|
||||||
bool check;
|
bool check;
|
||||||
|
if (httpClient is not null)
|
||||||
|
DownloadFile(httpClient, filePath);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
CommandTask<CommandResult> commandTask = Cli.Wrap("L:/Git/ffmpeg-2024-10-02-git-358fdf3083-full_build/bin/ffmpeg.exe")
|
CommandTask<CommandResult> commandTask = Cli.Wrap("L:/Git/ffmpeg-2024-10-02-git-358fdf3083-full_build/bin/ffmpeg.exe")
|
||||||
@ -83,75 +138,74 @@ public partial class Windows : IWindows, IDisposable
|
|||||||
return results.AsReadOnly();
|
return results.AsReadOnly();
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma warning disable CA1416
|
private static void DownloadFile(HttpClient httpClient, FilePath filePath)
|
||||||
|
|
||||||
private static DeterministicHashCode GetDeterministicHashCode(Stream stream)
|
|
||||||
{
|
{
|
||||||
DeterministicHashCode result;
|
FileStream fileStream = new(filePath.FullName, FileMode.Truncate);
|
||||||
int? id;
|
Task<HttpResponseMessage> httpResponseMessage = httpClient.GetAsync(filePath.FullName);
|
||||||
int? width;
|
httpResponseMessage.Wait();
|
||||||
int? height;
|
Task task = httpResponseMessage.Result.Content.CopyToAsync(fileStream);
|
||||||
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();
|
task.Wait();
|
||||||
result = task.Result;
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static DeterministicHashCode GetDeterministicHashCode(HttpClient httpClient, Uri uri)
|
public Windows(List<string> args, ILogger<Program>? logger, AppSettings appSettings, bool isSilent, IConsole console)
|
||||||
{
|
{
|
||||||
DeterministicHashCode result;
|
if (isSilent)
|
||||||
Stream stream = GetStream(httpClient, uri);
|
{ }
|
||||||
result = GetDeterministicHashCode(stream);
|
if (args is null)
|
||||||
stream.Dispose();
|
throw new NullReferenceException(nameof(args));
|
||||||
return result;
|
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 IWindows.GetDeterministicHashCode(HttpClient httpClient, Uri uri) =>
|
private static void LogNetToHoursSince(ILogger<Program>? logger)
|
||||||
GetDeterministicHashCode(httpClient, uri);
|
|
||||||
|
|
||||||
DeterministicHashCode IWindows.GetDeterministicHashCode(HttpClient? httpClient, FilePath filePath)
|
|
||||||
{
|
{
|
||||||
DeterministicHashCode result;
|
double secondsInAHour = 3600f;
|
||||||
if (httpClient is not null)
|
long epoch = new DateTime(1970, 1, 1).Ticks;
|
||||||
result = GetDeterministicHashCode(httpClient, new Uri(filePath.FullName));
|
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 WindowsWork(ILogger<Program>? logger, AppSettings appSettings, IWindows windows, long ticks)
|
||||||
|
{
|
||||||
|
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
|
else
|
||||||
{
|
{
|
||||||
Stream stream = File.OpenRead(filePath.FullName);
|
string sourceDirectory = Path.GetFullPath(appSettings.ResultSettings.RootDirectory);
|
||||||
result = GetDeterministicHashCode(stream);
|
if (!Directory.Exists(sourceDirectory))
|
||||||
stream.Dispose();
|
_ = Directory.CreateDirectory(sourceDirectory);
|
||||||
|
logger?.LogInformation("{Ticks} {RootDirectory}", ticks, sourceDirectory);
|
||||||
|
WindowsWork(logger, appSettings, windows, ticks, sourceDirectory);
|
||||||
}
|
}
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma warning restore CA1416
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
private static ReadOnlyCollection<NginxFileSystem>? GetRecursiveCollection(HttpClient httpClient, string host, string page)
|
private static ReadOnlyCollection<NginxFileSystem>? GetRecursiveCollection(HttpClient httpClient, string host, string page)
|
||||||
{
|
{
|
||||||
@ -209,6 +263,7 @@ public partial class Windows : IWindows, IDisposable
|
|||||||
string paddedId = IId.GetPaddedId(resultSettings: appSettings.ResultSettings,
|
string paddedId = IId.GetPaddedId(resultSettings: appSettings.ResultSettings,
|
||||||
metadataSettings: appSettings.MetadataSettings,
|
metadataSettings: appSettings.MetadataSettings,
|
||||||
id: deterministicHashCode.Id.Value,
|
id: deterministicHashCode.Id.Value,
|
||||||
|
extensionLowered: appSettings.ResultSettings.ValidImageFormatExtensions[0],
|
||||||
hasIgnoreKeyword: null,
|
hasIgnoreKeyword: null,
|
||||||
hasDateTimeOriginal: null,
|
hasDateTimeOriginal: null,
|
||||||
index: null);
|
index: null);
|
||||||
@ -216,19 +271,43 @@ public partial class Windows : IWindows, IDisposable
|
|||||||
messages.Add($"!{nginxFileSystem.Name}.StartsWith({paddedId})");
|
messages.Add($"!{nginxFileSystem.Name}.StartsWith({paddedId})");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void Verify(ILogger<Program>? logger, AppSettings appSettings, IWindows windows, string host, string page)
|
private void WindowsWork(ILogger<Program>? logger, AppSettings appSettings, IWindows windows, long ticks, string sourceDirectory)
|
||||||
{
|
{
|
||||||
List<string> messages = [];
|
ReadOnlyCollection<FirstPass> results;
|
||||||
HttpClient httpClient = new();
|
ReadOnlyCollection<string> files = Directory.GetFiles(sourceDirectory, "*", SearchOption.AllDirectories).AsReadOnly();
|
||||||
|
if (files.Count > 0)
|
||||||
|
_ = IPath.DeleteEmptyDirectories(sourceDirectory);
|
||||||
|
A_Metadata metadata = new(appSettings.ResultSettings, appSettings.MetadataSettings);
|
||||||
int appSettingsMaxDegreeOfParallelism = appSettings.WindowsSettings.MaxDegreeOfParallelism;
|
int appSettingsMaxDegreeOfParallelism = appSettings.WindowsSettings.MaxDegreeOfParallelism;
|
||||||
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = appSettingsMaxDegreeOfParallelism };
|
int filesCount = appSettingsMaxDegreeOfParallelism == 1 ? files.Count : 123000;
|
||||||
ReadOnlyCollection<NginxFileSystem> collection = GetRecursiveCollection(httpClient, host, page) ?? throw new Exception();
|
windows.ConstructProgressBar(filesCount, "EnumerateFiles load");
|
||||||
windows.ConstructProgressBar(collection.Count, nameof(Verify));
|
if (appSettingsMaxDegreeOfParallelism == 1)
|
||||||
_ = Parallel.For(0, collection.Count, parallelOptions, (i, state) =>
|
results = WindowsSynchronousWork(logger, appSettings, windows, files, metadata);
|
||||||
VerifyParallelFor(appSettings, windows, httpClient, collection[i], messages));
|
else
|
||||||
httpClient.Dispose();
|
results = WindowsAsynchronousWork(appSettings, windows, files, metadata, appSettingsMaxDegreeOfParallelism);
|
||||||
foreach (string message in messages)
|
string json = JsonSerializer.Serialize(results, FirstPassCollectionSourceGenerationContext.Default.ListFirstPass);
|
||||||
logger?.LogWarning("{message}", message);
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ReadOnlyCollection<NginxFileSystem> GetRecursiveCollection(string host, string page)
|
private static ReadOnlyCollection<NginxFileSystem> GetRecursiveCollection(string host, string page)
|
||||||
@ -240,10 +319,10 @@ public partial class Windows : IWindows, IDisposable
|
|||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<FirstPass> GetCollection(ILogger<Program>? logger, AppSettings appSettings, IWindows windows, IEnumerable<string> files, A_Metadata metadata)
|
private static int WindowsSynchronousWork(ILogger<Program>? logger, AppSettings appSettings, IWindows windows, A_Metadata metadata, List<FirstPass> results, int index, KeyValuePair<string, List<FileHolder>> keyValuePair)
|
||||||
{
|
{
|
||||||
List<FirstPass> results = [];
|
int result = index + 1;
|
||||||
int index = -1;
|
windows.Tick();
|
||||||
FilePath filePath;
|
FilePath filePath;
|
||||||
FirstPass firstPass;
|
FirstPass firstPass;
|
||||||
string directoryName;
|
string directoryName;
|
||||||
@ -253,29 +332,15 @@ public partial class Windows : IWindows, IDisposable
|
|||||||
bool fastForwardMovingPictureExpertsGroupUsed;
|
bool fastForwardMovingPictureExpertsGroupUsed;
|
||||||
MinimumYearAndPathCombined minimumYearAndPathCombined;
|
MinimumYearAndPathCombined minimumYearAndPathCombined;
|
||||||
FilePath? fastForwardMovingPictureExpertsGroupFilePath;
|
FilePath? fastForwardMovingPictureExpertsGroupFilePath;
|
||||||
ReadOnlyDictionary<string, List<FileHolder>> keyValuePairs;
|
|
||||||
ReadOnlyCollection<string>? fastForwardMovingPictureExpertsGroupFiles;
|
ReadOnlyCollection<string>? fastForwardMovingPictureExpertsGroupFiles;
|
||||||
HttpClient? httpClient = string.IsNullOrEmpty(appSettings.WindowsSettings.Host) || string.IsNullOrEmpty(appSettings.WindowsSettings.Page) ? null : new();
|
HttpClient? httpClient = string.IsNullOrEmpty(appSettings.WindowsSettings.Host) || string.IsNullOrEmpty(appSettings.WindowsSettings.Page) ? null : new();
|
||||||
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)
|
|
||||||
{
|
|
||||||
index += 1;
|
|
||||||
windows.Tick();
|
|
||||||
if (keyValuePair.Value.Count > 2)
|
|
||||||
throw new NotSupportedException("Too many sidecar files!");
|
|
||||||
foreach (FileHolder fileHolder in keyValuePair.Value)
|
foreach (FileHolder fileHolder in keyValuePair.Value)
|
||||||
{
|
{
|
||||||
if (appSettings.WindowsSettings.SidecarExtensions.Contains(fileHolder.ExtensionLowered))
|
if (appSettings.WindowsSettings.SidecarExtensions.Contains(fileHolder.ExtensionLowered))
|
||||||
continue;
|
continue;
|
||||||
if (appSettings.WindowsSettings.IgnoreExtensions.Contains(fileHolder.ExtensionLowered))
|
if (appSettings.ResultSettings.IgnoreExtensions.Contains(fileHolder.ExtensionLowered))
|
||||||
continue;
|
continue;
|
||||||
filePath = FilePath.Get(appSettings.ResultSettings, appSettings.MetadataSettings, fileHolder, index);
|
filePath = FilePath.Get(appSettings.ResultSettings, appSettings.MetadataSettings, fileHolder, result);
|
||||||
if (filePath.Id is not null && (filePath.IsIntelligentIdFormat || filePath.SortOrder is not null))
|
if (filePath.Id is not null && (filePath.IsIntelligentIdFormat || filePath.SortOrder is not null))
|
||||||
continue;
|
continue;
|
||||||
if (filePath.Id is not null)
|
if (filePath.Id is not null)
|
||||||
@ -288,8 +353,8 @@ public partial class Windows : IWindows, IDisposable
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fastForwardMovingPictureExpertsGroupFiles = windows.ConvertAndGetFastForwardMovingPictureExpertsGroupFiles(appSettings.WindowsSettings, httpClient, filePath);
|
fastForwardMovingPictureExpertsGroupFiles = windows.ConvertAndGetFastForwardMovingPictureExpertsGroupFiles(appSettings.ResultSettings, httpClient, filePath);
|
||||||
fastForwardMovingPictureExpertsGroupFilePath = fastForwardMovingPictureExpertsGroupFiles.Count == 0 ? null : FilePath.Get(appSettings.ResultSettings, appSettings.MetadataSettings, FileHolder.Get(fastForwardMovingPictureExpertsGroupFiles[0]), index);
|
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);
|
deterministicHashCode = fastForwardMovingPictureExpertsGroupFilePath is null ? windows.GetDeterministicHashCode(httpClient, filePath) : windows.GetDeterministicHashCode(httpClient, fastForwardMovingPictureExpertsGroupFilePath);
|
||||||
}
|
}
|
||||||
sidecarFiles = [];
|
sidecarFiles = [];
|
||||||
@ -313,56 +378,33 @@ public partial class Windows : IWindows, IDisposable
|
|||||||
foreach (string fastForwardMovingPictureExpertsGroupFile in fastForwardMovingPictureExpertsGroupFiles)
|
foreach (string fastForwardMovingPictureExpertsGroupFile in fastForwardMovingPictureExpertsGroupFiles)
|
||||||
File.Delete(fastForwardMovingPictureExpertsGroupFile);
|
File.Delete(fastForwardMovingPictureExpertsGroupFile);
|
||||||
}
|
}
|
||||||
if (!fastForwardMovingPictureExpertsGroupUsed && appSettings.WindowsSettings.ValidVideoFormatExtensions.Contains(filePath.ExtensionLowered))
|
if (!fastForwardMovingPictureExpertsGroupUsed && appSettings.ResultSettings.ValidVideoFormatExtensions.Contains(filePath.ExtensionLowered))
|
||||||
fastForwardMovingPictureExpertsGroupUsed = true;
|
fastForwardMovingPictureExpertsGroupUsed = true;
|
||||||
firstPass = new(exifDirectory, fastForwardMovingPictureExpertsGroupUsed, minimumYearAndPathCombined, sidecarFiles.ToArray());
|
firstPass = new(exifDirectory, fastForwardMovingPictureExpertsGroupUsed, minimumYearAndPathCombined, sidecarFiles.ToArray());
|
||||||
results.Add(firstPass);
|
results.Add(firstPass);
|
||||||
}
|
}
|
||||||
}
|
return result;
|
||||||
return results;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WindowsWork(ILogger<Program>? logger, AppSettings appSettings, IWindows windows, long ticks)
|
private ReadOnlyCollection<FirstPass> WindowsAsynchronousWork(AppSettings appSettings, IWindows windows, ReadOnlyCollection<string> files, A_Metadata metadata, int appSettingsMaxDegreeOfParallelism)
|
||||||
{
|
{
|
||||||
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
|
|
||||||
{
|
|
||||||
FirstPass firstPass;
|
|
||||||
List<FirstPass> results = [];
|
List<FirstPass> results = [];
|
||||||
string sourceDirectory = Path.GetFullPath(appSettings.ResultSettings.RootDirectory);
|
FirstPass firstPass;
|
||||||
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
|
|
||||||
{
|
|
||||||
List<string> distinct = [];
|
List<string> distinct = [];
|
||||||
List<MetadataGroup> metadataGroups = [];
|
List<MetadataGroup> metadataGroups = [];
|
||||||
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = appSettingsMaxDegreeOfParallelism };
|
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = appSettingsMaxDegreeOfParallelism };
|
||||||
files.AsParallel().ForAll(IMetadata.SetExifDirectoryCollection(windows, appSettings.ResultSettings, appSettings.MetadataSettings, appSettings.WindowsSettings, metadata, distinct, metadataGroups));
|
files.AsParallel().ForAll(IMetadata.SetExifDirectoryCollection(windows, appSettings.ResultSettings, appSettings.MetadataSettings, metadata, distinct, metadataGroups));
|
||||||
if (_ProgressBar?.CurrentTick != results.Count)
|
if (_ProgressBar?.CurrentTick != results.Count)
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
foreach (MetadataGroup metadataGroup in metadataGroups)
|
foreach (MetadataGroup metadataGroup in metadataGroups)
|
||||||
{
|
{
|
||||||
if (metadataGroup.FastForwardMovingPictureExpertsGroupUsed || !appSettings.WindowsSettings.ValidVideoFormatExtensions.Contains(metadataGroup.FilePath.ExtensionLowered))
|
if (metadataGroup.FastForwardMovingPictureExpertsGroupUsed || !appSettings.ResultSettings.ValidVideoFormatExtensions.Contains(metadataGroup.FilePath.ExtensionLowered))
|
||||||
firstPass = new(metadataGroup.ExifDirectory, metadataGroup.FastForwardMovingPictureExpertsGroupUsed, metadataGroup.MinimumYearAndPathCombined, metadataGroup.SidecarFiles.ToArray());
|
firstPass = new(metadataGroup.ExifDirectory, metadataGroup.FastForwardMovingPictureExpertsGroupUsed, metadataGroup.MinimumYearAndPathCombined, metadataGroup.SidecarFiles.ToArray());
|
||||||
else
|
else
|
||||||
firstPass = new(metadataGroup.ExifDirectory, FastForwardMovingPictureExpertsGroupUsed: true, metadataGroup.MinimumYearAndPathCombined, metadataGroup.SidecarFiles.ToArray());
|
firstPass = new(metadataGroup.ExifDirectory, FastForwardMovingPictureExpertsGroupUsed: true, metadataGroup.MinimumYearAndPathCombined, metadataGroup.SidecarFiles.ToArray());
|
||||||
results.Add(firstPass);
|
results.Add(firstPass);
|
||||||
}
|
}
|
||||||
}
|
return results.AsReadOnly();
|
||||||
string json = JsonSerializer.Serialize(results, FirstPassCollectionSourceGenerationContext.Default.ListFirstPass);
|
|
||||||
File.WriteAllText(Path.Combine(sourceDirectory, $"{ticks}.json"), json);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"sdk": {
|
"sdk": {
|
||||||
"rollForward": "latestMinor",
|
"rollForward": "latestMinor",
|
||||||
"version": "9.0.103"
|
"version": "9.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user