Merge Kristy Files
This commit is contained in:
parent
516e494928
commit
d7ed5d89d9
@ -22,6 +22,7 @@ taskTemplate: '^+^_${overdue ? ''^R'' : ''''}${name}^: ${relations ? (''\n^-^/^g
|
||||
- [photoview-in-docker-for-a-viewer-only](tasks/photoview-in-docker-for-a-viewer-only.md)
|
||||
- [family-tree-as-markdown-files](tasks/family-tree-as-markdown-files.md)
|
||||
- [image-size-distribution-per-exif-model-directory-when-no-model](tasks/image-size-distribution-per-exif-model-directory-when-no-model.md)
|
||||
- [cluster-questioning](tasks/cluster-questioning.md)
|
||||
|
||||
## Todo
|
||||
|
||||
|
13
.kanbn/tasks/cluster-questioning.md
Normal file
13
.kanbn/tasks/cluster-questioning.md
Normal file
@ -0,0 +1,13 @@
|
||||
---
|
||||
created: 2023-07-09T04:12:44.673Z
|
||||
updated: 2023-07-09T04:12:44.667Z
|
||||
assigned: ""
|
||||
progress: 0
|
||||
tags: []
|
||||
---
|
||||
|
||||
# Cluster Questioning
|
||||
|
||||
Ask Logan for more details
|
||||
|
||||
- [ ] [k-means-clustering-introduction](https://www.geeksforgeeks.org/k-means-clustering-introduction/)
|
@ -11,10 +11,16 @@ started: 2023-07-08T21:44:14.665Z
|
||||
|
||||
# Merge Kristy Files
|
||||
|
||||
```c#
|
||||
string[] test = (from l in new string[] { "Mikes", "Mike", "Mik-e" } orderby l.EndsWith('s'), l.Contains('-') select l).ToArray();
|
||||
return new(result, (from l in results orderby l.FileHolder.DirectoryName?.EndsWith('s'), l.FileHolder.DirectoryName?.Contains('-'), l.FileHolder.DirectoryName?.Length == 12 select l).ToArray());
|
||||
```
|
||||
|
||||
## Sub-tasks
|
||||
|
||||
- [x] Convert .tiff to .jpg with Nikon Nx Studio at 100%
|
||||
- [ ] Keep .tiff files in 2-Images-B
|
||||
- [ ] Rotate the .jpg only
|
||||
- [ ] Does original name matter to Kristy
|
||||
- [ ] Use Rename console app to rename for storage
|
||||
- [/] Keep .tiff files in 2-Images-B
|
||||
- [ ] Copy to each backup from question
|
||||
- [x] Rotate the .jpg only
|
||||
- [x] Use Rename console app to rename for storage
|
||||
- [ ] Move to production ...
|
||||
|
@ -87,6 +87,8 @@ public class CopyDistinct
|
||||
}
|
||||
}
|
||||
}
|
||||
if (move && _AppSettings.IfCanUseId)
|
||||
throw new NotSupportedException("Not allowed because it would irreversible!");
|
||||
return (move, filesCollection, anyLenFiles, moveBack);
|
||||
}
|
||||
|
||||
@ -128,9 +130,6 @@ public class CopyDistinct
|
||||
distinctFound.Add(file);
|
||||
}
|
||||
}
|
||||
if (distinctNeeded.Count != distinctFound.Count)
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
foreach (string[] files in filesCollection)
|
||||
@ -176,7 +175,7 @@ public class CopyDistinct
|
||||
progressBar = new(count, message, options);
|
||||
string key = string.IsNullOrEmpty(_AppSettings.ResultDirectoryKey) ? _PropertyConfiguration.ResultAllInOne : _AppSettings.ResultDirectoryKey;
|
||||
string[] directories = _FileGroups[key];
|
||||
(distinctDirectories, toDoCollection) = Shared.Models.Stateless.Methods.IDirectory.GetToDoCollection(_PropertyConfiguration, _AppSettings.CopyDuplicates, filesCollection, directories, () => progressBar.Tick());
|
||||
(distinctDirectories, toDoCollection) = Shared.Models.Stateless.Methods.IDirectory.GetToDoCollection(_PropertyConfiguration, _AppSettings.CopyDuplicates, _AppSettings.IfCanUseId, filesCollection, directories, () => progressBar.Tick());
|
||||
progressBar.Dispose();
|
||||
}
|
||||
foreach (string distinctDirectory in distinctDirectories)
|
||||
|
@ -1,10 +1,12 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace View_by_Distance.Copy.Distinct.Models;
|
||||
|
||||
public record AppSettings(string Company,
|
||||
bool CopyDuplicates,
|
||||
string CopyTo,
|
||||
bool IfCanUseId,
|
||||
int MaxDegreeOfParallelism,
|
||||
string ResultDirectoryKey,
|
||||
string WorkingDirectoryName)
|
||||
@ -12,8 +14,14 @@ public record AppSettings(string Company,
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true });
|
||||
string result = JsonSerializer.Serialize(this, AppSettingsSourceGenerationContext.Default.AppSettings);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||
[JsonSerializable(typeof(AppSettings))]
|
||||
internal partial class AppSettingsSourceGenerationContext : JsonSerializerContext
|
||||
{
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace View_by_Distance.Copy.Distinct.Models.Binder;
|
||||
|
||||
@ -7,15 +8,16 @@ public class AppSettings
|
||||
{
|
||||
|
||||
public string? Company { get; set; }
|
||||
public int? MaxDegreeOfParallelism { get; set; }
|
||||
public bool? CopyDuplicates { get; set; }
|
||||
public string? CopyTo { get; set; }
|
||||
public string? ResultDirectoryKey { init; get; }
|
||||
public bool? IfCanUseId { get; set; }
|
||||
public int? MaxDegreeOfParallelism { get; set; }
|
||||
public string? ResultDirectoryKey { get; set; }
|
||||
public string? WorkingDirectoryName { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true });
|
||||
string result = JsonSerializer.Serialize(this, BinderAppSettingsSourceGenerationContext.Default.AppSettings);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -28,6 +30,8 @@ public class AppSettings
|
||||
throw new NullReferenceException(nameof(appSettings.CopyDuplicates));
|
||||
if (appSettings?.CopyTo is null)
|
||||
throw new NullReferenceException(nameof(appSettings.CopyTo));
|
||||
if (appSettings?.IfCanUseId is null)
|
||||
throw new NullReferenceException(nameof(appSettings.IfCanUseId));
|
||||
if (appSettings?.MaxDegreeOfParallelism is null)
|
||||
throw new NullReferenceException(nameof(appSettings.MaxDegreeOfParallelism));
|
||||
if (appSettings?.ResultDirectoryKey is null)
|
||||
@ -38,6 +42,7 @@ public class AppSettings
|
||||
appSettings.Company,
|
||||
appSettings.CopyDuplicates.Value,
|
||||
appSettings.CopyTo,
|
||||
appSettings.IfCanUseId.Value,
|
||||
appSettings.MaxDegreeOfParallelism.Value,
|
||||
appSettings.ResultDirectoryKey,
|
||||
appSettings.WorkingDirectoryName
|
||||
@ -48,9 +53,17 @@ public class AppSettings
|
||||
public static Models.AppSettings Get(IConfigurationRoot configurationRoot)
|
||||
{
|
||||
Models.AppSettings result;
|
||||
#pragma warning disable IL3050, IL2026
|
||||
AppSettings? appSettings = configurationRoot.Get<AppSettings>();
|
||||
#pragma warning restore IL3050, IL2026
|
||||
result = Get(appSettings);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||
[JsonSerializable(typeof(AppSettings))]
|
||||
internal partial class BinderAppSettingsSourceGenerationContext : JsonSerializerContext
|
||||
{
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
"Company": "Mike Phares",
|
||||
"CopyDuplicates": true,
|
||||
"CopyTo": "",
|
||||
"IfCanUseId": true,
|
||||
"Linux": {},
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
|
@ -1,8 +1,10 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace View_by_Distance.Rename.Models;
|
||||
|
||||
public record AppSettings(string Company,
|
||||
string DefaultUnknownDirectoryName,
|
||||
bool ForceIdName,
|
||||
int MaxDegreeOfParallelism,
|
||||
int MaxMinutesDelta,
|
||||
@ -12,8 +14,14 @@ public record AppSettings(string Company,
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true });
|
||||
string result = JsonSerializer.Serialize(this, AppSettingsSourceGenerationContext.Default.AppSettings);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||
[JsonSerializable(typeof(AppSettings))]
|
||||
internal partial class AppSettingsSourceGenerationContext : JsonSerializerContext
|
||||
{
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace View_by_Distance.Rename.Models.Binder;
|
||||
|
||||
@ -7,6 +8,7 @@ public class AppSettings
|
||||
{
|
||||
|
||||
public string? Company { get; set; }
|
||||
public string? DefaultUnknownDirectoryName { get; set; }
|
||||
public bool? ForceIdName { get; set; }
|
||||
public int? MaxDegreeOfParallelism { get; set; }
|
||||
public int? MaxMinutesDelta { get; set; }
|
||||
@ -15,7 +17,7 @@ public class AppSettings
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true });
|
||||
string result = JsonSerializer.Serialize(this, BinderAppSettingsSourceGenerationContext.Default.AppSettings);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -24,6 +26,8 @@ public class AppSettings
|
||||
Models.AppSettings result;
|
||||
if (appSettings?.Company is null)
|
||||
throw new NullReferenceException(nameof(appSettings.Company));
|
||||
if (appSettings?.DefaultUnknownDirectoryName is null)
|
||||
throw new NullReferenceException(nameof(appSettings.DefaultUnknownDirectoryName));
|
||||
if (appSettings?.ForceIdName is null)
|
||||
throw new NullReferenceException(nameof(appSettings.ForceIdName));
|
||||
if (appSettings?.MaxDegreeOfParallelism is null)
|
||||
@ -36,6 +40,7 @@ public class AppSettings
|
||||
throw new NullReferenceException(nameof(appSettings.WorkingDirectoryName));
|
||||
result = new(
|
||||
appSettings.Company,
|
||||
appSettings.DefaultUnknownDirectoryName,
|
||||
appSettings.ForceIdName.Value,
|
||||
appSettings.MaxDegreeOfParallelism.Value,
|
||||
appSettings.MaxMinutesDelta.Value,
|
||||
@ -48,9 +53,17 @@ public class AppSettings
|
||||
public static Models.AppSettings Get(IConfigurationRoot configurationRoot)
|
||||
{
|
||||
Models.AppSettings result;
|
||||
#pragma warning disable IL3050, IL2026
|
||||
AppSettings? appSettings = configurationRoot.Get<AppSettings>();
|
||||
#pragma warning restore IL3050, IL2026
|
||||
result = Get(appSettings);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||
[JsonSerializable(typeof(AppSettings))]
|
||||
internal partial class BinderAppSettingsSourceGenerationContext : JsonSerializerContext
|
||||
{
|
||||
}
|
||||
|
206
Rename/Rename.cs
206
Rename/Rename.cs
@ -12,6 +12,15 @@ namespace View_by_Distance.Rename;
|
||||
public class Rename
|
||||
{
|
||||
|
||||
private record Record(int Index,
|
||||
bool IsIgnoreExtension,
|
||||
bool IsValidImageFormatExtension,
|
||||
List<FileHolder> FileHolders,
|
||||
bool FfmpegFilesPresent,
|
||||
DateTime? DateTimeOriginal,
|
||||
DateTime?[] DateTimes,
|
||||
int? Id);
|
||||
|
||||
private readonly AppSettings _AppSettings;
|
||||
private readonly string _WorkingDirectory;
|
||||
private readonly Configuration _Configuration;
|
||||
@ -90,44 +99,27 @@ public class Rename
|
||||
return results;
|
||||
}
|
||||
|
||||
private List<(FileHolder, string, string)> GetToDoCollection(ProgressBar progressBar, string[] files, bool nefPresent)
|
||||
private List<Record> GetRecords(int offset, ProgressBar progressBar, string[] files)
|
||||
{
|
||||
List<(FileHolder, string, string)> results = new();
|
||||
List<Record> results = new();
|
||||
int? id;
|
||||
int season;
|
||||
string? message;
|
||||
string checkFile;
|
||||
DateTime dateTime;
|
||||
bool? isWrongYear;
|
||||
string seasonName;
|
||||
string? directory;
|
||||
TimeSpan? timeSpan;
|
||||
string directoryName;
|
||||
DateTime?[] dateTimes;
|
||||
FileHolder fileHolder;
|
||||
string[]? ffmpegFiles;
|
||||
bool isIgnoreExtension;
|
||||
string? seasonDirectory;
|
||||
const string jpg = ".jpg";
|
||||
DateTime? minimumDateTime;
|
||||
string checkFileExtension;
|
||||
DateTime? dateTimeFromName;
|
||||
DateTime? dateTimeOriginal;
|
||||
const string jpeg = ".jpeg";
|
||||
DateTime?[] metadataDateTimes;
|
||||
List<string> distinct = new();
|
||||
string[] directoryNameSegments;
|
||||
bool isValidImageFormatExtension;
|
||||
DateTime? metadataDateTimeOriginal;
|
||||
bool nameWithoutExtensionIsIdFormat;
|
||||
IReadOnlyList<MetadataExtractor.Directory> directories;
|
||||
foreach (string file in files)
|
||||
for (int i = 0; i < files.Length; i++)
|
||||
{
|
||||
progressBar.Tick();
|
||||
fileHolder = new(file);
|
||||
fileHolder = new(files[i]);
|
||||
if (!fileHolder.Exists)
|
||||
continue;
|
||||
directory = Path.GetDirectoryName(file);
|
||||
directory = Path.GetDirectoryName(files[i]);
|
||||
if (string.IsNullOrEmpty(directory))
|
||||
continue;
|
||||
if (fileHolder.ExtensionLowered == ".id" || fileHolder.ExtensionLowered == ".lsv" || fileHolder.DirectoryName is null)
|
||||
@ -137,7 +129,6 @@ public class Rename
|
||||
nameWithoutExtensionIsIdFormat = Shared.Models.Stateless.Methods.IProperty.NameWithoutExtensionIsIdFormat(fileHolder);
|
||||
if (nameWithoutExtensionIsIdFormat)
|
||||
continue;
|
||||
dateTimeFromName = Shared.Models.Stateless.Methods.IProperty.GetDateTimeFromName(fileHolder);
|
||||
isValidImageFormatExtension = _PropertyConfiguration.ValidImageFormatExtensions.Contains(fileHolder.ExtensionLowered);
|
||||
isIgnoreExtension = isValidImageFormatExtension && _PropertyConfiguration.IgnoreExtensions.Contains(fileHolder.ExtensionLowered);
|
||||
if (!isIgnoreExtension && isValidImageFormatExtension)
|
||||
@ -145,11 +136,11 @@ public class Rename
|
||||
else
|
||||
{
|
||||
try
|
||||
{ directories = MetadataExtractor.ImageMetadataReader.ReadMetadata(file); }
|
||||
{ directories = MetadataExtractor.ImageMetadataReader.ReadMetadata(files[i]); }
|
||||
catch (Exception) { continue; }
|
||||
CommandTask<CommandResult> result = Cli.Wrap("ffmpeg.exe")
|
||||
// .WithArguments(new[] { "-ss", "00:00:00", "-t", "00:00:00", "-i", file, "-qscale:v", "2", "-r", "0.01", $"{fileHolder.Name}-%4d.jpg" })
|
||||
.WithArguments(new[] { "-i", file, "-vframes", "1", $"{fileHolder.Name}-%4d.jpg" })
|
||||
// .WithArguments(new[] { "-ss", "00:00:00", "-t", "00:00:00", "-i", files[i], "-qscale:v", "2", "-r", "0.01", $"{fileHolder.Name}-%4d.jpg" })
|
||||
.WithArguments(new[] { "-i", files[i], "-vframes", "1", $"{fileHolder.Name}-%4d.jpg" })
|
||||
.WithWorkingDirectory(fileHolder.DirectoryName)
|
||||
.ExecuteAsync();
|
||||
result.Task.Wait();
|
||||
@ -166,9 +157,57 @@ public class Rename
|
||||
if (fileHolder.DirectoryName is null)
|
||||
continue;
|
||||
}
|
||||
(dateTimeOriginal, dateTimes, id, message) = Shared.Models.Stateless.Methods.IProperty.Get(fileHolder, isIgnoreExtension, isValidImageFormatExtension, _PropertyConfiguration.PopulatePropertyId);
|
||||
if (ffmpegFiles is not null)
|
||||
{
|
||||
fileHolder = new(files[i]);
|
||||
foreach (string ffmpegFile in ffmpegFiles)
|
||||
File.Delete(ffmpegFile);
|
||||
}
|
||||
if (message is not null)
|
||||
throw new Exception(message);
|
||||
results.Add(new(i + offset, isIgnoreExtension, isValidImageFormatExtension, new() { fileHolder }, ffmpegFiles is null, dateTimeOriginal, dateTimes, id));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
private List<(FileHolder, string, string)> GetToDoCollection(ProgressBar progressBar, bool nefPresent, List<Record> records, int length)
|
||||
{
|
||||
List<(FileHolder, string, string)> results = new();
|
||||
string id;
|
||||
int season;
|
||||
string checkFile;
|
||||
bool? isWrongYear;
|
||||
DateTime dateTime;
|
||||
string seasonName;
|
||||
TimeSpan? timeSpan;
|
||||
string directoryName;
|
||||
FileHolder fileHolder;
|
||||
string? seasonDirectory;
|
||||
const string jpg = ".jpg";
|
||||
DateTime? minimumDateTime;
|
||||
string checkFileExtension;
|
||||
DateTime? dateTimeFromName;
|
||||
const string jpeg = ".jpeg";
|
||||
DateTime?[] metadataDateTimes;
|
||||
List<string> distinct = new();
|
||||
string[] directoryNameSegments;
|
||||
DateTime? dateTimeOriginalByLogic;
|
||||
DateTime? metadataDateTimeOriginal;
|
||||
IReadOnlyList<MetadataExtractor.Directory> directories;
|
||||
foreach (Record record in records)
|
||||
{
|
||||
progressBar.Tick();
|
||||
if (record.FileHolders.Count != 1)
|
||||
continue;
|
||||
fileHolder = record.FileHolders.First();
|
||||
if (!fileHolder.Exists)
|
||||
continue;
|
||||
if (string.IsNullOrEmpty(fileHolder.DirectoryName))
|
||||
continue;
|
||||
if (fileHolder.ExtensionLowered == jpeg)
|
||||
{
|
||||
if (!isIgnoreExtension && isValidImageFormatExtension)
|
||||
if (!record.IsIgnoreExtension && record.IsValidImageFormatExtension)
|
||||
{
|
||||
if (File.Exists($"{fileHolder.FullName}.id"))
|
||||
{
|
||||
@ -178,7 +217,7 @@ public class Rename
|
||||
if (distinct.Contains(checkFile))
|
||||
continue;
|
||||
distinct.Add(checkFile);
|
||||
results.Add(new(new($"{fileHolder.FullName}.id"), directory, checkFile));
|
||||
results.Add(new(new($"{fileHolder.FullName}.id"), fileHolder.DirectoryName, checkFile));
|
||||
}
|
||||
checkFile = Path.Combine(fileHolder.DirectoryName, $"{fileHolder.NameWithoutExtension}{jpg}");
|
||||
if (File.Exists(checkFile))
|
||||
@ -186,11 +225,11 @@ public class Rename
|
||||
if (distinct.Contains(checkFile))
|
||||
continue;
|
||||
distinct.Add(checkFile);
|
||||
results.Add(new(fileHolder, directory, checkFile));
|
||||
results.Add(new(fileHolder, fileHolder.DirectoryName, checkFile));
|
||||
if (nefPresent)
|
||||
results.Add(new(new($"{fileHolder.FullName[..^4]}.tif"), directory, $"{checkFile[..^4]}.tif"));
|
||||
results.Add(new(new($"{fileHolder.FullName[..^4]}.tif"), fileHolder.DirectoryName, $"{checkFile[..^4]}.tif"));
|
||||
if (nefPresent)
|
||||
results.Add(new(new($"{fileHolder.FullName[..^4]}.nef"), directory, $"{checkFile[..^4]}.nef"));
|
||||
results.Add(new(new($"{fileHolder.FullName[..^4]}.nef"), fileHolder.DirectoryName, $"{checkFile[..^4]}.nef"));
|
||||
if (File.Exists(checkFile))
|
||||
continue;
|
||||
File.Move(fileHolder.FullName, checkFile);
|
||||
@ -199,24 +238,16 @@ public class Rename
|
||||
continue;
|
||||
}
|
||||
}
|
||||
(dateTimeOriginal, dateTimes, id, message) = Shared.Models.Stateless.Methods.IProperty.Get(fileHolder, isIgnoreExtension, isValidImageFormatExtension, _PropertyConfiguration.PopulatePropertyId);
|
||||
if (ffmpegFiles is not null)
|
||||
{
|
||||
fileHolder = new(file);
|
||||
foreach (string ffmpegFile in ffmpegFiles)
|
||||
File.Delete(ffmpegFile);
|
||||
}
|
||||
if (fileHolder.DirectoryName is null)
|
||||
continue;
|
||||
minimumDateTime = dateTimes.Min();
|
||||
dateTimeFromName = Shared.Models.Stateless.Methods.IProperty.GetDateTimeFromName(fileHolder);
|
||||
minimumDateTime = record.DateTimes.Min();
|
||||
if (minimumDateTime is null)
|
||||
throw new NotSupportedException();
|
||||
if (dateTimeOriginal is null)
|
||||
if (record.DateTimeOriginal is null)
|
||||
timeSpan = null;
|
||||
else
|
||||
timeSpan = new(Math.Abs(minimumDateTime.Value.Ticks - dateTimeOriginal.Value.Ticks));
|
||||
timeSpan = new(Math.Abs(minimumDateTime.Value.Ticks - record.DateTimeOriginal.Value.Ticks));
|
||||
if (timeSpan is not null && timeSpan.Value.TotalMinutes < _AppSettings.MaxMinutesDelta)
|
||||
(metadataDateTimeOriginal, metadataDateTimes) = (null, Array.Empty<DateTime?>());
|
||||
(dateTimeOriginalByLogic, metadataDateTimeOriginal, metadataDateTimes) = (record.DateTimeOriginal, null, Array.Empty<DateTime?>());
|
||||
else
|
||||
{
|
||||
if (_PropertyConfiguration.IgnoreExtensions.Contains(fileHolder.ExtensionLowered))
|
||||
@ -225,16 +256,21 @@ public class Rename
|
||||
{ directories = MetadataExtractor.ImageMetadataReader.ReadMetadata(fileHolder.FullName); }
|
||||
catch (Exception) { continue; }
|
||||
(metadataDateTimeOriginal, metadataDateTimes) = Metadata.Models.Stateless.Methods.IMetadata.GetDateTimes(fileHolder, directories);
|
||||
dateTimeOriginal ??= metadataDateTimeOriginal;
|
||||
if (ffmpegFiles is not null && dateTimeOriginal is not null)
|
||||
minimumDateTime = dateTimeOriginal.Value;
|
||||
if (dateTimeOriginal is null)
|
||||
dateTimeOriginalByLogic = record.DateTimeOriginal is not null ? record.DateTimeOriginal : metadataDateTimeOriginal;
|
||||
if (record.FfmpegFilesPresent && dateTimeOriginalByLogic is not null)
|
||||
minimumDateTime = dateTimeOriginalByLogic.Value;
|
||||
if (dateTimeOriginalByLogic is null)
|
||||
timeSpan = null;
|
||||
else
|
||||
timeSpan = new(Math.Abs(minimumDateTime.Value.Ticks - dateTimeOriginal.Value.Ticks));
|
||||
timeSpan = new(Math.Abs(minimumDateTime.Value.Ticks - dateTimeOriginalByLogic.Value.Ticks));
|
||||
}
|
||||
if (timeSpan is null || timeSpan.Value.TotalMinutes > _AppSettings.MaxMinutesDelta)
|
||||
(isWrongYear, seasonDirectory) = (null, !_AppSettings.ForceIdName ? null : Path.Combine(fileHolder.DirectoryName, "Unknown"));
|
||||
{
|
||||
if (string.IsNullOrEmpty(_AppSettings.DefaultUnknownDirectoryName))
|
||||
(isWrongYear, seasonDirectory) = (null, !_AppSettings.ForceIdName ? null : fileHolder.DirectoryName);
|
||||
else
|
||||
(isWrongYear, seasonDirectory) = (null, !_AppSettings.ForceIdName ? null : Path.Combine(fileHolder.DirectoryName, _AppSettings.DefaultUnknownDirectoryName));
|
||||
}
|
||||
else
|
||||
{
|
||||
directoryName = Path.GetFileName(fileHolder.DirectoryName);
|
||||
@ -251,12 +287,10 @@ public class Rename
|
||||
{
|
||||
if (dateTimeFromName is not null && isWrongYear is not null && isWrongYear.Value)
|
||||
minimumDateTime = dateTimeFromName.Value;
|
||||
else if (dateTimeOriginal is not null)
|
||||
minimumDateTime = dateTimeOriginal.Value;
|
||||
else if (metadataDateTimeOriginal is not null)
|
||||
minimumDateTime = metadataDateTimeOriginal.Value;
|
||||
else if (dateTimeOriginalByLogic is not null)
|
||||
minimumDateTime = dateTimeOriginalByLogic.Value;
|
||||
else
|
||||
minimumDateTime = new DateTime?[] { dateTimes.Where(l => l is not null).Min(), metadataDateTimes.Where(l => l is not null).Min() }.Min();
|
||||
minimumDateTime = new DateTime?[] { record.DateTimes.Where(l => l is not null).Min(), metadataDateTimes.Where(l => l is not null).Min() }.Min();
|
||||
if (minimumDateTime is null)
|
||||
continue;
|
||||
checkFileExtension = fileHolder.ExtensionLowered == jpeg ? jpg : fileHolder.ExtensionLowered;
|
||||
@ -266,18 +300,19 @@ public class Rename
|
||||
if (distinct.Contains(checkFile))
|
||||
continue;
|
||||
distinct.Add(checkFile);
|
||||
results.Add(new(fileHolder, directory, checkFile));
|
||||
results.Add(new(fileHolder, fileHolder.DirectoryName, checkFile));
|
||||
if (nefPresent)
|
||||
results.Add(new(new($"{fileHolder.FullName[..^4]}.tif"), directory, $"{checkFile[..^4]}.tif"));
|
||||
results.Add(new(new($"{fileHolder.FullName[..^4]}.tif"), fileHolder.DirectoryName, $"{checkFile[..^4]}.tif"));
|
||||
if (nefPresent)
|
||||
results.Add(new(new($"{fileHolder.FullName[..^4]}.nef"), directory, $"{checkFile[..^4]}.nef"));
|
||||
results.Add(new(new($"{fileHolder.FullName[..^4]}.nef"), fileHolder.DirectoryName, $"{checkFile[..^4]}.nef"));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (id is null)
|
||||
if (record.Id is null)
|
||||
continue;
|
||||
id = Shared.Models.Stateless.Methods.IDirectory.GetPaddedId(length, record.Index, record.Id.Value);
|
||||
checkFileExtension = fileHolder.ExtensionLowered == jpeg ? jpg : fileHolder.ExtensionLowered;
|
||||
checkFile = Path.Combine(seasonDirectory, $"{id.Value}{checkFileExtension}");
|
||||
checkFile = Path.Combine(seasonDirectory, $"{id}{checkFileExtension}");
|
||||
if (checkFile == fileHolder.FullName)
|
||||
continue;
|
||||
if (File.Exists(checkFile))
|
||||
@ -317,13 +352,16 @@ public class Rename
|
||||
{
|
||||
List<string> results = new();
|
||||
string message;
|
||||
bool nefPresent;
|
||||
bool nefPresentCheck;
|
||||
bool nefPresent = false;
|
||||
ProgressBar progressBar;
|
||||
List<Record> records = new();
|
||||
const string fileSearchFilter = "*";
|
||||
const string directorySearchFilter = "*";
|
||||
List<string> distinctDirectories = new();
|
||||
List<(FileHolder, string)> verifiedToDoCollection = new();
|
||||
List<(FileHolder, string, string)> toDoCollection = new();
|
||||
int offset = Shared.Models.Stateless.Methods.IDirectory.GetOffset();
|
||||
ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true };
|
||||
List<string[]> filesCollection = Shared.Models.Stateless.Methods.IDirectory.GetFilesCollection(_PropertyConfiguration.RootDirectory, directorySearchFilter, fileSearchFilter);
|
||||
int count = filesCollection.Select(l => l.Length).Sum();
|
||||
@ -331,26 +369,50 @@ public class Rename
|
||||
{
|
||||
if (!files.Any())
|
||||
continue;
|
||||
// foreach (string file in files)
|
||||
// foreach (string files[i] in files)
|
||||
// {
|
||||
// if (!file.EndsWith(".del"))
|
||||
// if (!files[i].EndsWith(".del"))
|
||||
// continue;
|
||||
// File.Move(file, file[..^4]);
|
||||
// File.Move(files[i], files[i][..^4]);
|
||||
// }
|
||||
// continue;
|
||||
distinctDirectories.Clear();
|
||||
message = $") Renaming files for <{files.FirstOrDefault()}>";
|
||||
progressBar = new(files.Length, message, options);
|
||||
if (_AppSettings.RenameUndo)
|
||||
{
|
||||
message = $") Undo renaming files for <{files.FirstOrDefault()}>";
|
||||
progressBar = new(files.Length, message, options);
|
||||
toDoCollection.AddRange(GetRenameUndoToDoCollection(progressBar, files));
|
||||
}
|
||||
else
|
||||
{
|
||||
nefPresent = files.Any(l => l.EndsWith(".NEF"));
|
||||
if (!nefPresent)
|
||||
toDoCollection.AddRange(GetToDoCollection(progressBar, files, nefPresent));
|
||||
message = $"{records.Count:00000}) Gathering records for files next to <{files.FirstOrDefault()}>";
|
||||
progressBar = new(files.Length, message, options);
|
||||
nefPresentCheck = files.Any(l => l.EndsWith(".NEF"));
|
||||
if (!nefPresentCheck)
|
||||
records.AddRange(GetRecords(offset + records.Count, progressBar, files));
|
||||
else
|
||||
toDoCollection.AddRange(GetToDoCollection(progressBar, (from l in files where l.EndsWith(".JPG") select l).ToArray(), nefPresent));
|
||||
{
|
||||
if (!nefPresent)
|
||||
nefPresent = true;
|
||||
records.AddRange(GetRecords(offset + records.Count, progressBar, (from l in files where l.EndsWith(".JPG") select l).ToArray()));
|
||||
}
|
||||
}
|
||||
progressBar.Dispose();
|
||||
}
|
||||
if (records.Any())
|
||||
{
|
||||
int length = 0;
|
||||
foreach (Record record in records)
|
||||
{
|
||||
if (record.Id is null)
|
||||
continue;
|
||||
if (length > record.Id.Value.ToString().Length)
|
||||
continue;
|
||||
length = record.Id.Value.ToString().Length;
|
||||
}
|
||||
message = $"{length}) comparing records";
|
||||
progressBar = new(records.Count, message, options);
|
||||
toDoCollection.AddRange(GetToDoCollection(progressBar, nefPresent, records, length));
|
||||
progressBar.Dispose();
|
||||
}
|
||||
foreach ((FileHolder fileHolder, string directory, string to) in toDoCollection)
|
||||
@ -372,10 +434,10 @@ public class Rename
|
||||
File.WriteAllText($"{to}.id", $"{to}{Environment.NewLine}{fileHolder.FullName}");
|
||||
}
|
||||
ConsoleKey? consoleKey = null;
|
||||
log.Information($"Ready to Move {verifiedToDoCollection.Count} file(s)?");
|
||||
log.Information($"Ready to Move {verifiedToDoCollection.Count} files[i](s)?");
|
||||
for (int y = 0; y < int.MaxValue; y++)
|
||||
{
|
||||
log.Information("Press \"Y\" key to move file(s), \"N\" key to log file(s) or close console to not move files");
|
||||
log.Information("Press \"Y\" key to move files[i](s), \"N\" key to log files[i](s) or close console to not move files");
|
||||
consoleKey = System.Console.ReadKey().Key;
|
||||
if (consoleKey is ConsoleKey.Y or ConsoleKey.N)
|
||||
break;
|
||||
|
@ -3,6 +3,9 @@ namespace View_by_Distance.Shared.Models.Stateless.Methods;
|
||||
public interface IDirectory
|
||||
{
|
||||
|
||||
static int GetOffset() =>
|
||||
1000000;
|
||||
|
||||
char TestStatic_GetDirectory(string fileName) =>
|
||||
GetDirectory(fileName);
|
||||
static char GetDirectory(string fileName) =>
|
||||
@ -13,6 +16,11 @@ public interface IDirectory
|
||||
static int GetDirectory(char directory) =>
|
||||
directory == '-' ? 10 : int.TryParse(directory.ToString(), out int value) ? value : 11;
|
||||
|
||||
string TestStatic_GetPaddedId(int length, int index, int id) =>
|
||||
GetPaddedId(length, index, id);
|
||||
static string GetPaddedId(int length, int index, int id) =>
|
||||
id > -1 ? $"{index}070{id.ToString().PadLeft(length, '0')}" : $"{index}030{id.ToString()[1..].PadLeft(length, '0')}";
|
||||
|
||||
List<string[]> TestStatic_GetFilesCollection(string directory, string directorySearchFilter, string fileSearchFilter) =>
|
||||
GetFilesCollection(directory, directorySearchFilter, fileSearchFilter);
|
||||
static List<string[]> GetFilesCollection(string directory, string directorySearchFilter, string fileSearchFilter) =>
|
||||
@ -43,10 +51,10 @@ public interface IDirectory
|
||||
static void MoveFiles(List<string> files, string find, string replace) =>
|
||||
XDirectory.MoveFiles(files, find, replace);
|
||||
|
||||
(string[], List<(Models.FileHolder, string)>) TestStatic_GetToDoCollection(Properties.IPropertyConfiguration propertyConfiguration, bool copyDuplicates, List<string[]> filesCollection, string[] directories, Action? tick) =>
|
||||
GetToDoCollection(propertyConfiguration, copyDuplicates, filesCollection, directories, tick);
|
||||
static (string[], List<(Models.FileHolder, string)>) GetToDoCollection(Properties.IPropertyConfiguration propertyConfiguration, bool copyDuplicates, List<string[]> filesCollection, string[] directories, Action? tick) =>
|
||||
XDirectory.GetToDoCollection(propertyConfiguration, copyDuplicates, filesCollection, directories, tick);
|
||||
(string[], List<(Models.FileHolder, string)>) TestStatic_GetToDoCollection(Properties.IPropertyConfiguration propertyConfiguration, bool copyDuplicates, bool ifCanUseId, List<string[]> filesCollection, string[] directories, Action? tick) =>
|
||||
GetToDoCollection(propertyConfiguration, copyDuplicates, ifCanUseId, filesCollection, directories, tick);
|
||||
static (string[], List<(Models.FileHolder, string)>) GetToDoCollection(Properties.IPropertyConfiguration propertyConfiguration, bool copyDuplicates, bool ifCanUseId, List<string[]> filesCollection, string[] directories, Action? tick) =>
|
||||
XDirectory.GetToDoCollection(propertyConfiguration, copyDuplicates, ifCanUseId, filesCollection, directories, tick);
|
||||
|
||||
void TestStatic_CopyOrMove(List<(Models.FileHolder, string)> toDoCollection, bool move, bool moveBack, Action? tick) =>
|
||||
CopyOrMove(toDoCollection, move, moveBack, tick);
|
||||
|
@ -3,6 +3,8 @@ namespace View_by_Distance.Shared.Models.Stateless.Methods;
|
||||
internal abstract partial class XDirectory
|
||||
{
|
||||
|
||||
private record SortedRecord(int? Id, Models.FileHolder FileHolder);
|
||||
|
||||
private static int GetCeilingAverage(List<string[]> fileCollection)
|
||||
{
|
||||
List<int> counts = new();
|
||||
@ -278,15 +280,59 @@ internal abstract partial class XDirectory
|
||||
}
|
||||
}
|
||||
|
||||
private static List<Models.FileHolder> GetFileHolders(List<string[]> filesCollection)
|
||||
private static (bool, SortedRecord[]) GetSortedRecords(List<string[]> filesCollection)
|
||||
{
|
||||
List<Models.FileHolder> results = new();
|
||||
bool result = true;
|
||||
List<SortedRecord> results = new();
|
||||
int? id;
|
||||
short? multiplier;
|
||||
char negativeMarker;
|
||||
int absoluteValueOfId;
|
||||
Models.FileHolder fileHolder;
|
||||
int offset = IDirectory.GetOffset();
|
||||
int offsetLength = offset.ToString().Length;
|
||||
int sortOrderOnlyLengthIndex = offsetLength + 3;
|
||||
foreach (string[] files in filesCollection)
|
||||
results.AddRange(files.Select(l => new Models.FileHolder(l)));
|
||||
return results;
|
||||
{
|
||||
foreach (string file in files)
|
||||
{
|
||||
fileHolder = new Models.FileHolder(file);
|
||||
if (!result)
|
||||
multiplier = null;
|
||||
else
|
||||
{
|
||||
if (fileHolder.NameWithoutExtension.Length < sortOrderOnlyLengthIndex)
|
||||
{
|
||||
result = false;
|
||||
multiplier = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
negativeMarker = fileHolder.NameWithoutExtension[sortOrderOnlyLengthIndex - 2];
|
||||
if (negativeMarker == '7')
|
||||
multiplier = 1;
|
||||
else if (negativeMarker == '3')
|
||||
multiplier = -1;
|
||||
else
|
||||
{
|
||||
result = false;
|
||||
multiplier = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!result)
|
||||
id = null;
|
||||
else if (!int.TryParse(fileHolder.NameWithoutExtension[sortOrderOnlyLengthIndex..], out absoluteValueOfId))
|
||||
id = null;
|
||||
else
|
||||
id = absoluteValueOfId * multiplier;
|
||||
results.Add(new(id, fileHolder));
|
||||
}
|
||||
}
|
||||
return new(result, (from l in results orderby l.FileHolder.LastWriteTime, l.FileHolder.FullName.Length descending select l).ToArray());
|
||||
}
|
||||
|
||||
internal static (string[], List<(Models.FileHolder, string)>) GetToDoCollection(Properties.IPropertyConfiguration propertyConfiguration, bool copyDuplicates, List<string[]> filesCollection, string[] directories, Action? tick)
|
||||
internal static (string[], List<(Models.FileHolder, string)>) GetToDoCollection(Properties.IPropertyConfiguration propertyConfiguration, bool copyDuplicates, bool ifCanUseId, List<string[]> filesCollection, string[] directories, Action? tick)
|
||||
{
|
||||
List<(Models.FileHolder, string)> results = new();
|
||||
string checkFile;
|
||||
@ -295,18 +341,18 @@ internal abstract partial class XDirectory
|
||||
int directoryIndex;
|
||||
string directoryName;
|
||||
bool wrapped = false;
|
||||
List<int> distinctIds = new();
|
||||
List<string> distinct = new();
|
||||
List<string> distinctDirectories = new();
|
||||
List<Models.FileHolder> fileHolders = GetFileHolders(filesCollection);
|
||||
Models.FileHolder[] sortedFileHolders = (from l in fileHolders orderby l.LastWriteTime, l.FullName.Length descending select l).ToArray();
|
||||
foreach (Models.FileHolder fileHolder in sortedFileHolders)
|
||||
(bool all, SortedRecord[] sortedRecords) = GetSortedRecords(filesCollection);
|
||||
foreach (SortedRecord sortedRecord in sortedRecords)
|
||||
{
|
||||
tick?.Invoke();
|
||||
if (fileHolder.Name.EndsWith("len") || fileHolder.ExtensionLowered == ".id" || fileHolder.ExtensionLowered == ".lsv" || fileHolder.DirectoryName is null)
|
||||
if (sortedRecord.FileHolder.Name.EndsWith("len") || sortedRecord.FileHolder.ExtensionLowered == ".id" || sortedRecord.FileHolder.ExtensionLowered == ".lsv" || sortedRecord.FileHolder.DirectoryName is null)
|
||||
continue;
|
||||
(_, directoryIndex) = IPath.GetDirectoryNameAndIndex(propertyConfiguration.ResultAllInOneSubdirectoryLength, fileHolder.NameWithoutExtension);
|
||||
directoryName = Path.GetFileName(fileHolder.DirectoryName);
|
||||
if (directoryName.Length < propertyConfiguration.ResultAllInOneSubdirectoryLength + 3 || !fileHolder.Name.StartsWith(directoryName))
|
||||
(_, directoryIndex) = IPath.GetDirectoryNameAndIndex(propertyConfiguration.ResultAllInOneSubdirectoryLength, sortedRecord.FileHolder.NameWithoutExtension);
|
||||
directoryName = Path.GetFileName(sortedRecord.FileHolder.DirectoryName);
|
||||
if (directoryName.Length < propertyConfiguration.ResultAllInOneSubdirectoryLength + 3 || !sortedRecord.FileHolder.Name.StartsWith(directoryName))
|
||||
{
|
||||
if (wrapped)
|
||||
continue;
|
||||
@ -318,22 +364,38 @@ internal abstract partial class XDirectory
|
||||
wrapped = true;
|
||||
directory = Path.Combine(directories[directoryIndex], directoryName);
|
||||
}
|
||||
checkFile = Path.Combine(directory, $"{fileHolder.NameWithoutExtension}{fileHolder.ExtensionLowered}");
|
||||
if (distinct.Contains(checkFile))
|
||||
if (all)
|
||||
{
|
||||
if (sortedRecord.Id is null || !sortedRecord.FileHolder.NameWithoutExtension.EndsWith(sortedRecord.Id.Value.ToString()[1..]))
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
if (all && ifCanUseId)
|
||||
checkFile = Path.Combine(directory, $"{sortedRecord.Id}{sortedRecord.FileHolder.ExtensionLowered}");
|
||||
else
|
||||
checkFile = Path.Combine(directory, $"{sortedRecord.FileHolder.NameWithoutExtension}{sortedRecord.FileHolder.ExtensionLowered}");
|
||||
if ((sortedRecord.Id is not null && distinctIds.Contains(sortedRecord.Id.Value)) || distinct.Contains(checkFile))
|
||||
{
|
||||
if (string.IsNullOrEmpty(sortedRecord.FileHolder.DirectoryName))
|
||||
continue;
|
||||
if (!copyDuplicates)
|
||||
continue;
|
||||
for (int i = 1; i < int.MaxValue; i++)
|
||||
{
|
||||
fileInfo = new(checkFile);
|
||||
if (!fileInfo.Exists || fileHolder.Length == fileInfo.Length && fileHolder.LastWriteTime == fileInfo.LastWriteTime)
|
||||
checkFile = Path.Combine(directory, $"{fileHolder.NameWithoutExtension}.{i}dup{fileHolder.ExtensionLowered}");
|
||||
if (!fileInfo.Exists || sortedRecord.FileHolder.Length == fileInfo.Length && sortedRecord.FileHolder.LastWriteTime == fileInfo.LastWriteTime)
|
||||
checkFile = Path.Combine(directory, $"{sortedRecord.FileHolder.NameWithoutExtension}.{i}dup{sortedRecord.FileHolder.ExtensionLowered}");
|
||||
else
|
||||
checkFile = Path.Combine(directory, $"{fileHolder.NameWithoutExtension}.{i}why{fileHolder.ExtensionLowered}");
|
||||
checkFile = Path.Combine(directory, $"{sortedRecord.FileHolder.NameWithoutExtension}.{i}why{sortedRecord.FileHolder.ExtensionLowered}");
|
||||
if (sortedRecord.Id is not null)
|
||||
{
|
||||
if (distinctIds.Contains(sortedRecord.Id.Value))
|
||||
continue;
|
||||
distinctIds.Add(sortedRecord.Id.Value);
|
||||
}
|
||||
if (distinct.Contains(checkFile))
|
||||
continue;
|
||||
distinct.Add(checkFile);
|
||||
results.Add(new(fileHolder, checkFile));
|
||||
results.Add(new(sortedRecord.FileHolder, checkFile));
|
||||
if (!distinctDirectories.Contains(directory))
|
||||
distinctDirectories.Add(directory);
|
||||
break;
|
||||
@ -341,7 +403,9 @@ internal abstract partial class XDirectory
|
||||
continue;
|
||||
}
|
||||
distinct.Add(checkFile);
|
||||
results.Add(new(fileHolder, checkFile));
|
||||
if (sortedRecord.Id is not null)
|
||||
distinctIds.Add(sortedRecord.Id.Value);
|
||||
results.Add(new(sortedRecord.FileHolder, checkFile));
|
||||
if (!distinctDirectories.Contains(directory))
|
||||
distinctDirectories.Add(directory);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user