using CliWrap; using Microsoft.Extensions.Configuration; using Phares.Shared; using Serilog; using ShellProgressBar; using System.Text.Json; using View_by_Distance.Property.Models; using View_by_Distance.Rename.Models; using View_by_Distance.Shared.Models; using View_by_Distance.Shared.Models.Methods; using View_by_Distance.Shared.Models.Stateless.Methods; namespace View_by_Distance.Rename; public class Rename { private readonly AppSettings _AppSettings; private readonly string _WorkingDirectory; private readonly IsEnvironment _IsEnvironment; private readonly Models.Configuration _Configuration; private readonly IConfigurationRoot _ConfigurationRoot; private readonly Property.Models.Configuration _PropertyConfiguration; public Rename(List args, IsEnvironment isEnvironment, IConfigurationRoot configurationRoot, AppSettings appSettings, string workingDirectory, bool isSilent, IConsole console) { if (isSilent) { } if (console is null) { } bool any = false; _AppSettings = appSettings; _IsEnvironment = isEnvironment; long ticks = DateTime.Now.Ticks; _WorkingDirectory = workingDirectory; _ConfigurationRoot = configurationRoot; ILogger? log = Log.ForContext(); Dictionary>> fileSizeToCollection = new(); Property.Models.Configuration propertyConfiguration = Property.Models.Binder.Configuration.Get(isEnvironment, configurationRoot); Models.Configuration configuration = Models.Binder.Configuration.Get(isEnvironment, configurationRoot, propertyConfiguration); _PropertyConfiguration = propertyConfiguration; _Configuration = configuration; propertyConfiguration.Update(); string? comparePathRoot = Path.GetDirectoryName(appSettings.ComparePathsFile); if (comparePathRoot is null || comparePathRoot == propertyConfiguration.RootDirectory) throw new Exception("Nested isn't allowed!"); log.Information(propertyConfiguration.RootDirectory); Verify(); string json = File.ReadAllText(appSettings.ComparePathsFile); MatchNginx[]? matchNginxCollection = JsonSerializer.Deserialize(json); if (matchNginxCollection is null) throw new NullReferenceException(nameof(matchNginxCollection)); if (matchNginxCollection.Any()) { bool deleted; for (int i = 1; i < 5; i++) { deleted = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(matchNginxCollection.First().ConvertedPath); if (deleted && !any) any = true; } } if (!any) { if (matchNginxCollection.Length == 0 && matchNginxCollection[0].ConvertedPath.Contains("~~~")) MoveMatches(matchNginxCollection[0]); else if (matchNginxCollection.All(l => l.Name.StartsWith("#")) || matchNginxCollection.All(l => l.Name.StartsWith(" #")) || matchNginxCollection.All(l => l.Name.StartsWith("=20")) || matchNginxCollection.All(l => l.Name.StartsWith("#20"))) Rename2000(log, matchNginxCollection); else if (matchNginxCollection.All(l => l.Name.Length > 4) && matchNginxCollection.All(l => l.Name[0..3] is "198" or "199" or "200" or "201")) RenameByDateTaken(log, matchNginxCollection); else if (matchNginxCollection.Any()) { List lines = RenameFilesInDirectories(log, matchNginxCollection); File.WriteAllLines($"D:/Tmp/Phares/{DateTime.Now.Ticks}.tsv", lines); if (comparePathRoot != Path.GetPathRoot(matchNginxCollection[0].ConvertedPath)) _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(comparePathRoot); } } } private void Verify() { if (_AppSettings is null) { } if (_IsEnvironment is null) { } if (_Configuration is null) { } if (_ConfigurationRoot is null) { } if (_WorkingDirectory is null) { } if (_PropertyConfiguration is null) { } } private static void MoveMatches(MatchNginx matchNginx) { string moveDirectory; string checkDirectory; string compareDirectory = "D:/"; int length = matchNginx.ConvertedPath.Length; string[] directories = Directory.GetDirectories(matchNginx.ConvertedPath, "*", SearchOption.TopDirectoryOnly); foreach (string directory in directories) { if (!string.IsNullOrEmpty(directory)) continue; checkDirectory = string.Concat(compareDirectory, directory[length..]); if (!Directory.Exists(checkDirectory)) continue; moveDirectory = string.Concat(compareDirectory[..^1], directory[length..]); Directory.Move(checkDirectory, moveDirectory); } } private static List<(FileHolder, string)> GetRenameUndoToDoCollection(ProgressBar progressBar, string[] files) { List<(FileHolder, string)> results = new(); string[] lines; string fileName; FileHolder fileHolder; List distinct = new(); foreach (string file in files) { progressBar.Tick(); fileName = Path.GetFileName(file); if (!fileName.EndsWith(".id")) continue; lines = File.ReadAllLines(file); if (lines.Length < 2) continue; if (distinct.Contains(lines[1])) continue; distinct.Add(lines[1]); fileHolder = new(lines[0]); results.Add(new(fileHolder, lines[1])); } return results; } private List<(FileHolder, string)> GetToDoCollection(ProgressBar progressBar, string[] files) { List<(FileHolder, string)> results = new(); int? id; string fileName; string? message; string checkFile; string? directory; TimeSpan timeSpan; DateTime? dateTime; DateTime?[] dateTimes; FileHolder fileHolder; string[]? ffmpegFiles; bool isIgnoreExtension; string checkFileExtension; DateTime? minimumDateTime; const string jpg = ".jpg"; const string jpeg = ".jpeg"; List distinct = new(); bool isValidImageFormatExtension; bool nameWithoutExtensionIsIdFormat; IReadOnlyList directories; foreach (string file in files) { progressBar.Tick(); fileHolder = new(file); if (file.EndsWith(".rename")) { directory = Path.GetDirectoryName(file); if (string.IsNullOrEmpty(directory)) continue; checkFile = Path.Combine(directory, $"rename_{Path.GetFileName(file[..^7])}"); if (File.Exists(checkFile)) continue; if (distinct.Contains(checkFile)) continue; distinct.Add(checkFile); results.Add(new(fileHolder, checkFile)); continue; } if (file.EndsWith(".jpg.del")) { checkFile = file[..^4]; if (File.Exists(checkFile)) continue; if (distinct.Contains(checkFile)) continue; distinct.Add(checkFile); results.Add(new(fileHolder, checkFile)); continue; } if (fileHolder.ExtensionLowered == ".id" || fileHolder.ExtensionLowered == ".lsv" || fileHolder.DirectoryName is null) continue; if (files.Contains($"{fileHolder.FullName}.id")) continue; nameWithoutExtensionIsIdFormat = Shared.Models.Stateless.Methods.IProperty.NameWithoutExtensionIsIdFormat(fileHolder); if (nameWithoutExtensionIsIdFormat) continue; isValidImageFormatExtension = _PropertyConfiguration.ValidImageFormatExtensions.Contains(fileHolder.ExtensionLowered); isIgnoreExtension = isValidImageFormatExtension && _PropertyConfiguration.IgnoreExtensions.Contains(fileHolder.ExtensionLowered); if (!isIgnoreExtension && isValidImageFormatExtension) ffmpegFiles = null; else { try { directories = MetadataExtractor.ImageMetadataReader.ReadMetadata(file); } catch (Exception) { continue; } CommandTask 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" }) .WithWorkingDirectory(fileHolder.DirectoryName) .ExecuteAsync(); result.Task.Wait(); ffmpegFiles = Directory.GetFiles(fileHolder.DirectoryName, $"{fileHolder.Name}-*.jpg", SearchOption.TopDirectoryOnly); if (!ffmpegFiles.Any()) continue; fileHolder = new(ffmpegFiles.First()); if (!fileHolder.Name.EndsWith("-0001.jpg")) throw new Exception(); isValidImageFormatExtension = _PropertyConfiguration.ValidImageFormatExtensions.Contains(fileHolder.ExtensionLowered); isIgnoreExtension = isValidImageFormatExtension && _PropertyConfiguration.IgnoreExtensions.Contains(fileHolder.ExtensionLowered); if (isIgnoreExtension || !isValidImageFormatExtension) continue; if (fileHolder.DirectoryName is null) continue; } if (!isIgnoreExtension && isValidImageFormatExtension) { if (fileHolder.ExtensionLowered == jpeg) { if (File.Exists($"{fileHolder.FullName}.id")) { checkFile = Path.Combine(fileHolder.DirectoryName, $"{fileHolder.NameWithoutExtension}{jpg}.id"); if (File.Exists(checkFile)) continue; if (distinct.Contains(checkFile)) continue; distinct.Add(checkFile); results.Add(new(new($"{fileHolder.FullName}.id"), checkFile)); } checkFile = Path.Combine(fileHolder.DirectoryName, $"{fileHolder.NameWithoutExtension}{jpg}"); if (File.Exists(checkFile)) continue; if (distinct.Contains(checkFile)) continue; distinct.Add(checkFile); results.Add(new(fileHolder, checkFile)); if (File.Exists(checkFile)) continue; File.Move(fileHolder.FullName, checkFile); fileHolder = new(checkFile); if (fileHolder.DirectoryName is null) continue; } } (dateTimes, id, message) = Shared.Models.Stateless.Methods.IProperty.Get(fileHolder, isIgnoreExtension, isValidImageFormatExtension); minimumDateTime = dateTimes.Min(); if (minimumDateTime is null || !isIgnoreExtension && isValidImageFormatExtension) { dateTime = Shared.Models.Stateless.Methods.IProperty.GetDateTimeFromName(fileHolder); if (dateTime is null || minimumDateTime is null) timeSpan = new TimeSpan(0); else timeSpan = new(Math.Abs(minimumDateTime.Value.Ticks - dateTime.Value.Ticks)); } else { fileName = Path.GetFileName(fileHolder.DirectoryName); if (fileName.Length < 4 || !int.TryParse(fileName[..4], out int year)) year = minimumDateTime.Value.Year; if (_PropertyConfiguration.IgnoreExtensions.Contains(fileHolder.ExtensionLowered)) continue; try { directories = MetadataExtractor.ImageMetadataReader.ReadMetadata(file); } catch (Exception) { continue; } dateTime = Metadata.Models.Stateless.Methods.IMetadata.GetMinimumDateTime(dateTimes, year, directories); timeSpan = new TimeSpan(int.MaxValue); } if (dateTime is not null && string.IsNullOrEmpty(_AppSettings.CopyTo) && timeSpan.TotalMinutes > _AppSettings.MaxMinutesDelta) { checkFileExtension = fileHolder.ExtensionLowered == jpeg ? jpg : fileHolder.ExtensionLowered; checkFile = Path.Combine(fileHolder.DirectoryName, $"{dateTime.Value:yyyy-MM-dd}.{dateTime.Value.Ticks}.{fileHolder.Length}{checkFileExtension}"); if (checkFile == fileHolder.FullName) continue; if (distinct.Contains(checkFile)) continue; distinct.Add(checkFile); results.Add(new(fileHolder, checkFile)); if (ffmpegFiles is not null) { foreach (string ffmpegFile in ffmpegFiles) File.Delete(ffmpegFile); } continue; } if (id is null) continue; if (ffmpegFiles is not null) { foreach (string ffmpegFile in ffmpegFiles) File.Delete(ffmpegFile); fileHolder = new(file); if (fileHolder.DirectoryName is null) continue; } checkFileExtension = fileHolder.ExtensionLowered == jpeg ? jpg : fileHolder.ExtensionLowered; checkFile = Path.Combine(fileHolder.DirectoryName, $"{id.Value}{checkFileExtension}"); if (checkFile == fileHolder.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(fileHolder, checkFile)); } return results; } private static void Rename2000(ILogger log, MatchNginx[] matchNginxCollection) { string name; string check; string? directoryName; log.Information("Enter a suffix if any"); string? suffix = System.Console.ReadLine(); foreach (MatchNginx matchNginx in matchNginxCollection) { name = matchNginx.Name.Trim(); if (name.Length < 1 || (!name.StartsWith("zzz =20") && !name.StartsWith("=20") && !name.StartsWith("#20"))) continue; directoryName = Path.GetDirectoryName(matchNginx.ConvertedPath); if (directoryName is null) continue; if (name.StartsWith("=20") || name.StartsWith("#20")) check = Path.Combine(directoryName, $"{name[1..]}{suffix}"); else check = Path.Combine(directoryName, $"zzz {name[5..]}"); if (Directory.Exists(check) || File.Exists(check)) continue; if (!Directory.Exists(matchNginx.ConvertedPath)) File.Move(matchNginx.ConvertedPath, check); else Directory.Move(matchNginx.ConvertedPath, check); } } private static List GetAllFiles(MatchNginx[] matchNginxCollection) { List allFiles = new(); string[] files; string directoryName; ReadOnlySpan span; foreach (MatchNginx matchNginx in matchNginxCollection) { if (matchNginx.ConvertedPath.Length < 6) continue; directoryName = Path.GetFileName(matchNginx.ConvertedPath); if (matchNginx.ConvertedPath.Contains("!---")) span = "0"; else span = matchNginx.ConvertedPath.AsSpan(matchNginx.ConvertedPath.Length - 5, 3); if (directoryName.Length == 1 && int.TryParse(span, out int age)) continue; if (File.Exists(matchNginx.ConvertedPath)) continue; if (!Directory.Exists(matchNginx.ConvertedPath)) continue; files = Directory.GetFiles(matchNginx.ConvertedPath, "*", SearchOption.AllDirectories); if (files.All(l => l.EndsWith(".id"))) { foreach (string file in files) File.Delete(file); continue; } allFiles.AddRange(files); } return allFiles; } private static void CreateDirectories(List directories) { foreach (string directory in directories) { if (Directory.Exists(directory)) continue; _ = Directory.CreateDirectory(directory); } } private List CopyInstead(ILogger log, List<(FileHolder, string)> verifiedToDoCollection) { List results = new(); string copyTo; string? directory; ConsoleKey? consoleKey = null; List distinctDirectories = new(); List<(FileHolder, string)> copyCollection = new(); foreach ((FileHolder fileHolder, string to) in verifiedToDoCollection) { copyTo = $"{_AppSettings.CopyTo}{to[1..]}"; directory = Path.GetDirectoryName(copyTo); if (directory is null) continue; copyCollection.Add(new(fileHolder, copyTo)); if (distinctDirectories.Contains(directory)) continue; distinctDirectories.Add(directory); } CreateDirectories(distinctDirectories); log.Information($"Ready to Copy {verifiedToDoCollection.Count} file(s)?"); for (int y = 0; y < int.MaxValue; y++) { log.Information("Press \"Y\" key to copy file(s), \"N\" key to log file(s) or close console to not copy files"); consoleKey = System.Console.ReadKey().Key; if (consoleKey is ConsoleKey.Y or ConsoleKey.N) break; } log.Information(". . ."); if (consoleKey is null || consoleKey.Value != ConsoleKey.Y) log.Information("Nothing Copied!"); else { foreach ((FileHolder fileHolder, string to) in verifiedToDoCollection) { results.Add(fileHolder.NameWithoutExtension); File.Copy(fileHolder.FullName, to); } log.Information("Done Copying"); } return results; } private static List Move(ILogger log, List<(FileHolder, string)> verifiedToDoCollection) { List results = new(); ConsoleKey? consoleKey = null; log.Information($"Ready to Move {verifiedToDoCollection.Count} file(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"); consoleKey = System.Console.ReadKey().Key; if (consoleKey is ConsoleKey.Y or ConsoleKey.N) break; } log.Information(". . ."); if (consoleKey is null || consoleKey.Value != ConsoleKey.Y) log.Information("Nothing moved!"); else { foreach ((FileHolder fileHolder, string to) in verifiedToDoCollection) { results.Add(fileHolder.NameWithoutExtension); try { File.Move(fileHolder.FullName, to); } catch (Exception) { } } log.Information("Done Moving"); } return results; } private List RenameFilesInDirectories(ILogger log, MatchNginx[] matchNginxCollection) { List results = new(); string[] files; string message; List allFiles; ProgressBar progressBar; List<(FileHolder, string)> toDoCollection; allFiles = GetAllFiles(matchNginxCollection); List<(FileHolder, string)> verifiedToDoCollection; ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; for (int i = 1; i < 3; i++) { if (!allFiles.Any()) continue; message = $"{i}) Renaming files"; if (_AppSettings.RenameUndo && i == 1) continue; files = i == 2 ? allFiles.ToArray() : (from l in allFiles where l.Contains("Rename", StringComparison.OrdinalIgnoreCase) select l).ToArray(); progressBar = new(files.Length, message, options); if (!files.Any()) continue; if (!_AppSettings.RenameUndo) toDoCollection = GetToDoCollection(progressBar, files); else toDoCollection = GetRenameUndoToDoCollection(progressBar, files); verifiedToDoCollection = new(); foreach ((FileHolder fileHolder, string to) in toDoCollection) { if (File.Exists(to)) continue; verifiedToDoCollection.Add(new(fileHolder, to)); if (string.IsNullOrEmpty(_AppSettings.CopyTo)) File.WriteAllText($"{to}.id", $"{to}{Environment.NewLine}{fileHolder.FullName}"); } if (!verifiedToDoCollection.Any()) continue; if (string.IsNullOrEmpty(_AppSettings.CopyTo)) results.AddRange(Move(log, verifiedToDoCollection)); else results.AddRange(CopyInstead(log, verifiedToDoCollection)); allFiles = GetAllFiles(matchNginxCollection); progressBar.Dispose(); } return results; } private void RenameByDateTakenB(MatchNginx[] matchNginxCollection, string aPropertySingletonDirectory, string[] jsonFiles) { string json; char directory; string[] files; string fileName; string extension; string[] matches; string checkFile; DateTime dateTime; string[] segments; string? checkFileName; string checkDirectory; string? subdirectory; Shared.Models.Property? property; foreach (MatchNginx matchNginx in matchNginxCollection) { if (File.Exists(matchNginx.ConvertedPath)) continue; if (!Directory.Exists(matchNginx.ConvertedPath)) continue; subdirectory = Path.GetDirectoryName(matchNginx.ConvertedPath); if (string.IsNullOrEmpty(subdirectory)) continue; files = Directory.GetFiles(matchNginx.ConvertedPath, "*", SearchOption.AllDirectories); for (int i = 65; i < 91; i++) { checkDirectory = Path.Combine(subdirectory, nameof(Rename), ((char)i).ToString()); if (!Directory.Exists(checkDirectory)) _ = Directory.CreateDirectory(checkDirectory); } foreach (string file in files) { fileName = Path.GetFileName(file); segments = fileName.Split('.'); extension = Path.GetExtension(file); directory = IDirectory.GetDirectory(fileName); checkFileName = $"{segments.First()}{Path.GetExtension(Path.GetFileNameWithoutExtension(file))}.json"; checkDirectory = Path.Combine(aPropertySingletonDirectory, _PropertyConfiguration.ResultAllInOne, directory.ToString()); checkFile = Path.Combine(checkDirectory, checkFileName); matches = jsonFiles.Where(l => l == checkFile).ToArray(); if (!matches.Any()) { matches = jsonFiles.Where(l => l.EndsWith(checkFileName)).ToArray(); if (!matches.Any()) continue; } json = File.ReadAllText(matches.First()); property = JsonSerializer.Deserialize(json); if (property is null) continue; checkFileName = null; dateTime = property.DateTimeOriginal is not null ? property.DateTimeOriginal.Value : Shared.Models.Stateless.Methods.IProperty.GetMinimumDateTime(property); for (int i = 65; i < 91; i++) { if (checkFileName is not null && !File.Exists(checkFileName)) break; checkFileName = Path.Combine(subdirectory, nameof(Rename), ((char)i).ToString(), $"{dateTime.Ticks}{extension}"); } if (checkFileName is null || File.Exists(checkFileName)) continue; File.Move(file, checkFileName); } _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(subdirectory); } } private void RenameByDateTaken(ILogger log, MatchNginx[] matchNginxCollection) { string aResultsFullGroupDirectory = Property.Models.Stateless.IResult.GetResultsFullGroupDirectory( _PropertyConfiguration, nameof(A_Property), string.Empty, includeResizeGroup: false, includeModel: false, includePredictorModel: false); string aPropertySingletonDirectory = Path.GetFullPath(Path.Combine(aResultsFullGroupDirectory, "{}")); string[] jsonFiles = !Directory.Exists(aPropertySingletonDirectory) ? Array.Empty() : Directory.GetFiles(aPropertySingletonDirectory, "*.json", SearchOption.AllDirectories); if (!jsonFiles.Any()) log.Information($"No json file(s) found! Check directory <{aPropertySingletonDirectory}>"); else RenameByDateTakenB(matchNginxCollection, aPropertySingletonDirectory, jsonFiles); } }