af1152b001
Refactored all C# test files to use explicit namespace declarations and moved all using directives inside the namespace block for consistency. Updated assembly info and cache files to reflect the new build. Adjusted MvcTestingAppManifest.json to use fully qualified assembly names. No functional changes; all updates are related to code style, organization, and project metadata.
268 lines
10 KiB
C#
268 lines
10 KiB
C#
// <copyright file="PluginsController.cs" company="PlaceholderCompany">
|
|
// Copyright (c) PlaceholderCompany. All rights reserved.
|
|
// </copyright>
|
|
|
|
namespace Jellyfin.Api.Controllers;
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel.DataAnnotations;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text.Json;
|
|
using System.Threading.Tasks;
|
|
using Jellyfin.Api.Attributes;
|
|
using Jellyfin.Api.Constants;
|
|
using Jellyfin.Extensions.Json;
|
|
using MediaBrowser.Common.Api;
|
|
using MediaBrowser.Common.Plugins;
|
|
using MediaBrowser.Common.Updates;
|
|
using MediaBrowser.Model.Net;
|
|
using MediaBrowser.Model.Plugins;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Http;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
|
|
/// <summary>
|
|
/// Plugins controller.
|
|
/// </summary>
|
|
[Authorize(Policy = Policies.RequiresElevation)]
|
|
public class PluginsController : BaseJellyfinApiController
|
|
{
|
|
private readonly IInstallationManager _installationManager;
|
|
private readonly IPluginManager _pluginManager;
|
|
private readonly JsonSerializerOptions _serializerOptions;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="PluginsController"/> class.
|
|
/// </summary>
|
|
/// <param name="installationManager">Instance of the <see cref="IInstallationManager"/> interface.</param>
|
|
/// <param name="pluginManager">Instance of the <see cref="IPluginManager"/> interface.</param>
|
|
public PluginsController(
|
|
IInstallationManager installationManager,
|
|
IPluginManager pluginManager)
|
|
{
|
|
_installationManager = installationManager;
|
|
_pluginManager = pluginManager;
|
|
_serializerOptions = JsonDefaults.Options;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a list of currently installed plugins.
|
|
/// </summary>
|
|
/// <response code="200">Installed plugins returned.</response>
|
|
/// <returns>List of currently installed plugins.</returns>
|
|
[HttpGet]
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
public ActionResult<IEnumerable<PluginInfo>> GetPlugins()
|
|
{
|
|
return Ok(_pluginManager.Plugins
|
|
.OrderBy(p => p.Name)
|
|
.Select(p => p.GetPluginInfo()));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Enables a disabled plugin.
|
|
/// </summary>
|
|
/// <param name="pluginId">Plugin id.</param>
|
|
/// <param name="version">Plugin version.</param>
|
|
/// <response code="204">Plugin enabled.</response>
|
|
/// <response code="404">Plugin not found.</response>
|
|
/// <returns>An <see cref="NoContentResult"/> on success, or a <see cref="NotFoundResult"/> if the plugin could not be found.</returns>
|
|
[HttpPost("{pluginId}/{version}/Enable")]
|
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
|
public ActionResult EnablePlugin([FromRoute, Required] Guid pluginId, [FromRoute, Required] Version version)
|
|
{
|
|
var plugin = _pluginManager.GetPlugin(pluginId, version);
|
|
if (plugin is null)
|
|
{
|
|
return NotFound();
|
|
}
|
|
|
|
_pluginManager.EnablePlugin(plugin);
|
|
return NoContent();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Disable a plugin.
|
|
/// </summary>
|
|
/// <param name="pluginId">Plugin id.</param>
|
|
/// <param name="version">Plugin version.</param>
|
|
/// <response code="204">Plugin disabled.</response>
|
|
/// <response code="404">Plugin not found.</response>
|
|
/// <returns>An <see cref="NoContentResult"/> on success, or a <see cref="NotFoundResult"/> if the plugin could not be found.</returns>
|
|
[HttpPost("{pluginId}/{version}/Disable")]
|
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
|
public ActionResult DisablePlugin([FromRoute, Required] Guid pluginId, [FromRoute, Required] Version version)
|
|
{
|
|
var plugin = _pluginManager.GetPlugin(pluginId, version);
|
|
if (plugin is null)
|
|
{
|
|
return NotFound();
|
|
}
|
|
|
|
_pluginManager.DisablePlugin(plugin);
|
|
return NoContent();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Uninstalls a plugin by version.
|
|
/// </summary>
|
|
/// <param name="pluginId">Plugin id.</param>
|
|
/// <param name="version">Plugin version.</param>
|
|
/// <response code="204">Plugin uninstalled.</response>
|
|
/// <response code="404">Plugin not found.</response>
|
|
/// <returns>An <see cref="NoContentResult"/> on success, or a <see cref="NotFoundResult"/> if the plugin could not be found.</returns>
|
|
[HttpDelete("{pluginId}/{version}")]
|
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
|
public ActionResult UninstallPluginByVersion([FromRoute, Required] Guid pluginId, [FromRoute, Required] Version version)
|
|
{
|
|
var plugin = _pluginManager.GetPlugin(pluginId, version);
|
|
if (plugin is null)
|
|
{
|
|
return NotFound();
|
|
}
|
|
|
|
_installationManager.UninstallPlugin(plugin);
|
|
return NoContent();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Uninstalls a plugin.
|
|
/// </summary>
|
|
/// <param name="pluginId">Plugin id.</param>
|
|
/// <response code="204">Plugin uninstalled.</response>
|
|
/// <response code="404">Plugin not found.</response>
|
|
/// <returns>An <see cref="NoContentResult"/> on success, or a <see cref="NotFoundResult"/> if the plugin could not be found.</returns>
|
|
[HttpDelete("{pluginId}")]
|
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
|
[Obsolete("Please use the UninstallPluginByVersion API.")]
|
|
public ActionResult UninstallPlugin([FromRoute, Required] Guid pluginId)
|
|
{
|
|
// If no version is given, return the current instance.
|
|
var plugins = _pluginManager.Plugins.Where(p => p.Id.Equals(pluginId)).ToList();
|
|
|
|
// Select the un-instanced one first.
|
|
var plugin = plugins.FirstOrDefault(p => p.Instance is null) ?? plugins.MinBy(p => p.Manifest.Status);
|
|
|
|
if (plugin is not null)
|
|
{
|
|
_installationManager.UninstallPlugin(plugin);
|
|
return NoContent();
|
|
}
|
|
|
|
return NotFound();
|
|
}
|
|
|
|
/// <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")]
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
|
public ActionResult<BasePluginConfiguration> GetPluginConfiguration([FromRoute, Required] Guid pluginId)
|
|
{
|
|
var plugin = _pluginManager.GetPlugin(pluginId);
|
|
if (plugin?.Instance is IHasPluginConfiguration configPlugin)
|
|
{
|
|
return configPlugin.Configuration;
|
|
}
|
|
|
|
return NotFound();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Updates plugin configuration.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Accepts plugin configuration as JSON body.
|
|
/// </remarks>
|
|
/// <param name="pluginId">Plugin id.</param>
|
|
/// <response code="204">Plugin configuration updated.</response>
|
|
/// <response code="404">Plugin not found or plugin does not have configuration.</response>
|
|
/// <returns>An <see cref="NoContentResult"/> on success, or a <see cref="NotFoundResult"/> if the plugin could not be found.</returns>
|
|
[HttpPost("{pluginId}/Configuration")]
|
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
|
public async Task<ActionResult> UpdatePluginConfiguration([FromRoute, Required] Guid pluginId)
|
|
{
|
|
var plugin = _pluginManager.GetPlugin(pluginId);
|
|
if (plugin?.Instance is not IHasPluginConfiguration configPlugin)
|
|
{
|
|
return NotFound();
|
|
}
|
|
|
|
var configuration = (BasePluginConfiguration?)await JsonSerializer.DeserializeAsync(Request.Body, configPlugin.ConfigurationType, _serializerOptions)
|
|
.ConfigureAwait(false);
|
|
|
|
if (configuration is not null)
|
|
{
|
|
configPlugin.UpdateConfiguration(configuration);
|
|
}
|
|
|
|
return NoContent();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a plugin's image.
|
|
/// </summary>
|
|
/// <param name="pluginId">Plugin id.</param>
|
|
/// <param name="version">Plugin version.</param>
|
|
/// <response code="200">Plugin image returned.</response>
|
|
/// <returns>Plugin's image.</returns>
|
|
[HttpGet("{pluginId}/{version}/Image")]
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
|
[ProducesImageFile]
|
|
[AllowAnonymous]
|
|
public ActionResult GetPluginImage([FromRoute, Required] Guid pluginId, [FromRoute, Required] Version version)
|
|
{
|
|
var plugin = _pluginManager.GetPlugin(pluginId, version);
|
|
if (plugin is null)
|
|
{
|
|
return NotFound();
|
|
}
|
|
|
|
var imagePath = Path.Combine(plugin.Path, plugin.Manifest.ImagePath ?? string.Empty);
|
|
if (plugin.Manifest.ImagePath is null || !System.IO.File.Exists(imagePath))
|
|
{
|
|
return NotFound();
|
|
}
|
|
|
|
Response.Headers.ContentDisposition = "attachment";
|
|
|
|
imagePath = Path.Combine(plugin.Path, plugin.Manifest.ImagePath);
|
|
return PhysicalFile(imagePath, MimeTypes.GetMimeType(imagePath));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a plugin's manifest.
|
|
/// </summary>
|
|
/// <param name="pluginId">Plugin id.</param>
|
|
/// <response code="204">Plugin manifest returned.</response>
|
|
/// <response code="404">Plugin not found.</response>
|
|
/// <returns>A <see cref="PluginManifest"/> on success, or a <see cref="NotFoundResult"/> if the plugin could not be found.</returns>
|
|
[HttpPost("{pluginId}/Manifest")]
|
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
|
public ActionResult<PluginManifest> GetPluginManifest([FromRoute, Required] Guid pluginId)
|
|
{
|
|
var plugin = _pluginManager.GetPlugin(pluginId);
|
|
|
|
if (plugin is not null)
|
|
{
|
|
return plugin.Manifest;
|
|
}
|
|
|
|
return NotFound();
|
|
}
|
|
}
|