From 461b298be7247afd7f7962604efab3b58b9dae4b Mon Sep 17 00:00:00 2001 From: crobibero Date: Tue, 21 Apr 2020 19:15:27 -0600 Subject: Migrate DlnaServerController to Jellyfin.Api --- Jellyfin.Api/Controllers/DlnaServerController.cs | 259 +++++++++++++++++++++++ 1 file changed, 259 insertions(+) create mode 100644 Jellyfin.Api/Controllers/DlnaServerController.cs (limited to 'Jellyfin.Api/Controllers/DlnaServerController.cs') diff --git a/Jellyfin.Api/Controllers/DlnaServerController.cs b/Jellyfin.Api/Controllers/DlnaServerController.cs new file mode 100644 index 000000000..731d6707c --- /dev/null +++ b/Jellyfin.Api/Controllers/DlnaServerController.cs @@ -0,0 +1,259 @@ +#nullable enable + +using System; +using System.IO; +using System.Threading.Tasks; +using Emby.Dlna; +using Emby.Dlna.Main; +using Jellyfin.Api.Attributes; +using MediaBrowser.Controller.Dlna; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +#pragma warning disable CA1801 + +namespace Jellyfin.Api.Controllers +{ + /// + /// Dlna Server Controller. + /// + [Route("Dlna")] + public class DlnaServerController : BaseJellyfinApiController + { + private const string XMLContentType = "text/xml; charset=UTF-8"; + + private readonly IDlnaManager _dlnaManager; + private readonly IContentDirectory _contentDirectory; + private readonly IConnectionManager _connectionManager; + private readonly IMediaReceiverRegistrar _mediaReceiverRegistrar; + + /// + /// Initializes a new instance of the class. + /// + /// Instance of the interface. + public DlnaServerController(IDlnaManager dlnaManager) + { + _dlnaManager = dlnaManager; + _contentDirectory = DlnaEntryPoint.Current.ContentDirectory; + _connectionManager = DlnaEntryPoint.Current.ConnectionManager; + _mediaReceiverRegistrar = DlnaEntryPoint.Current.MediaReceiverRegistrar; + } + + /// + /// Get Description Xml. + /// + /// Server UUID. + /// Description Xml. + [HttpGet("{Uuid}/description.xml")] + [HttpGet("{Uuid}/description")] + [Produces(XMLContentType)] + [ProducesResponseType(StatusCodes.Status200OK)] + public ActionResult GetDescriptionXml([FromRoute] string uuid) + { + var url = GetAbsoluteUri(); + var serverAddress = url.Substring(0, url.IndexOf("/dlna/", StringComparison.OrdinalIgnoreCase)); + var xml = _dlnaManager.GetServerDescriptionXml(Request.Headers, uuid, serverAddress); + + // TODO GetStaticResult doesn't do anything special? + /* + var cacheLength = TimeSpan.FromDays(1); + var cacheKey = Request.Path.Value.GetMD5(); + var bytes = Encoding.UTF8.GetBytes(xml); + */ + return Ok(xml); + } + + /// + /// Gets Dlna content directory xml. + /// + /// Server UUID. + /// Dlna content directory xml. + [HttpGet("{Uuid}/ContentDirectory/ContentDirectory.xml")] + [HttpGet("{Uuid}/ContentDirectory/ContentDirectory")] + [Produces(XMLContentType)] + [ProducesResponseType(StatusCodes.Status200OK)] + public ActionResult GetContentDirectory([FromRoute] string uuid) + { + return Ok(_contentDirectory.GetServiceXml()); + } + + /// + /// Gets Dlna media receiver registrar xml. + /// + /// Server UUID. + /// Dlna media receiver registrar xml. + [HttpGet("{Uuid}/MediaReceiverRegistrar/MediaReceiverRegistrar.xml")] + [HttpGet("{Uuid}/MediaReceiverRegistrar/MediaReceiverRegistrar")] + [Produces(XMLContentType)] + [ProducesResponseType(StatusCodes.Status200OK)] + public ActionResult GetMediaReceiverRegistrar([FromRoute] string uuid) + { + return Ok(_mediaReceiverRegistrar.GetServiceXml()); + } + + /// + /// Gets Dlna media receiver registrar xml. + /// + /// Server UUID. + /// Dlna media receiver registrar xml. + [HttpGet("{Uuid}/ConnectionManager/ConnectionManager.xml")] + [HttpGet("{Uuid}/ConnectionManager/ConnectionManager")] + [Produces(XMLContentType)] + [ProducesResponseType(StatusCodes.Status200OK)] + public ActionResult GetConnectionManager([FromRoute] string uuid) + { + return Ok(_connectionManager.GetServiceXml()); + } + + /// + /// Process a content directory control request. + /// + /// Server UUID. + /// Control response. + [HttpPost("{Uuid}/ContentDirectory/Control")] + public async Task> ProcessContentDirectoryControlRequest([FromRoute] string uuid) + { + var response = await PostAsync(uuid, Request.Body, _contentDirectory).ConfigureAwait(false); + return Ok(response); + } + + /// + /// Process a connection manager control request. + /// + /// Server UUID. + /// Control response. + [HttpPost("{Uuid}/ConnectionManager/Control")] + public async Task> ProcessConnectionManagerControlRequest([FromRoute] string uuid) + { + var response = await PostAsync(uuid, Request.Body, _connectionManager).ConfigureAwait(false); + return Ok(response); + } + + /// + /// Process a media receiver registrar control request. + /// + /// Server UUID. + /// Control response. + [HttpPost("{Uuid}/MediaReceiverRegistrar/Control")] + public async Task> ProcessMediaReceiverRegistrarControlRequest([FromRoute] string uuid) + { + var response = await PostAsync(uuid, Request.Body, _mediaReceiverRegistrar).ConfigureAwait(false); + return Ok(response); + } + + /// + /// Processes an event subscription request. + /// + /// Server UUID. + /// Event subscription response. + [HttpSubscribe("{Uuid}/MediaReceiverRegistrar/Events")] + [HttpUnsubscribe("{Uuid}/MediaReceiverRegistrar/Events")] + public ActionResult ProcessMediaReceiverRegistrarEventRequest(string uuid) + { + return Ok(ProcessEventRequest(_mediaReceiverRegistrar)); + } + + /// + /// Processes an event subscription request. + /// + /// Server UUID. + /// Event subscription response. + [HttpSubscribe("{Uuid}/ContentDirectory/Events")] + [HttpUnsubscribe("{Uuid}/ContentDirectory/Events")] + public ActionResult ProcessContentDirectoryEventRequest(string uuid) + { + return Ok(ProcessEventRequest(_contentDirectory)); + } + + /// + /// Processes an event subscription request. + /// + /// Server UUID. + /// Event subscription response. + [HttpSubscribe("{Uuid}/ConnectionManager/Events")] + [HttpUnsubscribe("{Uuid}/ConnectionManager/Events")] + public ActionResult ProcessConnectionManagerEventRequest(string uuid) + { + return Ok(ProcessEventRequest(_connectionManager)); + } + + /// + /// Gets a server icon. + /// + /// Server UUID. + /// The icon filename. + /// Icon stream. + [HttpGet("{Uuid}/icons/{Filename}")] + public ActionResult GetIconId([FromRoute] string uuid, [FromRoute] string fileName) + { + return GetIcon(fileName); + } + + /// + /// Gets a server icon. + /// + /// Server UUID. + /// The icon filename. + /// Icon stream. + [HttpGet("icons/{Filename}")] + public ActionResult GetIcon([FromQuery] string uuid, [FromRoute] string fileName) + { + return GetIcon(fileName); + } + + private ActionResult GetIcon(string fileName) + { + var icon = _dlnaManager.GetIcon(fileName); + if (icon == null) + { + return NotFound(); + } + + var contentType = "image/" + Path.GetExtension(fileName) + .TrimStart('.') + .ToLowerInvariant(); + + return new FileStreamResult(icon.Stream, contentType); + } + + private string GetAbsoluteUri() + { + return $"{Request.Scheme}://{Request.Host}{Request.Path}"; + } + + private Task PostAsync(string id, Stream requestStream, IUpnpService service) + { + return service.ProcessControlRequestAsync(new ControlRequest + { + Headers = Request.Headers, + InputXml = requestStream, + TargetServerUuId = id, + RequestedUrl = GetAbsoluteUri() + }); + } + + private EventSubscriptionResponse ProcessEventRequest(IEventManager eventManager) + { + var subscriptionId = Request.Headers["SID"]; + if (string.Equals(Request.Method, "subscribe", StringComparison.OrdinalIgnoreCase)) + { + var notificationType = Request.Headers["NT"]; + var callback = Request.Headers["CALLBACK"]; + var timeoutString = Request.Headers["TIMEOUT"]; + + if (string.IsNullOrEmpty(notificationType)) + { + return eventManager.RenewEventSubscription( + subscriptionId, + notificationType, + timeoutString, + callback); + } + + return eventManager.CreateEventSubscription(notificationType, timeoutString, callback); + } + + return eventManager.CancelEventSubscription(subscriptionId); + } + } +} -- cgit v1.2.3 From 4bb1e1c29246e9293ee119e85c41edef51c39972 Mon Sep 17 00:00:00 2001 From: crobibero Date: Fri, 31 Jul 2020 11:01:30 -0600 Subject: Fix docs, params, return values --- Jellyfin.Api/Controllers/DlnaServerController.cs | 140 +++++++++++------------ 1 file changed, 69 insertions(+), 71 deletions(-) (limited to 'Jellyfin.Api/Controllers/DlnaServerController.cs') diff --git a/Jellyfin.Api/Controllers/DlnaServerController.cs b/Jellyfin.Api/Controllers/DlnaServerController.cs index 731d6707c..2f5561adb 100644 --- a/Jellyfin.Api/Controllers/DlnaServerController.cs +++ b/Jellyfin.Api/Controllers/DlnaServerController.cs @@ -1,6 +1,5 @@ -#nullable enable - using System; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Threading.Tasks; using Emby.Dlna; @@ -10,8 +9,6 @@ using MediaBrowser.Controller.Dlna; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -#pragma warning disable CA1801 - namespace Jellyfin.Api.Controllers { /// @@ -42,37 +39,33 @@ namespace Jellyfin.Api.Controllers /// /// Get Description Xml. /// - /// Server UUID. - /// Description Xml. - [HttpGet("{Uuid}/description.xml")] - [HttpGet("{Uuid}/description")] + /// Server UUID. + /// Description xml returned. + /// An containing the description xml. + [HttpGet("{serverId}/description.xml")] + [HttpGet("{serverId}/description")] [Produces(XMLContentType)] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult GetDescriptionXml([FromRoute] string uuid) + public ActionResult GetDescriptionXml([FromRoute] string serverId) { var url = GetAbsoluteUri(); var serverAddress = url.Substring(0, url.IndexOf("/dlna/", StringComparison.OrdinalIgnoreCase)); - var xml = _dlnaManager.GetServerDescriptionXml(Request.Headers, uuid, serverAddress); - - // TODO GetStaticResult doesn't do anything special? - /* - var cacheLength = TimeSpan.FromDays(1); - var cacheKey = Request.Path.Value.GetMD5(); - var bytes = Encoding.UTF8.GetBytes(xml); - */ + var xml = _dlnaManager.GetServerDescriptionXml(Request.Headers, serverId, serverAddress); return Ok(xml); } /// /// Gets Dlna content directory xml. /// - /// Server UUID. - /// Dlna content directory xml. - [HttpGet("{Uuid}/ContentDirectory/ContentDirectory.xml")] - [HttpGet("{Uuid}/ContentDirectory/ContentDirectory")] + /// Server UUID. + /// Dlna content directory returned. + /// An containing the dlna content directory xml. + [HttpGet("{serverId}/ContentDirectory/ContentDirectory.xml")] + [HttpGet("{serverId}/ContentDirectory/ContentDirectory")] [Produces(XMLContentType)] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult GetContentDirectory([FromRoute] string uuid) + [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")] + public ActionResult GetContentDirectory([FromRoute] string serverId) { return Ok(_contentDirectory.GetServiceXml()); } @@ -80,13 +73,14 @@ namespace Jellyfin.Api.Controllers /// /// Gets Dlna media receiver registrar xml. /// - /// Server UUID. + /// Server UUID. /// Dlna media receiver registrar xml. - [HttpGet("{Uuid}/MediaReceiverRegistrar/MediaReceiverRegistrar.xml")] - [HttpGet("{Uuid}/MediaReceiverRegistrar/MediaReceiverRegistrar")] + [HttpGet("{serverId}/MediaReceiverRegistrar/MediaReceiverRegistrar.xml")] + [HttpGet("{serverId}/MediaReceiverRegistrar/MediaReceiverRegistrar")] [Produces(XMLContentType)] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult GetMediaReceiverRegistrar([FromRoute] string uuid) + [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")] + public ActionResult GetMediaReceiverRegistrar([FromRoute] string serverId) { return Ok(_mediaReceiverRegistrar.GetServiceXml()); } @@ -94,13 +88,14 @@ namespace Jellyfin.Api.Controllers /// /// Gets Dlna media receiver registrar xml. /// - /// Server UUID. + /// Server UUID. /// Dlna media receiver registrar xml. - [HttpGet("{Uuid}/ConnectionManager/ConnectionManager.xml")] - [HttpGet("{Uuid}/ConnectionManager/ConnectionManager")] + [HttpGet("{serverId}/ConnectionManager/ConnectionManager.xml")] + [HttpGet("{serverId}/ConnectionManager/ConnectionManager")] [Produces(XMLContentType)] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult GetConnectionManager([FromRoute] string uuid) + [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")] + public ActionResult GetConnectionManager([FromRoute] string serverId) { return Ok(_connectionManager.GetServiceXml()); } @@ -108,100 +103,103 @@ namespace Jellyfin.Api.Controllers /// /// Process a content directory control request. /// - /// Server UUID. + /// Server UUID. /// Control response. - [HttpPost("{Uuid}/ContentDirectory/Control")] - public async Task> ProcessContentDirectoryControlRequest([FromRoute] string uuid) + [HttpPost("{serverId}/ContentDirectory/Control")] + public async Task> ProcessContentDirectoryControlRequest([FromRoute] string serverId) { - var response = await PostAsync(uuid, Request.Body, _contentDirectory).ConfigureAwait(false); - return Ok(response); + return await ProcessControlRequestInternalAsync(serverId, Request.Body, _contentDirectory).ConfigureAwait(false); } /// /// Process a connection manager control request. /// - /// Server UUID. + /// Server UUID. /// Control response. - [HttpPost("{Uuid}/ConnectionManager/Control")] - public async Task> ProcessConnectionManagerControlRequest([FromRoute] string uuid) + [HttpPost("{serverId}/ConnectionManager/Control")] + public async Task> ProcessConnectionManagerControlRequest([FromRoute] string serverId) { - var response = await PostAsync(uuid, Request.Body, _connectionManager).ConfigureAwait(false); - return Ok(response); + return await ProcessControlRequestInternalAsync(serverId, Request.Body, _connectionManager).ConfigureAwait(false); } /// /// Process a media receiver registrar control request. /// - /// Server UUID. + /// Server UUID. /// Control response. - [HttpPost("{Uuid}/MediaReceiverRegistrar/Control")] - public async Task> ProcessMediaReceiverRegistrarControlRequest([FromRoute] string uuid) + [HttpPost("{serverId}/MediaReceiverRegistrar/Control")] + public async Task> ProcessMediaReceiverRegistrarControlRequest([FromRoute] string serverId) { - var response = await PostAsync(uuid, Request.Body, _mediaReceiverRegistrar).ConfigureAwait(false); - return Ok(response); + return await ProcessControlRequestInternalAsync(serverId, Request.Body, _mediaReceiverRegistrar).ConfigureAwait(false); } /// /// Processes an event subscription request. /// - /// Server UUID. + /// Server UUID. /// Event subscription response. - [HttpSubscribe("{Uuid}/MediaReceiverRegistrar/Events")] - [HttpUnsubscribe("{Uuid}/MediaReceiverRegistrar/Events")] - public ActionResult ProcessMediaReceiverRegistrarEventRequest(string uuid) + [HttpSubscribe("{serverId}/MediaReceiverRegistrar/Events")] + [HttpUnsubscribe("{serverId}/MediaReceiverRegistrar/Events")] + [ApiExplorerSettings(IgnoreApi = true)] // Ignore in openapi docs + [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")] + public ActionResult ProcessMediaReceiverRegistrarEventRequest(string serverId) { - return Ok(ProcessEventRequest(_mediaReceiverRegistrar)); + return ProcessEventRequest(_mediaReceiverRegistrar); } /// /// Processes an event subscription request. /// - /// Server UUID. + /// Server UUID. /// Event subscription response. - [HttpSubscribe("{Uuid}/ContentDirectory/Events")] - [HttpUnsubscribe("{Uuid}/ContentDirectory/Events")] - public ActionResult ProcessContentDirectoryEventRequest(string uuid) + [HttpSubscribe("{serverId}/ContentDirectory/Events")] + [HttpUnsubscribe("{serverId}/ContentDirectory/Events")] + [ApiExplorerSettings(IgnoreApi = true)] // Ignore in openapi docs + [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")] + public ActionResult ProcessContentDirectoryEventRequest(string serverId) { - return Ok(ProcessEventRequest(_contentDirectory)); + return ProcessEventRequest(_contentDirectory); } /// /// Processes an event subscription request. /// - /// Server UUID. + /// Server UUID. /// Event subscription response. - [HttpSubscribe("{Uuid}/ConnectionManager/Events")] - [HttpUnsubscribe("{Uuid}/ConnectionManager/Events")] - public ActionResult ProcessConnectionManagerEventRequest(string uuid) + [HttpSubscribe("{serverId}/ConnectionManager/Events")] + [HttpUnsubscribe("{serverId}/ConnectionManager/Events")] + [ApiExplorerSettings(IgnoreApi = true)] // Ignore in openapi docs + [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")] + public ActionResult ProcessConnectionManagerEventRequest(string serverId) { - return Ok(ProcessEventRequest(_connectionManager)); + return ProcessEventRequest(_connectionManager); } /// /// Gets a server icon. /// - /// Server UUID. + /// Server UUID. /// The icon filename. /// Icon stream. - [HttpGet("{Uuid}/icons/{Filename}")] - public ActionResult GetIconId([FromRoute] string uuid, [FromRoute] string fileName) + [HttpGet("{serverId}/icons/{filename}")] + [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")] + public ActionResult GetIconId([FromRoute] string serverId, [FromRoute] string fileName) { - return GetIcon(fileName); + return GetIconInternal(fileName); } /// /// Gets a server icon. /// - /// Server UUID. /// The icon filename. /// Icon stream. - [HttpGet("icons/{Filename}")] - public ActionResult GetIcon([FromQuery] string uuid, [FromRoute] string fileName) + [HttpGet("icons/{filename}")] + public ActionResult GetIcon([FromRoute] string fileName) { - return GetIcon(fileName); + return GetIconInternal(fileName); } - private ActionResult GetIcon(string fileName) + private ActionResult GetIconInternal(string fileName) { var icon = _dlnaManager.GetIcon(fileName); if (icon == null) @@ -213,7 +211,7 @@ namespace Jellyfin.Api.Controllers .TrimStart('.') .ToLowerInvariant(); - return new FileStreamResult(icon.Stream, contentType); + return File(icon.Stream, contentType); } private string GetAbsoluteUri() @@ -221,7 +219,7 @@ namespace Jellyfin.Api.Controllers return $"{Request.Scheme}://{Request.Host}{Request.Path}"; } - private Task PostAsync(string id, Stream requestStream, IUpnpService service) + private Task ProcessControlRequestInternalAsync(string id, Stream requestStream, IUpnpService service) { return service.ProcessControlRequestAsync(new ControlRequest { -- cgit v1.2.3