This commit is contained in:
2024-05-18 17:06:47 -07:00
parent abbe2feac0
commit 30b8e2f5a9
10 changed files with 326 additions and 170 deletions

View File

@ -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,99 +140,119 @@ 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 (renameConfiguration.IgnoreExtensions.Contains(fileHolder.ExtensionLowered))
continue;
filePath = FilePath.Get(renameConfiguration.MetadataConfiguration, fileHolder, index);
if (filePath.ExtensionLowered == ".url" && filePath.Id is not null)
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)
{
RenameUrl(filePath);
continue;
}
if (renameConfiguration.SkipIdFiles && filePath.Id is not null && (filePath.IsIntelligentIdFormat || filePath.SortOrder is not null))
continue;
if (!renameConfiguration.ForceNewId && 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);
deterministicHashCode = ffmpegFilePath is null ? rename.GetDeterministicHashCode(filePath) : rename.GetDeterministicHashCode(ffmpegFilePath);
}
(fileInfo, exifDirectory) = metadata.GetMetadataCollection(renameConfiguration.MetadataConfiguration, filePath, deterministicHashCode);
results.Add(new(filePath, fileInfo, exifDirectory));
if (ffmpegFiles is not null)
{
foreach (string ffmpegFile in ffmpegFiles)
File.Delete(ffmpegFile);
if (renameConfiguration.SidecarExtensions.Contains(fileHolder.ExtensionLowered))
continue;
if (renameConfiguration.IgnoreExtensions.Contains(fileHolder.ExtensionLowered))
continue;
filePath = FilePath.Get(renameConfiguration.MetadataConfiguration, fileHolder, index);
if (filePath.ExtensionLowered == ".url" && filePath.Id is not null)
{
RenameUrl(filePath);
continue;
}
if (renameConfiguration.SkipIdFiles && filePath.Id is not null && (filePath.IsIntelligentIdFormat || filePath.SortOrder is not null))
continue;
if (!renameConfiguration.ForceNewId && 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);
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(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)
{