diff --git a/EllieHub/Common/Utilities.cs b/EllieHub/Common/Utilities.cs index eb2bc3c..e79ba96 100644 --- a/EllieHub/Common/Utilities.cs +++ b/EllieHub/Common/Utilities.cs @@ -1,5 +1,7 @@ using Avalonia.Platform; using SkiaSharp; +using System.ComponentModel; +using System.Diagnostics; namespace EllieHub.Common; @@ -9,29 +11,29 @@ namespace EllieHub.Common; internal static class Utilities { /// - /// Loads an image embedded with this application. + /// Loads an image embeded with this application. /// /// An uri that starts with "avares://" /// Valid uris must start with "avares://". - /// The embedded image or the default bot avatar placeholder. - /// Occurs when the embedded resource does not exist. - public static SKBitmap LoadEmbeddedImage(string? uri = default) + /// The embeded image or the default bot avatar placeholder. + /// Occurs when the embeded resource does not exist. + public static SKBitmap LoadEmbededImage(string? uri = default) { return (string.IsNullOrWhiteSpace(uri) || !uri.StartsWith("avares://", StringComparison.Ordinal)) - ? SKBitmap.Decode(AssetLoader.Open(new(AppConstants.BotAvatarUri))) - : SKBitmap.Decode(AssetLoader.Open(new(uri))); + ? SKBitmap.Decode(AssetLoader.Open(new Uri(AppConstants.BotAvatarUri))) + : SKBitmap.Decode(AssetLoader.Open(new Uri(uri))); } /// /// Loads the image at the specified location or the bot avatar placeholder if it was not found. /// - /// The absolute path to the image file or to get the avatar placeholder. - /// This fallsback to if doesn't point to a valid image file. + /// The absolute path to the image file or to get the avatar placeholder. + /// This fallsback to if doesn't point to a valid image file. /// The requested image or the default bot avatar placeholder. - public static SKBitmap LoadLocalImage(string? imagePath) + public static SKBitmap LoadLocalImage(string? uri = default) { - return (File.Exists(imagePath)) - ? SKBitmap.Decode(imagePath) - : LoadEmbeddedImage(imagePath); + return (File.Exists(uri)) + ? SKBitmap.Decode(uri) + : LoadEmbededImage(uri); } } \ No newline at end of file diff --git a/EllieHub/Features/AppConfig/Services/Abstractions/FfmpegResolver.cs b/EllieHub/Features/AppConfig/Services/Abstractions/FfmpegResolver.cs index 4760834..4fa6f9e 100644 --- a/EllieHub/Features/AppConfig/Services/Abstractions/FfmpegResolver.cs +++ b/EllieHub/Features/AppConfig/Services/Abstractions/FfmpegResolver.cs @@ -1,3 +1,5 @@ +using Toastie.Utilities; + namespace EllieHub.Features.AppConfig.Services.Abstractions; /// @@ -22,7 +24,7 @@ public abstract class FfmpegResolver : IFfmpegResolver public virtual async ValueTask CanUpdateAsync(CancellationToken cToken = default) { // Check where ffmpeg is referenced. - using var whereProcess = Utilities.StartProcess(_programVerifier, FfmpegProcessName); + using var whereProcess = ToastieUtilities.StartProcess(_programVerifier, FfmpegProcessName, true); var installationPath = await whereProcess.StandardOutput.ReadToEndAsync(cToken); // If ffmpeg is present but not managed by us, just report it is installed. @@ -32,7 +34,7 @@ public abstract class FfmpegResolver : IFfmpegResolver var currentVer = await GetCurrentVersionAsync(cToken); // If ffmpeg or ffprobe are absent, a reinstall needs to be performed. - if (currentVer is null || !await Utilities.ProgramExistsAsync("ffprobe", cToken)) + if (currentVer is null || !ToastieUtilities.ProgramExists("ffprobe")) return null; var latestVer = await GetLatestVersionAsync(cToken); @@ -44,20 +46,20 @@ public abstract class FfmpegResolver : IFfmpegResolver public virtual async ValueTask GetCurrentVersionAsync(CancellationToken cToken = default) { // If ffmpeg is not accessible from the shell... - if (!await Utilities.ProgramExistsAsync(FfmpegProcessName, cToken)) + if (!ToastieUtilities.ProgramExists(FfmpegProcessName)) { // And doesn't exist in the dependencies folder, // report that ffmpeg is not installed. - if (!File.Exists(Path.Combine(AppStatics.AppDepsUri, FileName))) + if (!File.Exists(Path.Join(AppStatics.AppDepsUri, FileName))) return null; // Else, add the dependencies directory to the PATH envar, // then try again. - Utilities.AddPathToPATHEnvar(AppStatics.AppDepsUri); + ToastieUtilities.AddPathToPATHEnvar(AppStatics.AppDepsUri); return await GetCurrentVersionAsync(cToken); } - using var ffmpeg = Utilities.StartProcess(FfmpegProcessName, "-version"); + using var ffmpeg = ToastieUtilities.StartProcess(FfmpegProcessName, "-version", true); var match = AppStatics.FfmpegVersionRegex.Match(await ffmpeg.StandardOutput.ReadLineAsync(cToken) ?? string.Empty); return match.Groups[1].Value; diff --git a/EllieHub/Features/AppConfig/Services/AppConfigManager.cs b/EllieHub/Features/AppConfig/Services/AppConfigManager.cs index 3c72c77..084d322 100644 --- a/EllieHub/Features/AppConfig/Services/AppConfigManager.cs +++ b/EllieHub/Features/AppConfig/Services/AppConfigManager.cs @@ -1,3 +1,4 @@ +using Toastie.Utilities; using EllieHub.Features.AppConfig.Models; using EllieHub.Features.AppConfig.Services.Abstractions; using EllieHub.Features.AppWindow.Models; @@ -33,7 +34,7 @@ public sealed class AppConfigManager : IAppConfigManager var newId = CreateNewId(); var newPosition = _appConfig.BotEntries.Count is 0 ? 0 : _appConfig.BotEntries.Values.Max(x => x.Position) + 1; var newBotName = "NewBot_" + newPosition; - var newEntry = new BotInstanceInfo(newBotName, Path.Combine(_appConfig.BotsDirectoryUri, newBotName), newPosition); + var newEntry = new BotInstanceInfo(newBotName, Path.Join(_appConfig.BotsDirectoryUri, newBotName), newPosition); if (!_appConfig.BotEntries.TryAdd(newId, newEntry)) throw new InvalidOperationException($"Could not create a new bot entry with Id {newId}."); @@ -49,7 +50,7 @@ public sealed class AppConfigManager : IAppConfigManager if (!_appConfig.BotEntries.TryRemove(id, out var removedEntry)) return null; - Utilities.TryDeleteDirectory(removedEntry.InstanceDirectoryUri); + ToastieUtilities.TryDeleteDirectory(removedEntry.InstanceDirectoryUri); await SaveAsync(cToken); diff --git a/EllieHub/Features/AppConfig/Services/FfmpegLinuxResolver.cs b/EllieHub/Features/AppConfig/Services/FfmpegLinuxResolver.cs index 4c6774e..74290b5 100644 --- a/EllieHub/Features/AppConfig/Services/FfmpegLinuxResolver.cs +++ b/EllieHub/Features/AppConfig/Services/FfmpegLinuxResolver.cs @@ -1,3 +1,4 @@ +using Toastie.Utilities; using EllieHub.Features.AppConfig.Services.Abstractions; using System.Runtime.InteropServices; using System.Runtime.Versioning; @@ -64,8 +65,8 @@ public sealed partial class FfmpegLinuxResolver : FfmpegResolver if (currentVersion == newVersion) return (currentVersion, null); - Utilities.TryDeleteFile(Path.Combine(installationUri, FileName)); - Utilities.TryDeleteFile(Path.Combine(installationUri, "ffprobe")); + ToastieUtilities.TryDeleteFile(Path.Join(installationUri, FileName)); + ToastieUtilities.TryDeleteFile(Path.Join(installationUri, "ffprobe")); } // Install @@ -77,29 +78,29 @@ public sealed partial class FfmpegLinuxResolver : FfmpegResolver await using var downloadStream = await http.GetStreamAsync($"https://johnvansickle.com/ffmpeg/releases/{tarFileName}", cToken); // Save tar file to the temporary directory. - var tarFilePath = Path.Combine(_tempDirectory, tarFileName); - var tarExtractDir = Path.Combine(_tempDirectory, $"ffmpeg-{newVersion}-{architecture}64-static"); + var tarFilePath = Path.Join(_tempDirectory, tarFileName); + var tarExtractDir = Path.Join(_tempDirectory, $"ffmpeg-{newVersion}-{architecture}64-static"); await using (var fileStream = new FileStream(tarFilePath, FileMode.Create)) await downloadStream.CopyToAsync(fileStream, cToken); // Extract the tar file. - using var extractProcess = Utilities.StartProcess("tar", $"xf \"{tarFilePath}\" --directory=\"{_tempDirectory}\""); + using var extractProcess = ToastieUtilities.StartProcess("tar", ["xf", tarFilePath, $"--directory=\"{_tempDirectory}\""]); await extractProcess.WaitForExitAsync(cToken); // Move ffmpeg to the dependencies directory. - File.Move(Path.Combine(tarExtractDir, FileName), Path.Combine(installationUri, FileName), true); - File.Move(Path.Combine(tarExtractDir, "ffprobe"), Path.Combine(installationUri, "ffprobe"), true); + ToastieUtilities.TryMoveFile(Path.Join(tarExtractDir, FileName), Path.Join(installationUri, FileName), true); + ToastieUtilities.TryMoveFile(Path.Join(tarExtractDir, "ffprobe"), Path.Join(installationUri, "ffprobe"), true); // Mark the files as executable. - using var chmod = Utilities.StartProcess("chmod", $"+x \"{Path.Combine(installationUri, FileName)}\" \"{Path.Combine(installationUri, "ffprobe")}\""); + using var chmod = ToastieUtilities.StartProcess("chmod", ["+x", Path.Join(installationUri, FileName), Path.Join(installationUri, "ffprobe")]); await chmod.WaitForExitAsync(cToken); // Cleanup - File.Delete(tarFilePath); - Directory.Delete(tarExtractDir, true); + ToastieUtilities.TryDeleteFile(tarFilePath); + ToastieUtilities.TryDeleteDirectory(tarExtractDir, true); // Update environment variable - Utilities.AddPathToPathEnvar(installationUri); + ToastieUtilities.AddPathToPATHEnvar(installationUri); _isUpdating = false; return (currentVersion, newVersion); diff --git a/EllieHub/Features/AppConfig/Services/FfmpegMacResolver.cs b/EllieHub/Features/AppConfig/Services/FfmpegMacResolver.cs index 7d04e2f..baaf149 100644 --- a/EllieHub/Features/AppConfig/Services/FfmpegMacResolver.cs +++ b/EllieHub/Features/AppConfig/Services/FfmpegMacResolver.cs @@ -1,3 +1,4 @@ +using Toastie.Utilities; using EllieHub.Features.AppConfig.Models.Api.Evermeet; using EllieHub.Features.AppConfig.Services.Abstractions; using System.IO.Compression; @@ -15,7 +16,7 @@ public sealed class FfmpegMacResolver : FfmpegResolver private const string _apiFfmpegInfoEndpoint = "https://evermeet.cx/ffmpeg/info/ffmpeg/release"; private const string _apiFfprobeInfoEndpoint = "https://evermeet.cx/ffmpeg/info/ffprobe/release"; private readonly string _tempDirectory = Path.GetTempPath(); - private bool _isUpdating = false; + private bool _isUpdating; private readonly IHttpClientFactory _httpClientFactory; /// @@ -57,8 +58,8 @@ public sealed class FfmpegMacResolver : FfmpegResolver return (currentVersion, null); } - Utilities.TryDeleteFile(Path.Combine(installationUri, FileName)); - Utilities.TryDeleteFile(Path.Combine(installationUri, "ffprobe")); + ToastieUtilities.TryDeleteFile(Path.Join(installationUri, FileName)); + ToastieUtilities.TryDeleteFile(Path.Join(installationUri, "ffprobe")); } // Install @@ -74,7 +75,7 @@ public sealed class FfmpegMacResolver : FfmpegResolver ); // Update environment variable - Utilities.AddPathToPathEnvar(installationUri); + ToastieUtilities.AddPathToPATHEnvar(installationUri); _isUpdating = false; return (currentVersion, newVersion); @@ -91,26 +92,26 @@ public sealed class FfmpegMacResolver : FfmpegResolver var http = _httpClientFactory.CreateClient(); var downloadUrl = downloadInfo.Download["zip"].Url; var zipFileName = downloadUrl[(downloadUrl.LastIndexOf('/') + 1)..]; - var zipFilePath = Path.Combine(_tempDirectory, zipFileName); + var zipFilePath = Path.Join(_tempDirectory, zipFileName); // Download the zip file and save it to the temporary directory. - using var zipStream = await http.GetStreamAsync(downloadUrl, cToken); + await using var zipStream = await http.GetStreamAsync(downloadUrl, cToken); - using (var fileStream = new FileStream(zipFilePath, FileMode.Create)) + await using (var fileStream = new FileStream(zipFilePath, FileMode.Create)) await zipStream.CopyToAsync(fileStream, cToken); // Extract the zip file. ZipFile.ExtractToDirectory(zipFileName, _tempDirectory); // Move the dependency binary. - var finalFileUri = Path.Combine(dependenciesUri, downloadInfo.Name); - File.Move(Path.Combine(_tempDirectory, downloadInfo.Name), finalFileUri, true); + var finalFileUri = Path.Join(dependenciesUri, downloadInfo.Name); + ToastieUtilities.TryMoveFile(Path.Join(_tempDirectory, downloadInfo.Name), finalFileUri, true); // Mark binary as executable. - using var chmod = Utilities.StartProcess("chmod", $"+x \"{finalFileUri}\""); + using var chmod = ToastieUtilities.StartProcess("chmod", $"+x \"{finalFileUri}\""); await chmod.WaitForExitAsync(cToken); // Cleanup. - File.Delete(zipFilePath); + ToastieUtilities.TryDeleteFile(zipFilePath); } } \ No newline at end of file diff --git a/EllieHub/Features/AppConfig/Services/FfmpegWindowsResolver.cs b/EllieHub/Features/AppConfig/Services/FfmpegWindowsResolver.cs index 40a5f1c..846c8da 100644 --- a/EllieHub/Features/AppConfig/Services/FfmpegWindowsResolver.cs +++ b/EllieHub/Features/AppConfig/Services/FfmpegWindowsResolver.cs @@ -1,3 +1,4 @@ +using Toastie.Utilities; using EllieHub.Features.AppConfig.Services.Abstractions; using System.IO.Compression; using System.Runtime.InteropServices; @@ -13,7 +14,7 @@ namespace EllieHub.Features.AppConfig.Services; public sealed class FfmpegWindowsResolver : FfmpegResolver { private readonly string _tempDirectory = Path.GetTempPath(); - private bool _isUpdating = false; + private bool _isUpdating; private readonly IHttpClientFactory _httpClientFactory; /// @@ -68,9 +69,9 @@ public sealed class FfmpegWindowsResolver : FfmpegResolver return (currentVersion, null); } - Utilities.TryDeleteFile(Path.Combine(installationUri, FileName)); - Utilities.TryDeleteFile(Path.Combine(installationUri, "ffprobe.exe")); - //Utilities.TryDeleteFile(Path.Combine(dependenciesUri, "ffplay.exe")); + ToastieUtilities.TryDeleteFile(Path.Join(installationUri, FileName)); + ToastieUtilities.TryDeleteFile(Path.Join(installationUri, "ffprobe.exe")); + //ToastieUtilities.TryDeleteFile(Path.Join(dependenciesUri, "ffplay.exe")); } // Install @@ -78,12 +79,12 @@ public sealed class FfmpegWindowsResolver : FfmpegResolver var zipFileName = $"ffmpeg-{newVersion}-full_build.zip"; var http = _httpClientFactory.CreateClient(); - using var downloadStream = await http.GetStreamAsync($"https://github.com/GyanD/codexffmpeg/releases/download/{newVersion}/{zipFileName}", cToken); + await using var downloadStream = await http.GetStreamAsync($"https://github.com/GyanD/codexffmpeg/releases/download/{newVersion}/{zipFileName}", cToken); // Save zip file to the temporary directory. - var zipFilePath = Path.Combine(_tempDirectory, zipFileName); - var zipExtractDir = Path.Combine(_tempDirectory, zipFileName[..^4]); - using (var fileStream = new FileStream(zipFilePath, FileMode.Create)) + var zipFilePath = Path.Join(_tempDirectory, zipFileName); + var zipExtractDir = Path.Join(_tempDirectory, zipFileName[..^4]); + await using (var fileStream = new FileStream(zipFilePath, FileMode.Create)) await downloadStream.CopyToAsync(fileStream, cToken); // Schedule installation to the thread-pool because ffmpeg is pretty @@ -94,17 +95,17 @@ public sealed class FfmpegWindowsResolver : FfmpegResolver ZipFile.ExtractToDirectory(zipFilePath, _tempDirectory); // Move ffmpeg to the dependencies directory. - File.Move(Path.Combine(zipExtractDir, "bin", FileName), Path.Combine(installationUri, FileName), true); - File.Move(Path.Combine(zipExtractDir, "bin", "ffprobe.exe"), Path.Combine(installationUri, "ffprobe.exe"), true); - //File.Move(Path.Combine(zipExtractDir, "bin", "ffplay.exe"), Path.Combine(dependenciesUri, "ffplay.exe")); + ToastieUtilities.TryMoveFile(Path.Join(zipExtractDir, "bin", FileName), Path.Join(installationUri, FileName), true); + ToastieUtilities.TryMoveFile(Path.Join(zipExtractDir, "bin", "ffprobe.exe"), Path.Join(installationUri, "ffprobe.exe"), true); + //ToastieUtilities.TryMoveFile(Path.Join(zipExtractDir, "bin", "ffplay.exe"), Path.Join(dependenciesUri, "ffplay.exe")); // Cleanup - File.Delete(zipFilePath); - Directory.Delete(zipExtractDir, true); + ToastieUtilities.TryDeleteFile(zipFilePath); + ToastieUtilities.TryDeleteDirectory(zipExtractDir); }, cToken); // Update environment variable - Utilities.AddPathToPathEnvar(installationUri); + ToastieUtilities.AddPathToPATHEnvar(installationUri); _isUpdating = false; return (currentVersion, newVersion); diff --git a/EllieHub/Features/AppConfig/Services/YtdlpResolver.cs b/EllieHub/Features/AppConfig/Services/YtdlpResolver.cs index dc30dae..ee8b40c 100644 --- a/EllieHub/Features/AppConfig/Services/YtdlpResolver.cs +++ b/EllieHub/Features/AppConfig/Services/YtdlpResolver.cs @@ -1,3 +1,4 @@ +using Toastie.Utilities; using Microsoft.Extensions.Caching.Memory; using EllieHub.Features.AppConfig.Services.Abstractions; using System.Runtime.InteropServices; @@ -13,7 +14,7 @@ public sealed class YtdlpResolver : IYtdlpResolver private const string _cachedCurrentVersionKey = "currentVersion:yt-dlp"; private const string _ytdlpProcessName = "yt-dlp"; private static readonly string _downloadedFileName = GetDownloadFileName(); - private bool _isUpdating = false; + private bool _isUpdating; private readonly IHttpClientFactory _httpClientFactory; private readonly IMemoryCache _memoryCache; @@ -56,16 +57,16 @@ public sealed class YtdlpResolver : IYtdlpResolver public async ValueTask GetCurrentVersionAsync(CancellationToken cToken = default) { // If yt-dlp is not accessible from the shell... - if (!await Utilities.ProgramExistsAsync(_ytdlpProcessName, cToken)) + if (!ToastieUtilities.ProgramExists(_ytdlpProcessName)) { // And doesn't exist in the dependencies folder, // report that yt-dlp is not installed. - if (!File.Exists(Path.Combine(AppStatics.AppDepsUri, FileName))) + if (!File.Exists(Path.Join(AppStatics.AppDepsUri, FileName))) return null; // Else, add the dependencies directory to the PATH envar, // then try again. - Utilities.AddPathToPathEnvar(AppStatics.AppDepsUri); + ToastieUtilities.AddPathToPATHEnvar(AppStatics.AppDepsUri); return await GetCurrentVersionAsync(cToken); } @@ -73,7 +74,7 @@ public sealed class YtdlpResolver : IYtdlpResolver if (_memoryCache.TryGetValue(_cachedCurrentVersionKey, out var currentVersion) && currentVersion is not null) return currentVersion; - using var ytdlp = Utilities.StartProcess(_ytdlpProcessName, "--version"); + using var ytdlp = ToastieUtilities.StartProcess(_ytdlpProcessName, "--version", true); var currentProcessVersion = (await ytdlp.StandardOutput.ReadToEndAsync(cToken)).Trim(); _memoryCache.Set(_cachedCurrentVersionKey, currentProcessVersion, TimeSpan.FromMinutes(1.5)); @@ -116,7 +117,7 @@ public sealed class YtdlpResolver : IYtdlpResolver return (currentVersion, null); } - using var ytdlp = Utilities.StartProcess(_ytdlpProcessName, "-U"); + using var ytdlp = ToastieUtilities.StartProcess(_ytdlpProcessName, "-U"); await ytdlp.WaitForExitAsync(cToken); _isUpdating = false; @@ -126,19 +127,19 @@ public sealed class YtdlpResolver : IYtdlpResolver // Install Directory.CreateDirectory(installationUri); - var finalFilePath = Path.Combine(installationUri, FileName); + var finalFilePath = Path.Join(installationUri, FileName); var http = _httpClientFactory.CreateClient(); - using var downloadStream = await http.GetStreamAsync($"https://github.com/yt-dlp/yt-dlp/releases/download/{newVersion}/{_downloadedFileName}", cToken); - using (var fileStream = new FileStream(finalFilePath, FileMode.Create)) + await using var downloadStream = await http.GetStreamAsync($"https://github.com/yt-dlp/yt-dlp/releases/download/{newVersion}/{_downloadedFileName}", cToken); + await using (var fileStream = new FileStream(finalFilePath, FileMode.Create)) await downloadStream.CopyToAsync(fileStream, cToken); // Update environment variable - Utilities.AddPathToPathEnvar(installationUri); + ToastieUtilities.AddPathToPATHEnvar(installationUri); // On Linux and MacOS, we need to mark the file as executable. if (Environment.OSVersion.Platform is PlatformID.Unix) { - using var chmod = Utilities.StartProcess("chmod", $"+x \"{finalFilePath}\""); + using var chmod = ToastieUtilities.StartProcess("chmod", ["+x", finalFilePath]); await chmod.WaitForExitAsync(cToken); } diff --git a/EllieHub/Features/AppWindow/Views/Windows/AppView.axaml.cs b/EllieHub/Features/AppWindow/Views/Windows/AppView.axaml.cs index f021f89..b379fac 100644 --- a/EllieHub/Features/AppWindow/Views/Windows/AppView.axaml.cs +++ b/EllieHub/Features/AppWindow/Views/Windows/AppView.axaml.cs @@ -125,7 +125,7 @@ public partial class AppView : ReactiveWindow base.Height = _appConfigManager.AppConfig.WindowSize.Height; base.Width = _appConfigManager.AppConfig.WindowSize.Width; - // Set the user prefered theme + // Set the user preferred theme base.RequestedThemeVariant = _appConfigManager.AppConfig.Theme switch { ThemeType.Auto => ThemeVariant.Default, @@ -256,9 +256,23 @@ public partial class AppView : ReactiveWindow _ = new UpdateView().ShowDialog(this); - await _appResolver.InstallOrUpdateAsync(AppContext.BaseDirectory); - _appResolver.LaunchNewVersion(); + try + { + Console.WriteLine("Downloading new updater..."); + await _appResolver.InstallOrUpdateAsync(AppContext.BaseDirectory); + } + catch (Exception ex) + { + Console.WriteLine(ex); + } + finally + { + Console.WriteLine("Launching new updater..."); + _appResolver.LaunchNewVersion(); + } + + Console.WriteLine("Finished update, closing down..."); base.Close(); } @@ -295,7 +309,7 @@ public partial class AppView : ReactiveWindow private static BotConfigViewModel GetBotConfigViewModel(Button button, IServiceScopeFactory scopeFactory) { using var scope = scopeFactory.CreateScope(); - var botId = (button.Content ?? throw new InvalidOperationException("Bot button has no valid Id.")); + var botId = button.Content ?? throw new InvalidOperationException("Bot button has no valid Id."); var botResolver = scope.ServiceProvider.GetParameterizedService(botId); return scope.ServiceProvider.GetParameterizedService(botResolver); diff --git a/EllieHub/Features/BotConfig/ViewModels/BotConfigViewModel.cs b/EllieHub/Features/BotConfig/ViewModels/BotConfigViewModel.cs index 0ffbbe0..0d0337f 100644 --- a/EllieHub/Features/BotConfig/ViewModels/BotConfigViewModel.cs +++ b/EllieHub/Features/BotConfig/ViewModels/BotConfigViewModel.cs @@ -183,7 +183,7 @@ public class BotConfigViewModel : ViewModelBase, IDisposable EnableButtons(!File.Exists(Path.Combine(botEntry.InstanceDirectoryUri, Resolver.FileName)), true); // Dispose when the view is deactivated - this.WhenActivated(disposables => Disposable.Create(() => Dispose()).DisposeWith(disposables)); + this.WhenActivated(disposables => Disposable.Create(Dispose).DisposeWith(disposables)); } /// @@ -425,7 +425,7 @@ public class BotConfigViewModel : ViewModelBase, IDisposable /// /// The bot resolver. /// The update bar. - private async static Task LoadUpdateBarAsync(IBotResolver botResolver, DependencyButtonViewModel updateBotBar) + private static async Task LoadUpdateBarAsync(IBotResolver botResolver, DependencyButtonViewModel updateBotBar) { updateBotBar.DependencyName = "Checking..."; updateBotBar.Status = DependencyStatus.Checking; diff --git a/EllieHub/Features/Common/ViewModels/UriInputBarViewModel.cs b/EllieHub/Features/Common/ViewModels/UriInputBarViewModel.cs index a08cc5f..7eccf61 100644 --- a/EllieHub/Features/Common/ViewModels/UriInputBarViewModel.cs +++ b/EllieHub/Features/Common/ViewModels/UriInputBarViewModel.cs @@ -1,4 +1,5 @@ using Avalonia.Platform.Storage; +using Toastie.Utilities; using EllieHub.Features.Abstractions; using EllieHub.Features.Common.Models; using EllieHub.Features.Common.Views.Controls; @@ -14,7 +15,7 @@ public class UriInputBarViewModel : ViewModelBase { private static readonly FolderPickerOpenOptions _folderPickerOptions = new(); private string _lastValidUri = AppStatics.AppDefaultConfigDirectoryUri; - private bool _isDirectoryValid = false; + private bool _isDirectoryValid; private string _currentUri = string.Empty; private readonly IStorageProvider _storageProvider; @@ -92,5 +93,5 @@ public class UriInputBarViewModel : ViewModelBase /// The absolute path to a directory. /// if the directory is valid, otherwise. private bool IsValidDirectory(string directoryUri) - => Directory.Exists(directoryUri) && Utilities.CanWriteTo(directoryUri); + => Directory.Exists(directoryUri) && ToastieUtilities.HasWritePermissionAt(directoryUri); } \ No newline at end of file diff --git a/EllieHub/Features/Home/Services/AppResolver.cs b/EllieHub/Features/Home/Services/AppResolver.cs index 20a46bb..950dbc5 100644 --- a/EllieHub/Features/Home/Services/AppResolver.cs +++ b/EllieHub/Features/Home/Services/AppResolver.cs @@ -43,7 +43,7 @@ public sealed class AppResolver : IAppResolver _httpClientFactory = httpClientFactory; _memoryCache = memoryCache; FileName = OperatingSystem.IsWindows() ? "EllieHub.exe" : "EllieHub"; - BinaryUri = Path.Combine(AppContext.BaseDirectory, FileName); + BinaryUri = Path.Join(AppContext.BaseDirectory, FileName); } /// @@ -117,18 +117,18 @@ public sealed class AppResolver : IAppResolver return (currentVersion, null); var http = _httpClientFactory.CreateClient(); // Do not initialize a ToastielabClient here, it returns 302 with no data - var appTempLocation = Path.Combine(_tempDirectory, _downloadedFileName[.._downloadedFileName.LastIndexOf('.')]); - var zipTempLocation = Path.Combine(_tempDirectory, _downloadedFileName); + var appTempLocation = Path.Join(_tempDirectory, _downloadedFileName[.._downloadedFileName.LastIndexOf('.')]); + var zipTempLocation = Path.Join(_tempDirectory, _downloadedFileName); try { - using var downloadStream = await http.GetStreamAsync( + await using var downloadStream = await http.GetStreamAsync( await GetDownloadUrlAsync(latestVersion, cToken), cToken ); // Save the zip file - using (var fileStream = new FileStream(zipTempLocation, FileMode.Create)) + await using (var fileStream = new FileStream(zipTempLocation, FileMode.Create)) await downloadStream.CopyToAsync(fileStream, cToken); // Extract the zip file @@ -139,19 +139,30 @@ public sealed class AppResolver : IAppResolver foreach (var newFileUri in newFilesUris) { - var destinationUri = Path.Combine(AppContext.BaseDirectory, newFileUri[(newFileUri.LastIndexOf(Path.DirectorySeparatorChar) + 1)..]); + var destinationUri = Path.Join(AppContext.BaseDirectory, newFileUri[(newFileUri.LastIndexOf(Path.DirectorySeparatorChar) + 1)..]); // Rename the original file from "file" to "file_old". if (File.Exists(destinationUri)) - File.Move(destinationUri, destinationUri + OldFileSuffix, true); + File.Move(destinationUri, destinationUri + OldFileSuffix, true); // This executes fine - ToastieUtilities.TryMoveFile(newFileUri, destinationUri, true); + // Move the new file to the application's directory. + // ... + // This is a workaround for really weird bug with applications published as single-file, where + // FileNotFoundException: Could not load file or assembly 'System.IO.Pipes, Version=9.0.0.0 [...] + if (Environment.OSVersion.Platform is not PlatformID.Unix) + File.Move(newFileUri, destinationUri, true); + else + { + // Circumvent this issue on Unix systems: https://github.com/dotnet/runtime/issues/31149 + using var moveProcess = ToastieUtilities.StartProcess("mv", [newFileUri, destinationUri]); + await moveProcess.WaitForExitAsync(cToken); + } } // Mark the new binary file as executable. if (Environment.OSVersion.Platform is PlatformID.Unix) { - using var chmod = ToastieUtilities.StartProcess("chmod", $"+x \"{BinaryUri}\""); + using var chmod = ToastieUtilities.StartProcess("chmod", ["+x", BinaryUri]); await chmod.WaitForExitAsync(cToken); }