diff --git a/UniversalExtensions.cs b/UniversalExtensions.cs index 585f916..c75206e 100644 --- a/UniversalExtensions.cs +++ b/UniversalExtensions.cs @@ -1,6 +1,470 @@ -namespace Xorog.UniversalExtensions; +using System.Diagnostics; +using System.Net; +using System.Security.Cryptography; -public class UniversalExtensions +namespace Xorog.UniversalExtensions; + +public static class UniversalExtensions { + /// + /// Remove unsupported characters from string to generate a valid filename + /// + /// The string with potentionally unwanted characters + /// The character the unwanted characters get replaced with (default: _) + /// A valid filename + public static string MakeValidFileName(this string name, char replace_char = '_') + { + string invalidChars = System.Text.RegularExpressions.Regex.Escape(new string(System.IO.Path.GetInvalidFileNameChars())); + string invalidRegStr = string.Format(@"([{0}]*\.+$)|([{0}]+)", invalidChars); + + return System.Text.RegularExpressions.Regex.Replace(name, invalidRegStr, replace_char.ToString()).Replace('&', replace_char); + } + + + + /// + /// Generate an ASCII Progressbar + /// + /// The current progress + /// The maximum progress + /// How long the ASCII Progressbar should be (default: 44) + /// What character the filled part should be (default: ) + /// What character the not-filled part should be (default: ) + /// What character the start-part should be (default: [) + /// What character the end-part part should be (default: ]) + /// A progressbar + public static string GenerateASCIIProgressbar(double current, double max, int charlength = 44, char fill = '█', char empty = '∙', char start = '[', char end = ']') + { + long first = (long)Math.Round((current / max) * charlength, 0); + + long second = charlength - first; + + string mediadisplay = start.ToString(); + + for (long i = 0; i < first; i++) + mediadisplay += fill; + + for (long i = 0; i < second; i++) + mediadisplay += empty; + + mediadisplay += end; + + return mediadisplay; + } + + + + /// + /// Get the URL a redirect leads to (limited to StatusCodes 301, 303, 307, 308) + /// + /// The shortened URL + /// The URL the redirect leads to + public static async Task UnshortenUrl(string url) + { + HttpClient client = new HttpClient(new HttpClientHandler() { AllowAutoRedirect = false }); + var request = await client.GetAsync(url); + + if (request.StatusCode != HttpStatusCode.Found || + request.StatusCode != HttpStatusCode.Redirect || + request.StatusCode != HttpStatusCode.RedirectKeepVerb || + request.StatusCode != HttpStatusCode.RedirectMethod || + request.StatusCode != HttpStatusCode.PermanentRedirect || + request.StatusCode != HttpStatusCode.TemporaryRedirect) + { + if (request.Headers is not null && request.Headers.Location is not null) + return await UnshortenUrl(request.Headers.Location.AbsoluteUri); + else + return url; + } + else + return url; + } + + + + /// + /// Try deleting the given files and directories until able to + /// + /// A list of directories to clean up + /// A list of files to clean up + /// + public static async Task CleanupFilesAndDirectories(List DirectoryPaths, List FilePaths) + { + foreach (string DirectoryPath in DirectoryPaths) + { + while (Directory.Exists(DirectoryPath)) + { + try + { + Directory.Delete(DirectoryPath, true); + } + catch (Exception) + { + await Task.Delay(5000); + } + } + } + + foreach (string FilePath in FilePaths) + { + while (File.Exists(FilePath)) + { + try + { + File.Delete(FilePath); + } + catch (Exception) + { + await Task.Delay(5000); + } + } + } + } + + + + /// + /// Compute the SHA256-Hash for a given file + /// + /// + /// + public static string ComputeSHA256Hash(this string filePath) + { + using (SHA256 _SHA256 = SHA256.Create()) + { + using (FileStream fileStream = File.OpenRead(filePath)) + return BitConverter.ToString(_SHA256.ComputeHash(fileStream)).Replace("-", "").ToLowerInvariant(); + } + } + + + + /// + /// Get a timespan from now to the given time. Negative on values in the past. + /// + /// + /// + public static TimeSpan GetTimespanUntil(this DateTime until) => + (until.ToUniversalTime() - DateTime.UtcNow); + + + /// + /// + /// + /// + /// + public static TimeSpan GetTimespanUntil(this DateTimeOffset until) => + (until.ToUniversalTime() - DateTime.UtcNow); + + + /// + /// Get the total seconds until a given DateTime. Negative on values in the past. + /// + /// + /// + public static long GetTotalSecondsUntil(this DateTime until) => + ((long)Math.Ceiling((until.ToUniversalTime() - DateTime.UtcNow).TotalSeconds)); + + + + /// + /// + /// + /// + /// + public static long GetTotalSecondsUntil(this DateTimeOffset until) => + ((long)Math.Ceiling((until.ToUniversalTime() - DateTime.UtcNow).TotalSeconds)); + + + + /// + /// Get a timespan from now to the given time. Negative on values in the future. + /// + /// + /// + public static TimeSpan GetTimespanSince(this DateTime until) => + (DateTime.UtcNow - until.ToUniversalTime()); + + + + /// + /// + /// + /// + /// + public static TimeSpan GetTimespanSince(this DateTimeOffset until) => + (DateTime.UtcNow - until.ToUniversalTime()); + + + + /// + /// Get the total seconds since a given DateTime. Negative on values in the future. + /// + /// + /// + public static long GetTotalSecondsSince(this DateTime until) => + ((long)Math.Ceiling((DateTime.UtcNow - until.ToUniversalTime()).TotalSeconds)); + + + + /// + /// + /// + /// + /// + public static long GetTotalSecondsSince(this DateTimeOffset until) => + ((long)Math.Ceiling((DateTime.UtcNow - until.ToUniversalTime()).TotalSeconds)); + + + + /// + /// Get a short human readable string for the given amount of seconds + /// + /// + /// + /// + public static string GetShortHumanReadable(this int seconds, TimeFormat timeFormat = TimeFormat.DAYS) => + TimeSpan.FromSeconds(seconds).GetShortTimeFormat(timeFormat); + + + + /// + /// + /// + /// + /// + /// + public static string GetShortHumanReadable(this long seconds, TimeFormat timeFormat = TimeFormat.DAYS) => + TimeSpan.FromSeconds(seconds).GetShortTimeFormat(timeFormat); + + + + /// + /// Get a human readable string for the given amount of seconds + /// + /// + /// + /// + public static string GetHumanReadable(this int seconds, TimeFormat timeFormat = TimeFormat.DAYS) => + TimeSpan.FromSeconds(seconds).GetTimeFormat(timeFormat); + + + + /// + /// + /// + /// + /// + /// + public static string GetHumanReadable(this long seconds, TimeFormat timeFormat = TimeFormat.DAYS) => + TimeSpan.FromSeconds(seconds).GetTimeFormat(timeFormat); + + + + private static string GetShortTimeFormat(this TimeSpan _timespan, TimeFormat timeFormat) + { + switch (timeFormat) + { + case TimeFormat.HOURS: + if (_timespan.TotalDays >= 1) + return $"{(Math.Floor(_timespan.TotalHours).ToString().Length == 1 ? $"0{Math.Floor(_timespan.TotalHours)}" : Math.Floor(_timespan.TotalHours))}:" + + $"{(_timespan.Minutes.ToString().Length == 1 ? $"0{_timespan.Minutes}" : _timespan.Minutes)}:" + + $"{(_timespan.Seconds.ToString().Length == 1 ? $"0{_timespan.Seconds}" : _timespan.Seconds)}"; + + if (_timespan.TotalHours >= 1) + return $"{(_timespan.Hours.ToString().Length == 1 ? $"0{_timespan.Hours}" : _timespan.Hours)}:" + + $"{(_timespan.Minutes.ToString().Length == 1 ? $"0{_timespan.Minutes}" : _timespan.Minutes)}:" + + $"{(_timespan.Seconds.ToString().Length == 1 ? $"0{_timespan.Seconds}" : _timespan.Seconds)}"; + + return $"{(_timespan.Minutes.ToString().Length == 1 ? $"0{_timespan.Minutes}" : _timespan.Minutes)}:" + + $"{(_timespan.Seconds.ToString().Length == 1 ? $"0{_timespan.Seconds}" : _timespan.Seconds)}"; + case TimeFormat.DAYS: + if (_timespan.TotalDays >= 1) + return $"{(Math.Floor(_timespan.TotalDays).ToString().Length == 1 ? $"0{Math.Floor(_timespan.TotalDays)}" : Math.Floor(_timespan.TotalDays))}" + + $"{(_timespan.Hours.ToString().Length == 1 ? $"0{_timespan.Hours}" : _timespan.Hours)}:" + + $"{(_timespan.Minutes.ToString().Length == 1 ? $"0{_timespan.Minutes}" : _timespan.Minutes)}:" + + $"{(_timespan.Seconds.ToString().Length == 1 ? $"0{_timespan.Seconds}" : _timespan.Seconds)}"; + + if (_timespan.TotalHours >= 1) + return $"{(Math.Floor(_timespan.TotalHours).ToString().Length == 1 ? $"0{Math.Floor(_timespan.TotalHours)}" : Math.Floor(_timespan.TotalHours))}:" + + $"{(_timespan.Minutes.ToString().Length == 1 ? $"0{_timespan.Minutes}" : _timespan.Minutes)}:" + + $"{(_timespan.Seconds.ToString().Length == 1 ? $"0{_timespan.Seconds}" : _timespan.Seconds)}"; + + return $"{(_timespan.Minutes.ToString().Length == 1 ? $"0{_timespan.Minutes}" : _timespan.Minutes)}:" + + $"{(_timespan.Seconds.ToString().Length == 1 ? $"0{_timespan.Seconds}" : _timespan.Seconds)}"; + + case TimeFormat.MINUTES: + if (_timespan.TotalHours >= 1) + return $"{(Math.Floor(_timespan.TotalMinutes).ToString().Length == 1 ? $"0{Math.Floor(_timespan.TotalMinutes)}" : Math.Floor(_timespan.TotalMinutes))}:" + + $"{(_timespan.Seconds.ToString().Length == 1 ? $"0{_timespan.Seconds}" : _timespan.Seconds)}"; + + return $"{(_timespan.Minutes.ToString().Length == 1 ? $"0{_timespan.Minutes}" : _timespan.Minutes)}:" + + $"{(_timespan.Seconds.ToString().Length == 1 ? $"0{_timespan.Seconds}" : _timespan.Seconds)}"; + + default: + return _timespan.ToString(); + } + } + private static string GetTimeFormat(this TimeSpan _timespan, TimeFormat timeFormat) + { + switch (timeFormat) + { + case TimeFormat.HOURS: + if (_timespan.TotalDays >= 1) + return $"{Math.Floor(_timespan.TotalHours)} hours, {_timespan.Minutes} minutes"; + + if (_timespan.TotalHours >= 1) + return $"{_timespan.Hours} hours, {_timespan.Minutes} minutes"; + + return $"{_timespan.Minutes} minutes, {_timespan.Seconds} seconds"; + case TimeFormat.DAYS: + if (_timespan.TotalDays >= 1) + return $"{Math.Floor(_timespan.TotalDays)} days, {_timespan.Hours} hours"; + + if (_timespan.TotalHours >= 1) + return $"{_timespan.Hours} hours, {_timespan.Minutes} minutes"; + + return $"{_timespan.Minutes} minutes, {_timespan.Seconds} seconds"; + + case TimeFormat.MINUTES: + if (_timespan.TotalHours >= 1) + return $"{Math.Floor(_timespan.TotalMinutes)} minutes, {_timespan.Seconds} seconds"; + return $"{_timespan.Minutes} minutes, {_timespan.Seconds} seconds"; + + default: + return _timespan.ToString(); + } + } + + + + /// + /// Check if a string contains only digits + /// + /// + /// + public static bool IsDigitsOnly(this string str) + { + foreach (char c in str) + { + if (c < '0' || c > '9') + return false; + } + + return true; + } + + + + /// + /// Get all digits from a string + /// + /// + /// + public static string GetAllDigits(this string str) => + new String(str.Where(Char.IsDigit).ToArray()); + + public enum TimeFormat + { + MINUTES, + HOURS, + DAYS + } + + + + /// + /// Get the current CPU Usage on all plattforms + /// + /// + public static async Task GetCpuUsageForProcess() + { + var startTime = DateTime.UtcNow; + var startCpuUsage = Process.GetCurrentProcess().TotalProcessorTime; + await Task.Delay(500); + + var endTime = DateTime.UtcNow; + var endCpuUsage = Process.GetCurrentProcess().TotalProcessorTime; + var cpuUsedMs = (endCpuUsage - startCpuUsage).TotalMilliseconds; + var totalMsPassed = (endTime - startTime).TotalMilliseconds; + var cpuUsageTotal = cpuUsedMs / (Environment.ProcessorCount * totalMsPassed); + return cpuUsageTotal * 100; + } + + + + /// + /// Copy a directory recursively + /// + /// + /// + /// + /// + public static void DirectoryCopy(string sourceDirName, string destDirName, bool copySubDirs) + { + // Get the subdirectories for the specified directory. + DirectoryInfo dir = new(sourceDirName); + + if (!dir.Exists) + { + throw new DirectoryNotFoundException( + "Source directory does not exist or could not be found: " + + sourceDirName); + } + + DirectoryInfo[] dirs = dir.GetDirectories(); + + // If the destination directory doesn't exist, create it. + Directory.CreateDirectory(destDirName); + + // Get the files in the directory and copy them to the new location. + FileInfo[] files = dir.GetFiles(); + foreach (FileInfo file in files) + { + string tempPath = Path.Combine(destDirName, file.Name); + file.CopyTo(tempPath, false); + } + + // If copying subdirectories, copy them and their contents to new location. + if (copySubDirs) + { + foreach (DirectoryInfo subdir in dirs) + { + string tempPath = Path.Combine(destDirName, subdir.Name); + DirectoryCopy(subdir.FullName, tempPath, copySubDirs); + } + } + } +} + +public static class StringExt +{ + /// + /// Shorten a string to the given length + /// + /// + /// + /// + public static string Truncate(this string value, int maxLength) + { + if (string.IsNullOrEmpty(value)) return value; + return value.Length <= maxLength ? value : value.Substring(0, maxLength); + } + + + + /// + /// Shorten a string to the given length and add ".." at the end + /// + /// + /// + /// + public static string TruncateWithIndication(this string value, int maxLength) + { + if (string.IsNullOrEmpty(value)) + return value; + + return value.Length <= maxLength ? value : $"{value.Substring(0, maxLength)}.."; + } } \ No newline at end of file