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:
parent
de6edaf565
commit
79894150d9
@ -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>
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
Progress.ProgressReported -= ProgressOnProgressReported;
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
|
Progress.ProgressReported -= ProgressOnProgressReported;
|
||||||
Progress.Report((1, 1));
|
Progress.Report((1, 1));
|
||||||
Status = "Finished downloading";
|
Status = "Finished downloading";
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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());
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user