#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); } } }