diff options
13 files changed, 131 insertions, 38 deletions
diff --git a/Emby.Server.Implementations/ConfigurationOptions.cs b/Emby.Server.Implementations/ConfigurationOptions.cs index dea9b6682..ff7ee085f 100644 --- a/Emby.Server.Implementations/ConfigurationOptions.cs +++ b/Emby.Server.Implementations/ConfigurationOptions.cs @@ -17,7 +17,6 @@ namespace Emby.Server.Implementations { { HostWebClientKey, bool.TrueString }, { HttpListenerHost.DefaultRedirectKey, "web/index.html" }, - { InstallationManager.PluginManifestUrlKey, "https://repo.jellyfin.org/releases/plugin/manifest-stable.json" }, { FfmpegProbeSizeKey, "1G" }, { FfmpegAnalyzeDurationKey, "200M" }, { PlaylistsAllowDuplicatesKey, bool.TrueString } diff --git a/Emby.Server.Implementations/IStartupOptions.cs b/Emby.Server.Implementations/IStartupOptions.cs index 0b9f80538..e7e72c686 100644 --- a/Emby.Server.Implementations/IStartupOptions.cs +++ b/Emby.Server.Implementations/IStartupOptions.cs @@ -37,11 +37,6 @@ namespace Emby.Server.Implementations string RestartArgs { get; } /// <summary> - /// Gets the value of the --plugin-manifest-url command line option. - /// </summary> - string PluginManifestUrl { get; } - - /// <summary> /// Gets the value of the --published-server-url command line option. /// </summary> Uri PublishedServerUrl { get; } diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs index 80326fddf..b1bab8cdd 100644 --- a/Emby.Server.Implementations/Updates/InstallationManager.cs +++ b/Emby.Server.Implementations/Updates/InstallationManager.cs @@ -19,6 +19,7 @@ using MediaBrowser.Common.Updates; using MediaBrowser.Controller.Configuration; using MediaBrowser.Model.Events; using MediaBrowser.Model.IO; +using MediaBrowser.Model.Net; using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Updates; using Microsoft.Extensions.Configuration; @@ -32,11 +33,6 @@ namespace Emby.Server.Implementations.Updates public class InstallationManager : IInstallationManager { /// <summary> - /// The key for a setting that specifies a URL for the plugin repository JSON manifest. - /// </summary> - public const string PluginManifestUrlKey = "InstallationManager:PluginManifestUrl"; - - /// <summary> /// The logger. /// </summary> private readonly ILogger<InstallationManager> _logger; @@ -122,16 +118,14 @@ namespace Emby.Server.Implementations.Updates public IEnumerable<InstallationInfo> CompletedInstallations => _completedInstallationsInternal; /// <inheritdoc /> - public async Task<IReadOnlyList<PackageInfo>> GetAvailablePackages(CancellationToken cancellationToken = default) + public async Task<IEnumerable<PackageInfo>> GetPackages(string manifest, CancellationToken cancellationToken = default) { - var manifestUrl = _appConfig.GetValue<string>(PluginManifestUrlKey); - try { using (var response = await _httpClient.SendAsync( new HttpRequestOptions { - Url = manifestUrl, + Url = manifest, CancellationToken = cancellationToken, CacheMode = CacheMode.Unconditional, CacheLength = TimeSpan.FromMinutes(3) @@ -145,26 +139,36 @@ namespace Emby.Server.Implementations.Updates } catch (SerializationException ex) { - const string LogTemplate = - "Failed to deserialize the plugin manifest retrieved from {PluginManifestUrl}. If you " + - "have specified a custom plugin repository manifest URL with --plugin-manifest-url or " + - PluginManifestUrlKey + ", please ensure that it is correct."; - _logger.LogError(ex, LogTemplate, manifestUrl); - throw; + _logger.LogError(ex, "Failed to deserialize the plugin manifest retrieved from {Manifest}", manifest); + return Enumerable.Empty<PackageInfo>(); } } } catch (UriFormatException ex) { - const string LogTemplate = - "The URL configured for the plugin repository manifest URL is not valid: {PluginManifestUrl}. " + - "Please check the URL configured by --plugin-manifest-url or " + PluginManifestUrlKey; - _logger.LogError(ex, LogTemplate, manifestUrl); - throw; + _logger.LogError(ex, "The URL configured for the plugin repository manifest URL is not valid: {Manifest}", manifest); + return Enumerable.Empty<PackageInfo>(); + } + catch (HttpException ex) + { + _logger.LogError(ex, "An error occurred while accessing the plugin manifest: {Manifest}", manifest); + return Enumerable.Empty<PackageInfo>(); } } /// <inheritdoc /> + public async Task<IReadOnlyList<PackageInfo>> GetAvailablePackages(CancellationToken cancellationToken = default) + { + var result = new List<PackageInfo>(); + foreach (RepositoryInfo repository in _config.Configuration.PluginRepositories) + { + result.AddRange(await GetPackages(repository.Url, cancellationToken).ConfigureAwait(true)); + } + + return result.AsReadOnly(); + } + + /// <inheritdoc /> public IEnumerable<PackageInfo> FilterPackages( IEnumerable<PackageInfo> availablePackages, string name = null, diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj index 5fd6b6e51..55c5ef1b1 100644 --- a/Jellyfin.Api/Jellyfin.Api.csproj +++ b/Jellyfin.Api/Jellyfin.Api.csproj @@ -16,7 +16,7 @@ <PackageReference Include="Microsoft.AspNetCore.Authentication" Version="2.2.0" /> <PackageReference Include="Microsoft.AspNetCore.Authorization" Version="3.1.5" /> <PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" /> - <PackageReference Include="Swashbuckle.AspNetCore" Version="5.4.1" /> + <PackageReference Include="Swashbuckle.AspNetCore" Version="5.5.0" /> </ItemGroup> <ItemGroup> diff --git a/Jellyfin.Server/Migrations/MigrationRunner.cs b/Jellyfin.Server/Migrations/MigrationRunner.cs index c98263223..d633c554d 100644 --- a/Jellyfin.Server/Migrations/MigrationRunner.cs +++ b/Jellyfin.Server/Migrations/MigrationRunner.cs @@ -20,6 +20,7 @@ namespace Jellyfin.Server.Migrations typeof(Routines.CreateUserLoggingConfigFile), typeof(Routines.MigrateActivityLogDb), typeof(Routines.RemoveDuplicateExtras), + typeof(Routines.AddDefaultPluginRepository), typeof(Routines.MigrateUserDb) }; diff --git a/Jellyfin.Server/Migrations/Routines/AddDefaultPluginRepository.cs b/Jellyfin.Server/Migrations/Routines/AddDefaultPluginRepository.cs new file mode 100644 index 000000000..a9d5ad16a --- /dev/null +++ b/Jellyfin.Server/Migrations/Routines/AddDefaultPluginRepository.cs @@ -0,0 +1,42 @@ +using System; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Model.Updates; + +namespace Jellyfin.Server.Migrations.Routines +{ + /// <summary> + /// Migration to initialize system configuration with the default plugin repository. + /// </summary> + public class AddDefaultPluginRepository : IMigrationRoutine + { + private readonly IServerConfigurationManager _serverConfigurationManager; + + private readonly RepositoryInfo _defaultRepositoryInfo = new RepositoryInfo + { + Name = "Jellyfin Stable", + Url = "https://repo.jellyfin.org/releases/plugin/manifest-stable.json" + }; + + /// <summary> + /// Initializes a new instance of the <see cref="AddDefaultPluginRepository"/> class. + /// </summary> + /// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param> + public AddDefaultPluginRepository(IServerConfigurationManager serverConfigurationManager) + { + _serverConfigurationManager = serverConfigurationManager; + } + + /// <inheritdoc/> + public Guid Id => Guid.Parse("EB58EBEE-9514-4B9B-8225-12E1A40020DF"); + + /// <inheritdoc/> + public string Name => "AddDefaultPluginRepository"; + + /// <inheritdoc/> + public void Perform() + { + _serverConfigurationManager.Configuration.PluginRepositories.Add(_defaultRepositoryInfo); + _serverConfigurationManager.SaveConfiguration(); + } + } +} diff --git a/Jellyfin.Server/StartupOptions.cs b/Jellyfin.Server/StartupOptions.cs index cc250b06e..a26114e77 100644 --- a/Jellyfin.Server/StartupOptions.cs +++ b/Jellyfin.Server/StartupOptions.cs @@ -80,10 +80,6 @@ namespace Jellyfin.Server public string? RestartArgs { get; set; } /// <inheritdoc /> - [Option("plugin-manifest-url", Required = false, HelpText = "A custom URL for the plugin repository JSON manifest")] - public string? PluginManifestUrl { get; set; } - - /// <inheritdoc /> [Option("published-server-url", Required = false, HelpText = "Jellyfin Server URL to publish via auto discover process")] public Uri? PublishedServerUrl { get; set; } @@ -95,11 +91,6 @@ namespace Jellyfin.Server { var config = new Dictionary<string, string>(); - if (PluginManifestUrl != null) - { - config.Add(InstallationManager.PluginManifestUrlKey, PluginManifestUrl); - } - if (NoWebClient) { config.Add(ConfigurationExtensions.HostWebClientKey, bool.FalseString); diff --git a/MediaBrowser.Api/PackageService.cs b/MediaBrowser.Api/PackageService.cs index a63d06ad5..a84556fcc 100644 --- a/MediaBrowser.Api/PackageService.cs +++ b/MediaBrowser.Api/PackageService.cs @@ -13,6 +13,18 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Api { + [Route("/Repositories", "GET", Summary = "Gets all package repositories")] + [Authenticated] + public class GetRepositories : IReturnVoid + { + } + + [Route("/Repositories", "POST", Summary = "Sets the enabled and existing package repositories")] + [Authenticated] + public class SetRepositories : List<RepositoryInfo>, IReturnVoid + { + } + /// <summary> /// Class GetPackage. /// </summary> @@ -94,6 +106,7 @@ namespace MediaBrowser.Api public class PackageService : BaseApiService { private readonly IInstallationManager _installationManager; + private readonly IServerConfigurationManager _serverConfigurationManager; public PackageService( ILogger<PackageService> logger, @@ -103,6 +116,19 @@ namespace MediaBrowser.Api : base(logger, serverConfigurationManager, httpResultFactory) { _installationManager = installationManager; + _serverConfigurationManager = serverConfigurationManager; + } + + public object Get(GetRepositories request) + { + var result = _serverConfigurationManager.Configuration.PluginRepositories; + return ToOptimizedResult(result); + } + + public void Post(SetRepositories request) + { + _serverConfigurationManager.Configuration.PluginRepositories = request; + _serverConfigurationManager.SaveConfiguration(); } /// <summary> diff --git a/MediaBrowser.Common/Updates/IInstallationManager.cs b/MediaBrowser.Common/Updates/IInstallationManager.cs index 965ffe0ec..a5025aee9 100644 --- a/MediaBrowser.Common/Updates/IInstallationManager.cs +++ b/MediaBrowser.Common/Updates/IInstallationManager.cs @@ -41,6 +41,14 @@ namespace MediaBrowser.Common.Updates IEnumerable<InstallationInfo> CompletedInstallations { get; } /// <summary> + /// Parses a plugin manifest at the supplied URL. + /// </summary> + /// <param name="manifest">The URL to query.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task{IReadOnlyList{PackageInfo}}.</returns> + Task<IEnumerable<PackageInfo>> GetPackages(string manifest, CancellationToken cancellationToken = default); + + /// <summary> /// Gets all available packages. /// </summary> /// <param name="cancellationToken">The cancellation token.</param> diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index 742887620..b87c8fbab 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -2,7 +2,9 @@ #pragma warning disable CS1591 using System; +using System.Collections.Generic; using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Updates; namespace MediaBrowser.Model.Configuration { @@ -229,6 +231,8 @@ namespace MediaBrowser.Model.Configuration public string[] CodecsUsed { get; set; } + public List<RepositoryInfo> PluginRepositories { get; set; } + public bool IgnoreVirtualInterfaces { get; set; } public bool EnableExternalContentInSuggestions { get; set; } diff --git a/MediaBrowser.Model/Updates/RepositoryInfo.cs b/MediaBrowser.Model/Updates/RepositoryInfo.cs new file mode 100644 index 000000000..905327c36 --- /dev/null +++ b/MediaBrowser.Model/Updates/RepositoryInfo.cs @@ -0,0 +1,23 @@ +#nullable disable +using System; + +namespace MediaBrowser.Model.Updates +{ + /// <summary> + /// Class RepositoryInfo. + /// </summary> + public class RepositoryInfo + { + /// <summary> + /// Gets or sets the name. + /// </summary> + /// <value>The name.</value> + public string Name { get; set; } + + /// <summary> + /// Gets or sets the URL. + /// </summary> + /// <value>The URL.</value> + public string Url { get; set; } + } +} diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj index d8c764c67..074bf2371 100644 --- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj +++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj @@ -21,7 +21,7 @@ <PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" /> <PackageReference Include="coverlet.collector" Version="1.3.0" /> - <PackageReference Include="Moq" Version="4.14.1" /> + <PackageReference Include="Moq" Version="4.14.3" /> </ItemGroup> <!-- Code Analyzers --> diff --git a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj index 2e6caba07..91fd0e2e2 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj +++ b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj @@ -16,7 +16,7 @@ <ItemGroup> <PackageReference Include="AutoFixture" Version="4.11.0" /> <PackageReference Include="AutoFixture.AutoMoq" Version="4.11.0" /> - <PackageReference Include="Moq" Version="4.14.1" /> + <PackageReference Include="Moq" Version="4.14.3" /> <PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" /> <PackageReference Include="coverlet.collector" Version="1.3.0" /> |
