mirror of
https://github.com/Flow-Launcher/Flow.Launcher.git
synced 2026-03-11 08:54:32 +00:00
Introduces the concept of a store of community plugins, which is currently limited to the official PluginsManifest repository. Each store can support more than one manifest file URLs. When fetching, all URLs are used until one of them succeeds. This fixes issues with geo-blocking such as #2195 Plugin stores can be expanded in the future to be user-configurable, see #2178
59 lines
2.4 KiB
C#
59 lines
2.4 KiB
C#
using Flow.Launcher.Infrastructure.Http;
|
|
using Flow.Launcher.Infrastructure.Logger;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Net;
|
|
using System.Net.Http;
|
|
using System.Text.Json;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace Flow.Launcher.Core.ExternalPlugins
|
|
{
|
|
public record CommunityPluginSource(string ManifestFileUrl)
|
|
{
|
|
private string latestEtag = "";
|
|
|
|
private List<UserPlugin> plugins = new();
|
|
|
|
/// <summary>
|
|
/// Fetch and deserialize the contents of a plugins.json file found at <see cref="ManifestFileUrl"/>.
|
|
/// We use conditional http requests to keep repeat requests fast.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// This method will only return plugin details when the underlying http request is successful (200 or 304).
|
|
/// In any other case, an exception is raised
|
|
/// </remarks>
|
|
public async Task<List<UserPlugin>> FetchAsync(CancellationToken token)
|
|
{
|
|
Log.Info(nameof(CommunityPluginSource), $"Loading plugins from {ManifestFileUrl}");
|
|
|
|
var request = new HttpRequestMessage(HttpMethod.Get, ManifestFileUrl);
|
|
|
|
request.Headers.Add("If-None-Match", latestEtag);
|
|
|
|
using var response = await Http.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, token).ConfigureAwait(false);
|
|
|
|
if (response.StatusCode == HttpStatusCode.OK)
|
|
{
|
|
await using var json = await response.Content.ReadAsStreamAsync(token).ConfigureAwait(false);
|
|
|
|
this.plugins = await JsonSerializer.DeserializeAsync<List<UserPlugin>>(json, cancellationToken: token).ConfigureAwait(false);
|
|
this.latestEtag = response.Headers.ETag.Tag;
|
|
|
|
Log.Info(nameof(CommunityPluginSource), $"Loaded {this.plugins.Count} plugins from {ManifestFileUrl}");
|
|
return this.plugins;
|
|
}
|
|
else if (response.StatusCode == HttpStatusCode.NotModified)
|
|
{
|
|
Log.Info(nameof(CommunityPluginSource), $"Resource {ManifestFileUrl} has not been modified.");
|
|
return this.plugins;
|
|
}
|
|
else
|
|
{
|
|
Log.Warn(nameof(CommunityPluginSource), $"Failed to load resource {ManifestFileUrl} with response {response.StatusCode}");
|
|
throw new Exception($"Failed to load resource {ManifestFileUrl} with response {response.StatusCode}");
|
|
}
|
|
}
|
|
}
|
|
}
|