diff --git a/Emby.Server.Implementations/ConfigurationOptions.cs b/Emby.Server.Implementations/ConfigurationOptions.cs index 4574a64fdb..20bdd18e70 100644 --- a/Emby.Server.Implementations/ConfigurationOptions.cs +++ b/Emby.Server.Implementations/ConfigurationOptions.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using Emby.Server.Implementations.HttpServer; +using Emby.Server.Implementations.Updates; using MediaBrowser.Providers.Music; using static MediaBrowser.Controller.Extensions.ConfigurationExtensions; @@ -17,6 +18,7 @@ namespace Emby.Server.Implementations { { HostWebClientKey, bool.TrueString }, { HttpListenerHost.DefaultRedirectKey, "web/index.html" }, + { InstallationManager.PluginManifestUrlKey, "https://repo.jellyfin.org/releases/plugin/manifest.json" }, { FfmpegProbeSizeKey, "1G" }, { FfmpegAnalyzeDurationKey, "200M" }, { PlaylistsAllowDuplicatesKey, bool.TrueString } diff --git a/Emby.Server.Implementations/IStartupOptions.cs b/Emby.Server.Implementations/IStartupOptions.cs index 6e915de3d1..16b68170be 100644 --- a/Emby.Server.Implementations/IStartupOptions.cs +++ b/Emby.Server.Implementations/IStartupOptions.cs @@ -3,33 +3,38 @@ namespace Emby.Server.Implementations public interface IStartupOptions { /// - /// --ffmpeg + /// Gets the value of the --ffmpeg command line option. /// string FFmpegPath { get; } /// - /// --service + /// Gets the value of the --service command line option. /// bool IsService { get; } /// - /// --noautorunwebapp + /// Gets the value of the --noautorunwebapp command line option. /// bool NoAutoRunWebApp { get; } /// - /// --package-name + /// Gets the value of the --package-name command line option. /// string PackageName { get; } /// - /// --restartpath + /// Gets the value of the --restartpath command line option. /// string RestartPath { get; } /// - /// --restartargs + /// Gets the value of the --restartargs command line option. /// string RestartArgs { get; } + + /// + /// Gets the value of the --plugin-manifest-url command line option. + /// + string PluginManifestUrl { get; } } } diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs index 7649779f15..25f70471ac 100644 --- a/Emby.Server.Implementations/Updates/InstallationManager.cs +++ b/Emby.Server.Implementations/Updates/InstallationManager.cs @@ -3,8 +3,10 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Net; using System.Net.Http; using System.Runtime.CompilerServices; +using System.Runtime.Serialization; using System.Security.Cryptography; using System.Threading; using System.Threading.Tasks; @@ -18,6 +20,7 @@ using MediaBrowser.Model.Events; using MediaBrowser.Model.IO; using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Updates; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.Updates @@ -27,6 +30,11 @@ namespace Emby.Server.Implementations.Updates /// public class InstallationManager : IInstallationManager { + /// + /// The key for a setting that specifies a URL for the plugin repository JSON manifest. + /// + public const string PluginManifestUrlKey = "InstallationManager:PluginManifestUrl"; + /// /// The _logger. /// @@ -44,6 +52,7 @@ namespace Emby.Server.Implementations.Updates private readonly IApplicationHost _applicationHost; private readonly IZipClient _zipClient; + private readonly IConfiguration _appConfig; private readonly object _currentInstallationsLock = new object(); @@ -65,7 +74,8 @@ namespace Emby.Server.Implementations.Updates IJsonSerializer jsonSerializer, IServerConfigurationManager config, IFileSystem fileSystem, - IZipClient zipClient) + IZipClient zipClient, + IConfiguration appConfig) { if (logger == null) { @@ -83,6 +93,7 @@ namespace Emby.Server.Implementations.Updates _config = config; _fileSystem = fileSystem; _zipClient = zipClient; + _appConfig = appConfig; } /// @@ -112,19 +123,43 @@ namespace Emby.Server.Implementations.Updates /// public async Task> GetAvailablePackages(CancellationToken cancellationToken = default) { - using (var response = await _httpClient.SendAsync( - new HttpRequestOptions - { - Url = "https://repo.jellyfin.org/releases/plugin/manifest.json", - CancellationToken = cancellationToken, - CacheMode = CacheMode.Unconditional, - CacheLength = TimeSpan.FromMinutes(3) - }, - HttpMethod.Get).ConfigureAwait(false)) - using (Stream stream = response.Content) + var manifestUrl = _appConfig.GetValue(PluginManifestUrlKey); + + try { - return await _jsonSerializer.DeserializeFromStreamAsync>( - stream).ConfigureAwait(false); + using (var response = await _httpClient.SendAsync( + new HttpRequestOptions + { + Url = manifestUrl, + CancellationToken = cancellationToken, + CacheMode = CacheMode.Unconditional, + CacheLength = TimeSpan.FromMinutes(3) + }, + HttpMethod.Get).ConfigureAwait(false)) + using (Stream stream = response.Content) + { + try + { + return await _jsonSerializer.DeserializeFromStreamAsync>(stream).ConfigureAwait(false); + } + 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; + } + } + } + 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; } } diff --git a/Jellyfin.Server/StartupOptions.cs b/Jellyfin.Server/StartupOptions.cs index c93577d3ea..6e15d058fc 100644 --- a/Jellyfin.Server/StartupOptions.cs +++ b/Jellyfin.Server/StartupOptions.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; -using System.Globalization; using CommandLine; using Emby.Server.Implementations; +using Emby.Server.Implementations.Updates; using MediaBrowser.Controller.Extensions; namespace Jellyfin.Server @@ -76,6 +76,10 @@ namespace Jellyfin.Server [Option("restartargs", Required = false, HelpText = "Arguments for restart script.")] public string? RestartArgs { get; set; } + /// + [Option("plugin-manifest-url", Required = false, HelpText = "A custom URL for the plugin repository JSON manifest")] + public string? PluginManifestUrl { get; set; } + /// /// Gets the command line options as a dictionary that can be used in the .NET configuration system. /// @@ -84,6 +88,11 @@ namespace Jellyfin.Server { var config = new Dictionary(); + if (PluginManifestUrl != null) + { + config.Add(InstallationManager.PluginManifestUrlKey, PluginManifestUrl); + } + if (NoWebClient) { config.Add(ConfigurationExtensions.HostWebClientKey, bool.FalseString);