< Summary - Jellyfin

Information
Class: Jellyfin.Api.Controllers.PluginsController
Assembly: Jellyfin.Api
File(s): /srv/git/jellyfin/Jellyfin.Api/Controllers/PluginsController.cs
Line coverage
23%
Covered lines: 10
Uncovered lines: 32
Coverable lines: 42
Total lines: 263
Line coverage: 23.8%
Branch coverage
0%
Covered branches: 0
Total branches: 20
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
GetPlugins()100%11100%
EnablePlugin(...)0%620%
DisablePlugin(...)0%620%
UninstallPluginByVersion(...)0%620%
GetPluginConfiguration(...)0%2040%
GetPluginImage(...)0%7280%
GetPluginManifest(...)0%620%

File(s)

/srv/git/jellyfin/Jellyfin.Api/Controllers/PluginsController.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.ComponentModel.DataAnnotations;
 4using System.IO;
 5using System.Linq;
 6using System.Text.Json;
 7using System.Threading.Tasks;
 8using Jellyfin.Api.Attributes;
 9using Jellyfin.Api.Constants;
 10using Jellyfin.Extensions.Json;
 11using MediaBrowser.Common.Api;
 12using MediaBrowser.Common.Plugins;
 13using MediaBrowser.Common.Updates;
 14using MediaBrowser.Model.Net;
 15using MediaBrowser.Model.Plugins;
 16using Microsoft.AspNetCore.Authorization;
 17using Microsoft.AspNetCore.Http;
 18using Microsoft.AspNetCore.Mvc;
 19
 20namespace Jellyfin.Api.Controllers;
 21
 22/// <summary>
 23/// Plugins controller.
 24/// </summary>
 25[Authorize(Policy = Policies.RequiresElevation)]
 26public class PluginsController : BaseJellyfinApiController
 27{
 28    private readonly IInstallationManager _installationManager;
 29    private readonly IPluginManager _pluginManager;
 30    private readonly JsonSerializerOptions _serializerOptions;
 31
 32    /// <summary>
 33    /// Initializes a new instance of the <see cref="PluginsController"/> class.
 34    /// </summary>
 35    /// <param name="installationManager">Instance of the <see cref="IInstallationManager"/> interface.</param>
 36    /// <param name="pluginManager">Instance of the <see cref="IPluginManager"/> interface.</param>
 137    public PluginsController(
 138        IInstallationManager installationManager,
 139        IPluginManager pluginManager)
 40    {
 141        _installationManager = installationManager;
 142        _pluginManager = pluginManager;
 143        _serializerOptions = JsonDefaults.Options;
 144    }
 45
 46    /// <summary>
 47    /// Gets a list of currently installed plugins.
 48    /// </summary>
 49    /// <response code="200">Installed plugins returned.</response>
 50    /// <returns>List of currently installed plugins.</returns>
 51    [HttpGet]
 52    [ProducesResponseType(StatusCodes.Status200OK)]
 53    public ActionResult<IEnumerable<PluginInfo>> GetPlugins()
 54    {
 155        return Ok(_pluginManager.Plugins
 156            .OrderBy(p => p.Name)
 157            .Select(p => p.GetPluginInfo()));
 58    }
 59
 60    /// <summary>
 61    /// Enables a disabled plugin.
 62    /// </summary>
 63    /// <param name="pluginId">Plugin id.</param>
 64    /// <param name="version">Plugin version.</param>
 65    /// <response code="204">Plugin enabled.</response>
 66    /// <response code="404">Plugin not found.</response>
 67    /// <returns>An <see cref="NoContentResult"/> on success, or a <see cref="NotFoundResult"/> if the plugin could not 
 68    [HttpPost("{pluginId}/{version}/Enable")]
 69    [ProducesResponseType(StatusCodes.Status204NoContent)]
 70    [ProducesResponseType(StatusCodes.Status404NotFound)]
 71    public ActionResult EnablePlugin([FromRoute, Required] Guid pluginId, [FromRoute, Required] Version version)
 72    {
 073        var plugin = _pluginManager.GetPlugin(pluginId, version);
 074        if (plugin is null)
 75        {
 076            return NotFound();
 77        }
 78
 079        _pluginManager.EnablePlugin(plugin);
 080        return NoContent();
 81    }
 82
 83    /// <summary>
 84    /// Disable a plugin.
 85    /// </summary>
 86    /// <param name="pluginId">Plugin id.</param>
 87    /// <param name="version">Plugin version.</param>
 88    /// <response code="204">Plugin disabled.</response>
 89    /// <response code="404">Plugin not found.</response>
 90    /// <returns>An <see cref="NoContentResult"/> on success, or a <see cref="NotFoundResult"/> if the plugin could not 
 91    [HttpPost("{pluginId}/{version}/Disable")]
 92    [ProducesResponseType(StatusCodes.Status204NoContent)]
 93    [ProducesResponseType(StatusCodes.Status404NotFound)]
 94    public ActionResult DisablePlugin([FromRoute, Required] Guid pluginId, [FromRoute, Required] Version version)
 95    {
 096        var plugin = _pluginManager.GetPlugin(pluginId, version);
 097        if (plugin is null)
 98        {
 099            return NotFound();
 100        }
 101
 0102        _pluginManager.DisablePlugin(plugin);
 0103        return NoContent();
 104    }
 105
 106    /// <summary>
 107    /// Uninstalls a plugin by version.
 108    /// </summary>
 109    /// <param name="pluginId">Plugin id.</param>
 110    /// <param name="version">Plugin version.</param>
 111    /// <response code="204">Plugin uninstalled.</response>
 112    /// <response code="404">Plugin not found.</response>
 113    /// <returns>An <see cref="NoContentResult"/> on success, or a <see cref="NotFoundResult"/> if the plugin could not 
 114    [HttpDelete("{pluginId}/{version}")]
 115    [ProducesResponseType(StatusCodes.Status204NoContent)]
 116    [ProducesResponseType(StatusCodes.Status404NotFound)]
 117    public ActionResult UninstallPluginByVersion([FromRoute, Required] Guid pluginId, [FromRoute, Required] Version vers
 118    {
 0119        var plugin = _pluginManager.GetPlugin(pluginId, version);
 0120        if (plugin is null)
 121        {
 0122            return NotFound();
 123        }
 124
 0125        _installationManager.UninstallPlugin(plugin);
 0126        return NoContent();
 127    }
 128
 129    /// <summary>
 130    /// Uninstalls a plugin.
 131    /// </summary>
 132    /// <param name="pluginId">Plugin id.</param>
 133    /// <response code="204">Plugin uninstalled.</response>
 134    /// <response code="404">Plugin not found.</response>
 135    /// <returns>An <see cref="NoContentResult"/> on success, or a <see cref="NotFoundResult"/> if the plugin could not 
 136    [HttpDelete("{pluginId}")]
 137    [ProducesResponseType(StatusCodes.Status204NoContent)]
 138    [ProducesResponseType(StatusCodes.Status404NotFound)]
 139    [Obsolete("Please use the UninstallPluginByVersion API.")]
 140    public ActionResult UninstallPlugin([FromRoute, Required] Guid pluginId)
 141    {
 142        // If no version is given, return the current instance.
 143        var plugins = _pluginManager.Plugins.Where(p => p.Id.Equals(pluginId)).ToList();
 144
 145        // Select the un-instanced one first.
 146        var plugin = plugins.FirstOrDefault(p => p.Instance is null) ?? plugins.MinBy(p => p.Manifest.Status);
 147
 148        if (plugin is not null)
 149        {
 150            _installationManager.UninstallPlugin(plugin);
 151            return NoContent();
 152        }
 153
 154        return NotFound();
 155    }
 156
 157    /// <summary>
 158    /// Gets plugin configuration.
 159    /// </summary>
 160    /// <param name="pluginId">Plugin id.</param>
 161    /// <response code="200">Plugin configuration returned.</response>
 162    /// <response code="404">Plugin not found or plugin configuration not found.</response>
 163    /// <returns>Plugin configuration.</returns>
 164    [HttpGet("{pluginId}/Configuration")]
 165    [ProducesResponseType(StatusCodes.Status200OK)]
 166    [ProducesResponseType(StatusCodes.Status404NotFound)]
 167    public ActionResult<BasePluginConfiguration> GetPluginConfiguration([FromRoute, Required] Guid pluginId)
 168    {
 0169        var plugin = _pluginManager.GetPlugin(pluginId);
 0170        if (plugin?.Instance is IHasPluginConfiguration configPlugin)
 171        {
 0172            return configPlugin.Configuration;
 173        }
 174
 0175        return NotFound();
 176    }
 177
 178    /// <summary>
 179    /// Updates plugin configuration.
 180    /// </summary>
 181    /// <remarks>
 182    /// Accepts plugin configuration as JSON body.
 183    /// </remarks>
 184    /// <param name="pluginId">Plugin id.</param>
 185    /// <response code="204">Plugin configuration updated.</response>
 186    /// <response code="404">Plugin not found or plugin does not have configuration.</response>
 187    /// <returns>An <see cref="NoContentResult"/> on success, or a <see cref="NotFoundResult"/> if the plugin could not 
 188    [HttpPost("{pluginId}/Configuration")]
 189    [ProducesResponseType(StatusCodes.Status204NoContent)]
 190    [ProducesResponseType(StatusCodes.Status404NotFound)]
 191    public async Task<ActionResult> UpdatePluginConfiguration([FromRoute, Required] Guid pluginId)
 192    {
 193        var plugin = _pluginManager.GetPlugin(pluginId);
 194        if (plugin?.Instance is not IHasPluginConfiguration configPlugin)
 195        {
 196            return NotFound();
 197        }
 198
 199        var configuration = (BasePluginConfiguration?)await JsonSerializer.DeserializeAsync(Request.Body, configPlugin.C
 200            .ConfigureAwait(false);
 201
 202        if (configuration is not null)
 203        {
 204            configPlugin.UpdateConfiguration(configuration);
 205        }
 206
 207        return NoContent();
 208    }
 209
 210    /// <summary>
 211    /// Gets a plugin's image.
 212    /// </summary>
 213    /// <param name="pluginId">Plugin id.</param>
 214    /// <param name="version">Plugin version.</param>
 215    /// <response code="200">Plugin image returned.</response>
 216    /// <returns>Plugin's image.</returns>
 217    [HttpGet("{pluginId}/{version}/Image")]
 218    [ProducesResponseType(StatusCodes.Status200OK)]
 219    [ProducesResponseType(StatusCodes.Status404NotFound)]
 220    [ProducesImageFile]
 221    [AllowAnonymous]
 222    public ActionResult GetPluginImage([FromRoute, Required] Guid pluginId, [FromRoute, Required] Version version)
 223    {
 0224        var plugin = _pluginManager.GetPlugin(pluginId, version);
 0225        if (plugin is null)
 226        {
 0227            return NotFound();
 228        }
 229
 0230        var imagePath = Path.Combine(plugin.Path, plugin.Manifest.ImagePath ?? string.Empty);
 0231        if (plugin.Manifest.ImagePath is null || !System.IO.File.Exists(imagePath))
 232        {
 0233            return NotFound();
 234        }
 235
 0236        Response.Headers.ContentDisposition = "attachment";
 237
 0238        imagePath = Path.Combine(plugin.Path, plugin.Manifest.ImagePath);
 0239        return PhysicalFile(imagePath, MimeTypes.GetMimeType(imagePath));
 240    }
 241
 242    /// <summary>
 243    /// Gets a plugin's manifest.
 244    /// </summary>
 245    /// <param name="pluginId">Plugin id.</param>
 246    /// <response code="204">Plugin manifest returned.</response>
 247    /// <response code="404">Plugin not found.</response>
 248    /// <returns>A <see cref="PluginManifest"/> on success, or a <see cref="NotFoundResult"/> if the plugin could not be
 249    [HttpPost("{pluginId}/Manifest")]
 250    [ProducesResponseType(StatusCodes.Status204NoContent)]
 251    [ProducesResponseType(StatusCodes.Status404NotFound)]
 252    public ActionResult<PluginManifest> GetPluginManifest([FromRoute, Required] Guid pluginId)
 253    {
 0254        var plugin = _pluginManager.GetPlugin(pluginId);
 255
 0256        if (plugin is not null)
 257        {
 0258            return plugin.Manifest;
 259        }
 260
 0261        return NotFound();
 262    }
 263}