From 0bce2bf22b8e81ce185795b45dec7b2db485756b Mon Sep 17 00:00:00 2001 From: Mike Phares Date: Tue, 4 Jul 2023 13:05:54 -0700 Subject: [PATCH] Rename: ForceIdName DeleteEmptyDirectories: UnauthorizedAccessException --- Rename/Models/AppSettings.cs | 31 +- Rename/Models/Binder/AppSettings.cs | 18 +- Rename/Rename.cs | 358 ++++------------------- Rename/appsettings.json | 1 + Shared/Models/Stateless/Methods/XPath.cs | 8 +- 5 files changed, 84 insertions(+), 332 deletions(-) diff --git a/Rename/Models/AppSettings.cs b/Rename/Models/AppSettings.cs index 7f3a7d3..a3df1aa 100644 --- a/Rename/Models/AppSettings.cs +++ b/Rename/Models/AppSettings.cs @@ -1,34 +1,15 @@ using System.Text.Json; -using System.Text.Json.Serialization; namespace View_by_Distance.Rename.Models; -public class AppSettings +public record AppSettings(string Company, + bool ForceIdName, + int MaxDegreeOfParallelism, + int MaxMinutesDelta, + bool RenameUndo, + string WorkingDirectoryName) { - public string Company { init; get; } - public string ComparePathsFile { init; get; } - public int MaxDegreeOfParallelism { init; get; } - public int MaxMinutesDelta { init; get; } - public bool RenameUndo { init; get; } - public string WorkingDirectoryName { init; get; } - - [JsonConstructor] - public AppSettings(string company, - string comparePathsFile, - int maxDegreeOfParallelism, - int maxMinutesDelta, - bool renameUndo, - string workingDirectoryName) - { - Company = company; - ComparePathsFile = comparePathsFile; - MaxDegreeOfParallelism = maxDegreeOfParallelism; - MaxMinutesDelta = maxMinutesDelta; - RenameUndo = renameUndo; - WorkingDirectoryName = workingDirectoryName; - } - public override string ToString() { string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true }); diff --git a/Rename/Models/Binder/AppSettings.cs b/Rename/Models/Binder/AppSettings.cs index 8237bd6..02d4b0e 100644 --- a/Rename/Models/Binder/AppSettings.cs +++ b/Rename/Models/Binder/AppSettings.cs @@ -6,16 +6,12 @@ namespace View_by_Distance.Rename.Models.Binder; public class AppSettings { -#nullable disable - - public string Company { get; set; } - public string ComparePathsFile { get; set; } + public string? Company { get; set; } + public bool? ForceIdName { get; set; } public int? MaxDegreeOfParallelism { get; set; } public int? MaxMinutesDelta { get; set; } public bool? RenameUndo { get; set; } - public string WorkingDirectoryName { get; set; } - -#nullable restore + public string? WorkingDirectoryName { get; set; } public override string ToString() { @@ -26,15 +22,21 @@ public class AppSettings private static Models.AppSettings Get(AppSettings? appSettings) { Models.AppSettings result; + if (appSettings?.Company is null) + throw new NullReferenceException(nameof(appSettings.Company)); + if (appSettings?.ForceIdName is null) + throw new NullReferenceException(nameof(appSettings.ForceIdName)); if (appSettings?.MaxDegreeOfParallelism is null) throw new NullReferenceException(nameof(appSettings.MaxDegreeOfParallelism)); if (appSettings?.MaxMinutesDelta is null) throw new NullReferenceException(nameof(appSettings.MaxMinutesDelta)); if (appSettings?.RenameUndo is null) throw new NullReferenceException(nameof(appSettings.RenameUndo)); + if (appSettings?.WorkingDirectoryName is null) + throw new NullReferenceException(nameof(appSettings.WorkingDirectoryName)); result = new( appSettings.Company, - appSettings.ComparePathsFile, + appSettings.ForceIdName.Value, appSettings.MaxDegreeOfParallelism.Value, appSettings.MaxMinutesDelta.Value, appSettings.RenameUndo.Value, diff --git a/Rename/Rename.cs b/Rename/Rename.cs index d50aaed..b220189 100644 --- a/Rename/Rename.cs +++ b/Rename/Rename.cs @@ -3,8 +3,6 @@ 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; @@ -16,8 +14,8 @@ public class Rename private readonly AppSettings _AppSettings; private readonly string _WorkingDirectory; + private readonly Configuration _Configuration; private readonly IsEnvironment _IsEnvironment; - private readonly Models.Configuration _Configuration; private readonly IConfigurationRoot _ConfigurationRoot; private readonly Property.Models.Configuration _PropertyConfiguration; @@ -26,92 +24,41 @@ public class Rename if (isSilent) { } if (console is null) - { } - bool any = false; + throw new NullReferenceException(nameof(console)); _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); + 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(); - log.Information(appSettings.ComparePathsFile); - string json = File.ReadAllText(appSettings.ComparePathsFile); - MatchNginx[]? matchNginxCollection = JsonSerializer.Deserialize(json); - if (matchNginxCollection is null) - throw new NullReferenceException(nameof(matchNginxCollection)); - if (matchNginxCollection.Any()) + List lines = RenameFilesInDirectories(log); + if (lines.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()) - { - log.Information(matchNginxCollection.First().ConvertedPath); - 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); - } + File.WriteAllLines($"D:/Tmp/Phares/{DateTime.Now.Ticks}.tsv", lines); + _ = Shared.Models.Stateless.Methods.IPath.DeleteEmptyDirectories(propertyConfiguration.RootDirectory); } } private void Verify() { if (_AppSettings is null) - { } + throw new NullReferenceException(nameof(_AppSettings)); if (_IsEnvironment is null) - { } + throw new NullReferenceException(nameof(_IsEnvironment)); if (_Configuration is null) - { } + throw new NullReferenceException(nameof(_Configuration)); if (_ConfigurationRoot is null) - { } + throw new NullReferenceException(nameof(_ConfigurationRoot)); if (_WorkingDirectory is null) - { } + throw new NullReferenceException(nameof(_WorkingDirectory)); 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); - } + throw new NullReferenceException(nameof(_PropertyConfiguration)); } private static List<(FileHolder, string, string)> GetRenameUndoToDoCollection(ProgressBar progressBar, string[] files) @@ -173,7 +120,6 @@ public class Rename bool isValidImageFormatExtension; DateTime? metadataDateTimeOriginal; bool nameWithoutExtensionIsIdFormat; - DateTime? metadataMinimumDateTime = null; IReadOnlyList directories; foreach (string file in files) { @@ -184,36 +130,6 @@ public class Rename directory = Path.GetDirectoryName(file); if (string.IsNullOrEmpty(directory)) continue; - if (file.EndsWith(".rename")) - { - 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, directory, checkFile)); - if (nefPresent) - results.Add(new(new($"{fileHolder.FullName[..^4]}.tif"), directory, $"{checkFile[..^4]}.tif")); - if (nefPresent) - results.Add(new(new($"{fileHolder.FullName[..^4]}.nef"), directory, $"{checkFile[..^4]}.nef")); - 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, directory, checkFile)); - if (nefPresent) - results.Add(new(new($"{fileHolder.FullName[..^4]}.tif"), directory, $"{checkFile[..^4]}.tif")); - if (nefPresent) - results.Add(new(new($"{fileHolder.FullName[..^4]}.nef"), directory, $"{checkFile[..^4]}.nef")); - continue; - } if (fileHolder.ExtensionLowered == ".id" || fileHolder.ExtensionLowered == ".lsv" || fileHolder.DirectoryName is null) continue; if (files.Contains($"{fileHolder.FullName}.id")) @@ -299,7 +215,9 @@ public class Rename timeSpan = null; else timeSpan = new(Math.Abs(minimumDateTime.Value.Ticks - dateTimeOriginal.Value.Ticks)); - if (timeSpan is null || timeSpan.Value.TotalMinutes > _AppSettings.MaxMinutesDelta) + if (timeSpan is not null && timeSpan.Value.TotalMinutes < _AppSettings.MaxMinutesDelta) + (metadataDateTimeOriginal, metadataDateTimes) = (null, Array.Empty()); + else { if (_PropertyConfiguration.IgnoreExtensions.Contains(fileHolder.ExtensionLowered)) continue; @@ -316,7 +234,7 @@ public class Rename timeSpan = new(Math.Abs(minimumDateTime.Value.Ticks - dateTimeOriginal.Value.Ticks)); } if (timeSpan is null || timeSpan.Value.TotalMinutes > _AppSettings.MaxMinutesDelta) - (isWrongYear, seasonDirectory) = (null, null); + (isWrongYear, seasonDirectory) = (null, !_AppSettings.ForceIdName ? null : Path.Combine(fileHolder.DirectoryName, "Unknown")); else { directoryName = Path.GetFileName(fileHolder.DirectoryName); @@ -335,8 +253,10 @@ public class Rename minimumDateTime = dateTimeFromName.Value; else if (dateTimeOriginal is not null) minimumDateTime = dateTimeOriginal.Value; + else if (metadataDateTimeOriginal is not null) + minimumDateTime = metadataDateTimeOriginal.Value; else - minimumDateTime = new DateTime?[] { dateTimeOriginal, metadataMinimumDateTime }.Min(); + minimumDateTime = new DateTime?[] { 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; @@ -379,141 +299,50 @@ public class Rename 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 static List Move(ILogger log, List<(FileHolder, string)> verifiedToDoCollection) + private static List Move(ProgressBar progressBar, 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++) + foreach ((FileHolder fileHolder, string to) in verifiedToDoCollection) { - 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"); + progressBar.Tick(); + results.Add(fileHolder.NameWithoutExtension); + try + { File.Move(fileHolder.FullName, to); } + catch (Exception) { } } return results; } - private List RenameFilesInDirectories(ILogger log, MatchNginx[] matchNginxCollection) + private List RenameFilesInDirectories(ILogger log) { List results = new(); - string[] files; - string message; bool nefPresent; - List allFiles; + ConsoleKey? consoleKey; ProgressBar progressBar; + const string fileSearchFilter = "*"; + string message = ") Renaming files"; + const string directorySearchFilter = "*"; List distinctDirectories = new(); - allFiles = GetAllFiles(matchNginxCollection); List<(FileHolder, string, string)> toDoCollection; List<(FileHolder, string)> verifiedToDoCollection; ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true }; - for (int i = 1; i < 3; i++) + List filesCollection = Shared.Models.Stateless.Methods.IDirectory.GetFilesCollection(_PropertyConfiguration.RootDirectory, directorySearchFilter, fileSearchFilter); + int count = filesCollection.Select(l => l.Length).Sum(); + foreach (string[] files in filesCollection) { - distinctDirectories.Clear(); - 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; + distinctDirectories.Clear(); + progressBar = new(count, message, options); if (_AppSettings.RenameUndo) toDoCollection = GetRenameUndoToDoCollection(progressBar, files); else { nefPresent = files.Any(l => l.EndsWith(".NEF")); - if (nefPresent) - files = (from l in files where l.EndsWith(".JPG") select l).ToArray(); - if (!files.Any()) - continue; - toDoCollection = GetToDoCollection(progressBar, files, nefPresent); + if (!nefPresent) + toDoCollection = GetToDoCollection(progressBar, files, nefPresent); + else + toDoCollection = GetToDoCollection(progressBar, (from l in files where l.EndsWith(".JPG") select l).ToArray(), nefPresent); } progressBar.Dispose(); verifiedToDoCollection = new(); @@ -537,94 +366,27 @@ public class Rename } if (!verifiedToDoCollection.Any()) continue; - results.AddRange(Move(log, verifiedToDoCollection)); - allFiles = GetAllFiles(matchNginxCollection); + 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 + { + progressBar = new(count, message, options); + results.AddRange(Move(progressBar, verifiedToDoCollection)); + progressBar.Dispose(); + log.Information("Done Moving"); + } } return results; } - private void RenameByDateTakenB(MatchNginx[] matchNginxCollection, string aPropertySingletonDirectory, string[] jsonFiles) - { - string json; - string[] files; - string fileName; - string checkFile; - string extension; - string[] matches; - DateTime dateTime; - string[] segments; - string directoryName; - string? subdirectory; - string checkDirectory; - string? checkFileName; - 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); - (directoryName, _) = Shared.Models.Stateless.Methods.IPath.GetDirectoryNameAndIndex(_Configuration.PropertyConfiguration.ResultAllInOneSubdirectoryLength, fileName); - checkFileName = $"{segments.First()}{Path.GetExtension(Path.GetFileNameWithoutExtension(file))}.json"; - checkDirectory = Path.Combine(aPropertySingletonDirectory, _PropertyConfiguration.ResultAllInOne, directoryName); - 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 directoryName <{aPropertySingletonDirectory}>"); - else - RenameByDateTakenB(matchNginxCollection, aPropertySingletonDirectory, jsonFiles); - } - } \ No newline at end of file diff --git a/Rename/appsettings.json b/Rename/appsettings.json index 164b0e9..88a5ebc 100644 --- a/Rename/appsettings.json +++ b/Rename/appsettings.json @@ -1,6 +1,7 @@ { "ComparePathsFile": "", "Company": "Mike Phares", + "ForceIdName": false, "Linux": {}, "Logging": { "LogLevel": { diff --git a/Shared/Models/Stateless/Methods/XPath.cs b/Shared/Models/Stateless/Methods/XPath.cs index 72e94de..170a5c8 100644 --- a/Shared/Models/Stateless/Methods/XPath.cs +++ b/Shared/Models/Stateless/Methods/XPath.cs @@ -49,7 +49,13 @@ internal abstract class XPath if (directories.Length == 0 && files.Length == 0) { deletedDirectories.Add(rootDirectory); - Directory.Delete(rootDirectory); + try + { Directory.Delete(rootDirectory); } + catch (UnauthorizedAccessException) + { + new DirectoryInfo(rootDirectory).Attributes = FileAttributes.Normal; + Directory.Delete(rootDirectory); + } } else {