Sidecar
This commit is contained in:
parent
abbe2feac0
commit
30b8e2f5a9
@ -82,28 +82,50 @@ csharp_style_var_elsewhere = false:warning
|
||||
csharp_style_var_for_built_in_types = false:warning
|
||||
csharp_style_var_when_type_is_apparent = false:warning
|
||||
csharp_using_directive_placement = outside_namespace
|
||||
dotnet_analyzer_diagnostic.category-Design.severity = error
|
||||
dotnet_analyzer_diagnostic.category-Documentation.severity = error
|
||||
dotnet_analyzer_diagnostic.category-Globalization.severity = none
|
||||
dotnet_analyzer_diagnostic.category-Interoperability.severity = error
|
||||
dotnet_analyzer_diagnostic.category-Maintainability.severity = error
|
||||
dotnet_analyzer_diagnostic.category-Naming.severity = none
|
||||
dotnet_analyzer_diagnostic.category-Performance.severity = none
|
||||
dotnet_analyzer_diagnostic.category-Reliability.severity = error
|
||||
dotnet_analyzer_diagnostic.category-Security.severity = error
|
||||
dotnet_analyzer_diagnostic.category-SingleFile.severity = error
|
||||
dotnet_analyzer_diagnostic.category-Style.severity = error
|
||||
dotnet_analyzer_diagnostic.category-Usage.severity = error
|
||||
dotnet_code_quality_unused_parameters = all
|
||||
dotnet_code_quality_unused_parameters = non_public # IDE0060: Remove unused parameter
|
||||
dotnet_code_quality.CAXXXX.api_surface = private, internal
|
||||
dotnet_diagnostic.CA1001.severity = error # CA1001: Types that own disposable fields should be disposable
|
||||
dotnet_diagnostic.CA1051.severity = error # CA1051: Do not declare visible instance fields
|
||||
dotnet_diagnostic.CA1825.severity = warning # CA1823: Avoid zero-length array allocations
|
||||
dotnet_diagnostic.CA1829.severity = warning # CA1829: Use Length/Count property instead of Count() when available
|
||||
dotnet_diagnostic.CA1834.severity = warning # CA1834: Consider using 'StringBuilder.Append(char)' when applicable
|
||||
dotnet_diagnostic.CA1860.severity = error # CA1860: Prefer comparing 'Count' to 0 rather than using 'Any()', both for clarity and for performance
|
||||
dotnet_diagnostic.CA1869.severity = none # CA1869: Avoid creating a new 'JsonSerializerOptions' instance for every serialization operation. Cache and reuse instances instead.
|
||||
dotnet_diagnostic.CA2254.severity = none # CA2254: The logging message template should not vary between calls to 'LoggerExtensions.LogInformation(ILogger, string?, params object?[])'
|
||||
dotnet_diagnostic.CA1861.severity = error # CA1861: Prefer 'static readonly' fields over constant array arguments
|
||||
dotnet_diagnostic.CA1869.severity = error # CA1869: Avoid creating a new 'JsonSerializerOptions' instance for every serialization operation. Cache and reuse instances instead.
|
||||
dotnet_diagnostic.CA2201.severity = none # CA2201: Exception type System.NullReferenceException is reserved by the runtime
|
||||
dotnet_diagnostic.CA2254.severity = error # CA2254: The logging message template should not vary between calls to 'LoggerExtensions.LogInformation(ILogger, string?, params object?[])'
|
||||
dotnet_diagnostic.IDE0001.severity = warning # IDE0001: Simplify name
|
||||
dotnet_diagnostic.IDE0002.severity = warning # Simplify (member access) - System.Version.Equals("1", "2"); Version.Equals("1", "2");
|
||||
dotnet_diagnostic.IDE0004.severity = warning # IDE0004: Cast is redundant.
|
||||
dotnet_diagnostic.IDE0005.severity = warning # Using directive is unnecessary
|
||||
dotnet_diagnostic.IDE0010.severity = error # Add missing cases to switch statement (IDE0010)
|
||||
dotnet_diagnostic.IDE0028.severity = error # IDE0028: Collection initialization can be simplified
|
||||
dotnet_diagnostic.IDE0031.severity = warning # Use null propagation (IDE0031)
|
||||
dotnet_diagnostic.IDE0047.severity = warning # IDE0047: Parentheses can be removed
|
||||
dotnet_diagnostic.IDE0048.severity = error # Parentheses preferences (IDE0047 and IDE0048)
|
||||
dotnet_diagnostic.IDE0049.severity = warning # Use language keywords instead of framework type names for type references (IDE0049)
|
||||
dotnet_diagnostic.IDE0051.severity = error # Private member '' is unused [, ]
|
||||
dotnet_diagnostic.IDE0060.severity = warning # IDE0060: Remove unused parameter
|
||||
dotnet_diagnostic.IDE0290.severity = none # Use primary constructor [Distance]csharp(IDE0290)
|
||||
dotnet_diagnostic.IDE0130.severity = error # Namespace does not match folder structure (IDE0130)
|
||||
dotnet_diagnostic.IDE0230.severity = warning # IDE0230: Use UTF-8 string literal
|
||||
dotnet_diagnostic.IDE0290.severity = error # Use primary constructor [Distance]csharp(IDE0290)
|
||||
dotnet_diagnostic.IDE0300.severity = error # IDE0300: Collection initialization can be simplified
|
||||
dotnet_diagnostic.IDE0301.severity = error #IDE0301: Collection initialization can be simplified
|
||||
dotnet_diagnostic.IDE0305.severity = none # IDE0305: Collection initialization can be simplified
|
||||
dotnet_diagnostic.JSON002.severity = warning # JSON002: Probable JSON string detected
|
||||
dotnet_naming_rule.abstract_method_should_be_pascal_case.severity = warning
|
||||
dotnet_naming_rule.abstract_method_should_be_pascal_case.style = pascal_case
|
||||
dotnet_naming_rule.abstract_method_should_be_pascal_case.symbols = abstract_method
|
||||
|
@ -2,7 +2,6 @@ using System.Collections.ObjectModel;
|
||||
using System.Text.Json;
|
||||
using View_by_Distance.Metadata.Models.Stateless;
|
||||
using View_by_Distance.Shared.Models;
|
||||
using View_by_Distance.Shared.Models.Properties;
|
||||
using View_by_Distance.Shared.Models.Stateless.Methods;
|
||||
|
||||
namespace View_by_Distance.Metadata.Models;
|
||||
@ -132,42 +131,4 @@ public class A_Metadata
|
||||
return (fileInfo, result);
|
||||
}
|
||||
|
||||
public static Action<string> SetExifDirectoryCollection(IRename rename, IRenameConfiguration renameConfiguration, A_Metadata metadata, List<(FilePath, FileInfo, ExifDirectory)> exifDirectories)
|
||||
{
|
||||
return file =>
|
||||
{
|
||||
rename.Tick();
|
||||
FileInfo fileInfo;
|
||||
FilePath? ffmpegFilePath;
|
||||
ExifDirectory exifDirectory;
|
||||
FileHolder fileHolder = new(file);
|
||||
ReadOnlyCollection<string>? ffmpegFiles;
|
||||
DeterministicHashCode deterministicHashCode;
|
||||
FilePath filePath = FilePath.Get(renameConfiguration.MetadataConfiguration, fileHolder, index: null);
|
||||
if (!renameConfiguration.SkipIdFiles || filePath.Id is null || !filePath.IsIntelligentIdFormat && filePath.SortOrder is not null)
|
||||
{
|
||||
if (filePath.Id is not null)
|
||||
{
|
||||
ffmpegFiles = null;
|
||||
deterministicHashCode = new(null, filePath.Id, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
ffmpegFiles = rename.ConvertAndGetFfmpegFiles(renameConfiguration, filePath);
|
||||
ffmpegFilePath = ffmpegFiles.Count == 0 ? null : FilePath.Get(renameConfiguration.MetadataConfiguration, new(ffmpegFiles[0]), index: null);
|
||||
deterministicHashCode = ffmpegFilePath is null ? rename.GetDeterministicHashCode(filePath) : rename.GetDeterministicHashCode(ffmpegFilePath);
|
||||
}
|
||||
(fileInfo, exifDirectory) = metadata.GetMetadataCollection(renameConfiguration.MetadataConfiguration, filePath, deterministicHashCode);
|
||||
lock (exifDirectories)
|
||||
exifDirectories.Add(new(filePath, fileInfo, exifDirectory));
|
||||
if (ffmpegFiles is not null)
|
||||
{
|
||||
foreach (string ffmpegFile in ffmpegFiles)
|
||||
File.Delete(ffmpegFile);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
75
Metadata/Models/Stateless/Get.cs
Normal file
75
Metadata/Models/Stateless/Get.cs
Normal file
@ -0,0 +1,75 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using View_by_Distance.Shared.Models;
|
||||
using View_by_Distance.Shared.Models.Properties;
|
||||
using View_by_Distance.Shared.Models.Stateless.Methods;
|
||||
|
||||
namespace View_by_Distance.Metadata.Models.Stateless.Methods;
|
||||
|
||||
internal static class Get
|
||||
{
|
||||
|
||||
internal static ReadOnlyDictionary<string, List<FileHolder>> GetKeyValuePairs(IEnumerable<string> files)
|
||||
{
|
||||
Dictionary<string, List<FileHolder>> results = [];
|
||||
string key;
|
||||
FileHolder fileHolder;
|
||||
List<FileHolder>? fileHolders;
|
||||
foreach (string file in files)
|
||||
{
|
||||
fileHolder = FileHolder.Get(file);
|
||||
if (fileHolder.DirectoryName is null)
|
||||
continue;
|
||||
key = $"{Path.Combine(fileHolder.DirectoryName, fileHolder.NameWithoutExtension)}";
|
||||
if (!results.TryGetValue(key, out fileHolders))
|
||||
{
|
||||
results.Add(key, []);
|
||||
if (!results.TryGetValue(key, out fileHolders))
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
fileHolders.Add(fileHolder);
|
||||
}
|
||||
return new(results);
|
||||
}
|
||||
|
||||
internal static Action<string> SetExifDirectoryCollection(IRename rename, IRenameConfiguration renameConfiguration, A_Metadata metadata, List<string> distinct, List<(FilePath, FileInfo, ExifDirectory, ReadOnlyCollection<FileHolder>)> collection)
|
||||
{
|
||||
return file =>
|
||||
{
|
||||
rename.Tick();
|
||||
FileInfo fileInfo;
|
||||
FilePath? ffmpegFilePath;
|
||||
ExifDirectory exifDirectory;
|
||||
ReadOnlyCollection<string>? ffmpegFiles;
|
||||
DeterministicHashCode deterministicHashCode;
|
||||
FileHolder fileHolder = FileHolder.Get(file);
|
||||
FilePath filePath = FilePath.Get(renameConfiguration.MetadataConfiguration, fileHolder, index: null);
|
||||
string key = $"{Path.Combine(fileHolder.DirectoryName ?? throw new NotSupportedException(), fileHolder.NameWithoutExtension)}";
|
||||
if (distinct.Contains(key))
|
||||
throw new NotSupportedException("Turn off parallelism when sidecar files are present!");
|
||||
if (!renameConfiguration.SkipIdFiles || filePath.Id is null || !filePath.IsIntelligentIdFormat && filePath.SortOrder is not null)
|
||||
{
|
||||
if (filePath.Id is not null)
|
||||
{
|
||||
ffmpegFiles = null;
|
||||
deterministicHashCode = new(null, filePath.Id, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
ffmpegFiles = rename.ConvertAndGetFfmpegFiles(renameConfiguration, filePath);
|
||||
ffmpegFilePath = ffmpegFiles.Count == 0 ? null : FilePath.Get(renameConfiguration.MetadataConfiguration, FileHolder.Get(ffmpegFiles[0]), index: null);
|
||||
deterministicHashCode = ffmpegFilePath is null ? rename.GetDeterministicHashCode(filePath) : rename.GetDeterministicHashCode(ffmpegFilePath);
|
||||
}
|
||||
(fileInfo, exifDirectory) = metadata.GetMetadataCollection(renameConfiguration.MetadataConfiguration, filePath, deterministicHashCode);
|
||||
lock (collection)
|
||||
collection.Add(new(filePath, fileInfo, exifDirectory, new([])));
|
||||
if (ffmpegFiles is not null)
|
||||
{
|
||||
foreach (string ffmpegFile in ffmpegFiles)
|
||||
File.Delete(ffmpegFile);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
using MetadataExtractor;
|
||||
using System.Collections.ObjectModel;
|
||||
using View_by_Distance.Shared.Models;
|
||||
using View_by_Distance.Shared.Models.Properties;
|
||||
using View_by_Distance.Shared.Models.Stateless.Methods;
|
||||
|
||||
namespace View_by_Distance.Metadata.Models.Stateless.Methods;
|
||||
|
||||
@ -55,4 +57,14 @@ public interface IMetadata
|
||||
static double? GetDistance(double originLatitude, double originLongitude, double destinationLatitude, double destinationLongitude, int decimalPlaces = 1, DistanceUnit distanceUnit = DistanceUnit.Miles) =>
|
||||
GPS.GetDistance(originLatitude, originLongitude, destinationLatitude, destinationLongitude, decimalPlaces, distanceUnit);
|
||||
|
||||
Action<string> TestStatic_SetExifDirectoryCollection(IRename rename, IRenameConfiguration renameConfiguration, A_Metadata metadata, List<string> distinct, List<(FilePath, FileInfo, ExifDirectory, ReadOnlyCollection<FileHolder>)> collection) =>
|
||||
SetExifDirectoryCollection(rename, renameConfiguration, metadata, distinct, collection);
|
||||
static Action<string> SetExifDirectoryCollection(IRename rename, IRenameConfiguration renameConfiguration, A_Metadata metadata, List<string> distinct, List<(FilePath, FileInfo, ExifDirectory, ReadOnlyCollection<FileHolder>)> collection) =>
|
||||
Get.SetExifDirectoryCollection(rename, renameConfiguration, metadata, distinct, collection);
|
||||
|
||||
ReadOnlyDictionary<string, List<FileHolder>> TestStatic_GetKeyValuePairs(IEnumerable<string> files) =>
|
||||
GetKeyValuePairs(files);
|
||||
static ReadOnlyDictionary<string, List<FileHolder>> GetKeyValuePairs(IEnumerable<string> files) =>
|
||||
Get.GetKeyValuePairs(files);
|
||||
|
||||
}
|
@ -11,6 +11,7 @@ public class RenameConfiguration
|
||||
public bool? ForceNewId { get; set; }
|
||||
public string[]? IgnoreExtensions { get; set; }
|
||||
public string? RelativePropertyCollectionFile { get; set; }
|
||||
public string[]? SidecarExtensions { get; set; }
|
||||
public bool? SkipIdFiles { get; set; }
|
||||
public string[]? ValidImageFormatExtensions { get; set; }
|
||||
public string[]? ValidVideoFormatExtensions { get; set; }
|
||||
@ -53,6 +54,7 @@ public class RenameConfiguration
|
||||
if (configuration.ForceNewId is null) throw new NullReferenceException(nameof(configuration.ForceNewId));
|
||||
if (configuration.IgnoreExtensions is null) throw new NullReferenceException(nameof(configuration.IgnoreExtensions));
|
||||
if (configuration.RelativePropertyCollectionFile is null) throw new NullReferenceException(nameof(configuration.RelativePropertyCollectionFile));
|
||||
if (configuration.SidecarExtensions is null) throw new NullReferenceException(nameof(configuration.SidecarExtensions));
|
||||
if (configuration.SkipIdFiles is null) throw new NullReferenceException(nameof(configuration.SkipIdFiles));
|
||||
if (configuration.ValidImageFormatExtensions is null) throw new NullReferenceException(nameof(configuration.ValidImageFormatExtensions));
|
||||
if (configuration.ValidVideoFormatExtensions is null) throw new NullReferenceException(nameof(configuration.ValidVideoFormatExtensions));
|
||||
@ -62,6 +64,7 @@ public class RenameConfiguration
|
||||
configuration.ForceNewId.Value,
|
||||
configuration.IgnoreExtensions,
|
||||
configuration.RelativePropertyCollectionFile,
|
||||
configuration.SidecarExtensions,
|
||||
configuration.SkipIdFiles.Value,
|
||||
configuration.ValidImageFormatExtensions,
|
||||
configuration.ValidVideoFormatExtensions);
|
||||
|
@ -3,7 +3,7 @@ using System.Text.Json.Serialization;
|
||||
|
||||
namespace View_by_Distance.Rename.Models;
|
||||
|
||||
internal record Identifier(int Id, string PaddedId)
|
||||
internal sealed record Identifier(int Id, string PaddedId)
|
||||
{
|
||||
|
||||
public override string ToString()
|
||||
|
@ -9,6 +9,7 @@ public record RenameConfiguration(Shared.Models.MetadataConfiguration MetadataCo
|
||||
bool ForceNewId,
|
||||
string[] IgnoreExtensions,
|
||||
string RelativePropertyCollectionFile,
|
||||
string[] SidecarExtensions,
|
||||
bool SkipIdFiles,
|
||||
string[] ValidImageFormatExtensions,
|
||||
string[] ValidVideoFormatExtensions) : Shared.Models.Properties.IRenameConfiguration
|
||||
|
142
Rename/Rename.cs
142
Rename/Rename.cs
@ -17,11 +17,14 @@ using View_by_Distance.Shared.Models.Stateless.Methods;
|
||||
|
||||
namespace View_by_Distance.Rename;
|
||||
|
||||
public partial class Rename : IRename
|
||||
public partial class Rename : IRename, IDisposable
|
||||
{
|
||||
|
||||
private record ToDo(string? Directory, FilePath FilePath, string File, bool JsonFile);
|
||||
private record Record(DateTime DateTime, ExifDirectory ExifDirectory, FilePath FilePath, bool HasDateTimeOriginal, bool HasIgnoreKeyword, string JsonFile);
|
||||
private sealed record ToDo(string? Directory, FilePath FilePath, string File, bool JsonFile);
|
||||
|
||||
private sealed record RecordA(ExifDirectory ExifDirectory, FileInfo FileInfo, FilePath FilePath, ReadOnlyCollection<FileHolder> SidecarFiles);
|
||||
|
||||
private sealed record RecordB(DateTime DateTime, ExifDirectory ExifDirectory, FilePath FilePath, ReadOnlyCollection<FileHolder> SidecarFiles, bool HasDateTimeOriginal, bool HasIgnoreKeyword, string JsonFile);
|
||||
|
||||
private ProgressBar? _ProgressBar;
|
||||
|
||||
@ -44,6 +47,12 @@ public partial class Rename : IRename
|
||||
void IRename.Tick() =>
|
||||
_ProgressBar?.Tick();
|
||||
|
||||
void IDisposable.Dispose()
|
||||
{
|
||||
_ProgressBar?.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
ReadOnlyCollection<string> IRename.ConvertAndGetFfmpegFiles(IRenameConfiguration renameConfiguration, FilePath filePath)
|
||||
{
|
||||
List<string> results = [];
|
||||
@ -114,10 +123,11 @@ public partial class Rename : IRename
|
||||
|
||||
private static void RenameUrl(FilePath filePath)
|
||||
{
|
||||
FileHolder fileHolder;
|
||||
string[] lines = File.ReadAllLines(filePath.FullName);
|
||||
if (lines.Length == 1)
|
||||
{
|
||||
FileHolder fileHolder = new(lines[0]);
|
||||
fileHolder = FileHolder.Get(lines[0]);
|
||||
if (fileHolder.Exists)
|
||||
{
|
||||
string checkFile;
|
||||
@ -130,22 +140,30 @@ public partial class Rename : IRename
|
||||
}
|
||||
}
|
||||
|
||||
private static List<(FilePath, FileInfo, ExifDirectory)> GetExifDirectoryCollection(IRename rename, RenameConfiguration renameConfiguration, IEnumerable<string> files, A_Metadata metadata)
|
||||
private static List<RecordA> GetRecordACollection(IRename rename, RenameConfiguration renameConfiguration, IEnumerable<string> files, A_Metadata metadata)
|
||||
{
|
||||
List<(FilePath, FileInfo, ExifDirectory)> results = [];
|
||||
List<RecordA> results = [];
|
||||
int index = -1;
|
||||
FileInfo fileInfo;
|
||||
FilePath filePath;
|
||||
FileHolder fileHolder;
|
||||
FilePath? ffmpegFilePath;
|
||||
ExifDirectory exifDirectory;
|
||||
List<FileHolder> sidecarFiles;
|
||||
ReadOnlyCollection<string>? ffmpegFiles;
|
||||
DeterministicHashCode deterministicHashCode;
|
||||
foreach (string file in files)
|
||||
ReadOnlyDictionary<string, List<FileHolder>> keyValuePairs = IMetadata.GetKeyValuePairs(files);
|
||||
foreach (KeyValuePair<string, List<FileHolder>> keyValuePair in keyValuePairs)
|
||||
{
|
||||
index += 1;
|
||||
rename.Tick();
|
||||
fileHolder = new(file);
|
||||
if (keyValuePair.Value.Count > 1 && !renameConfiguration.ForceNewId)
|
||||
throw new NotSupportedException($"When sidecar files are present {nameof(renameConfiguration.ForceNewId)} must be true!");
|
||||
if (keyValuePair.Value.Count > 2)
|
||||
throw new NotSupportedException("Too many sidecar files!");
|
||||
foreach (FileHolder fileHolder in keyValuePair.Value)
|
||||
{
|
||||
if (renameConfiguration.SidecarExtensions.Contains(fileHolder.ExtensionLowered))
|
||||
continue;
|
||||
if (renameConfiguration.IgnoreExtensions.Contains(fileHolder.ExtensionLowered))
|
||||
continue;
|
||||
filePath = FilePath.Get(renameConfiguration.MetadataConfiguration, fileHolder, index);
|
||||
@ -164,65 +182,77 @@ public partial class Rename : IRename
|
||||
else
|
||||
{
|
||||
ffmpegFiles = rename.ConvertAndGetFfmpegFiles(renameConfiguration, filePath);
|
||||
ffmpegFilePath = ffmpegFiles.Count == 0 ? null : FilePath.Get(renameConfiguration.MetadataConfiguration, new(ffmpegFiles[0]), index);
|
||||
ffmpegFilePath = ffmpegFiles.Count == 0 ? null : FilePath.Get(renameConfiguration.MetadataConfiguration, FileHolder.Get(ffmpegFiles[0]), index);
|
||||
deterministicHashCode = ffmpegFilePath is null ? rename.GetDeterministicHashCode(filePath) : rename.GetDeterministicHashCode(ffmpegFilePath);
|
||||
}
|
||||
sidecarFiles = [];
|
||||
for (int i = 0; i < keyValuePair.Value.Count; i++)
|
||||
{
|
||||
if (keyValuePair.Value[i].ExtensionLowered == fileHolder.ExtensionLowered)
|
||||
continue;
|
||||
sidecarFiles.Add(keyValuePair.Value[i]);
|
||||
}
|
||||
(fileInfo, exifDirectory) = metadata.GetMetadataCollection(renameConfiguration.MetadataConfiguration, filePath, deterministicHashCode);
|
||||
results.Add(new(filePath, fileInfo, exifDirectory));
|
||||
results.Add(new(exifDirectory, fileInfo, filePath, new(sidecarFiles)));
|
||||
if (ffmpegFiles is not null)
|
||||
{
|
||||
foreach (string ffmpegFile in ffmpegFiles)
|
||||
File.Delete(ffmpegFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
private static ReadOnlyCollection<Record> GetExifDirectoryCollection(MetadataConfiguration metadataConfiguration, List<(FilePath, FileInfo, ExifDirectory)> exifDirectories)
|
||||
private static ReadOnlyCollection<RecordB> GetRecordBCollection(MetadataConfiguration metadataConfiguration, List<RecordA> recordACollection)
|
||||
{
|
||||
List<Record> results = [];
|
||||
List<RecordB> results = [];
|
||||
DateTime? dateTime;
|
||||
bool hasIgnoreKeyword;
|
||||
bool hasDateTimeOriginal;
|
||||
ReadOnlyCollection<string> keywords;
|
||||
foreach ((FilePath filePath, FileInfo fileInfo, ExifDirectory exifDirectory) in exifDirectories)
|
||||
foreach (RecordA recordA in recordACollection)
|
||||
{
|
||||
dateTime = IDate.GetDateTimeOriginal(exifDirectory);
|
||||
dateTime = IDate.GetDateTimeOriginal(recordA.ExifDirectory);
|
||||
hasDateTimeOriginal = dateTime is not null;
|
||||
dateTime ??= IDate.GetMinimum(exifDirectory);
|
||||
keywords = IMetadata.GetKeywords(exifDirectory);
|
||||
dateTime ??= IDate.GetMinimum(recordA.ExifDirectory);
|
||||
keywords = IMetadata.GetKeywords(recordA.ExifDirectory);
|
||||
hasIgnoreKeyword = metadataConfiguration.IgnoreRulesKeyWords.Any(l => keywords.Contains(l));
|
||||
results.Add(new(dateTime.Value, exifDirectory, filePath, hasDateTimeOriginal, hasIgnoreKeyword, fileInfo.FullName));
|
||||
results.Add(new(dateTime.Value, recordA.ExifDirectory, recordA.FilePath, recordA.SidecarFiles, hasDateTimeOriginal, hasIgnoreKeyword, recordA.FileInfo.FullName));
|
||||
}
|
||||
return new(results);
|
||||
}
|
||||
|
||||
private ReadOnlyCollection<Record> GetExifDirectoryCollection(IRename rename, AppSettings appSettings, RenameConfiguration renameConfiguration, DirectoryInfo directoryInfo)
|
||||
private ReadOnlyCollection<RecordB> GetRecordBCollection(IRename rename, AppSettings appSettings, RenameConfiguration renameConfiguration, DirectoryInfo directoryInfo)
|
||||
{
|
||||
ReadOnlyCollection<Record> results;
|
||||
List<(FilePath, FileInfo, ExifDirectory)> exifDirectories = [];
|
||||
ReadOnlyCollection<RecordB> results;
|
||||
List<RecordA> recordACollection = [];
|
||||
A_Metadata metadata = new(renameConfiguration.MetadataConfiguration);
|
||||
int appSettingsMaxDegreeOfParallelism = appSettings.MaxDegreeOfParallelism;
|
||||
IEnumerable<string> files = appSettingsMaxDegreeOfParallelism == 1 ? Directory.GetFiles(directoryInfo.FullName, "*", SearchOption.AllDirectories) : Directory.EnumerateFiles(directoryInfo.FullName, "*", SearchOption.AllDirectories);
|
||||
int filesCount = appSettingsMaxDegreeOfParallelism == 1 ? files.Count() : 123000;
|
||||
_ProgressBar = new(filesCount, "EnumerateFiles load", new ProgressBarOptions() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true });
|
||||
if (appSettingsMaxDegreeOfParallelism == 1)
|
||||
exifDirectories.AddRange(GetExifDirectoryCollection(rename, renameConfiguration, files, metadata));
|
||||
recordACollection.AddRange(GetRecordACollection(rename, renameConfiguration, files, metadata));
|
||||
else
|
||||
{
|
||||
List<string> distinct = [];
|
||||
List<(FilePath, FileInfo, ExifDirectory, ReadOnlyCollection<FileHolder>)> collection = [];
|
||||
ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = appSettingsMaxDegreeOfParallelism };
|
||||
files.AsParallel().ForAll(A_Metadata.SetExifDirectoryCollection(rename, renameConfiguration, metadata, exifDirectories));
|
||||
if (_ProgressBar.CurrentTick != exifDirectories.Count)
|
||||
files.AsParallel().ForAll(IMetadata.SetExifDirectoryCollection(rename, renameConfiguration, metadata, distinct, collection));
|
||||
if (_ProgressBar.CurrentTick != recordACollection.Count)
|
||||
throw new NotSupportedException();
|
||||
foreach ((FilePath filePath, FileInfo fileInfo, ExifDirectory exifDirectory, ReadOnlyCollection<FileHolder> sidecarFiles) in collection)
|
||||
recordACollection.Add(new(exifDirectory, fileInfo, filePath, sidecarFiles));
|
||||
}
|
||||
_ProgressBar.Dispose();
|
||||
results = GetExifDirectoryCollection(renameConfiguration.MetadataConfiguration, exifDirectories);
|
||||
results = GetRecordBCollection(renameConfiguration.MetadataConfiguration, recordACollection);
|
||||
return results;
|
||||
}
|
||||
|
||||
private static void VerifyIntMinValueLength(MetadataConfiguration metadataConfiguration, ReadOnlyCollection<Record> exifDirectories)
|
||||
private static void VerifyIntMinValueLength(MetadataConfiguration metadataConfiguration, ReadOnlyCollection<RecordB> recordBCollection)
|
||||
{
|
||||
foreach ((DateTime _, ExifDirectory exifDirectory, FilePath _, bool _, bool _, string _) in exifDirectories)
|
||||
foreach ((DateTime _, ExifDirectory exifDirectory, FilePath _, ReadOnlyCollection<FileHolder> _, bool _, bool _, string _) in recordBCollection)
|
||||
{
|
||||
if (exifDirectory.Id is null)
|
||||
continue;
|
||||
@ -231,7 +261,7 @@ public partial class Rename : IRename
|
||||
}
|
||||
}
|
||||
|
||||
private static string? GetCheckDirectory(RenameConfiguration renameConfiguration, Record record, FilePath filePath, ReadOnlyCollection<int> ids, bool multipleDirectoriesWithFiles)
|
||||
private static string? GetCheckDirectory(RenameConfiguration renameConfiguration, RecordB record, FilePath filePath, ReadOnlyCollection<int> ids, bool multipleDirectoriesWithFiles)
|
||||
{
|
||||
string? result;
|
||||
if (filePath.DirectoryName is null)
|
||||
@ -252,10 +282,37 @@ public partial class Rename : IRename
|
||||
return result;
|
||||
}
|
||||
|
||||
private static ReadOnlyCollection<ToDo> GetToDoCollection(RenameConfiguration renameConfiguration, Identifier[]? identifiers, ReadOnlyCollection<Record> records)
|
||||
private static List<ToDo> GetSidecarFiles(MetadataConfiguration metadataConfiguration, RecordB record, List<string> distinct, string checkDirectory, string paddedId)
|
||||
{
|
||||
List<ToDo> results = [];
|
||||
Record record;
|
||||
string checkFile;
|
||||
FilePath filePath;
|
||||
string checkFileExtension;
|
||||
foreach (FileHolder fileHolder in record.SidecarFiles)
|
||||
{
|
||||
checkFileExtension = fileHolder.ExtensionLowered;
|
||||
filePath = FilePath.Get(metadataConfiguration, 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);
|
||||
results.Add(new(checkDirectory, filePath, checkFile, JsonFile: false));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
private static ReadOnlyCollection<ToDo> GetToDoCollection(RenameConfiguration renameConfiguration, Identifier[]? identifiers, ReadOnlyCollection<RecordB> recordBCollection)
|
||||
{
|
||||
List<ToDo> results = [];
|
||||
RecordB record;
|
||||
string jsonFile;
|
||||
string paddedId;
|
||||
string checkFile;
|
||||
@ -265,11 +322,11 @@ public partial class Rename : IRename
|
||||
string? checkDirectory;
|
||||
const string jpg = ".jpg";
|
||||
string checkFileExtension;
|
||||
bool multipleDirectoriesWithFiles;
|
||||
List<string> distinct = [];
|
||||
bool? directoryCheck = null;
|
||||
const string jpeg = ".jpeg";
|
||||
string jsonFileSubDirectory;
|
||||
bool multipleDirectoriesWithFiles;
|
||||
foreach (string directory in Directory.GetDirectories(renameConfiguration.MetadataConfiguration.ResultConfiguration.RootDirectory, "*", SearchOption.TopDirectoryOnly))
|
||||
{
|
||||
foreach (string _ in Directory.EnumerateFiles(directory, "*", SearchOption.AllDirectories))
|
||||
@ -283,14 +340,14 @@ public partial class Rename : IRename
|
||||
if (directoryCheck is not null && directoryCheck.Value)
|
||||
break;
|
||||
}
|
||||
VerifyIntMinValueLength(renameConfiguration.MetadataConfiguration, records);
|
||||
VerifyIntMinValueLength(renameConfiguration.MetadataConfiguration, recordBCollection);
|
||||
multipleDirectoriesWithFiles = directoryCheck is not null && directoryCheck.Value;
|
||||
ReadOnlyCollection<Record> collection = new((from l in records orderby l.DateTime select l).ToArray());
|
||||
ResultConfiguration resultConfiguration = renameConfiguration.MetadataConfiguration.ResultConfiguration;
|
||||
ReadOnlyCollection<int> ids = identifiers is null ? new([]) : new((from l in identifiers select l.Id).ToArray());
|
||||
for (int i = 0; i < collection.Count; i++)
|
||||
ReadOnlyCollection<RecordB> sorted = new((from l in recordBCollection orderby l.DateTime select l).ToArray());
|
||||
for (int i = 0; i < sorted.Count; i++)
|
||||
{
|
||||
record = collection[i];
|
||||
record = sorted[i];
|
||||
if (record.ExifDirectory.Id is null)
|
||||
continue;
|
||||
if (record.FilePath.DirectoryName is null)
|
||||
@ -314,7 +371,7 @@ public partial class Rename : IRename
|
||||
jsonFile = Path.Combine(jsonFileSubDirectory, directoryName, $"{record.ExifDirectory.Id.Value}{checkFileExtension}.json");
|
||||
if (record.JsonFile != jsonFile)
|
||||
{
|
||||
fileHolder = new(record.JsonFile);
|
||||
fileHolder = FileHolder.Get(record.JsonFile);
|
||||
filePath = FilePath.Get(renameConfiguration.MetadataConfiguration, fileHolder, index: null);
|
||||
results.Add(new(null, filePath, jsonFile, JsonFile: true));
|
||||
}
|
||||
@ -322,6 +379,9 @@ public partial class Rename : IRename
|
||||
continue;
|
||||
distinct.Add(checkFile);
|
||||
results.Add(new(checkDirectory, record.FilePath, checkFile, JsonFile: false));
|
||||
if (record.SidecarFiles.Count == 0)
|
||||
continue;
|
||||
results.AddRange(GetSidecarFiles(renameConfiguration.MetadataConfiguration, record, distinct, checkDirectory, paddedId));
|
||||
}
|
||||
return new(results);
|
||||
}
|
||||
@ -370,11 +430,11 @@ public partial class Rename : IRename
|
||||
return new(results);
|
||||
}
|
||||
|
||||
private static void SaveIdentifiersToDisk(long ticks, RenameConfiguration renameConfiguration, string aMetadataCollectionDirectory, ReadOnlyCollection<Record> records)
|
||||
private static void SaveIdentifiersToDisk(long ticks, RenameConfiguration renameConfiguration, string aMetadataCollectionDirectory, ReadOnlyCollection<RecordB> recordBCollection)
|
||||
{
|
||||
string paddedId;
|
||||
List<Identifier> identifiers = [];
|
||||
foreach (Record record in records)
|
||||
foreach (RecordB record in recordBCollection)
|
||||
{
|
||||
if (record.ExifDirectory.Id is null)
|
||||
continue;
|
||||
@ -395,9 +455,9 @@ public partial class Rename : IRename
|
||||
throw new Exception($"Invalid {nameof(renameConfiguration.RelativePropertyCollectionFile)}");
|
||||
DirectoryInfo directoryInfo = new(Path.GetFullPath(renameConfiguration.MetadataConfiguration.ResultConfiguration.RootDirectory));
|
||||
logger?.LogInformation("{Ticks} {RootDirectory}", ticks, directoryInfo.FullName);
|
||||
ReadOnlyCollection<Record> records = GetExifDirectoryCollection(rename, appSettings, renameConfiguration, directoryInfo);
|
||||
SaveIdentifiersToDisk(ticks, renameConfiguration, aMetadataCollectionDirectory, records);
|
||||
ReadOnlyCollection<ToDo> toDoCollection = GetToDoCollection(renameConfiguration, identifiers, records);
|
||||
ReadOnlyCollection<RecordB> recordBCollection = GetRecordBCollection(rename, appSettings, renameConfiguration, directoryInfo);
|
||||
SaveIdentifiersToDisk(ticks, renameConfiguration, aMetadataCollectionDirectory, recordBCollection);
|
||||
ReadOnlyCollection<ToDo> toDoCollection = GetToDoCollection(renameConfiguration, identifiers, recordBCollection);
|
||||
ReadOnlyCollection<string> lines = RenameFilesInDirectories(toDoCollection);
|
||||
if (lines.Count != 0)
|
||||
{
|
||||
|
@ -3,75 +3,97 @@ using System.Text.Json.Serialization;
|
||||
|
||||
namespace View_by_Distance.Shared.Models;
|
||||
|
||||
public class FileHolder
|
||||
public record FileHolder(DateTime? CreationTime,
|
||||
string? DirectoryName,
|
||||
bool Exists,
|
||||
string ExtensionLowered,
|
||||
string FullName,
|
||||
int? Id,
|
||||
DateTime? LastWriteTime,
|
||||
long? Length,
|
||||
string Name,
|
||||
string NameWithoutExtension)
|
||||
{
|
||||
|
||||
protected readonly DateTime? _CreationTime;
|
||||
protected readonly string? _DirectoryName;
|
||||
protected readonly bool _Exists;
|
||||
protected readonly string _ExtensionLowered;
|
||||
protected readonly string _FullName;
|
||||
protected readonly int? _Id;
|
||||
protected readonly DateTime? _LastWriteTime;
|
||||
protected readonly long? _Length;
|
||||
protected readonly string _Name;
|
||||
protected readonly string _NameWithoutExtension;
|
||||
public DateTime? CreationTime => _CreationTime;
|
||||
public string? DirectoryName => _DirectoryName;
|
||||
public bool Exists => _Exists;
|
||||
public string ExtensionLowered => _ExtensionLowered;
|
||||
public string FullName => _FullName;
|
||||
public int? Id => _Id;
|
||||
public DateTime? LastWriteTime => _LastWriteTime;
|
||||
public long? Length => _Length;
|
||||
public string Name => _Name;
|
||||
public string NameWithoutExtension => _NameWithoutExtension;
|
||||
|
||||
public FileHolder(DateTime? creationTime, string? directoryName, bool exists, string extensionLowered, string fullName, int? id, DateTime? lastWriteTime, long? length, string name, string nameWithoutExtension)
|
||||
{
|
||||
_CreationTime = creationTime;
|
||||
_DirectoryName = directoryName;
|
||||
_Exists = exists;
|
||||
_ExtensionLowered = extensionLowered;
|
||||
_FullName = fullName;
|
||||
_Id = id;
|
||||
_LastWriteTime = lastWriteTime;
|
||||
_Length = length;
|
||||
_Name = name;
|
||||
_NameWithoutExtension = nameWithoutExtension;
|
||||
}
|
||||
|
||||
public FileHolder(FileInfo fileInfo, int? id)
|
||||
{
|
||||
if (fileInfo.Exists)
|
||||
{
|
||||
_CreationTime = fileInfo.CreationTime;
|
||||
_LastWriteTime = fileInfo.LastWriteTime;
|
||||
_Length = fileInfo.Length;
|
||||
}
|
||||
_DirectoryName = fileInfo.DirectoryName;
|
||||
_Exists = fileInfo.Exists;
|
||||
_ExtensionLowered = fileInfo.Extension.ToLower();
|
||||
_Id = id;
|
||||
_FullName = fileInfo.FullName;
|
||||
_Name = fileInfo.Name;
|
||||
_NameWithoutExtension = Path.GetFileNameWithoutExtension(fileInfo.FullName);
|
||||
}
|
||||
|
||||
public FileHolder(string fileName) :
|
||||
this(new FileInfo(fileName), null)
|
||||
{ }
|
||||
|
||||
public FileHolder(string fileName, int? id) :
|
||||
this(new FileInfo(fileName), id)
|
||||
{ }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string result = JsonSerializer.Serialize(this, FileHolderSourceGenerationContext.Default.FileHolder);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static FileHolder GetExisting(FileInfo fileInfo, int? id) =>
|
||||
new(fileInfo.CreationTime,
|
||||
fileInfo.DirectoryName,
|
||||
fileInfo.Exists,
|
||||
fileInfo.Extension.ToLower(),
|
||||
fileInfo.FullName,
|
||||
id,
|
||||
fileInfo.LastWriteTime,
|
||||
fileInfo.Length,
|
||||
fileInfo.Name,
|
||||
Path.GetFileNameWithoutExtension(fileInfo.FullName));
|
||||
|
||||
private static FileHolder GetNonExisting(FileInfo fileInfo, int? id) =>
|
||||
new(null,
|
||||
fileInfo.DirectoryName,
|
||||
fileInfo.Exists,
|
||||
fileInfo.Extension.ToLower(),
|
||||
fileInfo.FullName,
|
||||
id,
|
||||
null,
|
||||
null,
|
||||
fileInfo.Name,
|
||||
Path.GetFileNameWithoutExtension(fileInfo.FullName));
|
||||
|
||||
public static FileHolder Get(FileInfo fileInfo, int? id) =>
|
||||
fileInfo.Exists ? GetExisting(fileInfo, id) : GetNonExisting(fileInfo, id);
|
||||
|
||||
public static FileHolder Get(FilePath filePath, int? id)
|
||||
{
|
||||
FileHolder result;
|
||||
result = new(new(filePath.CreationTicks),
|
||||
filePath.DirectoryName,
|
||||
true,
|
||||
filePath.ExtensionLowered,
|
||||
filePath.FullName,
|
||||
id,
|
||||
new(filePath.LastWriteTicks),
|
||||
filePath.Length,
|
||||
filePath.Name,
|
||||
Path.GetFileNameWithoutExtension(filePath.FullName));
|
||||
return result;
|
||||
}
|
||||
|
||||
private static FileHolder GetExisting(FileHolder fileHolder) =>
|
||||
new(fileHolder.CreationTime,
|
||||
fileHolder.DirectoryName,
|
||||
fileHolder.Exists,
|
||||
fileHolder.ExtensionLowered,
|
||||
fileHolder.FullName,
|
||||
Id: null,
|
||||
fileHolder.LastWriteTime,
|
||||
fileHolder.Length,
|
||||
fileHolder.Name,
|
||||
Path.GetFileNameWithoutExtension(fileHolder.FullName));
|
||||
|
||||
private static FileHolder GetNonExisting(FileHolder fileHolder) =>
|
||||
new(null,
|
||||
fileHolder.DirectoryName,
|
||||
fileHolder.Exists,
|
||||
fileHolder.ExtensionLowered,
|
||||
fileHolder.FullName,
|
||||
Id: null,
|
||||
null,
|
||||
null,
|
||||
fileHolder.Name,
|
||||
Path.GetFileNameWithoutExtension(fileHolder.FullName));
|
||||
|
||||
public static FileHolder Get(FileHolder fileHolder) =>
|
||||
fileHolder.Exists ? GetExisting(fileHolder) : GetNonExisting(fileHolder);
|
||||
|
||||
public static FileHolder Get(string file) =>
|
||||
Get(new FileInfo(file), id: null);
|
||||
|
||||
}
|
||||
|
||||
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||
|
@ -28,7 +28,7 @@ public record FilePath(long CreationTicks,
|
||||
public static FilePath Get(MetadataConfiguration metadataConfiguration, FileHolder fileHolder, int? index)
|
||||
{
|
||||
if (fileHolder.CreationTime is null)
|
||||
fileHolder = new(fileHolder.FullName);
|
||||
fileHolder = FileHolder.Get(fileHolder);
|
||||
if (fileHolder.CreationTime is null)
|
||||
throw new NullReferenceException(nameof(fileHolder.CreationTime));
|
||||
if (fileHolder.LastWriteTime is null)
|
||||
|
Loading…
x
Reference in New Issue
Block a user