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
+ var manifestUrl = _appConfig.GetValue(PluginManifestUrlKey);
+
+ try
+ {
+ 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)
{
- 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)
+ 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)
{
- return await _jsonSerializer.DeserializeFromStreamAsync>(
- stream).ConfigureAwait(false);
+ 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);