Download plugin have progress status
This commit is contained in:
@@ -12,7 +12,7 @@ public static class Entry
|
|||||||
/// Some startup actions that can are executed when the console first starts. This actions are invoked externally at application launch
|
/// Some startup actions that can are executed when the console first starts. This actions are invoked externally at application launch
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static readonly List<IStartupAction> StartupActions = [
|
private static readonly List<IStartupAction> StartupActions = [
|
||||||
new StartupAction("/purge_plugins", () => {
|
new StartupAction("--clean-up", () => {
|
||||||
foreach (var plugin in Directory.GetFiles("./Data/Plugins", "*.dll", SearchOption.AllDirectories))
|
foreach (var plugin in Directory.GetFiles("./Data/Plugins", "*.dll", SearchOption.AllDirectories))
|
||||||
{
|
{
|
||||||
File.Delete(plugin);
|
File.Delete(plugin);
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ public class ApiManager
|
|||||||
AddEndpoint(new PluginListEndpoint());
|
AddEndpoint(new PluginListEndpoint());
|
||||||
AddEndpoint(new PluginListInstalledEndpoint());
|
AddEndpoint(new PluginListInstalledEndpoint());
|
||||||
AddEndpoint(new PluginInstallEndpoint());
|
AddEndpoint(new PluginInstallEndpoint());
|
||||||
AddEndpoint(new PluginInstallGetProgressEndpoint());
|
|
||||||
|
|
||||||
AddEndpoint(new SettingsChangeEndpoint());
|
AddEndpoint(new SettingsChangeEndpoint());
|
||||||
AddEndpoint(new SettingsGetEndpoint());
|
AddEndpoint(new SettingsGetEndpoint());
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ public class PluginInstallEndpoint : IEndpoint
|
|||||||
return ApiResponse.Fail("Plugin not found.");
|
return ApiResponse.Fail("Plugin not found.");
|
||||||
}
|
}
|
||||||
|
|
||||||
await Application.CurrentApplication.PluginManager.InstallPluginWithNoProgress(pluginInfo);
|
Application.CurrentApplication.PluginManager.InstallPluginWithNoProgress(pluginInfo);
|
||||||
return ApiResponse.Ok();
|
return ApiResponse.Ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.Globalization;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using DiscordBotCore.Interfaces.API;
|
|
||||||
using DiscordBotCore.Others;
|
|
||||||
|
|
||||||
namespace DiscordBotCore.API.Endpoints.PluginManagement;
|
|
||||||
|
|
||||||
public class PluginInstallGetProgressEndpoint : IEndpoint
|
|
||||||
{
|
|
||||||
public string Path => "/api/plugin/install/progress";
|
|
||||||
public EndpointType HttpMethod => EndpointType.Get;
|
|
||||||
public async Task<ApiResponse> HandleRequest(string? jsonRequest)
|
|
||||||
{
|
|
||||||
if (!Application.CurrentApplication.PluginManager.InstallingPluginInformation.IsInstalling)
|
|
||||||
{
|
|
||||||
return ApiResponse.Fail("No plugin is currently being installed.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var progress = Application.CurrentApplication.PluginManager.InstallingPluginInformation.InstallationProgress;
|
|
||||||
string stringProgress = progress.ToString(CultureInfo.InvariantCulture);
|
|
||||||
var response = new Dictionary<string, string>
|
|
||||||
{
|
|
||||||
{"progress", stringProgress},
|
|
||||||
{"pluginName", Application.CurrentApplication.PluginManager.InstallingPluginInformation.PluginName}
|
|
||||||
};
|
|
||||||
return ApiResponse.From(await JsonManager.ConvertToJsonString(response), true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -31,8 +31,8 @@ internal class SocketResponse
|
|||||||
return new SocketResponse(data, true, true, false);
|
return new SocketResponse(data, true, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static SocketResponse Fail()
|
internal static SocketResponse Fail(bool closeConnection)
|
||||||
{
|
{
|
||||||
return new SocketResponse(new byte[0], true, false, false);
|
return new SocketResponse(new byte[0], true, false, closeConnection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,15 +6,17 @@ namespace DiscordBotCore.API.Sockets.Sockets;
|
|||||||
|
|
||||||
internal class PluginDownloadProgressSocket : ISocket
|
internal class PluginDownloadProgressSocket : ISocket
|
||||||
{
|
{
|
||||||
private float value = 0.0f;
|
|
||||||
public string Path => "/plugin/download/progress";
|
public string Path => "/plugin/download/progress";
|
||||||
public Task<SocketResponse> HandleRequest(byte[] request, int count)
|
public Task<SocketResponse> HandleRequest(byte[] request, int count)
|
||||||
{
|
{
|
||||||
value += 0.1f;
|
if (!Application.CurrentApplication.PluginManager.InstallingPluginInformation.IsInstalling)
|
||||||
string pluginName = Encoding.UTF8.GetString(request, 0, count);
|
{
|
||||||
Application.CurrentApplication.Logger.Log($"Received plugin download progress for {pluginName}.");
|
return Task.FromResult(SocketResponse.Fail(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
float value = Application.CurrentApplication.PluginManager.InstallingPluginInformation.InstallationProgress;
|
||||||
SocketResponse response = SocketResponse.From(Encoding.UTF8.GetBytes(value.ToString()));
|
SocketResponse response = SocketResponse.From(Encoding.UTF8.GetBytes(value.ToString()));
|
||||||
response.CloseConnectionAfterResponse = value > 1.0f;
|
response.CloseConnectionAfterResponse = false;
|
||||||
return Task.FromResult(response);
|
return Task.FromResult(response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -194,46 +194,73 @@ public sealed class PluginManager
|
|||||||
{
|
{
|
||||||
InstallingPluginInformation = new InstallingPluginInformation() { PluginName = pluginData.Name };
|
InstallingPluginInformation = new InstallingPluginInformation() { PluginName = pluginData.Name };
|
||||||
|
|
||||||
|
// Calculate the total number of steps: main file + dependencies
|
||||||
int totalSteps = pluginData.HasFileDependencies ? pluginData.Dependencies.Count + 1 : 1;
|
int totalSteps = pluginData.HasFileDependencies ? pluginData.Dependencies.Count + 1 : 1;
|
||||||
|
|
||||||
float stepProgress = 1f / totalSteps;
|
// Each step contributes this percentage to the total progress
|
||||||
|
float stepFraction = 100f / totalSteps;
|
||||||
|
|
||||||
|
// Tracks the current cumulative progress in percentage
|
||||||
float currentProgress = 0f;
|
float currentProgress = 0f;
|
||||||
|
|
||||||
InstallingPluginInformation.IsInstalling = true;
|
InstallingPluginInformation.IsInstalling = true;
|
||||||
|
|
||||||
IProgress<float> downloadProgress = new Progress<float>(f => InstallingPluginInformation.InstallationProgress = currentProgress + stepProgress * f);
|
// Create a progress updater that maps the file's 0–100 progress to its portion of the total progress
|
||||||
|
IProgress<float> downloadProgress = new Progress<float>(fileProgress =>
|
||||||
|
{
|
||||||
|
// Map the file progress (0-100) to the total progress
|
||||||
|
InstallingPluginInformation.InstallationProgress = currentProgress + (fileProgress / 100f) * stepFraction;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Download the main plugin file and map its progress
|
||||||
await ServerCom.DownloadFileAsync(pluginData.DownLoadLink,
|
await ServerCom.DownloadFileAsync(pluginData.DownLoadLink,
|
||||||
$"{Application.CurrentApplication.ApplicationEnvironmentVariables.Get<string>("PluginFolder")}/{pluginData.Name}.dll",
|
$"{Application.CurrentApplication.ApplicationEnvironmentVariables.Get<string>("PluginFolder")}/{pluginData.Name}.dll",
|
||||||
downloadProgress
|
downloadProgress
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Update cumulative progress after the main file
|
||||||
|
currentProgress += stepFraction;
|
||||||
|
|
||||||
|
// Download file dependencies if they exist
|
||||||
if (pluginData.HasFileDependencies)
|
if (pluginData.HasFileDependencies)
|
||||||
|
{
|
||||||
foreach (var dependency in pluginData.Dependencies)
|
foreach (var dependency in pluginData.Dependencies)
|
||||||
{
|
{
|
||||||
string dependencyLocation = GenerateDependencyRelativePath(pluginData.Name, dependency.DownloadLocation);
|
string dependencyLocation =
|
||||||
|
GenerateDependencyRelativePath(pluginData.Name, dependency.DownloadLocation);
|
||||||
|
|
||||||
|
// Download dependency and map its progress
|
||||||
await ServerCom.DownloadFileAsync(dependency.DownloadLink, dependencyLocation, downloadProgress);
|
await ServerCom.DownloadFileAsync(dependency.DownloadLink, dependencyLocation, downloadProgress);
|
||||||
|
|
||||||
currentProgress += stepProgress;
|
// Update cumulative progress after each dependency
|
||||||
|
currentProgress += stepFraction;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Run script dependencies if any (doesn't affect download progress percentage)
|
||||||
if (pluginData.HasScriptDependencies)
|
if (pluginData.HasScriptDependencies)
|
||||||
|
{
|
||||||
foreach (var scriptDependency in pluginData.ScriptDependencies)
|
foreach (var scriptDependency in pluginData.ScriptDependencies)
|
||||||
{
|
{
|
||||||
|
|
||||||
string console = OperatingSystem.IsWindows() ? "start cmd.exe" : "bash";
|
string console = OperatingSystem.IsWindows() ? "start cmd.exe" : "bash";
|
||||||
string arguments = OperatingSystem.IsWindows() ? $"/c {scriptDependency.ScriptContent}" : scriptDependency.ScriptContent;
|
string arguments = OperatingSystem.IsWindows()
|
||||||
|
? $"/c {scriptDependency.ScriptContent}"
|
||||||
|
: scriptDependency.ScriptContent;
|
||||||
|
|
||||||
await ServerCom.RunConsoleCommand(console, arguments);
|
await ServerCom.RunConsoleCommand(console, arguments);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register the plugin in the database
|
||||||
PluginInfo pluginInfo = PluginInfo.FromOnlineInfo(pluginData);
|
PluginInfo pluginInfo = PluginInfo.FromOnlineInfo(pluginData);
|
||||||
|
|
||||||
await AppendPluginToDatabase(pluginInfo);
|
await AppendPluginToDatabase(pluginInfo);
|
||||||
|
|
||||||
InstallingPluginInformation.IsInstalling = false;
|
InstallingPluginInformation.IsInstalling = false; // Mark installation as complete
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public async Task InstallPluginWithProgressBar(PluginOnlineInfo pluginData, IProgress<float>? installProgress)
|
public async Task InstallPluginWithProgressBar(PluginOnlineInfo pluginData, IProgress<float>? installProgress)
|
||||||
{
|
{
|
||||||
installProgress?.Report(0f);
|
installProgress?.Report(0f);
|
||||||
|
|||||||
32
DiscordBotWebUI/Components/CustomTags/ProgressDialog.razor
Normal file
32
DiscordBotWebUI/Components/CustomTags/ProgressDialog.razor
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
@using DiscordBotWebUI.ServerCommunication.Sockets
|
||||||
|
@inject DialogService DialogService
|
||||||
|
|
||||||
|
<RadzenProgressBar Value="progressValue" ShowValue="false"/>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private float progressValue = 0f;
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public string ProgressUrl { get; set; }
|
||||||
|
|
||||||
|
[Parameter]
|
||||||
|
public string FirstMessage { get; set; }
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
|
||||||
|
WebSocketClientService service = new WebSocketClientService();
|
||||||
|
await service.ConnectAsync(ProgressUrl);
|
||||||
|
await Task.Delay(1000);
|
||||||
|
await service.SendMessageAsync(FirstMessage);
|
||||||
|
service.OnMessageReceived += (msg) =>
|
||||||
|
{
|
||||||
|
progressValue = float.Parse(msg);
|
||||||
|
StateHasChanged();
|
||||||
|
};
|
||||||
|
await service.ReceiveMessages();
|
||||||
|
await Task.Delay(500);
|
||||||
|
DialogService.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
@using DiscordBotWebUI.Components.CustomTags
|
@using DiscordBotWebUI.Components.CustomTags
|
||||||
@using DiscordBotWebUI.Models
|
@using DiscordBotWebUI.Models
|
||||||
@using DiscordBotWebUI.ServerCommunication
|
@using DiscordBotWebUI.ServerCommunication
|
||||||
|
@inject DialogService DialogService
|
||||||
|
|
||||||
<Marketplace PluginModels="_PluginModels" OnPluginDownloadClick="OnModelSelected"/>
|
<Marketplace PluginModels="_PluginModels" OnPluginDownloadClick="OnModelSelected"/>
|
||||||
|
|
||||||
@@ -20,12 +21,22 @@
|
|||||||
});
|
});
|
||||||
if(!response.Success)
|
if(!response.Success)
|
||||||
{
|
{
|
||||||
|
|
||||||
Console.WriteLine(response.Message);
|
Console.WriteLine(response.Message);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine("Plugin installed");
|
await DialogService.OpenAsync<ProgressDialog>($"Installing {itemName}", new Dictionary<string, object>()
|
||||||
|
{
|
||||||
|
{"ProgressUrl", "/plugin/download/progress"},
|
||||||
|
{"FirstMessage", itemName}
|
||||||
|
}, new DialogOptions()
|
||||||
|
{
|
||||||
|
Draggable = false,
|
||||||
|
CloseDialogOnEsc = false,
|
||||||
|
CloseDialogOnOverlayClick = false,
|
||||||
|
ShowClose = false
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
using System;
|
||||||
|
using System.Net.WebSockets;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DiscordBotWebUI.ServerCommunication.Sockets;
|
||||||
|
|
||||||
|
public class WebSocketClientService
|
||||||
|
{
|
||||||
|
private ClientWebSocket _webSocket;
|
||||||
|
public event Action<string> OnMessageReceived;
|
||||||
|
|
||||||
|
private readonly string _BaseUrl = "ws://localhost:5055";
|
||||||
|
|
||||||
|
public async Task ConnectAsync(string uri)
|
||||||
|
{
|
||||||
|
_webSocket = new ClientWebSocket();
|
||||||
|
await _webSocket.ConnectAsync(new Uri(_BaseUrl + uri), CancellationToken.None);
|
||||||
|
Console.WriteLine("Connected to WebSocket server.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SendMessageAsync(string message)
|
||||||
|
{
|
||||||
|
var messageBytes = Encoding.UTF8.GetBytes(message);
|
||||||
|
await _webSocket.SendAsync(new ArraySegment<byte>(messageBytes), WebSocketMessageType.Text, true, CancellationToken.None);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ReceiveMessages()
|
||||||
|
{
|
||||||
|
byte[] buffer = new byte[102400];
|
||||||
|
while (_webSocket.State == WebSocketState.Open)
|
||||||
|
{
|
||||||
|
var result = await _webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
|
||||||
|
if (result.MessageType == WebSocketMessageType.Close)
|
||||||
|
{
|
||||||
|
await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None);
|
||||||
|
Console.WriteLine("WebSocket connection closed.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
string receivedMessage = Encoding.UTF8.GetString(buffer, 0, result.Count);
|
||||||
|
OnMessageReceived?.Invoke(receivedMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user