mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-12 13:28:33 +00:00
Added layout archive generation
This commit is contained in:
parent
59fe1df40f
commit
6b4e84c95a
@ -47,7 +47,6 @@
|
||||
<PackageReference Include="McMaster.NETCore.Plugins" Version="1.4.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="RGB.NET.Core" Version="$(RGBDotNetVersion)" />
|
||||
<PackageReference Include="RGB.NET.Layout" Version="$(RGBDotNetVersion)" />
|
||||
<PackageReference Include="RGB.NET.Presets" Version="$(RGBDotNetVersion)" />
|
||||
<PackageReference Include="Serilog" Version="3.0.1" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />
|
||||
@ -65,4 +64,10 @@
|
||||
<None Include="Resources/intro-profile.json" CopyToOutputDirectory="PreserveNewest" />
|
||||
<None Include="DefaultLayouts/**" CopyToOutputDirectory="PreserveNewest" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="RGB.NET.Layout">
|
||||
<HintPath>..\..\..\RGB.NET\bin\net7.0\RGB.NET.Layout.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@ -64,7 +64,12 @@ public class ArtemisLedLayout
|
||||
|
||||
private void ApplyLogicalLayout(LayoutCustomLedDataLogicalLayout logicalLayout)
|
||||
{
|
||||
string? layoutDirectory = Path.GetDirectoryName(DeviceLayout.FilePath);
|
||||
|
||||
LogicalName = logicalLayout.Name;
|
||||
Image = new Uri(Path.Combine(Path.GetDirectoryName(DeviceLayout.FilePath)!, logicalLayout.Image!), UriKind.Absolute);
|
||||
if (layoutDirectory != null && logicalLayout.Image != null)
|
||||
Image = new Uri(Path.Combine(layoutDirectory, logicalLayout.Image!), UriKind.Absolute);
|
||||
else
|
||||
Image = null;
|
||||
}
|
||||
}
|
||||
@ -29,4 +29,9 @@
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Artemis.Core\Artemis.Core.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="RGB.NET.Layout">
|
||||
<HintPath>..\..\..\RGB.NET\bin\net7.0\RGB.NET.Layout.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@ -44,7 +44,6 @@
|
||||
<PackageReference Include="ReactiveUI" Version="19.5.1" />
|
||||
<PackageReference Include="ReactiveUI.Validation" Version="3.1.7" />
|
||||
<PackageReference Include="RGB.NET.Core" Version="$(RGBDotNetVersion)" />
|
||||
<PackageReference Include="RGB.NET.Layout" Version="$(RGBDotNetVersion)" />
|
||||
<PackageReference Include="SkiaSharp" Version="$(SkiaSharpVersion)" />
|
||||
<PackageReference Include="Splat.DryIoc" Version="14.7.1" />
|
||||
<PackageReference Include="TextMateSharp.Grammars" Version="1.0.56" />
|
||||
@ -53,4 +52,10 @@
|
||||
<ItemGroup>
|
||||
<AvaloniaResource Include="Assets\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="RGB.NET.Layout">
|
||||
<HintPath>..\..\..\RGB.NET\bin\net7.0\RGB.NET.Layout.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@ -1,9 +1,7 @@
|
||||
using System.IO;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Artemis.UI.Shared;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Avalonia.Media.Imaging;
|
||||
using Avalonia.Threading;
|
||||
using PropertyChanged.SourceGenerator;
|
||||
|
||||
@ -9,6 +9,7 @@ using Artemis.Core.Services;
|
||||
using Artemis.UI.Screens.Workshop.Layout.Dialogs;
|
||||
using Artemis.UI.Shared;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Artemis.WebClient.Workshop.Handlers.UploadHandlers;
|
||||
using PropertyChanged.SourceGenerator;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Validation.Extensions;
|
||||
@ -24,7 +25,7 @@ public partial class LayoutInfoViewModel : ValidatableViewModelBase
|
||||
[Notify] private Guid _deviceProviderId;
|
||||
[Notify] private string? _deviceProviderIdInput;
|
||||
[Notify] private ICommand? _remove;
|
||||
|
||||
|
||||
public LayoutInfoViewModel(ArtemisLayout layout,
|
||||
IDeviceService deviceService,
|
||||
IWindowService windowService,
|
||||
@ -65,4 +66,14 @@ public partial class LayoutInfoViewModel : ValidatableViewModelBase
|
||||
if (deviceProvider != null)
|
||||
DeviceProviderId = deviceProvider.Plugin.Guid;
|
||||
}
|
||||
|
||||
public LayoutInfo ToLayoutInfo()
|
||||
{
|
||||
return new LayoutInfo
|
||||
{
|
||||
Model = Model ?? string.Empty,
|
||||
Vendor = Vendor ?? string.Empty,
|
||||
DeviceProviderId = DeviceProviderId
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -11,7 +11,6 @@ using Artemis.UI.Shared.Services.Builders;
|
||||
using Artemis.UI.Shared.Utilities;
|
||||
using Artemis.WebClient.Workshop;
|
||||
using Artemis.WebClient.Workshop.Handlers.InstallationHandlers;
|
||||
using Artemis.WebClient.Workshop.Handlers.InstallationHandlers.Implementations;
|
||||
using PropertyChanged.SourceGenerator;
|
||||
using ReactiveUI;
|
||||
using StrawberryShake;
|
||||
|
||||
@ -14,6 +14,7 @@ namespace Artemis.UI.Screens.Workshop.SubmissionWizard.Steps;
|
||||
|
||||
public class ImagesStepViewModel : SubmissionViewModel
|
||||
{
|
||||
private const long MAX_FILE_SIZE = 10 * 1024 * 1024; // 10 MB
|
||||
private readonly IWindowService _windowService;
|
||||
private readonly SourceList<Stream> _imageStreams;
|
||||
|
||||
@ -60,7 +61,14 @@ public class ImagesStepViewModel : SubmissionViewModel
|
||||
if (_imageStreams.Items.Any(i => i is FileStream fs && fs.Name == path))
|
||||
continue;
|
||||
|
||||
FileStream stream = new(path, FileMode.Open);
|
||||
FileStream stream = new(path, FileMode.Open, FileAccess.Read);
|
||||
if (stream.Length > MAX_FILE_SIZE)
|
||||
{
|
||||
await _windowService.ShowConfirmContentDialog("File too big", $"File {path} exceeds maximum file size of 10 MB", "Skip file", null);
|
||||
await stream.DisposeAsync();
|
||||
continue;
|
||||
}
|
||||
|
||||
_imageStreams.Add(stream);
|
||||
State.Images.Add(stream);
|
||||
}
|
||||
|
||||
@ -85,40 +85,15 @@ public partial class LayoutInfoStepViewModel : SubmissionViewModel
|
||||
return;
|
||||
|
||||
layoutEntrySource.PhysicalLayout = PhysicalLayout;
|
||||
// layoutEntrySource.LayoutInfo = new List<LayoutInfo>(LayoutInfo.Select(i => i.ToLayoutInfo()));
|
||||
layoutEntrySource.LayoutInfo = new List<LayoutInfo>(LayoutInfo.Select(i => i.ToLayoutInfo()));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(State.Name))
|
||||
State.Name = layoutEntrySource.Layout.RgbLayout.Name ?? "";
|
||||
if (string.IsNullOrWhiteSpace(State.Summary))
|
||||
{
|
||||
State.Summary = !string.IsNullOrWhiteSpace(layoutEntrySource.Layout.RgbLayout.Vendor)
|
||||
? $"{layoutEntrySource.Layout.RgbLayout.Vendor} {layoutEntrySource.Layout.RgbLayout.Type} device layout"
|
||||
: $"{layoutEntrySource.Layout.RgbLayout.Type} device layout";
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(State.Description))
|
||||
{
|
||||
State.Description = $@"### Layout properties
|
||||
**Name**
|
||||
{layoutEntrySource.Layout.RgbLayout.Name ?? "N/A"}
|
||||
**Description**
|
||||
{layoutEntrySource.Layout.RgbLayout.Description ?? "N/A"}
|
||||
**Author**
|
||||
{layoutEntrySource.Layout.RgbLayout.Author ?? "N/A"}
|
||||
**Type**
|
||||
{layoutEntrySource.Layout.RgbLayout.Type}
|
||||
**Vendor**
|
||||
{layoutEntrySource.Layout.RgbLayout.Vendor ?? "N/A"}
|
||||
**Model**
|
||||
{layoutEntrySource.Layout.RgbLayout.Model ?? "N/A"}
|
||||
**Shape**
|
||||
{layoutEntrySource.Layout.RgbLayout.Shape}
|
||||
**Width**
|
||||
{layoutEntrySource.Layout.RgbLayout.Width}mm
|
||||
**Height**
|
||||
{layoutEntrySource.Layout.RgbLayout.Height}mm";
|
||||
}
|
||||
|
||||
|
||||
State.Categories = new List<long> {8}; // Device category, yes this could change but why would it
|
||||
if (State.EntryId == null)
|
||||
State.ChangeScreen<SpecificationsStepViewModel>();
|
||||
|
||||
@ -9,6 +9,7 @@ using System.IO;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Reactive.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.UI.Exceptions;
|
||||
using Artemis.UI.Screens.Workshop.SubmissionWizard.Models;
|
||||
using Artemis.UI.Shared.Extensions;
|
||||
using Artemis.UI.Shared.Services;
|
||||
@ -60,15 +61,23 @@ public partial class LayoutSelectionStepViewModel : SubmissionViewModel
|
||||
if (selected == null || selected.Length != 1)
|
||||
return;
|
||||
|
||||
ArtemisLayout layout = new(selected[0], LayoutSource.User);
|
||||
if (!layout.IsValid)
|
||||
try
|
||||
{
|
||||
await _windowService.ShowConfirmContentDialog("Invalid layout file", "The selected file does not appear to be a valid RGB.NET layout file", cancel: null);
|
||||
return;
|
||||
}
|
||||
ArtemisLayout layout = new(selected[0], LayoutSource.User);
|
||||
if (!layout.IsValid)
|
||||
{
|
||||
await _windowService.ShowConfirmContentDialog("Failed to load layout", "The selected file does not appear to be a valid RGB.NET layout file.", "Close", null);
|
||||
return;
|
||||
}
|
||||
|
||||
SelectedDevice = null;
|
||||
Layout = layout;
|
||||
SelectedDevice = null;
|
||||
Layout = layout;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
await _windowService.ShowConfirmContentDialog("Failed to load layout", "The selected file does not appear to be a valid RGB.NET layout file.\r\n" + e.Message, "Close", null);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private void CreatePreviewDevice(ArtemisLayout? layout)
|
||||
@ -87,25 +96,69 @@ public partial class LayoutSelectionStepViewModel : SubmissionViewModel
|
||||
{
|
||||
if (Layout == null)
|
||||
return;
|
||||
if (!await ValidateLayout(Layout))
|
||||
return;
|
||||
|
||||
State.EntrySource = new LayoutEntrySource(Layout);
|
||||
await Dispatcher.UIThread.InvokeAsync(SetDeviceImages, DispatcherPriority.Background);
|
||||
State.ChangeScreen<LayoutInfoStepViewModel>();
|
||||
}
|
||||
|
||||
private async Task<bool> ValidateLayout(ArtemisLayout? layout)
|
||||
{
|
||||
if (layout == null)
|
||||
return true;
|
||||
string? layoutPath = Path.GetDirectoryName(layout.FilePath);
|
||||
if (layoutPath == null)
|
||||
throw new ArtemisUIException($"Could not determine directory of {layout.FilePath}");
|
||||
|
||||
if (layout.LayoutCustomDeviceData.DeviceImage != null && !File.Exists(Path.Combine(layoutPath, layout.LayoutCustomDeviceData.DeviceImage)))
|
||||
{
|
||||
await _windowService.ShowConfirmContentDialog(
|
||||
"Device image not found",
|
||||
$"{Path.Combine(layoutPath, layout.LayoutCustomDeviceData.DeviceImage)} does not exist.",
|
||||
"Close",
|
||||
null
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (ArtemisLedLayout ledLayout in layout.Leds)
|
||||
{
|
||||
if (ledLayout.LayoutCustomLedData.LogicalLayouts == null)
|
||||
continue;
|
||||
|
||||
foreach (LayoutCustomLedDataLogicalLayout customData in ledLayout.LayoutCustomLedData.LogicalLayouts)
|
||||
{
|
||||
if (customData.Image == null || File.Exists(Path.Combine(layoutPath, customData.Image)))
|
||||
continue;
|
||||
await _windowService.ShowConfirmContentDialog(
|
||||
$"Image not found for LED {ledLayout.RgbLayout.Id} ({customData.Name})",
|
||||
$"{Path.Combine(layoutPath, customData.Image)} does not exist.",
|
||||
"Close",
|
||||
null
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void SetDeviceImages()
|
||||
{
|
||||
if (Layout == null)
|
||||
return;
|
||||
|
||||
|
||||
MemoryStream deviceWithoutLeds = new();
|
||||
MemoryStream deviceWithLeds = new();
|
||||
|
||||
|
||||
using (RenderTargetBitmap image = Layout.RenderLayout(false))
|
||||
{
|
||||
image.Save(deviceWithoutLeds);
|
||||
deviceWithoutLeds.Seek(0, SeekOrigin.Begin);
|
||||
}
|
||||
|
||||
using (RenderTargetBitmap image = Layout.RenderLayout(true))
|
||||
{
|
||||
image.Save(deviceWithLeds);
|
||||
@ -116,7 +169,7 @@ public partial class LayoutSelectionStepViewModel : SubmissionViewModel
|
||||
foreach (Stream stateImage in State.Images)
|
||||
stateImage.Dispose();
|
||||
State.Images.Clear();
|
||||
|
||||
|
||||
// Go through the hassle of resizing the image to 128x128 without losing aspect ratio, padding is added for this
|
||||
State.Icon = ResizeImage(deviceWithoutLeds, 128);
|
||||
State.Images.Add(deviceWithoutLeds);
|
||||
@ -126,11 +179,11 @@ public partial class LayoutSelectionStepViewModel : SubmissionViewModel
|
||||
private Stream ResizeImage(Stream image, int size)
|
||||
{
|
||||
MemoryStream output = new();
|
||||
using MemoryStream input = new();
|
||||
|
||||
using MemoryStream input = new();
|
||||
|
||||
image.CopyTo(input);
|
||||
input.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
|
||||
using SKBitmap? sourceBitmap = SKBitmap.Decode(input);
|
||||
int sourceWidth = sourceBitmap.Width;
|
||||
int sourceHeight = sourceBitmap.Height;
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Reactive.Linq;
|
||||
@ -131,12 +132,34 @@ public partial class UploadStepViewModel : SubmissionViewModel
|
||||
State.ChangeScreen<SubmitStepViewModel>();
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
foreach (Stream image in State.Images.ToList())
|
||||
{
|
||||
// Upload image
|
||||
try
|
||||
{
|
||||
ImageUploadResult imageUploadResult = await _workshopService.UploadEntryImage(entryId.Value, _progress, image, cancellationToken);
|
||||
if (!imageUploadResult.IsSuccess)
|
||||
throw new ArtemisWorkshopException(imageUploadResult.Message);
|
||||
State.Images.Remove(image);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// It's not critical if this fails
|
||||
await _windowService.ShowConfirmContentDialog("Failed to upload image", "Your submission will continue, you can try upload a new image afterwards\r\n" + e.Message, "Continue", null);
|
||||
}
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
State.ChangeScreen<SubmitStepViewModel>();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (State.Icon == null)
|
||||
return entryId;
|
||||
|
||||
// Upload image
|
||||
// Upload icon
|
||||
try
|
||||
{
|
||||
ImageUploadResult imageUploadResult = await _workshopService.SetEntryIcon(entryId.Value, _progress, State.Icon, cancellationToken);
|
||||
@ -146,12 +169,7 @@ public partial class UploadStepViewModel : SubmissionViewModel
|
||||
catch (Exception e)
|
||||
{
|
||||
// It's not critical if this fails
|
||||
await _windowService.ShowConfirmContentDialog(
|
||||
"Failed to upload icon",
|
||||
"Your submission will continue, you can try upload a new image afterwards\r\n" + e.Message,
|
||||
"Continue",
|
||||
null
|
||||
);
|
||||
await _windowService.ShowConfirmContentDialog("Failed to upload icon", "Your submission will continue, you can try upload a new image afterwards\r\n" + e.Message, "Continue", null);
|
||||
}
|
||||
|
||||
return entryId;
|
||||
|
||||
@ -53,4 +53,10 @@
|
||||
<ItemGroup>
|
||||
<Folder Include="Handlers\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="RGB.NET.Layout">
|
||||
<HintPath>..\..\..\RGB.NET\bin\net7.0\RGB.NET.Layout.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@ -0,0 +1,4 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=handlers_005Cinstallationhandlers_005Cimplementations/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=handlers_005Cuploadhandlers_005Cimplementations/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=services_005Cinterfaces/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
@ -1,5 +1,4 @@
|
||||
using Artemis.WebClient.Workshop.Handlers.InstallationHandlers.Implementations;
|
||||
using DryIoc;
|
||||
using DryIoc;
|
||||
|
||||
namespace Artemis.WebClient.Workshop.Handlers.InstallationHandlers;
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@ using Artemis.UI.Shared.Extensions;
|
||||
using Artemis.UI.Shared.Utilities;
|
||||
using Artemis.WebClient.Workshop.Services;
|
||||
|
||||
namespace Artemis.WebClient.Workshop.Handlers.InstallationHandlers.Implementations;
|
||||
namespace Artemis.WebClient.Workshop.Handlers.InstallationHandlers;
|
||||
|
||||
public class ProfileEntryInstallationHandler : IEntryInstallationHandler
|
||||
{
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
using System.Xml.Serialization;
|
||||
using System.IO.Compression;
|
||||
using Artemis.Core;
|
||||
using Artemis.UI.Shared.Utilities;
|
||||
using Artemis.WebClient.Workshop.Exceptions;
|
||||
using RGB.NET.Layout;
|
||||
|
||||
namespace Artemis.WebClient.Workshop.Handlers.UploadHandlers;
|
||||
@ -11,15 +13,56 @@ public class LayoutEntryUploadHandler : IEntryUploadHandler
|
||||
{
|
||||
if (entrySource is not LayoutEntrySource source)
|
||||
throw new InvalidOperationException("Can only create releases for layouts");
|
||||
|
||||
// Create a copy of the layout, image paths are about to be rewritten
|
||||
XmlSerializer serializer = new(typeof(DeviceLayout));
|
||||
using MemoryStream ms = new();
|
||||
await using StreamWriter writer = new(ms);
|
||||
serializer.Serialize(writer, source.Layout.RgbLayout);
|
||||
await writer.FlushAsync();
|
||||
ms.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
using MemoryStream archiveStream = new();
|
||||
using MemoryStream layoutStream = new();
|
||||
|
||||
source.Layout.RgbLayout.Save(layoutStream);
|
||||
layoutStream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
// Create an archive
|
||||
string? layoutPath = Path.GetDirectoryName(source.Layout.FilePath);
|
||||
if (layoutPath == null)
|
||||
throw new ArtemisWorkshopException($"Could not determine directory of {source.Layout.FilePath}");
|
||||
|
||||
using (ZipArchive archive = new(archiveStream, ZipArchiveMode.Create, true))
|
||||
{
|
||||
// Add the layout to the archive
|
||||
ZipArchiveEntry archiveEntry = archive.CreateEntry("layout.xml");
|
||||
await using (Stream layoutArchiveStream = archiveEntry.Open())
|
||||
await layoutStream.CopyToAsync(layoutArchiveStream, cancellationToken);
|
||||
|
||||
// Add the layout image to the archive
|
||||
CopyImage(layoutPath, source.Layout.LayoutCustomDeviceData.DeviceImage, archive);
|
||||
|
||||
// Add the LED images to the archive
|
||||
foreach (ArtemisLedLayout ledLayout in source.Layout.Leds)
|
||||
{
|
||||
if (ledLayout.LayoutCustomLedData.LogicalLayouts == null)
|
||||
continue;
|
||||
foreach (LayoutCustomLedDataLogicalLayout customData in ledLayout.LayoutCustomLedData.LogicalLayouts)
|
||||
CopyImage(layoutPath, customData.Image, archive);
|
||||
}
|
||||
}
|
||||
|
||||
archiveStream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
|
||||
string filePath = Path.Combine(desktopPath, "layout-test.zip");
|
||||
await using (FileStream fileStream = new(filePath, FileMode.Create, FileAccess.Write))
|
||||
{
|
||||
archiveStream.WriteTo(fileStream);
|
||||
}
|
||||
|
||||
return new EntryUploadResult();
|
||||
}
|
||||
|
||||
private static void CopyImage(string layoutPath, string? imagePath, ZipArchive archive)
|
||||
{
|
||||
if (imagePath == null)
|
||||
return;
|
||||
|
||||
string fullPath = Path.Combine(layoutPath, imagePath);
|
||||
archive.CreateEntryFromFile(fullPath, imagePath);
|
||||
}
|
||||
}
|
||||
@ -7,6 +7,7 @@ public interface IWorkshopService
|
||||
{
|
||||
Task<Stream?> GetEntryIcon(long entryId, CancellationToken cancellationToken);
|
||||
Task<ImageUploadResult> SetEntryIcon(long entryId, Progress<StreamProgress> progress, Stream icon, CancellationToken cancellationToken);
|
||||
Task<ImageUploadResult> UploadEntryImage(long entryId, Progress<StreamProgress> progress, Stream image, CancellationToken cancellationToken);
|
||||
Task<WorkshopStatus> GetWorkshopStatus(CancellationToken cancellationToken);
|
||||
Task<bool> ValidateWorkshopStatus(CancellationToken cancellationToken);
|
||||
Task NavigateToEntry(long entryId, EntryType entryType);
|
||||
|
||||
@ -56,6 +56,27 @@ public class WorkshopService : IWorkshopService
|
||||
return ImageUploadResult.FromSuccess();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<ImageUploadResult> UploadEntryImage(long entryId, Progress<StreamProgress> progress, Stream image, CancellationToken cancellationToken)
|
||||
{
|
||||
image.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
// Submit the archive
|
||||
HttpClient client = _httpClientFactory.CreateClient(WorkshopConstants.WORKSHOP_CLIENT_NAME);
|
||||
|
||||
// Construct the request
|
||||
MultipartFormDataContent content = new();
|
||||
ProgressableStreamContent streamContent = new(image, progress);
|
||||
streamContent.Headers.ContentType = new MediaTypeHeaderValue("image/png");
|
||||
content.Add(streamContent, "file", "file.png");
|
||||
|
||||
// Submit
|
||||
HttpResponseMessage response = await client.PostAsync($"entries/{entryId}/image", content, cancellationToken);
|
||||
if (!response.IsSuccessStatusCode)
|
||||
return ImageUploadResult.FromFailure($"{response.StatusCode} - {await response.Content.ReadAsStringAsync(cancellationToken)}");
|
||||
return ImageUploadResult.FromSuccess();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IWorkshopService.WorkshopStatus> GetWorkshopStatus(CancellationToken cancellationToken)
|
||||
{
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user