Procházet zdrojové kódy

Merge pull request #3280 from crobibero/api-plugin

Move PluginService to Jellyfin.Api
David před 5 roky
rodič
revize
efe998ecf4

+ 189 - 0
Jellyfin.Api/Controllers/PluginsController.cs

@@ -0,0 +1,189 @@
+#nullable enable
+#pragma warning disable CA1801
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text.Json;
+using System.Threading.Tasks;
+using Jellyfin.Api.Constants;
+using Jellyfin.Api.Models.PluginDtos;
+using MediaBrowser.Common;
+using MediaBrowser.Common.Plugins;
+using MediaBrowser.Common.Updates;
+using MediaBrowser.Model.Plugins;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.ModelBinding;
+
+namespace Jellyfin.Api.Controllers
+{
+    /// <summary>
+    /// Plugins controller.
+    /// </summary>
+    [Authorize]
+    public class PluginsController : BaseJellyfinApiController
+    {
+        private readonly IApplicationHost _appHost;
+        private readonly IInstallationManager _installationManager;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="PluginsController"/> class.
+        /// </summary>
+        /// <param name="appHost">Instance of the <see cref="IApplicationHost"/> interface.</param>
+        /// <param name="installationManager">Instance of the <see cref="IInstallationManager"/> interface.</param>
+        public PluginsController(
+            IApplicationHost appHost,
+            IInstallationManager installationManager)
+        {
+            _appHost = appHost;
+            _installationManager = installationManager;
+        }
+
+        /// <summary>
+        /// Gets a list of currently installed plugins.
+        /// </summary>
+        /// <param name="isAppStoreEnabled">Optional. Unused.</param>
+        /// <response code="200">Installed plugins returned.</response>
+        /// <returns>List of currently installed plugins.</returns>
+        [HttpGet]
+        public ActionResult<IEnumerable<PluginInfo>> GetPlugins([FromRoute] bool? isAppStoreEnabled)
+        {
+            return Ok(_appHost.Plugins.OrderBy(p => p.Name).Select(p => p.GetPluginInfo()));
+        }
+
+        /// <summary>
+        /// Uninstalls a plugin.
+        /// </summary>
+        /// <param name="pluginId">Plugin id.</param>
+        /// <response code="200">Plugin uninstalled.</response>
+        /// <response code="404">Plugin not found.</response>
+        /// <returns>An <see cref="OkResult"/> on success, or a <see cref="NotFoundResult"/> if the file could not be found.</returns>
+        [HttpDelete("{pluginId}")]
+        [Authorize(Policy = Policies.RequiresElevation)]
+        public ActionResult UninstallPlugin([FromRoute] Guid pluginId)
+        {
+            var plugin = _appHost.Plugins.FirstOrDefault(p => p.Id == pluginId);
+            if (plugin == null)
+            {
+                return NotFound();
+            }
+
+            _installationManager.UninstallPlugin(plugin);
+            return Ok();
+        }
+
+        /// <summary>
+        /// Gets plugin configuration.
+        /// </summary>
+        /// <param name="pluginId">Plugin id.</param>
+        /// <response code="200">Plugin configuration returned.</response>
+        /// <response code="404">Plugin not found or plugin configuration not found.</response>
+        /// <returns>Plugin configuration.</returns>
+        [HttpGet("{pluginId}/Configuration")]
+        public ActionResult<BasePluginConfiguration> GetPluginConfiguration([FromRoute] Guid pluginId)
+        {
+            if (!(_appHost.Plugins.FirstOrDefault(p => p.Id == pluginId) is IHasPluginConfiguration plugin))
+            {
+                return NotFound();
+            }
+
+            return plugin.Configuration;
+        }
+
+        /// <summary>
+        /// Updates plugin configuration.
+        /// </summary>
+        /// <remarks>
+        /// Accepts plugin configuration as JSON body.
+        /// </remarks>
+        /// <param name="pluginId">Plugin id.</param>
+        /// <response code="200">Plugin configuration updated.</response>
+        /// <response code="200">Plugin not found or plugin does not have configuration.</response>
+        /// <returns>
+        /// A <see cref="Task" /> that represents the asynchronous operation to update plugin configuration.
+        ///    The task result contains an <see cref="OkResult"/> indicating success, or <see cref="NotFoundResult"/>
+        ///    when plugin not found or plugin doesn't have configuration.
+        /// </returns>
+        [HttpPost("{pluginId}/Configuration")]
+        public async Task<ActionResult> UpdatePluginConfiguration([FromRoute] Guid pluginId)
+        {
+            if (!(_appHost.Plugins.FirstOrDefault(p => p.Id == pluginId) is IHasPluginConfiguration plugin))
+            {
+                return NotFound();
+            }
+
+            var configuration = (BasePluginConfiguration)await JsonSerializer.DeserializeAsync(Request.Body, plugin.ConfigurationType)
+                .ConfigureAwait(false);
+
+            plugin.UpdateConfiguration(configuration);
+            return Ok();
+        }
+
+        /// <summary>
+        /// Get plugin security info.
+        /// </summary>
+        /// <response code="200">Plugin security info returned.</response>
+        /// <returns>Plugin security info.</returns>
+        [Obsolete("This endpoint should not be used.")]
+        [HttpGet("SecurityInfo")]
+        public ActionResult<PluginSecurityInfo> GetPluginSecurityInfo()
+        {
+            return new PluginSecurityInfo
+            {
+                IsMbSupporter = true,
+                SupporterKey = "IAmTotallyLegit"
+            };
+        }
+
+        /// <summary>
+        /// Updates plugin security info.
+        /// </summary>
+        /// <param name="pluginSecurityInfo">Plugin security info.</param>
+        /// <response code="200">Plugin security info updated.</response>
+        /// <returns>An <see cref="OkResult"/>.</returns>
+        [Obsolete("This endpoint should not be used.")]
+        [HttpPost("SecurityInfo")]
+        [Authorize(Policy = Policies.RequiresElevation)]
+        public ActionResult UpdatePluginSecurityInfo([FromBody, BindRequired] PluginSecurityInfo pluginSecurityInfo)
+        {
+            return Ok();
+        }
+
+        /// <summary>
+        /// Gets registration status for a feature.
+        /// </summary>
+        /// <param name="name">Feature name.</param>
+        /// <response code="200">Registration status returned.</response>
+        /// <returns>Mb registration record.</returns>
+        [Obsolete("This endpoint should not be used.")]
+        [HttpPost("RegistrationRecords/{name}")]
+        public ActionResult<MBRegistrationRecord> GetRegistrationStatus([FromRoute] string name)
+        {
+            return new MBRegistrationRecord
+            {
+                IsRegistered = true,
+                RegChecked = true,
+                TrialVersion = false,
+                IsValid = true,
+                RegError = false
+            };
+        }
+
+        /// <summary>
+        /// Gets registration status for a feature.
+        /// </summary>
+        /// <param name="name">Feature name.</param>
+        /// <response code="501">Not implemented.</response>
+        /// <returns>Not Implemented.</returns>
+        /// <exception cref="NotImplementedException">This endpoint is not implemented.</exception>
+        [Obsolete("Paid plugins are not supported")]
+        [HttpGet("/Registrations/{name}")]
+        public ActionResult GetRegistration([FromRoute] string name)
+        {
+            // TODO Once we have proper apps and plugins and decide to break compatibility with paid plugins,
+            // delete all these registration endpoints. They are only kept for compatibility.
+            throw new NotImplementedException();
+        }
+    }
+}

+ 42 - 0
Jellyfin.Api/Models/PluginDtos/MBRegistrationRecord.cs

@@ -0,0 +1,42 @@
+#nullable enable
+
+using System;
+
+namespace Jellyfin.Api.Models.PluginDtos
+{
+    /// <summary>
+    /// MB Registration Record.
+    /// </summary>
+    public class MBRegistrationRecord
+    {
+        /// <summary>
+        /// Gets or sets expiration date.
+        /// </summary>
+        public DateTime ExpirationDate { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether is registered.
+        /// </summary>
+        public bool IsRegistered { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether reg checked.
+        /// </summary>
+        public bool RegChecked { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether reg error.
+        /// </summary>
+        public bool RegError { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether trial version.
+        /// </summary>
+        public bool TrialVersion { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether is valid.
+        /// </summary>
+        public bool IsValid { get; set; }
+    }
+}

+ 20 - 0
Jellyfin.Api/Models/PluginDtos/PluginSecurityInfo.cs

@@ -0,0 +1,20 @@
+#nullable enable
+
+namespace Jellyfin.Api.Models.PluginDtos
+{
+    /// <summary>
+    /// Plugin security info.
+    /// </summary>
+    public class PluginSecurityInfo
+    {
+        /// <summary>
+        /// Gets or sets the supporter key.
+        /// </summary>
+        public string? SupporterKey { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether is mb supporter.
+        /// </summary>
+        public bool IsMbSupporter { get; set; }
+    }
+}

+ 0 - 268
MediaBrowser.Api/PluginService.cs

@@ -1,268 +0,0 @@
-using System;
-using System.IO;
-using System.Linq;
-using System.Threading.Tasks;
-using MediaBrowser.Common;
-using MediaBrowser.Common.Plugins;
-using MediaBrowser.Common.Updates;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Net;
-using MediaBrowser.Model.Plugins;
-using MediaBrowser.Model.Serialization;
-using MediaBrowser.Model.Services;
-using Microsoft.Extensions.Logging;
-
-namespace MediaBrowser.Api
-{
-    /// <summary>
-    /// Class Plugins
-    /// </summary>
-    [Route("/Plugins", "GET", Summary = "Gets a list of currently installed plugins")]
-    [Authenticated]
-    public class GetPlugins : IReturn<PluginInfo[]>
-    {
-        public bool? IsAppStoreEnabled { get; set; }
-    }
-
-    /// <summary>
-    /// Class UninstallPlugin
-    /// </summary>
-    [Route("/Plugins/{Id}", "DELETE", Summary = "Uninstalls a plugin")]
-    [Authenticated(Roles = "Admin")]
-    public class UninstallPlugin : IReturnVoid
-    {
-        /// <summary>
-        /// Gets or sets the id.
-        /// </summary>
-        /// <value>The id.</value>
-        [ApiMember(Name = "Id", Description = "Plugin Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
-        public string Id { get; set; }
-    }
-
-    /// <summary>
-    /// Class GetPluginConfiguration
-    /// </summary>
-    [Route("/Plugins/{Id}/Configuration", "GET", Summary = "Gets a plugin's configuration")]
-    [Authenticated]
-    public class GetPluginConfiguration
-    {
-        /// <summary>
-        /// Gets or sets the id.
-        /// </summary>
-        /// <value>The id.</value>
-        [ApiMember(Name = "Id", Description = "Plugin Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
-        public string Id { get; set; }
-    }
-
-    /// <summary>
-    /// Class UpdatePluginConfiguration
-    /// </summary>
-    [Route("/Plugins/{Id}/Configuration", "POST", Summary = "Updates a plugin's configuration")]
-    [Authenticated]
-    public class UpdatePluginConfiguration : IRequiresRequestStream, IReturnVoid
-    {
-        /// <summary>
-        /// Gets or sets the id.
-        /// </summary>
-        /// <value>The id.</value>
-        [ApiMember(Name = "Id", Description = "Plugin Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
-        public string Id { get; set; }
-
-        /// <summary>
-        /// The raw Http Request Input Stream
-        /// </summary>
-        /// <value>The request stream.</value>
-        public Stream RequestStream { get; set; }
-    }
-
-    //TODO Once we have proper apps and plugins and decide to break compatibility with paid plugins,
-    // delete all these registration endpoints. They are only kept for compatibility.
-    [Route("/Registrations/{Name}", "GET", Summary = "Gets registration status for a feature", IsHidden = true)]
-    [Authenticated]
-    public class GetRegistration : IReturn<RegistrationInfo>
-    {
-        [ApiMember(Name = "Name", Description = "Feature Name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
-        public string Name { get; set; }
-    }
-
-    /// <summary>
-    /// Class GetPluginSecurityInfo
-    /// </summary>
-    [Route("/Plugins/SecurityInfo", "GET", Summary = "Gets plugin registration information", IsHidden = true)]
-    [Authenticated]
-    public class GetPluginSecurityInfo : IReturn<PluginSecurityInfo>
-    {
-    }
-
-    /// <summary>
-    /// Class UpdatePluginSecurityInfo
-    /// </summary>
-    [Route("/Plugins/SecurityInfo", "POST", Summary = "Updates plugin registration information", IsHidden = true)]
-    [Authenticated(Roles = "Admin")]
-    public class UpdatePluginSecurityInfo : PluginSecurityInfo, IReturnVoid
-    {
-    }
-
-    [Route("/Plugins/RegistrationRecords/{Name}", "GET", Summary = "Gets registration status for a feature", IsHidden = true)]
-    [Authenticated]
-    public class GetRegistrationStatus
-    {
-        [ApiMember(Name = "Name", Description = "Feature Name", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
-        public string Name { get; set; }
-    }
-
-    // TODO these two classes are only kept for compability with paid plugins and should be removed
-    public class RegistrationInfo
-    {
-        public string Name { get; set; }
-        public DateTime ExpirationDate { get; set; }
-        public bool IsTrial { get; set; }
-        public bool IsRegistered { get; set; }
-    }
-
-    public class MBRegistrationRecord
-    {
-        public DateTime ExpirationDate { get; set; }
-        public bool IsRegistered { get; set; }
-        public bool RegChecked { get; set; }
-        public bool RegError { get; set; }
-        public bool TrialVersion { get; set; }
-        public bool IsValid { get; set; }
-    }
-
-    public class PluginSecurityInfo
-    {
-        public string SupporterKey { get; set; }
-        public bool IsMBSupporter { get; set; }
-    }
-    /// <summary>
-    /// Class PluginsService
-    /// </summary>
-    public class PluginService : BaseApiService
-    {
-        /// <summary>
-        /// The _json serializer
-        /// </summary>
-        private readonly IJsonSerializer _jsonSerializer;
-
-        /// <summary>
-        /// The _app host
-        /// </summary>
-        private readonly IApplicationHost _appHost;
-        private readonly IInstallationManager _installationManager;
-
-        public PluginService(
-            ILogger<PluginService> logger,
-            IServerConfigurationManager serverConfigurationManager,
-            IHttpResultFactory httpResultFactory,
-            IJsonSerializer jsonSerializer,
-            IApplicationHost appHost,
-            IInstallationManager installationManager)
-            : base(logger, serverConfigurationManager, httpResultFactory)
-        {
-            _appHost = appHost;
-            _installationManager = installationManager;
-            _jsonSerializer = jsonSerializer;
-        }
-
-        /// <summary>
-        /// Gets the specified request.
-        /// </summary>
-        /// <param name="request">The request.</param>
-        /// <returns>System.Object.</returns>
-        public object Get(GetRegistrationStatus request)
-        {
-            var record = new MBRegistrationRecord
-            {
-                IsRegistered = true,
-                RegChecked = true,
-                TrialVersion = false,
-                IsValid = true,
-                RegError = false
-            };
-
-            return ToOptimizedResult(record);
-        }
-
-        /// <summary>
-        /// Gets the specified request.
-        /// </summary>
-        /// <param name="request">The request.</param>
-        /// <returns>System.Object.</returns>
-        public object Get(GetPlugins request)
-        {
-            var result = _appHost.Plugins.OrderBy(p => p.Name).Select(p => p.GetPluginInfo()).ToArray();
-            return ToOptimizedResult(result);
-        }
-
-        /// <summary>
-        /// Gets the specified request.
-        /// </summary>
-        /// <param name="request">The request.</param>
-        /// <returns>System.Object.</returns>
-        public object Get(GetPluginConfiguration request)
-        {
-            var guid = new Guid(request.Id);
-            var plugin = _appHost.Plugins.First(p => p.Id == guid) as IHasPluginConfiguration;
-
-            return ToOptimizedResult(plugin.Configuration);
-        }
-
-        /// <summary>
-        /// Gets the specified request.
-        /// </summary>
-        /// <param name="request">The request.</param>
-        /// <returns>System.Object.</returns>
-        public object Get(GetPluginSecurityInfo request)
-        {
-            var result = new PluginSecurityInfo
-            {
-                IsMBSupporter = true,
-                SupporterKey = "IAmTotallyLegit"
-            };
-
-            return ToOptimizedResult(result);
-        }
-
-        /// <summary>
-        /// Posts the specified request.
-        /// </summary>
-        /// <param name="request">The request.</param>
-        public Task Post(UpdatePluginSecurityInfo request)
-        {
-            return Task.CompletedTask;
-        }
-
-        /// <summary>
-        /// Posts the specified request.
-        /// </summary>
-        /// <param name="request">The request.</param>
-        public async Task Post(UpdatePluginConfiguration request)
-        {
-            // We need to parse this manually because we told service stack not to with IRequiresRequestStream
-            // https://code.google.com/p/servicestack/source/browse/trunk/Common/ServiceStack.Text/ServiceStack.Text/Controller/PathInfo.cs
-            var id = Guid.Parse(GetPathValue(1));
-
-            if (!(_appHost.Plugins.First(p => p.Id == id) is IHasPluginConfiguration plugin))
-            {
-                throw new FileNotFoundException();
-            }
-
-            var configuration = (await _jsonSerializer.DeserializeFromStreamAsync(request.RequestStream, plugin.ConfigurationType).ConfigureAwait(false)) as BasePluginConfiguration;
-
-            plugin.UpdateConfiguration(configuration);
-        }
-
-        /// <summary>
-        /// Deletes the specified request.
-        /// </summary>
-        /// <param name="request">The request.</param>
-        public void Delete(UninstallPlugin request)
-        {
-            var guid = new Guid(request.Id);
-            var plugin = _appHost.Plugins.First(p => p.Id == guid);
-
-            _installationManager.UninstallPlugin(plugin);
-        }
-    }
-}