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