This lets mass deployments and modchip setups apply the same install/update flow from a PC, with generated deletion lists kept in sync with the payload headers.
101 lines
3.5 KiB
C#
101 lines
3.5 KiB
C#
using System.Net.Http.Json;
|
|
using System.Text.Json.Serialization;
|
|
using OmniNX.DesktopInstaller.Models;
|
|
|
|
namespace OmniNX.DesktopInstaller.Services;
|
|
|
|
public sealed class GiteaClient
|
|
{
|
|
public const string DefaultBaseUrl = "https://git.niklascfw.de";
|
|
private readonly HttpClient _http;
|
|
private readonly string _baseUrl;
|
|
|
|
public GiteaClient(HttpClient? http = null, string? baseUrl = null)
|
|
{
|
|
_http = http ?? new HttpClient();
|
|
_http.DefaultRequestHeaders.UserAgent.ParseAdd("OmniNX-Desktop-Installer/1.0");
|
|
_baseUrl = (baseUrl ?? DefaultBaseUrl).TrimEnd('/');
|
|
}
|
|
|
|
public async Task<ReleaseInfo> GetLatestReleaseAsync(CancellationToken cancellationToken = default)
|
|
{
|
|
var url = $"{_baseUrl}/api/v1/repos/OmniNX/OmniNX/releases/latest";
|
|
var response = await _http.GetFromJsonAsync<GiteaReleaseDto>(url, cancellationToken)
|
|
?? throw new InvalidOperationException("Empty release response from Gitea.");
|
|
|
|
return MapRelease(response);
|
|
}
|
|
|
|
public async Task<string> DownloadAssetAsync(
|
|
ReleaseAsset asset,
|
|
string destinationPath,
|
|
IProgress<(long downloaded, long total)>? progress = null,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
PathUtils.EnsureDirectory(Path.GetDirectoryName(destinationPath)!);
|
|
|
|
using var response = await _http.GetAsync(asset.DownloadUrl, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
|
|
response.EnsureSuccessStatusCode();
|
|
|
|
var total = response.Content.Headers.ContentLength ?? asset.Size;
|
|
await using var input = await response.Content.ReadAsStreamAsync(cancellationToken);
|
|
await using var output = File.Create(destinationPath);
|
|
|
|
var buffer = new byte[81920];
|
|
long downloaded = 0;
|
|
int read;
|
|
while ((read = await input.ReadAsync(buffer, cancellationToken)) > 0)
|
|
{
|
|
await output.WriteAsync(buffer.AsMemory(0, read), cancellationToken);
|
|
downloaded += read;
|
|
progress?.Report((downloaded, total));
|
|
}
|
|
|
|
return destinationPath;
|
|
}
|
|
|
|
public ReleaseAsset? FindPackAsset(ReleaseInfo release, OmniNxVariant variant)
|
|
{
|
|
var prefix = VariantHelper.GetZipAssetPrefix(variant);
|
|
return release.Assets.FirstOrDefault(a =>
|
|
a.Name.StartsWith(prefix, StringComparison.OrdinalIgnoreCase) &&
|
|
a.Name.EndsWith(".zip", StringComparison.OrdinalIgnoreCase));
|
|
}
|
|
|
|
private static ReleaseInfo MapRelease(GiteaReleaseDto dto) => new()
|
|
{
|
|
TagName = dto.TagName ?? string.Empty,
|
|
Name = dto.Name ?? dto.TagName ?? string.Empty,
|
|
Assets = dto.Assets?.Select(a => new ReleaseAsset
|
|
{
|
|
Name = a.Name ?? string.Empty,
|
|
DownloadUrl = a.BrowserDownloadUrl ?? string.Empty,
|
|
Size = a.Size,
|
|
}).ToList() ?? [],
|
|
};
|
|
|
|
private sealed class GiteaReleaseDto
|
|
{
|
|
[JsonPropertyName("tag_name")]
|
|
public string? TagName { get; set; }
|
|
|
|
[JsonPropertyName("name")]
|
|
public string? Name { get; set; }
|
|
|
|
[JsonPropertyName("assets")]
|
|
public List<GiteaAssetDto>? Assets { get; set; }
|
|
}
|
|
|
|
private sealed class GiteaAssetDto
|
|
{
|
|
[JsonPropertyName("name")]
|
|
public string? Name { get; set; }
|
|
|
|
[JsonPropertyName("browser_download_url")]
|
|
public string? BrowserDownloadUrl { get; set; }
|
|
|
|
[JsonPropertyName("size")]
|
|
public long Size { get; set; }
|
|
}
|
|
}
|