1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00

Prerequisites - Fixed install dialog not updating to next action

Prerequisites - Finished download file action
Prerequisites - Added extract ZIP action
This commit is contained in:
Robert 2021-05-03 17:24:41 +02:00
parent de6edaf565
commit 79894150d9
4 changed files with 128 additions and 38 deletions

View File

@ -1,4 +1,4 @@
// Based on: https://www.codeproject.com/Tips/5274597/An-Improved-Stream-CopyToAsync-that-Reports-Progre // Based on: https://www.codeproject.com/Tips/5274597/An-Improved-Stream-CopyToAsync-that-Reports-Progre
// The MIT License // The MIT License
// //
// Copyright (c) 2020 honey the codewitch // Copyright (c) 2020 honey the codewitch
@ -37,7 +37,7 @@ namespace Artemis.Core
private const int DefaultBufferSize = 81920; private const int DefaultBufferSize = 81920;
/// <summary> /// <summary>
/// Copys a stream to another stream /// Copies a stream to another stream
/// </summary> /// </summary>
/// <param name="source">The source <see cref="Stream" /> to copy from</param> /// <param name="source">The source <see cref="Stream" /> to copy from</param>
/// <param name="sourceLength">The length of the source stream, if known - used for progress reporting</param> /// <param name="sourceLength">The length of the source stream, if known - used for progress reporting</param>
@ -54,32 +54,33 @@ namespace Artemis.Core
IProgress<(long, long)> progress, IProgress<(long, long)> progress,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
if (0 == bufferSize) if (source == null)
throw new ArgumentNullException(nameof(source));
if (!source.CanRead)
throw new ArgumentException("Has to be readable", nameof(source));
if (destination == null)
throw new ArgumentNullException(nameof(destination));
if (!destination.CanWrite)
throw new ArgumentException("Has to be writable", nameof(destination));
if (bufferSize <= 0)
bufferSize = DefaultBufferSize; bufferSize = DefaultBufferSize;
byte[]? buffer = new byte[bufferSize];
if (0 > sourceLength && source.CanSeek) byte[] buffer = new byte[bufferSize];
sourceLength = source.Length - source.Position; long totalBytesRead = 0;
long totalBytesCopied = 0L; int bytesRead;
if (null != progress) while ((bytesRead = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken).ConfigureAwait(false)) != 0)
progress.Report((totalBytesCopied, sourceLength));
int bytesRead = -1;
while (0 != bytesRead && !cancellationToken.IsCancellationRequested)
{ {
bytesRead = await source.ReadAsync(buffer, 0, buffer.Length, cancellationToken); await destination.WriteAsync(buffer, 0, bytesRead, cancellationToken).ConfigureAwait(false);
if (0 == bytesRead || cancellationToken.IsCancellationRequested) totalBytesRead += bytesRead;
break; progress?.Report((totalBytesRead, sourceLength));
await destination.WriteAsync(buffer, 0, buffer.Length, cancellationToken);
totalBytesCopied += bytesRead;
progress?.Report((totalBytesCopied, sourceLength));
} }
if (0 < totalBytesCopied) progress?.Report((totalBytesRead, sourceLength));
progress?.Report((totalBytesCopied, sourceLength));
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
} }
/// <summary> /// <summary>
/// Copys a stream to another stream /// Copies a stream to another stream
/// </summary> /// </summary>
/// <param name="source">The source <see cref="Stream" /> to copy from</param> /// <param name="source">The source <see cref="Stream" /> to copy from</param>
/// <param name="sourceLength">The length of the source stream, if known - used for progress reporting</param> /// <param name="sourceLength">The length of the source stream, if known - used for progress reporting</param>
@ -93,7 +94,7 @@ namespace Artemis.Core
} }
/// <summary> /// <summary>
/// Copys a stream to another stream /// Copies a stream to another stream
/// </summary> /// </summary>
/// <param name="source">The source <see cref="Stream" /> to copy from</param> /// <param name="source">The source <see cref="Stream" /> to copy from</param>
/// <param name="destination">The destination <see cref="Stream" /> to copy to</param> /// <param name="destination">The destination <see cref="Stream" /> to copy to</param>
@ -106,7 +107,7 @@ namespace Artemis.Core
} }
/// <summary> /// <summary>
/// Copys a stream to another stream /// Copies a stream to another stream
/// </summary> /// </summary>
/// <param name="source">The source <see cref="Stream" /> to copy from</param> /// <param name="source">The source <see cref="Stream" /> to copy from</param>
/// <param name="sourceLength">The length of the source stream, if known - used for progress reporting</param> /// <param name="sourceLength">The length of the source stream, if known - used for progress reporting</param>
@ -119,7 +120,7 @@ namespace Artemis.Core
} }
/// <summary> /// <summary>
/// Copys a stream to another stream /// Copies a stream to another stream
/// </summary> /// </summary>
/// <param name="source">The source <see cref="Stream" /> to copy from</param> /// <param name="source">The source <see cref="Stream" /> to copy from</param>
/// <param name="destination">The destination <see cref="Stream" /> to copy to</param> /// <param name="destination">The destination <see cref="Stream" /> to copy to</param>

View File

@ -16,12 +16,12 @@ namespace Artemis.Core
/// Creates a new instance of a copy folder action /// Creates a new instance of a copy folder action
/// </summary> /// </summary>
/// <param name="name">The name of the action</param> /// <param name="name">The name of the action</param>
/// <param name="source">The source URL to download</param> /// <param name="url">The source URL to download</param>
/// <param name="target">The target file to save as (will be created if needed)</param> /// <param name="fileName">The target file to save as (will be created if needed)</param>
public DownloadFileAction(string name, string source, string target) : base(name) public DownloadFileAction(string name, string url, string fileName) : base(name)
{ {
Source = source ?? throw new ArgumentNullException(nameof(source)); Url = url ?? throw new ArgumentNullException(nameof(url));
Target = target ?? throw new ArgumentNullException(nameof(target)); FileName = fileName ?? throw new ArgumentNullException(nameof(fileName));
ShowProgressBar = true; ShowProgressBar = true;
} }
@ -29,31 +29,31 @@ namespace Artemis.Core
/// <summary> /// <summary>
/// Gets the source URL to download /// Gets the source URL to download
/// </summary> /// </summary>
public string Source { get; } public string Url { get; }
/// <summary> /// <summary>
/// Gets the target file to save as (will be created if needed) /// Gets the target file to save as (will be created if needed)
/// </summary> /// </summary>
public string Target { get; } public string FileName { get; }
/// <inheritdoc /> /// <inheritdoc />
public override async Task Execute(CancellationToken cancellationToken) public override async Task Execute(CancellationToken cancellationToken)
{ {
using HttpClient client = new(); using HttpClient client = new();
await using FileStream destinationStream = File.Create(Target); await using FileStream destinationStream = new(FileName, FileMode.OpenOrCreate);
void ProgressOnProgressReported(object? sender, EventArgs e) void ProgressOnProgressReported(object? sender, EventArgs e)
{ {
if (Progress.ProgressPerSecond != 0) if (Progress.ProgressPerSecond != 0)
Status = $"Downloading {Target} - {Progress.ProgressPerSecond.Bytes().Humanize("#.##")}/sec"; Status = $"Downloading {Url} - {Progress.ProgressPerSecond.Bytes().Humanize("#.##")}/sec";
else else
Status = $"Downloading {Target}"; Status = $"Downloading {Url}";
} }
Progress.ProgressReported += ProgressOnProgressReported; Progress.ProgressReported += ProgressOnProgressReported;
// Get the http headers first to examine the content length // Get the http headers first to examine the content length
using HttpResponseMessage response = await client.GetAsync(Target, HttpCompletionOption.ResponseHeadersRead, cancellationToken); using HttpResponseMessage response = await client.GetAsync(Url, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
await using Stream download = await response.Content.ReadAsStreamAsync(cancellationToken); await using Stream download = await response.Content.ReadAsStreamAsync(cancellationToken);
long? contentLength = response.Content.Headers.ContentLength; long? contentLength = response.Content.Headers.ContentLength;
@ -63,6 +63,7 @@ namespace Artemis.Core
{ {
ProgressIndeterminate = true; ProgressIndeterminate = true;
await download.CopyToAsync(destinationStream, Progress, cancellationToken); await download.CopyToAsync(destinationStream, Progress, cancellationToken);
ProgressIndeterminate = false;
} }
else else
{ {
@ -70,8 +71,9 @@ namespace Artemis.Core
await download.CopyToAsync(contentLength.Value, destinationStream, Progress, cancellationToken); await download.CopyToAsync(contentLength.Value, destinationStream, Progress, cancellationToken);
} }
cancellationToken.ThrowIfCancellationRequested();
Progress.ProgressReported -= ProgressOnProgressReported; Progress.ProgressReported -= ProgressOnProgressReported;
Progress.Report((1, 1)); Progress.Report((1, 1));
Status = "Finished downloading"; Status = "Finished downloading";
} }

View File

@ -0,0 +1,83 @@
using System;
using System.IO;
using System.IO.Compression;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace Artemis.Core
{
/// <summary>
/// Represents a plugin prerequisite action that extracts a ZIP file
/// </summary>
public class ExtractArchiveAction : PluginPrerequisiteAction
{
/// <summary>
/// Creates a new instance of <see cref="ExtractArchiveAction"/>.
/// </summary>
/// <param name="name">The name of the action</param>
/// <param name="fileName">The ZIP file to extract</param>
/// <param name="target">The folder into which to extract the file</param>
public ExtractArchiveAction(string name, string fileName, string target) : base(name)
{
FileName = fileName ?? throw new ArgumentNullException(nameof(fileName));
Target = target ?? throw new ArgumentNullException(nameof(target));
ShowProgressBar = true;
}
/// <summary>
/// Gets the file to extract
/// </summary>
public string FileName { get; }
/// <summary>
/// Gets the folder into which to extract the file
/// </summary>
public string Target { get; }
/// <inheritdoc />
public override async Task Execute(CancellationToken cancellationToken)
{
using HttpClient client = new();
ShowSubProgressBar = true;
Status = $"Extracting {FileName}";
Utilities.CreateAccessibleDirectory(Target);
await using (FileStream fileStream = new(FileName, FileMode.Open))
{
ZipArchive archive = new(fileStream);
long count = 0;
foreach (ZipArchiveEntry entry in archive.Entries)
{
await using Stream unzippedEntryStream = entry.Open();
Progress.Report((count, archive.Entries.Count));
if (entry.Length > 0)
{
string path = Path.Combine(Target, entry.FullName);
CreateDirectoryForFile(path);
await using Stream extractStream = new FileStream(path, FileMode.OpenOrCreate);
await unzippedEntryStream.CopyToAsync(entry.Length, extractStream, SubProgress, cancellationToken);
}
count++;
}
}
Progress.Report((1, 1));
ShowSubProgressBar = false;
Status = "Finished extracting";
}
private static void CreateDirectoryForFile(string path)
{
string? directory = Path.GetDirectoryName(path);
if (directory == null)
throw new ArtemisCoreException($"Failed to get directory from path {path}");
Utilities.CreateAccessibleDirectory(directory);
}
}
}

View File

@ -114,7 +114,11 @@ namespace Artemis.UI.Screens.Plugins
private void ActivateCurrentAction() private void ActivateCurrentAction()
{ {
ActiveItem = Items.FirstOrDefault(i => i.Action == PluginPrerequisite.CurrentAction); PluginPrerequisiteActionViewModel newActiveItem = Items.FirstOrDefault(i => i.Action == PluginPrerequisite.CurrentAction);
if (newActiveItem == null)
return;
ActiveItem = newActiveItem;
NotifyOfPropertyChange(nameof(ActiveStemNumber)); NotifyOfPropertyChange(nameof(ActiveStemNumber));
} }
@ -123,14 +127,14 @@ namespace Artemis.UI.Screens.Plugins
/// <inheritdoc /> /// <inheritdoc />
protected override void OnClose() protected override void OnClose()
{ {
PluginPrerequisite.PropertyChanged += PluginPrerequisiteOnPropertyChanged; PluginPrerequisite.PropertyChanged -= PluginPrerequisiteOnPropertyChanged;
base.OnClose(); base.OnClose();
} }
/// <inheritdoc /> /// <inheritdoc />
protected override void OnInitialActivate() protected override void OnInitialActivate()
{ {
PluginPrerequisite.PropertyChanged -= PluginPrerequisiteOnPropertyChanged; PluginPrerequisite.PropertyChanged += PluginPrerequisiteOnPropertyChanged;
// Could be slow so take it off of the UI thread // Could be slow so take it off of the UI thread
Task.Run(() => IsMet = PluginPrerequisite.IsMet()); Task.Run(() => IsMet = PluginPrerequisite.IsMet());