Merge Kristy Files

This commit is contained in:
Mike Phares 2023-07-09 00:26:58 -07:00
parent 516e494928
commit d7ed5d89d9
12 changed files with 309 additions and 113 deletions

View File

@ -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) - [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) - [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) - [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 ## Todo

View 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/)

View File

@ -11,10 +11,16 @@ started: 2023-07-08T21:44:14.665Z
# Merge Kristy Files # 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 ## Sub-tasks
- [x] Convert .tiff to .jpg with Nikon Nx Studio at 100% - [x] Convert .tiff to .jpg with Nikon Nx Studio at 100%
- [ ] Keep .tiff files in 2-Images-B - [/] Keep .tiff files in 2-Images-B
- [ ] Rotate the .jpg only - [ ] Copy to each backup from question
- [ ] Does original name matter to Kristy - [x] Rotate the .jpg only
- [ ] Use Rename console app to rename for storage - [x] Use Rename console app to rename for storage
- [ ] Move to production ...

View File

@ -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); return (move, filesCollection, anyLenFiles, moveBack);
} }
@ -128,9 +130,6 @@ public class CopyDistinct
distinctFound.Add(file); distinctFound.Add(file);
} }
} }
if (distinctNeeded.Count != distinctFound.Count)
continue;
break;
} }
} }
foreach (string[] files in filesCollection) foreach (string[] files in filesCollection)
@ -176,7 +175,7 @@ public class CopyDistinct
progressBar = new(count, message, options); progressBar = new(count, message, options);
string key = string.IsNullOrEmpty(_AppSettings.ResultDirectoryKey) ? _PropertyConfiguration.ResultAllInOne : _AppSettings.ResultDirectoryKey; string key = string.IsNullOrEmpty(_AppSettings.ResultDirectoryKey) ? _PropertyConfiguration.ResultAllInOne : _AppSettings.ResultDirectoryKey;
string[] directories = _FileGroups[key]; 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(); progressBar.Dispose();
} }
foreach (string distinctDirectory in distinctDirectories) foreach (string distinctDirectory in distinctDirectories)

View File

@ -1,10 +1,12 @@
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization;
namespace View_by_Distance.Copy.Distinct.Models; namespace View_by_Distance.Copy.Distinct.Models;
public record AppSettings(string Company, public record AppSettings(string Company,
bool CopyDuplicates, bool CopyDuplicates,
string CopyTo, string CopyTo,
bool IfCanUseId,
int MaxDegreeOfParallelism, int MaxDegreeOfParallelism,
string ResultDirectoryKey, string ResultDirectoryKey,
string WorkingDirectoryName) string WorkingDirectoryName)
@ -12,8 +14,14 @@ public record AppSettings(string Company,
public override string ToString() public override string ToString()
{ {
string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true }); string result = JsonSerializer.Serialize(this, AppSettingsSourceGenerationContext.Default.AppSettings);
return result; return result;
} }
} }
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(AppSettings))]
internal partial class AppSettingsSourceGenerationContext : JsonSerializerContext
{
}

View File

@ -1,5 +1,6 @@
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization;
namespace View_by_Distance.Copy.Distinct.Models.Binder; namespace View_by_Distance.Copy.Distinct.Models.Binder;
@ -7,15 +8,16 @@ public class AppSettings
{ {
public string? Company { get; set; } public string? Company { get; set; }
public int? MaxDegreeOfParallelism { get; set; }
public bool? CopyDuplicates { get; set; } public bool? CopyDuplicates { get; set; }
public string? CopyTo { 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 string? WorkingDirectoryName { get; set; }
public override string ToString() public override string ToString()
{ {
string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true }); string result = JsonSerializer.Serialize(this, BinderAppSettingsSourceGenerationContext.Default.AppSettings);
return result; return result;
} }
@ -28,6 +30,8 @@ public class AppSettings
throw new NullReferenceException(nameof(appSettings.CopyDuplicates)); throw new NullReferenceException(nameof(appSettings.CopyDuplicates));
if (appSettings?.CopyTo is null) if (appSettings?.CopyTo is null)
throw new NullReferenceException(nameof(appSettings.CopyTo)); throw new NullReferenceException(nameof(appSettings.CopyTo));
if (appSettings?.IfCanUseId is null)
throw new NullReferenceException(nameof(appSettings.IfCanUseId));
if (appSettings?.MaxDegreeOfParallelism is null) if (appSettings?.MaxDegreeOfParallelism is null)
throw new NullReferenceException(nameof(appSettings.MaxDegreeOfParallelism)); throw new NullReferenceException(nameof(appSettings.MaxDegreeOfParallelism));
if (appSettings?.ResultDirectoryKey is null) if (appSettings?.ResultDirectoryKey is null)
@ -38,6 +42,7 @@ public class AppSettings
appSettings.Company, appSettings.Company,
appSettings.CopyDuplicates.Value, appSettings.CopyDuplicates.Value,
appSettings.CopyTo, appSettings.CopyTo,
appSettings.IfCanUseId.Value,
appSettings.MaxDegreeOfParallelism.Value, appSettings.MaxDegreeOfParallelism.Value,
appSettings.ResultDirectoryKey, appSettings.ResultDirectoryKey,
appSettings.WorkingDirectoryName appSettings.WorkingDirectoryName
@ -48,9 +53,17 @@ public class AppSettings
public static Models.AppSettings Get(IConfigurationRoot configurationRoot) public static Models.AppSettings Get(IConfigurationRoot configurationRoot)
{ {
Models.AppSettings result; Models.AppSettings result;
#pragma warning disable IL3050, IL2026
AppSettings? appSettings = configurationRoot.Get<AppSettings>(); AppSettings? appSettings = configurationRoot.Get<AppSettings>();
#pragma warning restore IL3050, IL2026
result = Get(appSettings); result = Get(appSettings);
return result; return result;
} }
} }
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(AppSettings))]
internal partial class BinderAppSettingsSourceGenerationContext : JsonSerializerContext
{
}

View File

@ -3,6 +3,7 @@
"Company": "Mike Phares", "Company": "Mike Phares",
"CopyDuplicates": true, "CopyDuplicates": true,
"CopyTo": "", "CopyTo": "",
"IfCanUseId": true,
"Linux": {}, "Linux": {},
"Logging": { "Logging": {
"LogLevel": { "LogLevel": {

View File

@ -1,8 +1,10 @@
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization;
namespace View_by_Distance.Rename.Models; namespace View_by_Distance.Rename.Models;
public record AppSettings(string Company, public record AppSettings(string Company,
string DefaultUnknownDirectoryName,
bool ForceIdName, bool ForceIdName,
int MaxDegreeOfParallelism, int MaxDegreeOfParallelism,
int MaxMinutesDelta, int MaxMinutesDelta,
@ -12,8 +14,14 @@ public record AppSettings(string Company,
public override string ToString() public override string ToString()
{ {
string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true }); string result = JsonSerializer.Serialize(this, AppSettingsSourceGenerationContext.Default.AppSettings);
return result; return result;
} }
} }
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(AppSettings))]
internal partial class AppSettingsSourceGenerationContext : JsonSerializerContext
{
}

View File

@ -1,5 +1,6 @@
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization;
namespace View_by_Distance.Rename.Models.Binder; namespace View_by_Distance.Rename.Models.Binder;
@ -7,6 +8,7 @@ public class AppSettings
{ {
public string? Company { get; set; } public string? Company { get; set; }
public string? DefaultUnknownDirectoryName { get; set; }
public bool? ForceIdName { get; set; } public bool? ForceIdName { get; set; }
public int? MaxDegreeOfParallelism { get; set; } public int? MaxDegreeOfParallelism { get; set; }
public int? MaxMinutesDelta { get; set; } public int? MaxMinutesDelta { get; set; }
@ -15,7 +17,7 @@ public class AppSettings
public override string ToString() public override string ToString()
{ {
string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true }); string result = JsonSerializer.Serialize(this, BinderAppSettingsSourceGenerationContext.Default.AppSettings);
return result; return result;
} }
@ -24,6 +26,8 @@ public class AppSettings
Models.AppSettings result; Models.AppSettings result;
if (appSettings?.Company is null) if (appSettings?.Company is null)
throw new NullReferenceException(nameof(appSettings.Company)); throw new NullReferenceException(nameof(appSettings.Company));
if (appSettings?.DefaultUnknownDirectoryName is null)
throw new NullReferenceException(nameof(appSettings.DefaultUnknownDirectoryName));
if (appSettings?.ForceIdName is null) if (appSettings?.ForceIdName is null)
throw new NullReferenceException(nameof(appSettings.ForceIdName)); throw new NullReferenceException(nameof(appSettings.ForceIdName));
if (appSettings?.MaxDegreeOfParallelism is null) if (appSettings?.MaxDegreeOfParallelism is null)
@ -36,6 +40,7 @@ public class AppSettings
throw new NullReferenceException(nameof(appSettings.WorkingDirectoryName)); throw new NullReferenceException(nameof(appSettings.WorkingDirectoryName));
result = new( result = new(
appSettings.Company, appSettings.Company,
appSettings.DefaultUnknownDirectoryName,
appSettings.ForceIdName.Value, appSettings.ForceIdName.Value,
appSettings.MaxDegreeOfParallelism.Value, appSettings.MaxDegreeOfParallelism.Value,
appSettings.MaxMinutesDelta.Value, appSettings.MaxMinutesDelta.Value,
@ -48,9 +53,17 @@ public class AppSettings
public static Models.AppSettings Get(IConfigurationRoot configurationRoot) public static Models.AppSettings Get(IConfigurationRoot configurationRoot)
{ {
Models.AppSettings result; Models.AppSettings result;
#pragma warning disable IL3050, IL2026
AppSettings? appSettings = configurationRoot.Get<AppSettings>(); AppSettings? appSettings = configurationRoot.Get<AppSettings>();
#pragma warning restore IL3050, IL2026
result = Get(appSettings); result = Get(appSettings);
return result; return result;
} }
} }
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(AppSettings))]
internal partial class BinderAppSettingsSourceGenerationContext : JsonSerializerContext
{
}

View File

@ -12,6 +12,15 @@ namespace View_by_Distance.Rename;
public class 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 AppSettings _AppSettings;
private readonly string _WorkingDirectory; private readonly string _WorkingDirectory;
private readonly Configuration _Configuration; private readonly Configuration _Configuration;
@ -90,44 +99,27 @@ public class Rename
return results; 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? id;
int season;
string? message; string? message;
string checkFile;
DateTime dateTime;
bool? isWrongYear;
string seasonName;
string? directory; string? directory;
TimeSpan? timeSpan;
string directoryName;
DateTime?[] dateTimes; DateTime?[] dateTimes;
FileHolder fileHolder; FileHolder fileHolder;
string[]? ffmpegFiles; string[]? ffmpegFiles;
bool isIgnoreExtension; bool isIgnoreExtension;
string? seasonDirectory;
const string jpg = ".jpg";
DateTime? minimumDateTime;
string checkFileExtension;
DateTime? dateTimeFromName;
DateTime? dateTimeOriginal; DateTime? dateTimeOriginal;
const string jpeg = ".jpeg";
DateTime?[] metadataDateTimes;
List<string> distinct = new();
string[] directoryNameSegments;
bool isValidImageFormatExtension; bool isValidImageFormatExtension;
DateTime? metadataDateTimeOriginal;
bool nameWithoutExtensionIsIdFormat; bool nameWithoutExtensionIsIdFormat;
IReadOnlyList<MetadataExtractor.Directory> directories; IReadOnlyList<MetadataExtractor.Directory> directories;
foreach (string file in files) for (int i = 0; i < files.Length; i++)
{ {
progressBar.Tick(); progressBar.Tick();
fileHolder = new(file); fileHolder = new(files[i]);
if (!fileHolder.Exists) if (!fileHolder.Exists)
continue; continue;
directory = Path.GetDirectoryName(file); directory = Path.GetDirectoryName(files[i]);
if (string.IsNullOrEmpty(directory)) if (string.IsNullOrEmpty(directory))
continue; continue;
if (fileHolder.ExtensionLowered == ".id" || fileHolder.ExtensionLowered == ".lsv" || fileHolder.DirectoryName is null) 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); nameWithoutExtensionIsIdFormat = Shared.Models.Stateless.Methods.IProperty.NameWithoutExtensionIsIdFormat(fileHolder);
if (nameWithoutExtensionIsIdFormat) if (nameWithoutExtensionIsIdFormat)
continue; continue;
dateTimeFromName = Shared.Models.Stateless.Methods.IProperty.GetDateTimeFromName(fileHolder);
isValidImageFormatExtension = _PropertyConfiguration.ValidImageFormatExtensions.Contains(fileHolder.ExtensionLowered); isValidImageFormatExtension = _PropertyConfiguration.ValidImageFormatExtensions.Contains(fileHolder.ExtensionLowered);
isIgnoreExtension = isValidImageFormatExtension && _PropertyConfiguration.IgnoreExtensions.Contains(fileHolder.ExtensionLowered); isIgnoreExtension = isValidImageFormatExtension && _PropertyConfiguration.IgnoreExtensions.Contains(fileHolder.ExtensionLowered);
if (!isIgnoreExtension && isValidImageFormatExtension) if (!isIgnoreExtension && isValidImageFormatExtension)
@ -145,11 +136,11 @@ public class Rename
else else
{ {
try try
{ directories = MetadataExtractor.ImageMetadataReader.ReadMetadata(file); } { directories = MetadataExtractor.ImageMetadataReader.ReadMetadata(files[i]); }
catch (Exception) { continue; } catch (Exception) { continue; }
CommandTask<CommandResult> result = Cli.Wrap("ffmpeg.exe") 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[] { "-ss", "00:00:00", "-t", "00:00:00", "-i", files[i], "-qscale:v", "2", "-r", "0.01", $"{fileHolder.Name}-%4d.jpg" })
.WithArguments(new[] { "-i", file, "-vframes", "1", $"{fileHolder.Name}-%4d.jpg" }) .WithArguments(new[] { "-i", files[i], "-vframes", "1", $"{fileHolder.Name}-%4d.jpg" })
.WithWorkingDirectory(fileHolder.DirectoryName) .WithWorkingDirectory(fileHolder.DirectoryName)
.ExecuteAsync(); .ExecuteAsync();
result.Task.Wait(); result.Task.Wait();
@ -166,9 +157,57 @@ public class Rename
if (fileHolder.DirectoryName is null) if (fileHolder.DirectoryName is null)
continue; 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 (fileHolder.ExtensionLowered == jpeg)
{ {
if (!isIgnoreExtension && isValidImageFormatExtension) if (!record.IsIgnoreExtension && record.IsValidImageFormatExtension)
{ {
if (File.Exists($"{fileHolder.FullName}.id")) if (File.Exists($"{fileHolder.FullName}.id"))
{ {
@ -178,7 +217,7 @@ public class Rename
if (distinct.Contains(checkFile)) if (distinct.Contains(checkFile))
continue; continue;
distinct.Add(checkFile); 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}"); checkFile = Path.Combine(fileHolder.DirectoryName, $"{fileHolder.NameWithoutExtension}{jpg}");
if (File.Exists(checkFile)) if (File.Exists(checkFile))
@ -186,11 +225,11 @@ public class Rename
if (distinct.Contains(checkFile)) if (distinct.Contains(checkFile))
continue; continue;
distinct.Add(checkFile); distinct.Add(checkFile);
results.Add(new(fileHolder, directory, checkFile)); results.Add(new(fileHolder, fileHolder.DirectoryName, checkFile));
if (nefPresent) 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) 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)) if (File.Exists(checkFile))
continue; continue;
File.Move(fileHolder.FullName, checkFile); File.Move(fileHolder.FullName, checkFile);
@ -199,24 +238,16 @@ public class Rename
continue; continue;
} }
} }
(dateTimeOriginal, dateTimes, id, message) = Shared.Models.Stateless.Methods.IProperty.Get(fileHolder, isIgnoreExtension, isValidImageFormatExtension, _PropertyConfiguration.PopulatePropertyId); dateTimeFromName = Shared.Models.Stateless.Methods.IProperty.GetDateTimeFromName(fileHolder);
if (ffmpegFiles is not null) minimumDateTime = record.DateTimes.Min();
{
fileHolder = new(file);
foreach (string ffmpegFile in ffmpegFiles)
File.Delete(ffmpegFile);
}
if (fileHolder.DirectoryName is null)
continue;
minimumDateTime = dateTimes.Min();
if (minimumDateTime is null) if (minimumDateTime is null)
throw new NotSupportedException(); throw new NotSupportedException();
if (dateTimeOriginal is null) if (record.DateTimeOriginal is null)
timeSpan = null; timeSpan = null;
else 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) 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 else
{ {
if (_PropertyConfiguration.IgnoreExtensions.Contains(fileHolder.ExtensionLowered)) if (_PropertyConfiguration.IgnoreExtensions.Contains(fileHolder.ExtensionLowered))
@ -225,16 +256,21 @@ public class Rename
{ directories = MetadataExtractor.ImageMetadataReader.ReadMetadata(fileHolder.FullName); } { directories = MetadataExtractor.ImageMetadataReader.ReadMetadata(fileHolder.FullName); }
catch (Exception) { continue; } catch (Exception) { continue; }
(metadataDateTimeOriginal, metadataDateTimes) = Metadata.Models.Stateless.Methods.IMetadata.GetDateTimes(fileHolder, directories); (metadataDateTimeOriginal, metadataDateTimes) = Metadata.Models.Stateless.Methods.IMetadata.GetDateTimes(fileHolder, directories);
dateTimeOriginal ??= metadataDateTimeOriginal; dateTimeOriginalByLogic = record.DateTimeOriginal is not null ? record.DateTimeOriginal : metadataDateTimeOriginal;
if (ffmpegFiles is not null && dateTimeOriginal is not null) if (record.FfmpegFilesPresent && dateTimeOriginalByLogic is not null)
minimumDateTime = dateTimeOriginal.Value; minimumDateTime = dateTimeOriginalByLogic.Value;
if (dateTimeOriginal is null) if (dateTimeOriginalByLogic is null)
timeSpan = null; timeSpan = null;
else 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) 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 else
{ {
directoryName = Path.GetFileName(fileHolder.DirectoryName); directoryName = Path.GetFileName(fileHolder.DirectoryName);
@ -251,12 +287,10 @@ public class Rename
{ {
if (dateTimeFromName is not null && isWrongYear is not null && isWrongYear.Value) if (dateTimeFromName is not null && isWrongYear is not null && isWrongYear.Value)
minimumDateTime = dateTimeFromName.Value; minimumDateTime = dateTimeFromName.Value;
else if (dateTimeOriginal is not null) else if (dateTimeOriginalByLogic is not null)
minimumDateTime = dateTimeOriginal.Value; minimumDateTime = dateTimeOriginalByLogic.Value;
else if (metadataDateTimeOriginal is not null)
minimumDateTime = metadataDateTimeOriginal.Value;
else 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) if (minimumDateTime is null)
continue; continue;
checkFileExtension = fileHolder.ExtensionLowered == jpeg ? jpg : fileHolder.ExtensionLowered; checkFileExtension = fileHolder.ExtensionLowered == jpeg ? jpg : fileHolder.ExtensionLowered;
@ -266,18 +300,19 @@ public class Rename
if (distinct.Contains(checkFile)) if (distinct.Contains(checkFile))
continue; continue;
distinct.Add(checkFile); distinct.Add(checkFile);
results.Add(new(fileHolder, directory, checkFile)); results.Add(new(fileHolder, fileHolder.DirectoryName, checkFile));
if (nefPresent) 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) 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 else
{ {
if (id is null) if (record.Id is null)
continue; continue;
id = Shared.Models.Stateless.Methods.IDirectory.GetPaddedId(length, record.Index, record.Id.Value);
checkFileExtension = fileHolder.ExtensionLowered == jpeg ? jpg : fileHolder.ExtensionLowered; checkFileExtension = fileHolder.ExtensionLowered == jpeg ? jpg : fileHolder.ExtensionLowered;
checkFile = Path.Combine(seasonDirectory, $"{id.Value}{checkFileExtension}"); checkFile = Path.Combine(seasonDirectory, $"{id}{checkFileExtension}");
if (checkFile == fileHolder.FullName) if (checkFile == fileHolder.FullName)
continue; continue;
if (File.Exists(checkFile)) if (File.Exists(checkFile))
@ -317,13 +352,16 @@ public class Rename
{ {
List<string> results = new(); List<string> results = new();
string message; string message;
bool nefPresent; bool nefPresentCheck;
bool nefPresent = false;
ProgressBar progressBar; ProgressBar progressBar;
List<Record> records = new();
const string fileSearchFilter = "*"; const string fileSearchFilter = "*";
const string directorySearchFilter = "*"; const string directorySearchFilter = "*";
List<string> distinctDirectories = new(); List<string> distinctDirectories = new();
List<(FileHolder, string)> verifiedToDoCollection = new(); List<(FileHolder, string)> verifiedToDoCollection = new();
List<(FileHolder, string, string)> toDoCollection = new(); List<(FileHolder, string, string)> toDoCollection = new();
int offset = Shared.Models.Stateless.Methods.IDirectory.GetOffset();
ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true };
List<string[]> filesCollection = Shared.Models.Stateless.Methods.IDirectory.GetFilesCollection(_PropertyConfiguration.RootDirectory, directorySearchFilter, fileSearchFilter); List<string[]> filesCollection = Shared.Models.Stateless.Methods.IDirectory.GetFilesCollection(_PropertyConfiguration.RootDirectory, directorySearchFilter, fileSearchFilter);
int count = filesCollection.Select(l => l.Length).Sum(); int count = filesCollection.Select(l => l.Length).Sum();
@ -331,28 +369,52 @@ public class Rename
{ {
if (!files.Any()) if (!files.Any())
continue; continue;
// foreach (string file in files) // foreach (string files[i] in files)
// { // {
// if (!file.EndsWith(".del")) // if (!files[i].EndsWith(".del"))
// continue; // continue;
// File.Move(file, file[..^4]); // File.Move(files[i], files[i][..^4]);
// } // }
// continue; // continue;
distinctDirectories.Clear(); distinctDirectories.Clear();
message = $") Renaming files for <{files.FirstOrDefault()}>";
progressBar = new(files.Length, message, options);
if (_AppSettings.RenameUndo) if (_AppSettings.RenameUndo)
{
message = $") Undo renaming files for <{files.FirstOrDefault()}>";
progressBar = new(files.Length, message, options);
toDoCollection.AddRange(GetRenameUndoToDoCollection(progressBar, files)); toDoCollection.AddRange(GetRenameUndoToDoCollection(progressBar, files));
}
else else
{ {
nefPresent = files.Any(l => l.EndsWith(".NEF")); message = $"{records.Count:00000}) Gathering records for files next to <{files.FirstOrDefault()}>";
if (!nefPresent) progressBar = new(files.Length, message, options);
toDoCollection.AddRange(GetToDoCollection(progressBar, files, nefPresent)); nefPresentCheck = files.Any(l => l.EndsWith(".NEF"));
if (!nefPresentCheck)
records.AddRange(GetRecords(offset + records.Count, progressBar, files));
else 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(); 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) foreach ((FileHolder fileHolder, string directory, string to) in toDoCollection)
{ {
if (distinctDirectories.Contains(directory)) if (distinctDirectories.Contains(directory))
@ -372,10 +434,10 @@ public class Rename
File.WriteAllText($"{to}.id", $"{to}{Environment.NewLine}{fileHolder.FullName}"); File.WriteAllText($"{to}.id", $"{to}{Environment.NewLine}{fileHolder.FullName}");
} }
ConsoleKey? consoleKey = null; 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++) 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; consoleKey = System.Console.ReadKey().Key;
if (consoleKey is ConsoleKey.Y or ConsoleKey.N) if (consoleKey is ConsoleKey.Y or ConsoleKey.N)
break; break;

View File

@ -3,6 +3,9 @@ namespace View_by_Distance.Shared.Models.Stateless.Methods;
public interface IDirectory public interface IDirectory
{ {
static int GetOffset() =>
1000000;
char TestStatic_GetDirectory(string fileName) => char TestStatic_GetDirectory(string fileName) =>
GetDirectory(fileName); GetDirectory(fileName);
static char GetDirectory(string fileName) => static char GetDirectory(string fileName) =>
@ -13,6 +16,11 @@ public interface IDirectory
static int GetDirectory(char directory) => static int GetDirectory(char directory) =>
directory == '-' ? 10 : int.TryParse(directory.ToString(), out int value) ? value : 11; 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) => List<string[]> TestStatic_GetFilesCollection(string directory, string directorySearchFilter, string fileSearchFilter) =>
GetFilesCollection(directory, directorySearchFilter, fileSearchFilter); GetFilesCollection(directory, directorySearchFilter, fileSearchFilter);
static List<string[]> GetFilesCollection(string directory, string directorySearchFilter, string 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) => static void MoveFiles(List<string> files, string find, string replace) =>
XDirectory.MoveFiles(files, find, 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) => (string[], List<(Models.FileHolder, string)>) TestStatic_GetToDoCollection(Properties.IPropertyConfiguration propertyConfiguration, bool copyDuplicates, bool ifCanUseId, List<string[]> filesCollection, string[] directories, Action? tick) =>
GetToDoCollection(propertyConfiguration, copyDuplicates, filesCollection, directories, tick); GetToDoCollection(propertyConfiguration, copyDuplicates, ifCanUseId, filesCollection, directories, tick);
static (string[], List<(Models.FileHolder, string)>) GetToDoCollection(Properties.IPropertyConfiguration propertyConfiguration, bool copyDuplicates, List<string[]> filesCollection, string[] directories, Action? 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, filesCollection, directories, tick); XDirectory.GetToDoCollection(propertyConfiguration, copyDuplicates, ifCanUseId, filesCollection, directories, tick);
void TestStatic_CopyOrMove(List<(Models.FileHolder, string)> toDoCollection, bool move, bool moveBack, Action? tick) => void TestStatic_CopyOrMove(List<(Models.FileHolder, string)> toDoCollection, bool move, bool moveBack, Action? tick) =>
CopyOrMove(toDoCollection, move, moveBack, tick); CopyOrMove(toDoCollection, move, moveBack, tick);

View File

@ -3,6 +3,8 @@ namespace View_by_Distance.Shared.Models.Stateless.Methods;
internal abstract partial class XDirectory internal abstract partial class XDirectory
{ {
private record SortedRecord(int? Id, Models.FileHolder FileHolder);
private static int GetCeilingAverage(List<string[]> fileCollection) private static int GetCeilingAverage(List<string[]> fileCollection)
{ {
List<int> counts = new(); 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) 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(); List<(Models.FileHolder, string)> results = new();
string checkFile; string checkFile;
@ -295,18 +341,18 @@ internal abstract partial class XDirectory
int directoryIndex; int directoryIndex;
string directoryName; string directoryName;
bool wrapped = false; bool wrapped = false;
List<int> distinctIds = new();
List<string> distinct = new(); List<string> distinct = new();
List<string> distinctDirectories = new(); List<string> distinctDirectories = new();
List<Models.FileHolder> fileHolders = GetFileHolders(filesCollection); (bool all, SortedRecord[] sortedRecords) = GetSortedRecords(filesCollection);
Models.FileHolder[] sortedFileHolders = (from l in fileHolders orderby l.LastWriteTime, l.FullName.Length descending select l).ToArray(); foreach (SortedRecord sortedRecord in sortedRecords)
foreach (Models.FileHolder fileHolder in sortedFileHolders)
{ {
tick?.Invoke(); 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; continue;
(_, directoryIndex) = IPath.GetDirectoryNameAndIndex(propertyConfiguration.ResultAllInOneSubdirectoryLength, fileHolder.NameWithoutExtension); (_, directoryIndex) = IPath.GetDirectoryNameAndIndex(propertyConfiguration.ResultAllInOneSubdirectoryLength, sortedRecord.FileHolder.NameWithoutExtension);
directoryName = Path.GetFileName(fileHolder.DirectoryName); directoryName = Path.GetFileName(sortedRecord.FileHolder.DirectoryName);
if (directoryName.Length < propertyConfiguration.ResultAllInOneSubdirectoryLength + 3 || !fileHolder.Name.StartsWith(directoryName)) if (directoryName.Length < propertyConfiguration.ResultAllInOneSubdirectoryLength + 3 || !sortedRecord.FileHolder.Name.StartsWith(directoryName))
{ {
if (wrapped) if (wrapped)
continue; continue;
@ -318,22 +364,38 @@ internal abstract partial class XDirectory
wrapped = true; wrapped = true;
directory = Path.Combine(directories[directoryIndex], directoryName); directory = Path.Combine(directories[directoryIndex], directoryName);
} }
checkFile = Path.Combine(directory, $"{fileHolder.NameWithoutExtension}{fileHolder.ExtensionLowered}"); if (all)
if (distinct.Contains(checkFile))
{ {
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) if (!copyDuplicates)
continue; continue;
for (int i = 1; i < int.MaxValue; i++) for (int i = 1; i < int.MaxValue; i++)
{ {
fileInfo = new(checkFile); fileInfo = new(checkFile);
if (!fileInfo.Exists || fileHolder.Length == fileInfo.Length && fileHolder.LastWriteTime == fileInfo.LastWriteTime) if (!fileInfo.Exists || sortedRecord.FileHolder.Length == fileInfo.Length && sortedRecord.FileHolder.LastWriteTime == fileInfo.LastWriteTime)
checkFile = Path.Combine(directory, $"{fileHolder.NameWithoutExtension}.{i}dup{fileHolder.ExtensionLowered}"); checkFile = Path.Combine(directory, $"{sortedRecord.FileHolder.NameWithoutExtension}.{i}dup{sortedRecord.FileHolder.ExtensionLowered}");
else 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)) if (distinct.Contains(checkFile))
continue; continue;
distinct.Add(checkFile); distinct.Add(checkFile);
results.Add(new(fileHolder, checkFile)); results.Add(new(sortedRecord.FileHolder, checkFile));
if (!distinctDirectories.Contains(directory)) if (!distinctDirectories.Contains(directory))
distinctDirectories.Add(directory); distinctDirectories.Add(directory);
break; break;
@ -341,7 +403,9 @@ internal abstract partial class XDirectory
continue; continue;
} }
distinct.Add(checkFile); 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)) if (!distinctDirectories.Contains(directory))
distinctDirectories.Add(directory); distinctDirectories.Add(directory);
} }