diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index f73cd1ea4..b91ba6b6c 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -462,7 +462,7 @@ namespace Emby.Server.Implementations { // Convert to list so this isn't executed for each iteration var parts = GetExportTypes() - .Select(defaultFunc) + .Select(i => defaultFunc(i)) .Where(i => i != null) .Cast() .ToList(); diff --git a/Emby.Server.Implementations/Plugins/PluginManager.cs b/Emby.Server.Implementations/Plugins/PluginManager.cs index 629975abb..c06359757 100644 --- a/Emby.Server.Implementations/Plugins/PluginManager.cs +++ b/Emby.Server.Implementations/Plugins/PluginManager.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Reflection; using System.Text; using System.Text.Json; +using System.Threading.Tasks; using MediaBrowser.Common; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Json; @@ -86,7 +87,8 @@ namespace Emby.Server.Implementations.Plugins var plugin = _plugins[i]; if (plugin.Manifest.Status == PluginStatus.Deleted && DeletePlugin(plugin)) { - UpdateSuccessors(plugin); + // See if there is another version, and if so make that active. + ProcessAlternative(plugin); } } @@ -208,12 +210,19 @@ namespace Emby.Server.Implementations.Plugins if (DeletePlugin(plugin)) { + ProcessAlternative(plugin); return true; } _logger.LogWarning("Unable to delete {Path}, so marking as deleteOnStartup.", plugin.Path); // Unable to delete, so disable. - return ChangePluginState(plugin, PluginStatus.Deleted); + if (ChangePluginState(plugin, PluginStatus.Deleted)) + { + ProcessAlternative(plugin); + return true; + } + + return false; } /// @@ -232,9 +241,9 @@ namespace Emby.Server.Implementations.Plugins var plugins = _plugins.Where(p => p.Id.Equals(id)).ToList(); plugin = plugins.FirstOrDefault(p => p.Instance != null); - if (plugin == null && plugins.Length > 0) + if (plugin == null) { - plugin = plugins.OrderByDescending(p => p.Version)[0]; + plugin = plugins.OrderByDescending(p => p.Version).FirstOrDefault(); } } else @@ -259,7 +268,8 @@ namespace Emby.Server.Implementations.Plugins if (ChangePluginState(plugin, PluginStatus.Active)) { - UpdateSuccessors(plugin); + // See if there is another version, and if so, supercede it. + ProcessAlternative(plugin); } } @@ -277,7 +287,8 @@ namespace Emby.Server.Implementations.Plugins // Update the manifest on disk if (ChangePluginState(plugin, PluginStatus.Disabled)) { - UpdateSuccessors(plugin); + // If there is another version, activate it. + ProcessAlternative(plugin); } } @@ -639,27 +650,33 @@ namespace Emby.Server.Implementations.Plugins /// Changes the status of the other versions of the plugin to "Superceded". /// /// The that's master. - private void UpdateSuccessors(LocalPlugin plugin) + private void ProcessAlternative(LocalPlugin plugin) { - // This value is memory only - so that the web will show restart required. - plugin.Manifest.Status = PluginStatus.Restart; - // Detect whether there is another version of this plugin that needs disabling. - var predecessor = _plugins.OrderByDescending(p => p.Version) + var previousVersion = _plugins.OrderByDescending(p => p.Version) .FirstOrDefault( p => p.Id.Equals(plugin.Id) && p.IsEnabledAndSupported && p.Version != plugin.Version); - if (predecessor == null) + if (previousVersion == null) { + // This value is memory only - so that the web will show restart required. + plugin.Manifest.Status = PluginStatus.Restart; return; } - if (predecessor.Manifest.Status == PluginStatus.Active && !ChangePluginState(predecessor, PluginStatus.Superceded)) + if (plugin.Manifest.Status == PluginStatus.Active && !ChangePluginState(previousVersion, PluginStatus.Superceded)) { - _logger.LogError("Unable to disable version {Version} of {Name}", predecessor.Version, predecessor.Name); + _logger.LogError("Unable to enable version {Version} of {Name}", previousVersion.Version, previousVersion.Name); } + else if (plugin.Manifest.Status == PluginStatus.Superceded && !ChangePluginState(previousVersion, PluginStatus.Active)) + { + _logger.LogError("Unable to supercede version {Version} of {Name}", previousVersion.Version, previousVersion.Name); + } + + // This value is memory only - so that the web will show restart required. + plugin.Manifest.Status = PluginStatus.Restart; } } } diff --git a/Jellyfin.Api/Controllers/PluginsController.cs b/Jellyfin.Api/Controllers/PluginsController.cs index 365bb2248..b73611c97 100644 --- a/Jellyfin.Api/Controllers/PluginsController.cs +++ b/Jellyfin.Api/Controllers/PluginsController.cs @@ -198,7 +198,7 @@ namespace Jellyfin.Api.Controllers /// Plugin id. /// Plugin uninstalled. /// Plugin not found. - /// An on success, or a if the file could not be found. + /// An on success, or a if the plugin could not be found. [HttpDelete("{pluginId}")] [Authorize(Policy = Policies.RequiresElevation)] [ProducesResponseType(StatusCodes.Status204NoContent)] @@ -210,7 +210,7 @@ namespace Jellyfin.Api.Controllers var plugins = _pluginManager.Plugins.Where(p => p.Id.Equals(pluginId)); // Select the un-instanced one first. - var plugin = plugins.FirstOrDefault(p => p.Instance != null); + var plugin = plugins.FirstOrDefault(p => p.Instance == null); if (plugin == null) { // Then by the status. @@ -256,11 +256,7 @@ namespace Jellyfin.Api.Controllers /// Plugin id. /// Plugin configuration updated. /// Plugin not found or plugin does not have configuration. - /// - /// A that represents the asynchronous operation to update plugin configuration. - /// The task result contains an indicating success, or - /// when plugin not found or plugin doesn't have configuration. - /// + /// An on success, or a if the plugin could not be found. [HttpPost("{pluginId}/Configuration")] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] @@ -321,11 +317,7 @@ namespace Jellyfin.Api.Controllers /// Plugin id. /// Plugin manifest returned. /// Plugin not found. - /// - /// A that represents the asynchronous operation to get the plugin's manifest. - /// The task result contains an indicating success, or - /// when plugin not found. - /// + /// A on success, or a if the plugin could not be found. [HttpPost("{pluginId}/Manifest")] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] diff --git a/Jellyfin.Api/Models/ConfigurationPageInfo.cs b/Jellyfin.Api/Models/ConfigurationPageInfo.cs index c15ed05d3..f56ef5976 100644 --- a/Jellyfin.Api/Models/ConfigurationPageInfo.cs +++ b/Jellyfin.Api/Models/ConfigurationPageInfo.cs @@ -23,7 +23,6 @@ namespace Jellyfin.Api.Models if (page.Plugin != null) { DisplayName = page.Plugin.Name; - // Don't use "N" because it needs to match Plugin.Id PluginId = page.Plugin.Id; } } diff --git a/MediaBrowser.Common/Plugins/BasePluginOfT.cs b/MediaBrowser.Common/Plugins/BasePluginOfT.cs index ea05a722b..d5c780851 100644 --- a/MediaBrowser.Common/Plugins/BasePluginOfT.cs +++ b/MediaBrowser.Common/Plugins/BasePluginOfT.cs @@ -25,8 +25,6 @@ namespace MediaBrowser.Common.Plugins /// private readonly object _configurationSaveLock = new object(); - private Action _directoryCreateFn; - /// /// The configuration. /// @@ -65,11 +63,6 @@ namespace MediaBrowser.Common.Plugins assemblyPlugin.SetId(assemblyId); } } - - if (this is IHasPluginConfiguration hasPluginConfiguration) - { - hasPluginConfiguration.SetStartupInfo(s => Directory.CreateDirectory(s)); - } } /// @@ -145,13 +138,6 @@ namespace MediaBrowser.Common.Plugins /// The configuration. BasePluginConfiguration IHasPluginConfiguration.Configuration => Configuration; - /// - public void SetStartupInfo(Action directoryCreateFn) - { - // hack alert, until the .net core transition is complete - _directoryCreateFn = directoryCreateFn; - } - /// /// Saves the current configuration to the file system. /// @@ -160,7 +146,11 @@ namespace MediaBrowser.Common.Plugins { lock (_configurationSaveLock) { - _directoryCreateFn(Path.GetDirectoryName(ConfigurationFilePath)); + var folder = Path.GetDirectoryName(ConfigurationFilePath); + if (!Directory.Exists(folder)) + { + Directory.CreateDirectory(folder); + } XmlSerializer.SerializeToFile(config, ConfigurationFilePath); } diff --git a/MediaBrowser.Common/Plugins/IHasPluginConfiguration.cs b/MediaBrowser.Common/Plugins/IHasPluginConfiguration.cs index 42ad85dd3..af9272caa 100644 --- a/MediaBrowser.Common/Plugins/IHasPluginConfiguration.cs +++ b/MediaBrowser.Common/Plugins/IHasPluginConfiguration.cs @@ -23,11 +23,5 @@ namespace MediaBrowser.Common.Plugins /// /// The configuration. void UpdateConfiguration(BasePluginConfiguration configuration); - - /// - /// Sets the startup directory creation function. - /// - /// The directory function used to create the configuration folder. - void SetStartupInfo(Action directoryCreateFn); } } diff --git a/MediaBrowser.Common/Plugins/IPluginManager.cs b/MediaBrowser.Common/Plugins/IPluginManager.cs index 7f7381b03..3da34d8bb 100644 --- a/MediaBrowser.Common/Plugins/IPluginManager.cs +++ b/MediaBrowser.Common/Plugins/IPluginManager.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Reflection; +using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; namespace MediaBrowser.Common.Plugins diff --git a/MediaBrowser.Common/Plugins/PluginManifest.cs b/MediaBrowser.Common/Plugins/PluginManifest.cs index 334c8d908..4c724f694 100644 --- a/MediaBrowser.Common/Plugins/PluginManifest.cs +++ b/MediaBrowser.Common/Plugins/PluginManifest.cs @@ -19,14 +19,12 @@ namespace MediaBrowser.Common.Plugins Category = string.Empty; Changelog = string.Empty; Description = string.Empty; - Status = PluginStatus.Active; Id = Guid.Empty; Name = string.Empty; Owner = string.Empty; Overview = string.Empty; TargetAbi = string.Empty; Version = string.Empty; - AutoUpdate = true; } /// @@ -99,7 +97,7 @@ namespace MediaBrowser.Common.Plugins /// Gets or sets a value indicating whether this plugin should automatically update. /// [JsonPropertyName("autoUpdate")] - public bool AutoUpdate { get; set; } + public bool AutoUpdate { get; set; } = true; // DO NOT MOVE THIS INTO THE CONSTRUCTOR. /// /// Gets or sets the ImagePath