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 From 9e00aa3014c0044c0918a775c3394763666b30af Mon Sep 17 00:00:00 2001 From: crobibero Date: Mon, 3 Aug 2020 14:38:51 -0600 Subject: fix openapi validation errors --- Jellyfin.Api/Controllers/AudioController.cs | 8 ++-- Jellyfin.Api/Controllers/BrandingController.cs | 2 +- Jellyfin.Api/Controllers/DlnaServerController.cs | 12 +++--- Jellyfin.Api/Controllers/DynamicHlsController.cs | 4 +- Jellyfin.Api/Controllers/HlsSegmentController.cs | 4 +- Jellyfin.Api/Controllers/ImageController.cs | 28 +++++++------- Jellyfin.Api/Controllers/ItemsController.cs | 2 +- Jellyfin.Api/Controllers/LibraryController.cs | 14 +++---- Jellyfin.Api/Controllers/LiveTvController.cs | 6 +-- Jellyfin.Api/Controllers/SessionController.cs | 2 +- Jellyfin.Api/Controllers/StartupController.cs | 4 +- Jellyfin.Api/Controllers/SubtitleController.cs | 2 +- Jellyfin.Api/Controllers/SyncPlayController.cs | 18 ++++----- Jellyfin.Api/Controllers/SystemController.cs | 4 +- .../Controllers/UniversalAudioController.cs | 6 +-- Jellyfin.Api/Controllers/VideosController.cs | 6 +-- .../Extensions/ApiServiceCollectionExtensions.cs | 11 +++++- tests/Jellyfin.Api.Tests/GetPathValueTests.cs | 45 ---------------------- 18 files changed, 70 insertions(+), 108 deletions(-) delete mode 100644 tests/Jellyfin.Api.Tests/GetPathValueTests.cs (limited to 'Jellyfin.Api/Controllers/DlnaServerController.cs') diff --git a/Jellyfin.Api/Controllers/AudioController.cs b/Jellyfin.Api/Controllers/AudioController.cs index ebae1caa0..4de87616c 100644 --- a/Jellyfin.Api/Controllers/AudioController.cs +++ b/Jellyfin.Api/Controllers/AudioController.cs @@ -144,10 +144,10 @@ namespace Jellyfin.Api.Controllers /// Optional. The streaming options. /// Audio stream returned. /// A containing the audio file. - [HttpGet("{itemId}/{stream=stream}.{container?}")] - [HttpGet("{itemId}/stream")] - [HttpHead("{itemId}/{stream=stream}.{container?}")] - [HttpHead("{itemId}/stream")] + [HttpGet("{itemId}/{stream=stream}.{container?}", Name = "GetAudioStreamByContainer")] + [HttpGet("{itemId}/stream", Name = "GetAudioStream")] + [HttpHead("{itemId}/{stream=stream}.{container?}", Name = "HeadAudioStreamByContainer")] + [HttpHead("{itemId}/stream", Name = "HeadAudioStream")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task GetAudioStream( [FromRoute] Guid itemId, diff --git a/Jellyfin.Api/Controllers/BrandingController.cs b/Jellyfin.Api/Controllers/BrandingController.cs index 67790c0e4..1d4836f27 100644 --- a/Jellyfin.Api/Controllers/BrandingController.cs +++ b/Jellyfin.Api/Controllers/BrandingController.cs @@ -44,7 +44,7 @@ namespace Jellyfin.Api.Controllers /// or a if the css is not configured. /// [HttpGet("Css")] - [HttpGet("Css.css")] + [HttpGet("Css.css", Name = "GetBrandingCss_2")] [Produces("text/css")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status204NoContent)] diff --git a/Jellyfin.Api/Controllers/DlnaServerController.cs b/Jellyfin.Api/Controllers/DlnaServerController.cs index 2f5561adb..ef507f2ed 100644 --- a/Jellyfin.Api/Controllers/DlnaServerController.cs +++ b/Jellyfin.Api/Controllers/DlnaServerController.cs @@ -42,8 +42,8 @@ namespace Jellyfin.Api.Controllers /// Server UUID. /// Description xml returned. /// An containing the description xml. - [HttpGet("{serverId}/description.xml")] [HttpGet("{serverId}/description")] + [HttpGet("{serverId}/description.xml", Name = "GetDescriptionXml_2")] [Produces(XMLContentType)] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult GetDescriptionXml([FromRoute] string serverId) @@ -60,8 +60,8 @@ namespace Jellyfin.Api.Controllers /// Server UUID. /// Dlna content directory returned. /// An containing the dlna content directory xml. - [HttpGet("{serverId}/ContentDirectory/ContentDirectory.xml")] [HttpGet("{serverId}/ContentDirectory/ContentDirectory")] + [HttpGet("{serverId}/ContentDirectory/ContentDirectory.xml", Name = "GetContentDirectory_2")] [Produces(XMLContentType)] [ProducesResponseType(StatusCodes.Status200OK)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")] @@ -75,8 +75,8 @@ namespace Jellyfin.Api.Controllers /// /// Server UUID. /// Dlna media receiver registrar xml. - [HttpGet("{serverId}/MediaReceiverRegistrar/MediaReceiverRegistrar.xml")] [HttpGet("{serverId}/MediaReceiverRegistrar/MediaReceiverRegistrar")] + [HttpGet("{serverId}/MediaReceiverRegistrar/MediaReceiverRegistrar.xml", Name = "GetMediaReceiverRegistrar_2")] [Produces(XMLContentType)] [ProducesResponseType(StatusCodes.Status200OK)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")] @@ -90,8 +90,8 @@ namespace Jellyfin.Api.Controllers /// /// Server UUID. /// Dlna media receiver registrar xml. - [HttpGet("{serverId}/ConnectionManager/ConnectionManager.xml")] [HttpGet("{serverId}/ConnectionManager/ConnectionManager")] + [HttpGet("{serverId}/ConnectionManager/ConnectionManager.xml", Name = "GetConnectionManager_2")] [Produces(XMLContentType)] [ProducesResponseType(StatusCodes.Status200OK)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")] @@ -181,7 +181,7 @@ namespace Jellyfin.Api.Controllers /// Server UUID. /// The icon filename. /// Icon stream. - [HttpGet("{serverId}/icons/{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) { @@ -193,7 +193,7 @@ namespace Jellyfin.Api.Controllers /// /// The icon filename. /// Icon stream. - [HttpGet("icons/{filename}")] + [HttpGet("icons/{fileName}")] public ActionResult GetIcon([FromRoute] string fileName) { return GetIconInternal(fileName); diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index b7e1837c9..c4f79ce95 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -165,7 +165,7 @@ namespace Jellyfin.Api.Controllers /// Video stream returned. /// A containing the playlist file. [HttpGet("/Videos/{itemId}/master.m3u8")] - [HttpHead("/Videos/{itemId}/master.m3u8")] + [HttpHead("/Videos/{itemId}/master.m3u8", Name = "HeadMasterHlsVideoPlaylist")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task GetMasterHlsVideoPlaylist( [FromRoute] Guid itemId, @@ -335,7 +335,7 @@ namespace Jellyfin.Api.Controllers /// Audio stream returned. /// A containing the playlist file. [HttpGet("/Audio/{itemId}/master.m3u8")] - [HttpHead("/Audio/{itemId}/master.m3u8")] + [HttpHead("/Audio/{itemId}/master.m3u8", Name = "HeadMasterHlsAudioPlaylist")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task GetMasterHlsAudioPlaylist( [FromRoute] Guid itemId, diff --git a/Jellyfin.Api/Controllers/HlsSegmentController.cs b/Jellyfin.Api/Controllers/HlsSegmentController.cs index efdb6a369..7bf9326a7 100644 --- a/Jellyfin.Api/Controllers/HlsSegmentController.cs +++ b/Jellyfin.Api/Controllers/HlsSegmentController.cs @@ -50,8 +50,8 @@ namespace Jellyfin.Api.Controllers /// A containing the audio stream. // Can't require authentication just yet due to seeing some requests come from Chrome without full query string // [Authenticated] - [HttpGet("/Audio/{itemId}/hls/{segmentId}/stream.mp3")] - [HttpGet("/Audio/{itemId}/hls/{segmentId}/stream.aac")] + [HttpGet("/Audio/{itemId}/hls/{segmentId}/stream.mp3", Name = "GetHlsAudioSegmentLegacyMp3")] + [HttpGet("/Audio/{itemId}/hls/{segmentId}/stream.aac", Name = "GetHlsAudioSegmentLegacyAac")] [ProducesResponseType(StatusCodes.Status200OK)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "itemId", Justification = "Required for ServiceStack")] public ActionResult GetHlsAudioSegmentLegacy([FromRoute] string itemId, [FromRoute] string segmentId) diff --git a/Jellyfin.Api/Controllers/ImageController.cs b/Jellyfin.Api/Controllers/ImageController.cs index 18220c5f3..3a445b1b3 100644 --- a/Jellyfin.Api/Controllers/ImageController.cs +++ b/Jellyfin.Api/Controllers/ImageController.cs @@ -82,7 +82,7 @@ namespace Jellyfin.Api.Controllers /// User does not have permission to delete the image. /// A . [HttpPost("/Users/{userId}/Images/{imageType}")] - [HttpPost("/Users/{userId}/Images/{imageType}/{index?}")] + [HttpPost("/Users/{userId}/Images/{imageType}/{index?}", Name = "PostUserImage_2")] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "imageType", Justification = "Imported from ServiceStack")] @@ -128,7 +128,7 @@ namespace Jellyfin.Api.Controllers /// User does not have permission to delete the image. /// A . [HttpDelete("/Users/{userId}/Images/{itemType}")] - [HttpDelete("/Users/{userId}/Images/{itemType}/{index?}")] + [HttpDelete("/Users/{userId}/Images/{itemType}/{index?}", Name = "DeleteUserImage_2")] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "imageType", Justification = "Imported from ServiceStack")] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "index", Justification = "Imported from ServiceStack")] [ProducesResponseType(StatusCodes.Status204NoContent)] @@ -167,7 +167,7 @@ namespace Jellyfin.Api.Controllers /// Item not found. /// A on success, or a if item not found. [HttpDelete("/Items/{itemId}/Images/{imageType}")] - [HttpDelete("/Items/{itemId}/Images/{imageType}/{imageIndex?}")] + [HttpDelete("/Items/{itemId}/Images/{imageType}/{imageIndex?}", Name = "DeleteItemImage_2")] [Authorize(Policy = Policies.RequiresElevation)] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] @@ -196,7 +196,7 @@ namespace Jellyfin.Api.Controllers /// Item not found. /// A on success, or a if item not found. [HttpPost("/Items/{itemId}/Images/{imageType}")] - [HttpPost("/Items/{itemId}/Images/{imageType}/{imageIndex?}")] + [HttpPost("/Items/{itemId}/Images/{imageType}/{imageIndex?}", Name = "SetItemImage_2")] [Authorize(Policy = Policies.RequiresElevation)] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] @@ -342,9 +342,9 @@ namespace Jellyfin.Api.Controllers /// or a if item not found. /// [HttpGet("/Items/{itemId}/Images/{imageType}")] - [HttpHead("/Items/{itemId}/Images/{imageType}")] - [HttpGet("/Items/{itemId}/Images/{imageType}/{imageIndex?}")] - [HttpHead("/Items/{itemId}/Images/{imageType}/{imageIndex?}")] + [HttpHead("/Items/{itemId}/Images/{imageType}", Name = "HeadItemImage")] + [HttpGet("/Items/{itemId}/Images/{imageType}/{imageIndex?}", Name = "GetItemImage_2")] + [HttpHead("/Items/{itemId}/Images/{imageType}/{imageIndex?}", Name = "HeadItemImage_2")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task GetItemImage( @@ -422,7 +422,7 @@ namespace Jellyfin.Api.Controllers /// or a if item not found. /// [HttpGet("/Items/{itemId}/Images/{imageType}/{imageIndex}/{tag}/{format}/{maxWidth}/{maxHeight}/{percentPlayed}/{unplayedCount}")] - [HttpHead("/Items/{itemId}/Images/{imageType}/{imageIndex}/{tag}/{format}/{maxWidth}/{maxHeight}/{percentPlayed}/{unplayedCount}")] + [HttpHead("/Items/{itemId}/Images/{imageType}/{imageIndex}/{tag}/{format}/{maxWidth}/{maxHeight}/{percentPlayed}/{unplayedCount}", Name = "HeadItemImage2")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task GetItemImage2( @@ -500,7 +500,7 @@ namespace Jellyfin.Api.Controllers /// or a if item not found. /// [HttpGet("/Artists/{name}/Images/{imageType}/{imageIndex?}")] - [HttpHead("/Artists/{name}/Images/{imageType}/{imageIndex?}")] + [HttpHead("/Artists/{name}/Images/{imageType}/{imageIndex?}", Name = "HeadArtistImage")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task GetArtistImage( @@ -578,7 +578,7 @@ namespace Jellyfin.Api.Controllers /// or a if item not found. /// [HttpGet("/Genres/{name}/Images/{imageType}/{imageIndex?}")] - [HttpHead("/Genres/{name}/Images/{imageType}/{imageIndex?}")] + [HttpHead("/Genres/{name}/Images/{imageType}/{imageIndex?}", Name = "HeadGenreImage")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task GetGenreImage( @@ -656,7 +656,7 @@ namespace Jellyfin.Api.Controllers /// or a if item not found. /// [HttpGet("/MusicGenres/{name}/Images/{imageType}/{imageIndex?}")] - [HttpHead("/MusicGenres/{name}/Images/{imageType}/{imageIndex?}")] + [HttpHead("/MusicGenres/{name}/Images/{imageType}/{imageIndex?}", Name = "HeadMusicGenreImage")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task GetMusicGenreImage( @@ -734,7 +734,7 @@ namespace Jellyfin.Api.Controllers /// or a if item not found. /// [HttpGet("/Persons/{name}/Images/{imageType}/{imageIndex?}")] - [HttpHead("/Persons/{name}/Images/{imageType}/{imageIndex?}")] + [HttpHead("/Persons/{name}/Images/{imageType}/{imageIndex?}", Name = "HeadPersonImage")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task GetPersonImage( @@ -812,7 +812,7 @@ namespace Jellyfin.Api.Controllers /// or a if item not found. /// [HttpGet("/Studios/{name}/Images/{imageType}/{imageIndex?}")] - [HttpHead("/Studios/{name}/Images/{imageType}/{imageIndex?}")] + [HttpHead("/Studios/{name}/Images/{imageType}/{imageIndex?}", Name = "HeadStudioImage")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task GetStudioImage( @@ -890,7 +890,7 @@ namespace Jellyfin.Api.Controllers /// or a if item not found. /// [HttpGet("/Users/{userId}/Images/{imageType}/{imageIndex?}")] - [HttpHead("/Users/{userId}/Images/{imageType}/{imageIndex?}")] + [HttpHead("/Users/{userId}/Images/{imageType}/{imageIndex?}", Name = "HeadUserImage")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task GetUserImage( diff --git a/Jellyfin.Api/Controllers/ItemsController.cs b/Jellyfin.Api/Controllers/ItemsController.cs index 49fb9238f..354741ced 100644 --- a/Jellyfin.Api/Controllers/ItemsController.cs +++ b/Jellyfin.Api/Controllers/ItemsController.cs @@ -140,7 +140,7 @@ namespace Jellyfin.Api.Controllers /// Optional, include image information in output. /// A with the items. [HttpGet("/Items")] - [HttpGet("/Users/{uId}/Items")] + [HttpGet("/Users/{uId}/Items", Name = "GetItems_2")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetItems( [FromRoute] Guid? uId, diff --git a/Jellyfin.Api/Controllers/LibraryController.cs b/Jellyfin.Api/Controllers/LibraryController.cs index 5ad466c55..0ec7e2b8c 100644 --- a/Jellyfin.Api/Controllers/LibraryController.cs +++ b/Jellyfin.Api/Controllers/LibraryController.cs @@ -521,7 +521,7 @@ namespace Jellyfin.Api.Controllers /// The tvdbId. /// Report success. /// A . - [HttpPost("/Library/Series/Added")] + [HttpPost("/Library/Series/Added", Name = "PostAddedSeries")] [HttpPost("/Library/Series/Updated")] [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status204NoContent)] @@ -551,7 +551,7 @@ namespace Jellyfin.Api.Controllers /// The imdbId. /// Report success. /// A . - [HttpPost("/Library/Movies/Added")] + [HttpPost("/Library/Movies/Added", Name = "PostAddedMovies")] [HttpPost("/Library/Movies/Updated")] [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status204NoContent)] @@ -679,12 +679,12 @@ namespace Jellyfin.Api.Controllers /// Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimited. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines, TrailerUrls. /// Similar items returned. /// A containing the similar items. - [HttpGet("/Artists/{itemId}/Similar")] + [HttpGet("/Artists/{itemId}/Similar", Name = "GetSimilarArtists2")] [HttpGet("/Items/{itemId}/Similar")] - [HttpGet("/Albums/{itemId}/Similar")] - [HttpGet("/Shows/{itemId}/Similar")] - [HttpGet("/Movies/{itemId}/Similar")] - [HttpGet("/Trailers/{itemId}/Similar")] + [HttpGet("/Albums/{itemId}/Similar", Name = "GetSimilarAlbums2")] + [HttpGet("/Shows/{itemId}/Similar", Name = "GetSimilarShows2")] + [HttpGet("/Movies/{itemId}/Similar", Name = "GetSimilarMovies2")] + [HttpGet("/Trailers/{itemId}/Similar", Name = "GetSimilarTrailers2")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetSimilarItems( [FromRoute] Guid itemId, diff --git a/Jellyfin.Api/Controllers/LiveTvController.cs b/Jellyfin.Api/Controllers/LiveTvController.cs index bbe5544f9..89112eea7 100644 --- a/Jellyfin.Api/Controllers/LiveTvController.cs +++ b/Jellyfin.Api/Controllers/LiveTvController.cs @@ -127,7 +127,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Channels")] [ProducesResponseType(StatusCodes.Status200OK)] [Authorize(Policy = Policies.DefaultAuthorization)] - public ActionResult> GetChannels( + public ActionResult> GetLiveTvChannels( [FromQuery] ChannelType? type, [FromQuery] Guid? userId, [FromQuery] int? startIndex, @@ -535,7 +535,7 @@ namespace Jellyfin.Api.Controllers [HttpGet("Programs")] [ProducesResponseType(StatusCodes.Status200OK)] [Authorize(Policy = Policies.DefaultAuthorization)] - public async Task>> GetPrograms( + public async Task>> GetLiveTvPrograms( [FromQuery] string? channelIds, [FromQuery] Guid? userId, [FromQuery] DateTime? minStartDate, @@ -933,7 +933,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status404NotFound)] [Obsolete("This endpoint is obsolete.")] - public ActionResult GetRecordingGroup([FromQuery] Guid? groupId) + public ActionResult GetRecordingGroup([FromRoute] Guid? groupId) { return NotFound(); } diff --git a/Jellyfin.Api/Controllers/SessionController.cs b/Jellyfin.Api/Controllers/SessionController.cs index 0c98a8e71..1b300e0d8 100644 --- a/Jellyfin.Api/Controllers/SessionController.cs +++ b/Jellyfin.Api/Controllers/SessionController.cs @@ -241,7 +241,7 @@ namespace Jellyfin.Api.Controllers /// The command to send. /// General command sent to session. /// A . - [HttpPost("/Sessions/{sessionId}/Command/{Command}")] + [HttpPost("/Sessions/{sessionId}/Command/{command}")] [ProducesResponseType(StatusCodes.Status204NoContent)] public ActionResult SendGeneralCommand( [FromRoute] string? sessionId, diff --git a/Jellyfin.Api/Controllers/StartupController.cs b/Jellyfin.Api/Controllers/StartupController.cs index f9e4e61b5..c8e3cc4f5 100644 --- a/Jellyfin.Api/Controllers/StartupController.cs +++ b/Jellyfin.Api/Controllers/StartupController.cs @@ -106,7 +106,7 @@ namespace Jellyfin.Api.Controllers /// Initial user retrieved. /// The first user. [HttpGet("User")] - [HttpGet("FirstUser")] + [HttpGet("FirstUser", Name = "GetFirstUser_2")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task GetFirstUser() { @@ -131,7 +131,7 @@ namespace Jellyfin.Api.Controllers /// [HttpPost("User")] [ProducesResponseType(StatusCodes.Status204NoContent)] - public async Task UpdateUser([FromForm] StartupUserDto startupUserDto) + public async Task UpdateStartupUser([FromForm] StartupUserDto startupUserDto) { var user = _userManager.Users.First(); diff --git a/Jellyfin.Api/Controllers/SubtitleController.cs b/Jellyfin.Api/Controllers/SubtitleController.cs index b62ff80fc..f8c19d15c 100644 --- a/Jellyfin.Api/Controllers/SubtitleController.cs +++ b/Jellyfin.Api/Controllers/SubtitleController.cs @@ -182,7 +182,7 @@ namespace Jellyfin.Api.Controllers /// File returned. /// A with the subtitle file. [HttpGet("/Videos/{itemId}/{mediaSourceId}/Subtitles/{index}/Stream.{format}")] - [HttpGet("/Videos/{itemId}/{mediaSourceId}/Subtitles/{index}/{startPositionTicks?}/Stream.{format}")] + [HttpGet("/Videos/{itemId}/{mediaSourceId}/Subtitles/{index}/{startPositionTicks?}/Stream.{format}", Name = "GetSubtitle_2")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task GetSubtitle( [FromRoute, Required] Guid itemId, diff --git a/Jellyfin.Api/Controllers/SyncPlayController.cs b/Jellyfin.Api/Controllers/SyncPlayController.cs index 55ed42227..2b1b95b1b 100644 --- a/Jellyfin.Api/Controllers/SyncPlayController.cs +++ b/Jellyfin.Api/Controllers/SyncPlayController.cs @@ -47,7 +47,7 @@ namespace Jellyfin.Api.Controllers /// A indicating success. [HttpPost("New")] [ProducesResponseType(StatusCodes.Status204NoContent)] - public ActionResult CreateNewGroup() + public ActionResult SyncPlayCreateGroup() { var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request); _syncPlayManager.NewGroup(currentSession, CancellationToken.None); @@ -62,7 +62,7 @@ namespace Jellyfin.Api.Controllers /// A indicating success. [HttpPost("Join")] [ProducesResponseType(StatusCodes.Status204NoContent)] - public ActionResult JoinGroup([FromQuery, Required] Guid groupId) + public ActionResult SyncPlayJoinGroup([FromQuery, Required] Guid groupId) { var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request); @@ -82,7 +82,7 @@ namespace Jellyfin.Api.Controllers /// A indicating success. [HttpPost("Leave")] [ProducesResponseType(StatusCodes.Status204NoContent)] - public ActionResult LeaveGroup() + public ActionResult SyncPlayLeaveGroup() { var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request); _syncPlayManager.LeaveGroup(currentSession, CancellationToken.None); @@ -97,7 +97,7 @@ namespace Jellyfin.Api.Controllers /// An containing the available SyncPlay groups. [HttpGet("List")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult> GetSyncPlayGroups([FromQuery] Guid? filterItemId) + public ActionResult> SyncPlayGetGroups([FromQuery] Guid? filterItemId) { var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request); return Ok(_syncPlayManager.ListGroups(currentSession, filterItemId.HasValue ? filterItemId.Value : Guid.Empty)); @@ -110,7 +110,7 @@ namespace Jellyfin.Api.Controllers /// A indicating success. [HttpPost("Play")] [ProducesResponseType(StatusCodes.Status204NoContent)] - public ActionResult Play() + public ActionResult SyncPlayPlay() { var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request); var syncPlayRequest = new PlaybackRequest() @@ -128,7 +128,7 @@ namespace Jellyfin.Api.Controllers /// A indicating success. [HttpPost("Pause")] [ProducesResponseType(StatusCodes.Status204NoContent)] - public ActionResult Pause() + public ActionResult SyncPlayPause() { var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request); var syncPlayRequest = new PlaybackRequest() @@ -147,7 +147,7 @@ namespace Jellyfin.Api.Controllers /// A indicating success. [HttpPost("Seek")] [ProducesResponseType(StatusCodes.Status204NoContent)] - public ActionResult Seek([FromQuery] long positionTicks) + public ActionResult SyncPlaySeek([FromQuery] long positionTicks) { var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request); var syncPlayRequest = new PlaybackRequest() @@ -169,7 +169,7 @@ namespace Jellyfin.Api.Controllers /// A indicating success. [HttpPost("Buffering")] [ProducesResponseType(StatusCodes.Status204NoContent)] - public ActionResult Buffering([FromQuery] DateTime when, [FromQuery] long positionTicks, [FromQuery] bool bufferingDone) + public ActionResult SyncPlayBuffering([FromQuery] DateTime when, [FromQuery] long positionTicks, [FromQuery] bool bufferingDone) { var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request); var syncPlayRequest = new PlaybackRequest() @@ -190,7 +190,7 @@ namespace Jellyfin.Api.Controllers /// A indicating success. [HttpPost("Ping")] [ProducesResponseType(StatusCodes.Status204NoContent)] - public ActionResult Ping([FromQuery] double ping) + public ActionResult SyncPlayPing([FromQuery] double ping) { var currentSession = RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request); var syncPlayRequest = new PlaybackRequest() diff --git a/Jellyfin.Api/Controllers/SystemController.cs b/Jellyfin.Api/Controllers/SystemController.cs index bc606f7aa..e0bce3a41 100644 --- a/Jellyfin.Api/Controllers/SystemController.cs +++ b/Jellyfin.Api/Controllers/SystemController.cs @@ -85,8 +85,8 @@ namespace Jellyfin.Api.Controllers /// /// Information retrieved. /// The server name. - [HttpGet("Ping")] - [HttpPost("Ping")] + [HttpGet("Ping", Name = "GetPingSystem")] + [HttpPost("Ping", Name = "PostPingSystem")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult PingSystem() { diff --git a/Jellyfin.Api/Controllers/UniversalAudioController.cs b/Jellyfin.Api/Controllers/UniversalAudioController.cs index 50ab0ac05..5a9bec2b0 100644 --- a/Jellyfin.Api/Controllers/UniversalAudioController.cs +++ b/Jellyfin.Api/Controllers/UniversalAudioController.cs @@ -69,9 +69,9 @@ namespace Jellyfin.Api.Controllers /// Redirected to remote audio stream. /// A containing the audio file. [HttpGet("/Audio/{itemId}/universal")] - [HttpGet("/Audio/{itemId}/{universal=universal}.{container?}")] - [HttpHead("/Audio/{itemId}/universal")] - [HttpHead("/Audio/{itemId}/{universal=universal}.{container?}")] + [HttpGet("/Audio/{itemId}/{universal=universal}.{container?}", Name = "GetUniversalAudioStream_2")] + [HttpHead("/Audio/{itemId}/universal", Name = "HeadUniversalAudioStream")] + [HttpHead("/Audio/{itemId}/{universal=universal}.{container?}", Name = "HeadUniversalAudioStream_2")] [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status302Found)] diff --git a/Jellyfin.Api/Controllers/VideosController.cs b/Jellyfin.Api/Controllers/VideosController.cs index d1ef817eb..ebe88a9c0 100644 --- a/Jellyfin.Api/Controllers/VideosController.cs +++ b/Jellyfin.Api/Controllers/VideosController.cs @@ -316,10 +316,10 @@ namespace Jellyfin.Api.Controllers /// Optional. The streaming options. /// Video stream returned. /// A containing the audio file. - [HttpGet("{itemId}/{stream=stream}.{container?}")] + [HttpGet("{itemId}/{stream=stream}.{container?}", Name = "GetVideoStream_2")] [HttpGet("{itemId}/stream")] - [HttpHead("{itemId}/{stream=stream}.{container?}")] - [HttpHead("{itemId}/stream")] + [HttpHead("{itemId}/{stream=stream}.{container?}", Name = "HeadVideoStream_2")] + [HttpHead("{itemId}/stream", Name = "HeadVideoStream")] [ProducesResponseType(StatusCodes.Status200OK)] public async Task GetVideoStream( [FromRoute] Guid itemId, diff --git a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs index cfbabf795..6e91042df 100644 --- a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs +++ b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs @@ -198,8 +198,15 @@ namespace Jellyfin.Server.Extensions $"{description.ActionDescriptor.RouteValues["controller"]}_{description.RelativePath}"); // Use method name as operationId - c.CustomOperationIds(description => - description.TryGetMethodInfo(out MethodInfo methodInfo) ? methodInfo.Name : null); + c.CustomOperationIds( + description => + { + description.TryGetMethodInfo(out MethodInfo methodInfo); + // Attribute name, method name, none. + return description?.ActionDescriptor?.AttributeRouteInfo?.Name + ?? methodInfo?.Name + ?? null; + }); // TODO - remove when all types are supported in System.Text.Json c.AddSwaggerTypeMappings(); diff --git a/tests/Jellyfin.Api.Tests/GetPathValueTests.cs b/tests/Jellyfin.Api.Tests/GetPathValueTests.cs deleted file mode 100644 index 397eb2edc..000000000 --- a/tests/Jellyfin.Api.Tests/GetPathValueTests.cs +++ /dev/null @@ -1,45 +0,0 @@ -using MediaBrowser.Api; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Net; -using MediaBrowser.Model.Configuration; -using MediaBrowser.Model.Services; -using Microsoft.Extensions.Logging.Abstractions; -using Moq; -using Xunit; - -namespace Jellyfin.Api.Tests -{ - public class GetPathValueTests - { - [Theory] - [InlineData("https://localhost:8096/ScheduledTasks/1234/Triggers", "", 1, "1234")] - [InlineData("https://localhost:8096/emby/ScheduledTasks/1234/Triggers", "", 1, "1234")] - [InlineData("https://localhost:8096/mediabrowser/ScheduledTasks/1234/Triggers", "", 1, "1234")] - [InlineData("https://localhost:8096/jellyfin/2/ScheduledTasks/1234/Triggers", "jellyfin/2", 1, "1234")] - [InlineData("https://localhost:8096/jellyfin/2/emby/ScheduledTasks/1234/Triggers", "jellyfin/2", 1, "1234")] - [InlineData("https://localhost:8096/jellyfin/2/mediabrowser/ScheduledTasks/1234/Triggers", "jellyfin/2", 1, "1234")] - [InlineData("https://localhost:8096/JELLYFIN/2/ScheduledTasks/1234/Triggers", "jellyfin/2", 1, "1234")] - [InlineData("https://localhost:8096/JELLYFIN/2/Emby/ScheduledTasks/1234/Triggers", "jellyfin/2", 1, "1234")] - [InlineData("https://localhost:8096/JELLYFIN/2/MediaBrowser/ScheduledTasks/1234/Triggers", "jellyfin/2", 1, "1234")] - public void GetPathValueTest(string path, string baseUrl, int index, string value) - { - var reqMock = Mock.Of(x => x.PathInfo == path); - var conf = new ServerConfiguration() - { - BaseUrl = baseUrl - }; - - var confManagerMock = Mock.Of(x => x.Configuration == conf); - - var service = new TestService( - new NullLogger(), - confManagerMock, - Mock.Of()) - { - Request = reqMock - }; - - Assert.Equal(value, service.GetPathValue(index).ToString()); - } - } -} -- cgit v1.2.3 From da9bcc5fb3a9544e2f9bf9fa3ccbd03837d060ed Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Thu, 13 Aug 2020 15:47:31 -0400 Subject: Rename IEventManager to IDlnaEventManager --- Emby.Dlna/Emby.Dlna.csproj | 2 +- Emby.Dlna/Eventing/DlnaEventManager.cs | 200 +++++++++++++++++++++++ Emby.Dlna/Eventing/EventManager.cs | 200 ----------------------- Emby.Dlna/IConnectionManager.cs | 2 +- Emby.Dlna/IContentDirectory.cs | 2 +- Emby.Dlna/IDlnaEventManager.cs | 23 +++ Emby.Dlna/IEventManager.cs | 23 --- Emby.Dlna/IMediaReceiverRegistrar.cs | 2 +- Emby.Dlna/Service/BaseService.cs | 12 +- Jellyfin.Api/Controllers/DlnaServerController.cs | 8 +- 10 files changed, 237 insertions(+), 237 deletions(-) create mode 100644 Emby.Dlna/Eventing/DlnaEventManager.cs delete mode 100644 Emby.Dlna/Eventing/EventManager.cs create mode 100644 Emby.Dlna/IDlnaEventManager.cs delete mode 100644 Emby.Dlna/IEventManager.cs (limited to 'Jellyfin.Api/Controllers/DlnaServerController.cs') diff --git a/Emby.Dlna/Emby.Dlna.csproj b/Emby.Dlna/Emby.Dlna.csproj index 42a5f95c1..1eaa16f6b 100644 --- a/Emby.Dlna/Emby.Dlna.csproj +++ b/Emby.Dlna/Emby.Dlna.csproj @@ -20,7 +20,7 @@ netstandard2.1 false true - true + true diff --git a/Emby.Dlna/Eventing/DlnaEventManager.cs b/Emby.Dlna/Eventing/DlnaEventManager.cs new file mode 100644 index 000000000..782295290 --- /dev/null +++ b/Emby.Dlna/Eventing/DlnaEventManager.cs @@ -0,0 +1,200 @@ +#pragma warning disable CS1591 + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.Net; +using Microsoft.Extensions.Logging; + +namespace Emby.Dlna.Eventing +{ + public class DlnaEventManager : IDlnaEventManager + { + private readonly ConcurrentDictionary _subscriptions = + new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); + + private readonly ILogger _logger; + private readonly IHttpClient _httpClient; + + public DlnaEventManager(ILogger logger, IHttpClient httpClient) + { + _httpClient = httpClient; + _logger = logger; + } + + public EventSubscriptionResponse RenewEventSubscription(string subscriptionId, string notificationType, string requestedTimeoutString, string callbackUrl) + { + var subscription = GetSubscription(subscriptionId, false); + if (subscription != null) + { + subscription.TimeoutSeconds = ParseTimeout(requestedTimeoutString) ?? 300; + int timeoutSeconds = subscription.TimeoutSeconds; + subscription.SubscriptionTime = DateTime.UtcNow; + + _logger.LogDebug( + "Renewing event subscription for {0} with timeout of {1} to {2}", + subscription.NotificationType, + timeoutSeconds, + subscription.CallbackUrl); + + return GetEventSubscriptionResponse(subscriptionId, requestedTimeoutString, timeoutSeconds); + } + + return new EventSubscriptionResponse + { + Content = string.Empty, + ContentType = "text/plain" + }; + } + + public EventSubscriptionResponse CreateEventSubscription(string notificationType, string requestedTimeoutString, string callbackUrl) + { + var timeout = ParseTimeout(requestedTimeoutString) ?? 300; + var id = "uuid:" + Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture); + + _logger.LogDebug("Creating event subscription for {0} with timeout of {1} to {2}", + notificationType, + timeout, + callbackUrl); + + _subscriptions.TryAdd(id, new EventSubscription + { + Id = id, + CallbackUrl = callbackUrl, + SubscriptionTime = DateTime.UtcNow, + TimeoutSeconds = timeout + }); + + return GetEventSubscriptionResponse(id, requestedTimeoutString, timeout); + } + + private int? ParseTimeout(string header) + { + if (!string.IsNullOrEmpty(header)) + { + // Starts with SECOND- + header = header.Split('-').Last(); + + if (int.TryParse(header, NumberStyles.Integer, _usCulture, out var val)) + { + return val; + } + } + + return null; + } + + public EventSubscriptionResponse CancelEventSubscription(string subscriptionId) + { + _logger.LogDebug("Cancelling event subscription {0}", subscriptionId); + + _subscriptions.TryRemove(subscriptionId, out EventSubscription sub); + + return new EventSubscriptionResponse + { + Content = string.Empty, + ContentType = "text/plain" + }; + } + + private readonly CultureInfo _usCulture = new CultureInfo("en-US"); + private EventSubscriptionResponse GetEventSubscriptionResponse(string subscriptionId, string requestedTimeoutString, int timeoutSeconds) + { + var response = new EventSubscriptionResponse + { + Content = string.Empty, + ContentType = "text/plain" + }; + + response.Headers["SID"] = subscriptionId; + response.Headers["TIMEOUT"] = string.IsNullOrEmpty(requestedTimeoutString) ? ("SECOND-" + timeoutSeconds.ToString(_usCulture)) : requestedTimeoutString; + + return response; + } + + public EventSubscription GetSubscription(string id) + { + return GetSubscription(id, false); + } + + private EventSubscription GetSubscription(string id, bool throwOnMissing) + { + if (!_subscriptions.TryGetValue(id, out EventSubscription e) && throwOnMissing) + { + throw new ResourceNotFoundException("Event with Id " + id + " not found."); + } + + return e; + } + + public Task TriggerEvent(string notificationType, IDictionary stateVariables) + { + var subs = _subscriptions.Values + .Where(i => !i.IsExpired && string.Equals(notificationType, i.NotificationType, StringComparison.OrdinalIgnoreCase)) + .ToList(); + + var tasks = subs.Select(i => TriggerEvent(i, stateVariables)); + + return Task.WhenAll(tasks); + } + + private async Task TriggerEvent(EventSubscription subscription, IDictionary stateVariables) + { + var builder = new StringBuilder(); + + builder.Append(""); + builder.Append(""); + foreach (var key in stateVariables.Keys) + { + builder.Append("") + .Append('<') + .Append(key) + .Append('>') + .Append(stateVariables[key]) + .Append("') + .Append(""); + } + + builder.Append(""); + + var options = new HttpRequestOptions + { + RequestContent = builder.ToString(), + RequestContentType = "text/xml", + Url = subscription.CallbackUrl, + BufferContent = false + }; + + options.RequestHeaders.Add("NT", subscription.NotificationType); + options.RequestHeaders.Add("NTS", "upnp:propchange"); + options.RequestHeaders.Add("SID", subscription.Id); + options.RequestHeaders.Add("SEQ", subscription.TriggerCount.ToString(_usCulture)); + + try + { + using (await _httpClient.SendAsync(options, new HttpMethod("NOTIFY")).ConfigureAwait(false)) + { + } + } + catch (OperationCanceledException) + { + } + catch + { + // Already logged at lower levels + } + finally + { + subscription.IncrementTriggerCount(); + } + } + } +} diff --git a/Emby.Dlna/Eventing/EventManager.cs b/Emby.Dlna/Eventing/EventManager.cs deleted file mode 100644 index 7d02f5e96..000000000 --- a/Emby.Dlna/Eventing/EventManager.cs +++ /dev/null @@ -1,200 +0,0 @@ -#pragma warning disable CS1591 - -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Net.Http; -using System.Text; -using System.Threading.Tasks; -using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Net; -using Microsoft.Extensions.Logging; - -namespace Emby.Dlna.Eventing -{ - public class EventManager : IEventManager - { - private readonly ConcurrentDictionary _subscriptions = - new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); - - private readonly ILogger _logger; - private readonly IHttpClient _httpClient; - - public EventManager(ILogger logger, IHttpClient httpClient) - { - _httpClient = httpClient; - _logger = logger; - } - - public EventSubscriptionResponse RenewEventSubscription(string subscriptionId, string notificationType, string requestedTimeoutString, string callbackUrl) - { - var subscription = GetSubscription(subscriptionId, false); - if (subscription != null) - { - subscription.TimeoutSeconds = ParseTimeout(requestedTimeoutString) ?? 300; - int timeoutSeconds = subscription.TimeoutSeconds; - subscription.SubscriptionTime = DateTime.UtcNow; - - _logger.LogDebug( - "Renewing event subscription for {0} with timeout of {1} to {2}", - subscription.NotificationType, - timeoutSeconds, - subscription.CallbackUrl); - - return GetEventSubscriptionResponse(subscriptionId, requestedTimeoutString, timeoutSeconds); - } - - return new EventSubscriptionResponse - { - Content = string.Empty, - ContentType = "text/plain" - }; - } - - public EventSubscriptionResponse CreateEventSubscription(string notificationType, string requestedTimeoutString, string callbackUrl) - { - var timeout = ParseTimeout(requestedTimeoutString) ?? 300; - var id = "uuid:" + Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture); - - _logger.LogDebug("Creating event subscription for {0} with timeout of {1} to {2}", - notificationType, - timeout, - callbackUrl); - - _subscriptions.TryAdd(id, new EventSubscription - { - Id = id, - CallbackUrl = callbackUrl, - SubscriptionTime = DateTime.UtcNow, - TimeoutSeconds = timeout - }); - - return GetEventSubscriptionResponse(id, requestedTimeoutString, timeout); - } - - private int? ParseTimeout(string header) - { - if (!string.IsNullOrEmpty(header)) - { - // Starts with SECOND- - header = header.Split('-').Last(); - - if (int.TryParse(header, NumberStyles.Integer, _usCulture, out var val)) - { - return val; - } - } - - return null; - } - - public EventSubscriptionResponse CancelEventSubscription(string subscriptionId) - { - _logger.LogDebug("Cancelling event subscription {0}", subscriptionId); - - _subscriptions.TryRemove(subscriptionId, out EventSubscription sub); - - return new EventSubscriptionResponse - { - Content = string.Empty, - ContentType = "text/plain" - }; - } - - private readonly CultureInfo _usCulture = new CultureInfo("en-US"); - private EventSubscriptionResponse GetEventSubscriptionResponse(string subscriptionId, string requestedTimeoutString, int timeoutSeconds) - { - var response = new EventSubscriptionResponse - { - Content = string.Empty, - ContentType = "text/plain" - }; - - response.Headers["SID"] = subscriptionId; - response.Headers["TIMEOUT"] = string.IsNullOrEmpty(requestedTimeoutString) ? ("SECOND-" + timeoutSeconds.ToString(_usCulture)) : requestedTimeoutString; - - return response; - } - - public EventSubscription GetSubscription(string id) - { - return GetSubscription(id, false); - } - - private EventSubscription GetSubscription(string id, bool throwOnMissing) - { - if (!_subscriptions.TryGetValue(id, out EventSubscription e) && throwOnMissing) - { - throw new ResourceNotFoundException("Event with Id " + id + " not found."); - } - - return e; - } - - public Task TriggerEvent(string notificationType, IDictionary stateVariables) - { - var subs = _subscriptions.Values - .Where(i => !i.IsExpired && string.Equals(notificationType, i.NotificationType, StringComparison.OrdinalIgnoreCase)) - .ToList(); - - var tasks = subs.Select(i => TriggerEvent(i, stateVariables)); - - return Task.WhenAll(tasks); - } - - private async Task TriggerEvent(EventSubscription subscription, IDictionary stateVariables) - { - var builder = new StringBuilder(); - - builder.Append(""); - builder.Append(""); - foreach (var key in stateVariables.Keys) - { - builder.Append("") - .Append('<') - .Append(key) - .Append('>') - .Append(stateVariables[key]) - .Append("') - .Append(""); - } - - builder.Append(""); - - var options = new HttpRequestOptions - { - RequestContent = builder.ToString(), - RequestContentType = "text/xml", - Url = subscription.CallbackUrl, - BufferContent = false - }; - - options.RequestHeaders.Add("NT", subscription.NotificationType); - options.RequestHeaders.Add("NTS", "upnp:propchange"); - options.RequestHeaders.Add("SID", subscription.Id); - options.RequestHeaders.Add("SEQ", subscription.TriggerCount.ToString(_usCulture)); - - try - { - using (await _httpClient.SendAsync(options, new HttpMethod("NOTIFY")).ConfigureAwait(false)) - { - } - } - catch (OperationCanceledException) - { - } - catch - { - // Already logged at lower levels - } - finally - { - subscription.IncrementTriggerCount(); - } - } - } -} diff --git a/Emby.Dlna/IConnectionManager.cs b/Emby.Dlna/IConnectionManager.cs index 7b4a33a98..9f643a9e6 100644 --- a/Emby.Dlna/IConnectionManager.cs +++ b/Emby.Dlna/IConnectionManager.cs @@ -2,7 +2,7 @@ namespace Emby.Dlna { - public interface IConnectionManager : IEventManager, IUpnpService + public interface IConnectionManager : IDlnaEventManager, IUpnpService { } } diff --git a/Emby.Dlna/IContentDirectory.cs b/Emby.Dlna/IContentDirectory.cs index 83ef09c66..10f4d6386 100644 --- a/Emby.Dlna/IContentDirectory.cs +++ b/Emby.Dlna/IContentDirectory.cs @@ -2,7 +2,7 @@ namespace Emby.Dlna { - public interface IContentDirectory : IEventManager, IUpnpService + public interface IContentDirectory : IDlnaEventManager, IUpnpService { } } diff --git a/Emby.Dlna/IDlnaEventManager.cs b/Emby.Dlna/IDlnaEventManager.cs new file mode 100644 index 000000000..b535c3bde --- /dev/null +++ b/Emby.Dlna/IDlnaEventManager.cs @@ -0,0 +1,23 @@ +#pragma warning disable CS1591 + +namespace Emby.Dlna +{ + public interface IDlnaEventManager + { + /// + /// Cancels the event subscription. + /// + /// The subscription identifier. + EventSubscriptionResponse CancelEventSubscription(string subscriptionId); + + /// + /// Renews the event subscription. + /// + EventSubscriptionResponse RenewEventSubscription(string subscriptionId, string notificationType, string requestedTimeoutString, string callbackUrl); + + /// + /// Creates the event subscription. + /// + EventSubscriptionResponse CreateEventSubscription(string notificationType, string requestedTimeoutString, string callbackUrl); + } +} diff --git a/Emby.Dlna/IEventManager.cs b/Emby.Dlna/IEventManager.cs deleted file mode 100644 index 287203389..000000000 --- a/Emby.Dlna/IEventManager.cs +++ /dev/null @@ -1,23 +0,0 @@ -#pragma warning disable CS1591 - -namespace Emby.Dlna -{ - public interface IEventManager - { - /// - /// Cancels the event subscription. - /// - /// The subscription identifier. - EventSubscriptionResponse CancelEventSubscription(string subscriptionId); - - /// - /// Renews the event subscription. - /// - EventSubscriptionResponse RenewEventSubscription(string subscriptionId, string notificationType, string requestedTimeoutString, string callbackUrl); - - /// - /// Creates the event subscription. - /// - EventSubscriptionResponse CreateEventSubscription(string notificationType, string requestedTimeoutString, string callbackUrl); - } -} diff --git a/Emby.Dlna/IMediaReceiverRegistrar.cs b/Emby.Dlna/IMediaReceiverRegistrar.cs index b0376b6a9..43e934b53 100644 --- a/Emby.Dlna/IMediaReceiverRegistrar.cs +++ b/Emby.Dlna/IMediaReceiverRegistrar.cs @@ -2,7 +2,7 @@ namespace Emby.Dlna { - public interface IMediaReceiverRegistrar : IEventManager, IUpnpService + public interface IMediaReceiverRegistrar : IDlnaEventManager, IUpnpService { } } diff --git a/Emby.Dlna/Service/BaseService.cs b/Emby.Dlna/Service/BaseService.cs index 8794ec26a..2950eede2 100644 --- a/Emby.Dlna/Service/BaseService.cs +++ b/Emby.Dlna/Service/BaseService.cs @@ -6,9 +6,9 @@ using Microsoft.Extensions.Logging; namespace Emby.Dlna.Service { - public class BaseService : IEventManager + public class BaseService : IDlnaEventManager { - protected IEventManager EventManager; + protected IDlnaEventManager _dlnaEventManager; protected IHttpClient HttpClient; protected ILogger Logger; @@ -17,22 +17,22 @@ namespace Emby.Dlna.Service Logger = logger; HttpClient = httpClient; - EventManager = new EventManager(logger, HttpClient); + _dlnaEventManager = new DlnaEventManager(logger, HttpClient); } public EventSubscriptionResponse CancelEventSubscription(string subscriptionId) { - return EventManager.CancelEventSubscription(subscriptionId); + return _dlnaEventManager.CancelEventSubscription(subscriptionId); } public EventSubscriptionResponse RenewEventSubscription(string subscriptionId, string notificationType, string timeoutString, string callbackUrl) { - return EventManager.RenewEventSubscription(subscriptionId, notificationType, timeoutString, callbackUrl); + return _dlnaEventManager.RenewEventSubscription(subscriptionId, notificationType, timeoutString, callbackUrl); } public EventSubscriptionResponse CreateEventSubscription(string notificationType, string timeoutString, string callbackUrl) { - return EventManager.CreateEventSubscription(notificationType, timeoutString, callbackUrl); + return _dlnaEventManager.CreateEventSubscription(notificationType, timeoutString, callbackUrl); } } } diff --git a/Jellyfin.Api/Controllers/DlnaServerController.cs b/Jellyfin.Api/Controllers/DlnaServerController.cs index ef507f2ed..d5cfcdf3f 100644 --- a/Jellyfin.Api/Controllers/DlnaServerController.cs +++ b/Jellyfin.Api/Controllers/DlnaServerController.cs @@ -230,7 +230,7 @@ namespace Jellyfin.Api.Controllers }); } - private EventSubscriptionResponse ProcessEventRequest(IEventManager eventManager) + private EventSubscriptionResponse ProcessEventRequest(IDlnaEventManager dlnaEventManager) { var subscriptionId = Request.Headers["SID"]; if (string.Equals(Request.Method, "subscribe", StringComparison.OrdinalIgnoreCase)) @@ -241,17 +241,17 @@ namespace Jellyfin.Api.Controllers if (string.IsNullOrEmpty(notificationType)) { - return eventManager.RenewEventSubscription( + return dlnaEventManager.RenewEventSubscription( subscriptionId, notificationType, timeoutString, callback); } - return eventManager.CreateEventSubscription(notificationType, timeoutString, callback); + return dlnaEventManager.CreateEventSubscription(notificationType, timeoutString, callback); } - return eventManager.CancelEventSubscription(subscriptionId); + return dlnaEventManager.CancelEventSubscription(subscriptionId); } } } -- cgit v1.2.3 From 7fe372749e248721c57f60527733ee002215ad09 Mon Sep 17 00:00:00 2001 From: crobibero Date: Sun, 16 Aug 2020 08:32:13 -0600 Subject: fix dlna server routes --- Jellyfin.Api/Controllers/DlnaServerController.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'Jellyfin.Api/Controllers/DlnaServerController.cs') diff --git a/Jellyfin.Api/Controllers/DlnaServerController.cs b/Jellyfin.Api/Controllers/DlnaServerController.cs index ef507f2ed..0100d642b 100644 --- a/Jellyfin.Api/Controllers/DlnaServerController.cs +++ b/Jellyfin.Api/Controllers/DlnaServerController.cs @@ -60,8 +60,8 @@ namespace Jellyfin.Api.Controllers /// Server UUID. /// Dlna content directory returned. /// An containing the dlna content directory xml. - [HttpGet("{serverId}/ContentDirectory/ContentDirectory")] - [HttpGet("{serverId}/ContentDirectory/ContentDirectory.xml", Name = "GetContentDirectory_2")] + [HttpGet("{serverId}/ContentDirectory")] + [HttpGet("{serverId}/ContentDirectory.xml", Name = "GetContentDirectory_2")] [Produces(XMLContentType)] [ProducesResponseType(StatusCodes.Status200OK)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")] @@ -75,8 +75,8 @@ namespace Jellyfin.Api.Controllers /// /// Server UUID. /// Dlna media receiver registrar xml. - [HttpGet("{serverId}/MediaReceiverRegistrar/MediaReceiverRegistrar")] - [HttpGet("{serverId}/MediaReceiverRegistrar/MediaReceiverRegistrar.xml", Name = "GetMediaReceiverRegistrar_2")] + [HttpGet("{serverId}/MediaReceiverRegistrar")] + [HttpGet("{serverId}/MediaReceiverRegistrar.xml", Name = "GetMediaReceiverRegistrar_2")] [Produces(XMLContentType)] [ProducesResponseType(StatusCodes.Status200OK)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")] @@ -90,8 +90,8 @@ namespace Jellyfin.Api.Controllers /// /// Server UUID. /// Dlna media receiver registrar xml. - [HttpGet("{serverId}/ConnectionManager/ConnectionManager")] - [HttpGet("{serverId}/ConnectionManager/ConnectionManager.xml", Name = "GetConnectionManager_2")] + [HttpGet("{serverId}/ConnectionManager")] + [HttpGet("{serverId}/ConnectionManager.xml", Name = "GetConnectionManager_2")] [Produces(XMLContentType)] [ProducesResponseType(StatusCodes.Status200OK)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")] -- cgit v1.2.3 From 3c395814fd0ea3cb5c99cd39faa02573a3b33c5a Mon Sep 17 00:00:00 2001 From: crobibero Date: Sun, 16 Aug 2020 10:53:57 -0600 Subject: fix dlna routes again --- Jellyfin.Api/Controllers/DlnaServerController.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'Jellyfin.Api/Controllers/DlnaServerController.cs') diff --git a/Jellyfin.Api/Controllers/DlnaServerController.cs b/Jellyfin.Api/Controllers/DlnaServerController.cs index 0100d642b..b167c1d97 100644 --- a/Jellyfin.Api/Controllers/DlnaServerController.cs +++ b/Jellyfin.Api/Controllers/DlnaServerController.cs @@ -61,7 +61,8 @@ namespace Jellyfin.Api.Controllers /// Dlna content directory returned. /// An containing the dlna content directory xml. [HttpGet("{serverId}/ContentDirectory")] - [HttpGet("{serverId}/ContentDirectory.xml", Name = "GetContentDirectory_2")] + [HttpGet("{serverId}/ContentDirectory/ContentDirectory", Name = "GetContentDirectory_2")] + [HttpGet("{serverId}/ContentDirectory/ContentDirectory.xml", Name = "GetContentDirectory_3")] [Produces(XMLContentType)] [ProducesResponseType(StatusCodes.Status200OK)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")] @@ -76,7 +77,8 @@ namespace Jellyfin.Api.Controllers /// Server UUID. /// Dlna media receiver registrar xml. [HttpGet("{serverId}/MediaReceiverRegistrar")] - [HttpGet("{serverId}/MediaReceiverRegistrar.xml", Name = "GetMediaReceiverRegistrar_2")] + [HttpGet("{serverId}/MediaReceiverRegistrar/MediaReceiverRegistrar", Name = "GetMediaReceiverRegistrar_2")] + [HttpGet("{serverId}/MediaReceiverRegistrar/MediaReceiverRegistrar.xml", Name = "GetMediaReceiverRegistrar_3")] [Produces(XMLContentType)] [ProducesResponseType(StatusCodes.Status200OK)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")] @@ -91,7 +93,8 @@ namespace Jellyfin.Api.Controllers /// Server UUID. /// Dlna media receiver registrar xml. [HttpGet("{serverId}/ConnectionManager")] - [HttpGet("{serverId}/ConnectionManager.xml", Name = "GetConnectionManager_2")] + [HttpGet("{serverId}/ConnectionManager/ConnectionManager", Name = "GetConnectionManager_2")] + [HttpGet("{serverId}/ConnectionManager/ConnectionManager.xml", Name = "GetConnectionManager_3")] [Produces(XMLContentType)] [ProducesResponseType(StatusCodes.Status200OK)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")] -- cgit v1.2.3 From 170e434f92142a8e4c17d3a5eec0bd9c2d6a0037 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Thu, 20 Aug 2020 21:04:57 +0200 Subject: Fix all warnings in Emby.Dlna --- Emby.Dlna/Common/ServiceAction.cs | 2 +- Emby.Dlna/Common/StateVariable.cs | 3 +- Emby.Dlna/ConfigurationExtension.cs | 16 -- Emby.Dlna/ConnectionManager/ConnectionManager.cs | 45 ----- .../ConnectionManager/ConnectionManagerService.cs | 43 ++++ Emby.Dlna/ContentDirectory/ContentDirectory.cs | 145 -------------- .../ContentDirectory/ContentDirectoryService.cs | 145 ++++++++++++++ Emby.Dlna/ContentDirectory/ControlHandler.cs | 152 ++++++-------- Emby.Dlna/ContentDirectory/ServerItem.cs | 23 +++ Emby.Dlna/ContentDirectory/StubType.cs | 28 +++ Emby.Dlna/ControlRequest.cs | 6 +- Emby.Dlna/ControlResponse.cs | 2 +- Emby.Dlna/Didl/DidlBuilder.cs | 75 +++---- Emby.Dlna/Didl/Filter.cs | 4 +- Emby.Dlna/DlnaConfigurationFactory.cs | 24 +++ Emby.Dlna/DlnaManager.cs | 18 +- Emby.Dlna/Emby.Dlna.csproj | 2 +- Emby.Dlna/EventSubscriptionResponse.cs | 2 +- Emby.Dlna/IEventManager.cs | 10 + Emby.Dlna/Main/DlnaEntryPoint.cs | 33 +--- .../MediaReceiverRegistrar.cs | 39 ---- .../MediaReceiverRegistrarService.cs | 39 ++++ Emby.Dlna/PlayTo/Device.cs | 167 ++++++++-------- Emby.Dlna/PlayTo/MediaChangedEventArgs.cs | 4 +- Emby.Dlna/PlayTo/PlayToController.cs | 220 ++++++++++----------- Emby.Dlna/PlayTo/PlayToManager.cs | 2 +- Emby.Dlna/PlayTo/PlaybackProgressEventArgs.cs | 2 +- Emby.Dlna/PlayTo/PlaybackStartEventArgs.cs | 2 +- Emby.Dlna/PlayTo/PlaybackStoppedEventArgs.cs | 2 +- Emby.Dlna/PlayTo/TransportCommands.cs | 53 +++-- Emby.Dlna/PlayTo/TransportState.cs | 1 + Emby.Dlna/PlayTo/UpnpContainer.cs | 16 +- Emby.Dlna/PlayTo/uBaseObject.cs | 7 +- Emby.Dlna/PlayTo/uPnpNamespaces.cs | 90 ++++++--- Emby.Dlna/Service/BaseControlHandler.cs | 18 +- Emby.Dlna/Service/BaseService.cs | 20 +- Emby.Dlna/Service/ServiceXmlBuilder.cs | 2 +- Emby.Dlna/Ssdp/Extensions.cs | 27 --- Emby.Dlna/Ssdp/SsdpExtensions.cs | 27 +++ Jellyfin.Api/Controllers/DlnaServerController.cs | 3 +- 40 files changed, 772 insertions(+), 747 deletions(-) delete mode 100644 Emby.Dlna/ConnectionManager/ConnectionManager.cs create mode 100644 Emby.Dlna/ConnectionManager/ConnectionManagerService.cs delete mode 100644 Emby.Dlna/ContentDirectory/ContentDirectory.cs create mode 100644 Emby.Dlna/ContentDirectory/ContentDirectoryService.cs create mode 100644 Emby.Dlna/ContentDirectory/ServerItem.cs create mode 100644 Emby.Dlna/ContentDirectory/StubType.cs create mode 100644 Emby.Dlna/DlnaConfigurationFactory.cs delete mode 100644 Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrar.cs create mode 100644 Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarService.cs delete mode 100644 Emby.Dlna/Ssdp/Extensions.cs create mode 100644 Emby.Dlna/Ssdp/SsdpExtensions.cs (limited to 'Jellyfin.Api/Controllers/DlnaServerController.cs') diff --git a/Emby.Dlna/Common/ServiceAction.cs b/Emby.Dlna/Common/ServiceAction.cs index db4f27063..d458d7f3f 100644 --- a/Emby.Dlna/Common/ServiceAction.cs +++ b/Emby.Dlna/Common/ServiceAction.cs @@ -13,7 +13,7 @@ namespace Emby.Dlna.Common public string Name { get; set; } - public List ArgumentList { get; set; } + public List ArgumentList { get; } /// public override string ToString() diff --git a/Emby.Dlna/Common/StateVariable.cs b/Emby.Dlna/Common/StateVariable.cs index a2c2bf5dd..6daf7ab6b 100644 --- a/Emby.Dlna/Common/StateVariable.cs +++ b/Emby.Dlna/Common/StateVariable.cs @@ -1,6 +1,7 @@ #pragma warning disable CS1591 using System; +using System.Collections.Generic; namespace Emby.Dlna.Common { @@ -17,7 +18,7 @@ namespace Emby.Dlna.Common public bool SendsEvents { get; set; } - public string[] AllowedValues { get; set; } + public IReadOnlyList AllowedValues { get; set; } /// public override string ToString() diff --git a/Emby.Dlna/ConfigurationExtension.cs b/Emby.Dlna/ConfigurationExtension.cs index 3dcfe60d9..fc02e1751 100644 --- a/Emby.Dlna/ConfigurationExtension.cs +++ b/Emby.Dlna/ConfigurationExtension.cs @@ -1,7 +1,6 @@ #nullable enable #pragma warning disable CS1591 -using System.Collections.Generic; using Emby.Dlna.Configuration; using MediaBrowser.Common.Configuration; @@ -14,19 +13,4 @@ namespace Emby.Dlna return manager.GetConfiguration("dlna"); } } - - public class DlnaConfigurationFactory : IConfigurationFactory - { - public IEnumerable GetConfigurations() - { - return new[] - { - new ConfigurationStore - { - Key = "dlna", - ConfigurationType = typeof(DlnaOptions) - } - }; - } - } } diff --git a/Emby.Dlna/ConnectionManager/ConnectionManager.cs b/Emby.Dlna/ConnectionManager/ConnectionManager.cs deleted file mode 100644 index e32cc11bf..000000000 --- a/Emby.Dlna/ConnectionManager/ConnectionManager.cs +++ /dev/null @@ -1,45 +0,0 @@ -#pragma warning disable CS1591 - -using System.Threading.Tasks; -using Emby.Dlna.Service; -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Dlna; -using Microsoft.Extensions.Logging; - -namespace Emby.Dlna.ConnectionManager -{ - public class ConnectionManager : BaseService, IConnectionManager - { - private readonly IDlnaManager _dlna; - private readonly ILogger _logger; - private readonly IServerConfigurationManager _config; - - public ConnectionManager( - IDlnaManager dlna, - IServerConfigurationManager config, - ILogger logger, - IHttpClient httpClient) - : base(logger, httpClient) - { - _dlna = dlna; - _config = config; - _logger = logger; - } - - /// - public string GetServiceXml() - { - return new ConnectionManagerXmlBuilder().GetXml(); - } - - /// - public Task ProcessControlRequestAsync(ControlRequest request) - { - var profile = _dlna.GetProfile(request.Headers) ?? - _dlna.GetDefaultProfile(); - - return new ControlHandler(_config, _logger, profile).ProcessControlRequestAsync(request); - } - } -} diff --git a/Emby.Dlna/ConnectionManager/ConnectionManagerService.cs b/Emby.Dlna/ConnectionManager/ConnectionManagerService.cs new file mode 100644 index 000000000..12338e2b4 --- /dev/null +++ b/Emby.Dlna/ConnectionManager/ConnectionManagerService.cs @@ -0,0 +1,43 @@ +#pragma warning disable CS1591 + +using System.Threading.Tasks; +using Emby.Dlna.Service; +using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Dlna; +using Microsoft.Extensions.Logging; + +namespace Emby.Dlna.ConnectionManager +{ + public class ConnectionManagerService : BaseService, IConnectionManager + { + private readonly IDlnaManager _dlna; + private readonly IServerConfigurationManager _config; + + public ConnectionManagerService( + IDlnaManager dlna, + IServerConfigurationManager config, + ILogger logger, + IHttpClient httpClient) + : base(logger, httpClient) + { + _dlna = dlna; + _config = config; + } + + /// + public string GetServiceXml() + { + return new ConnectionManagerXmlBuilder().GetXml(); + } + + /// + public Task ProcessControlRequestAsync(ControlRequest request) + { + var profile = _dlna.GetProfile(request.Headers) ?? + _dlna.GetDefaultProfile(); + + return new ControlHandler(_config, Logger, profile).ProcessControlRequestAsync(request); + } + } +} diff --git a/Emby.Dlna/ContentDirectory/ContentDirectory.cs b/Emby.Dlna/ContentDirectory/ContentDirectory.cs deleted file mode 100644 index b1ce7e8ec..000000000 --- a/Emby.Dlna/ContentDirectory/ContentDirectory.cs +++ /dev/null @@ -1,145 +0,0 @@ -#pragma warning disable CS1591 - -using System; -using System.Linq; -using System.Threading.Tasks; -using Emby.Dlna.Service; -using Jellyfin.Data.Entities; -using Jellyfin.Data.Enums; -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Dlna; -using MediaBrowser.Controller.Drawing; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.MediaEncoding; -using MediaBrowser.Controller.TV; -using MediaBrowser.Model.Dlna; -using MediaBrowser.Model.Globalization; -using Microsoft.Extensions.Logging; - -namespace Emby.Dlna.ContentDirectory -{ - public class ContentDirectory : BaseService, IContentDirectory - { - private readonly ILibraryManager _libraryManager; - private readonly IImageProcessor _imageProcessor; - private readonly IUserDataManager _userDataManager; - private readonly IDlnaManager _dlna; - private readonly IServerConfigurationManager _config; - private readonly IUserManager _userManager; - private readonly ILocalizationManager _localization; - private readonly IMediaSourceManager _mediaSourceManager; - private readonly IUserViewManager _userViewManager; - private readonly IMediaEncoder _mediaEncoder; - private readonly ITVSeriesManager _tvSeriesManager; - - public ContentDirectory( - IDlnaManager dlna, - IUserDataManager userDataManager, - IImageProcessor imageProcessor, - ILibraryManager libraryManager, - IServerConfigurationManager config, - IUserManager userManager, - ILogger logger, - IHttpClient httpClient, - ILocalizationManager localization, - IMediaSourceManager mediaSourceManager, - IUserViewManager userViewManager, - IMediaEncoder mediaEncoder, - ITVSeriesManager tvSeriesManager) - : base(logger, httpClient) - { - _dlna = dlna; - _userDataManager = userDataManager; - _imageProcessor = imageProcessor; - _libraryManager = libraryManager; - _config = config; - _userManager = userManager; - _localization = localization; - _mediaSourceManager = mediaSourceManager; - _userViewManager = userViewManager; - _mediaEncoder = mediaEncoder; - _tvSeriesManager = tvSeriesManager; - } - - private int SystemUpdateId - { - get - { - var now = DateTime.UtcNow; - - return now.Year + now.DayOfYear + now.Hour; - } - } - - /// - public string GetServiceXml() - { - return new ContentDirectoryXmlBuilder().GetXml(); - } - - /// - public Task ProcessControlRequestAsync(ControlRequest request) - { - var profile = _dlna.GetProfile(request.Headers) ?? - _dlna.GetDefaultProfile(); - - var serverAddress = request.RequestedUrl.Substring(0, request.RequestedUrl.IndexOf("/dlna", StringComparison.OrdinalIgnoreCase)); - - var user = GetUser(profile); - - return new ControlHandler( - Logger, - _libraryManager, - profile, - serverAddress, - null, - _imageProcessor, - _userDataManager, - user, - SystemUpdateId, - _config, - _localization, - _mediaSourceManager, - _userViewManager, - _mediaEncoder, - _tvSeriesManager) - .ProcessControlRequestAsync(request); - } - - private User GetUser(DeviceProfile profile) - { - if (!string.IsNullOrEmpty(profile.UserId)) - { - var user = _userManager.GetUserById(Guid.Parse(profile.UserId)); - - if (user != null) - { - return user; - } - } - - var userId = _config.GetDlnaConfiguration().DefaultUserId; - - if (!string.IsNullOrEmpty(userId)) - { - var user = _userManager.GetUserById(Guid.Parse(userId)); - - if (user != null) - { - return user; - } - } - - foreach (var user in _userManager.Users) - { - if (user.HasPermission(PermissionKind.IsAdministrator)) - { - return user; - } - } - - return _userManager.Users.FirstOrDefault(); - } - } -} diff --git a/Emby.Dlna/ContentDirectory/ContentDirectoryService.cs b/Emby.Dlna/ContentDirectory/ContentDirectoryService.cs new file mode 100644 index 000000000..72732823a --- /dev/null +++ b/Emby.Dlna/ContentDirectory/ContentDirectoryService.cs @@ -0,0 +1,145 @@ +#pragma warning disable CS1591 + +using System; +using System.Linq; +using System.Threading.Tasks; +using Emby.Dlna.Service; +using Jellyfin.Data.Entities; +using Jellyfin.Data.Enums; +using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Dlna; +using MediaBrowser.Controller.Drawing; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.MediaEncoding; +using MediaBrowser.Controller.TV; +using MediaBrowser.Model.Dlna; +using MediaBrowser.Model.Globalization; +using Microsoft.Extensions.Logging; + +namespace Emby.Dlna.ContentDirectory +{ + public class ContentDirectoryService : BaseService, IContentDirectory + { + private readonly ILibraryManager _libraryManager; + private readonly IImageProcessor _imageProcessor; + private readonly IUserDataManager _userDataManager; + private readonly IDlnaManager _dlna; + private readonly IServerConfigurationManager _config; + private readonly IUserManager _userManager; + private readonly ILocalizationManager _localization; + private readonly IMediaSourceManager _mediaSourceManager; + private readonly IUserViewManager _userViewManager; + private readonly IMediaEncoder _mediaEncoder; + private readonly ITVSeriesManager _tvSeriesManager; + + public ContentDirectoryService( + IDlnaManager dlna, + IUserDataManager userDataManager, + IImageProcessor imageProcessor, + ILibraryManager libraryManager, + IServerConfigurationManager config, + IUserManager userManager, + ILogger logger, + IHttpClient httpClient, + ILocalizationManager localization, + IMediaSourceManager mediaSourceManager, + IUserViewManager userViewManager, + IMediaEncoder mediaEncoder, + ITVSeriesManager tvSeriesManager) + : base(logger, httpClient) + { + _dlna = dlna; + _userDataManager = userDataManager; + _imageProcessor = imageProcessor; + _libraryManager = libraryManager; + _config = config; + _userManager = userManager; + _localization = localization; + _mediaSourceManager = mediaSourceManager; + _userViewManager = userViewManager; + _mediaEncoder = mediaEncoder; + _tvSeriesManager = tvSeriesManager; + } + + private int SystemUpdateId + { + get + { + var now = DateTime.UtcNow; + + return now.Year + now.DayOfYear + now.Hour; + } + } + + /// + public string GetServiceXml() + { + return new ContentDirectoryXmlBuilder().GetXml(); + } + + /// + public Task ProcessControlRequestAsync(ControlRequest request) + { + var profile = _dlna.GetProfile(request.Headers) ?? + _dlna.GetDefaultProfile(); + + var serverAddress = request.RequestedUrl.Substring(0, request.RequestedUrl.IndexOf("/dlna", StringComparison.OrdinalIgnoreCase)); + + var user = GetUser(profile); + + return new ControlHandler( + Logger, + _libraryManager, + profile, + serverAddress, + null, + _imageProcessor, + _userDataManager, + user, + SystemUpdateId, + _config, + _localization, + _mediaSourceManager, + _userViewManager, + _mediaEncoder, + _tvSeriesManager) + .ProcessControlRequestAsync(request); + } + + private User GetUser(DeviceProfile profile) + { + if (!string.IsNullOrEmpty(profile.UserId)) + { + var user = _userManager.GetUserById(Guid.Parse(profile.UserId)); + + if (user != null) + { + return user; + } + } + + var userId = _config.GetDlnaConfiguration().DefaultUserId; + + if (!string.IsNullOrEmpty(userId)) + { + var user = _userManager.GetUserById(Guid.Parse(userId)); + + if (user != null) + { + return user; + } + } + + foreach (var user in _userManager.Users) + { + if (user.HasPermission(PermissionKind.IsAdministrator)) + { + return user; + } + } + + return _userManager.Users.FirstOrDefault(); + } + } +} diff --git a/Emby.Dlna/ContentDirectory/ControlHandler.cs b/Emby.Dlna/ContentDirectory/ControlHandler.cs index 72f2e7dd4..be1ed7872 100644 --- a/Emby.Dlna/ContentDirectory/ControlHandler.cs +++ b/Emby.Dlna/ContentDirectory/ControlHandler.cs @@ -40,6 +40,11 @@ namespace Emby.Dlna.ContentDirectory { public class ControlHandler : BaseControlHandler { + private const string NsDc = "http://purl.org/dc/elements/1.1/"; + private const string NsDidl = "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"; + private const string NsDlna = "urn:schemas-dlna-org:metadata-1-0/"; + private const string NsUpnp = "urn:schemas-upnp-org:metadata-1-0/upnp/"; + private readonly ILibraryManager _libraryManager; private readonly IUserDataManager _userDataManager; private readonly IServerConfigurationManager _config; @@ -47,11 +52,6 @@ namespace Emby.Dlna.ContentDirectory private readonly IUserViewManager _userViewManager; private readonly ITVSeriesManager _tvSeriesManager; - private const string NS_DC = "http://purl.org/dc/elements/1.1/"; - private const string NS_DIDL = "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"; - private const string NS_DLNA = "urn:schemas-dlna-org:metadata-1-0/"; - private const string NS_UPNP = "urn:schemas-upnp-org:metadata-1-0/upnp/"; - private readonly int _systemUpdateId; private readonly DidlBuilder _didlBuilder; @@ -181,7 +181,11 @@ namespace Emby.Dlna.ContentDirectory userdata.PlaybackPositionTicks = TimeSpan.FromSeconds(newbookmark).Ticks; - _userDataManager.SaveUserData(_user, item, userdata, UserDataSaveReason.TogglePlayed, + _userDataManager.SaveUserData( + _user, + item, + userdata, + UserDataSaveReason.TogglePlayed, CancellationToken.None); } @@ -286,18 +290,17 @@ namespace Emby.Dlna.ContentDirectory using (var writer = XmlWriter.Create(builder, settings)) { - writer.WriteStartElement(string.Empty, "DIDL-Lite", NS_DIDL); + writer.WriteStartElement(string.Empty, "DIDL-Lite", NsDidl); - writer.WriteAttributeString("xmlns", "dc", null, NS_DC); - writer.WriteAttributeString("xmlns", "dlna", null, NS_DLNA); - writer.WriteAttributeString("xmlns", "upnp", null, NS_UPNP); + writer.WriteAttributeString("xmlns", "dc", null, NsDc); + writer.WriteAttributeString("xmlns", "dlna", null, NsDlna); + writer.WriteAttributeString("xmlns", "upnp", null, NsUpnp); DidlBuilder.WriteXmlRootAttributes(_profile, writer); var serverItem = GetItemFromObjectId(id); var item = serverItem.Item; - if (string.Equals(flag, "BrowseMetadata", StringComparison.Ordinal)) { totalCount = 1; @@ -397,11 +400,11 @@ namespace Emby.Dlna.ContentDirectory using (var writer = XmlWriter.Create(builder, settings)) { - writer.WriteStartElement(string.Empty, "DIDL-Lite", NS_DIDL); + writer.WriteStartElement(string.Empty, "DIDL-Lite", NsDidl); - writer.WriteAttributeString("xmlns", "dc", null, NS_DC); - writer.WriteAttributeString("xmlns", "dlna", null, NS_DLNA); - writer.WriteAttributeString("xmlns", "upnp", null, NS_UPNP); + writer.WriteAttributeString("xmlns", "dc", null, NsDc); + writer.WriteAttributeString("xmlns", "dlna", null, NsDlna); + writer.WriteAttributeString("xmlns", "upnp", null, NsUpnp); DidlBuilder.WriteXmlRootAttributes(_profile, writer); @@ -783,11 +786,14 @@ namespace Emby.Dlna.ContentDirectory }) .ToArray(); - return ApplyPaging(new QueryResult - { - Items = folders, - TotalRecordCount = folders.Length - }, startIndex, limit); + return ApplyPaging( + new QueryResult + { + Items = folders, + TotalRecordCount = folders.Length + }, + startIndex, + limit); } private QueryResult GetTvFolders(BaseItem item, User user, StubType? stubType, SortCriteria sort, int? startIndex, int? limit) @@ -1135,14 +1141,16 @@ namespace Emby.Dlna.ContentDirectory { query.OrderBy = Array.Empty<(string, SortOrder)>(); - var items = _userViewManager.GetLatestItems(new LatestItemsQuery - { - UserId = user.Id, - Limit = 50, - IncludeItemTypes = new[] { nameof(Audio) }, - ParentId = parent?.Id ?? Guid.Empty, - GroupItems = true - }, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToArray(); + var items = _userViewManager.GetLatestItems( + new LatestItemsQuery + { + UserId = user.Id, + Limit = 50, + IncludeItemTypes = new[] { nameof(Audio) }, + ParentId = parent?.Id ?? Guid.Empty, + GroupItems = true + }, + query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToArray(); return ToResult(items); } @@ -1151,12 +1159,15 @@ namespace Emby.Dlna.ContentDirectory { query.OrderBy = Array.Empty<(string, SortOrder)>(); - var result = _tvSeriesManager.GetNextUp(new NextUpQuery - { - Limit = query.Limit, - StartIndex = query.StartIndex, - UserId = query.User.Id - }, new[] { parent }, query.DtoOptions); + var result = _tvSeriesManager.GetNextUp( + new NextUpQuery + { + Limit = query.Limit, + StartIndex = query.StartIndex, + UserId = query.User.Id + }, + new[] { parent }, + query.DtoOptions); return ToResult(result); } @@ -1165,14 +1176,16 @@ namespace Emby.Dlna.ContentDirectory { query.OrderBy = Array.Empty<(string, SortOrder)>(); - var items = _userViewManager.GetLatestItems(new LatestItemsQuery - { - UserId = user.Id, - Limit = 50, - IncludeItemTypes = new[] { typeof(Episode).Name }, - ParentId = parent == null ? Guid.Empty : parent.Id, - GroupItems = false - }, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToArray(); + var items = _userViewManager.GetLatestItems( + new LatestItemsQuery + { + UserId = user.Id, + Limit = 50, + IncludeItemTypes = new[] { typeof(Episode).Name }, + ParentId = parent == null ? Guid.Empty : parent.Id, + GroupItems = false + }, + query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToArray(); return ToResult(items); } @@ -1183,13 +1196,14 @@ namespace Emby.Dlna.ContentDirectory var items = _userViewManager.GetLatestItems( new LatestItemsQuery - { - UserId = user.Id, - Limit = 50, - IncludeItemTypes = new[] { nameof(Movie) }, - ParentId = parent?.Id ?? Guid.Empty, - GroupItems = true - }, query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToArray(); + { + UserId = user.Id, + Limit = 50, + IncludeItemTypes = new[] { nameof(Movie) }, + ParentId = parent?.Id ?? Guid.Empty, + GroupItems = true + }, + query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i != null).ToArray(); return ToResult(items); } @@ -1354,44 +1368,4 @@ namespace Emby.Dlna.ContentDirectory return new ServerItem(_libraryManager.GetUserRootFolder()); } } - - internal class ServerItem - { - public BaseItem Item { get; set; } - - public StubType? StubType { get; set; } - - public ServerItem(BaseItem item) - { - Item = item; - - if (item is IItemByName && !(item is Folder)) - { - StubType = Dlna.ContentDirectory.StubType.Folder; - } - } - } - - public enum StubType - { - Folder = 0, - Latest = 2, - Playlists = 3, - Albums = 4, - AlbumArtists = 5, - Artists = 6, - Songs = 7, - Genres = 8, - FavoriteSongs = 9, - FavoriteArtists = 10, - FavoriteAlbums = 11, - ContinueWatching = 12, - Movies = 13, - Collections = 14, - Favorites = 15, - NextUp = 16, - Series = 17, - FavoriteSeries = 18, - FavoriteEpisodes = 19 - } } diff --git a/Emby.Dlna/ContentDirectory/ServerItem.cs b/Emby.Dlna/ContentDirectory/ServerItem.cs new file mode 100644 index 000000000..e40605414 --- /dev/null +++ b/Emby.Dlna/ContentDirectory/ServerItem.cs @@ -0,0 +1,23 @@ +#pragma warning disable CS1591 + +using MediaBrowser.Controller.Entities; + +namespace Emby.Dlna.ContentDirectory +{ + internal class ServerItem + { + public ServerItem(BaseItem item) + { + Item = item; + + if (item is IItemByName && !(item is Folder)) + { + StubType = Dlna.ContentDirectory.StubType.Folder; + } + } + + public BaseItem Item { get; set; } + + public StubType? StubType { get; set; } + } +} diff --git a/Emby.Dlna/ContentDirectory/StubType.cs b/Emby.Dlna/ContentDirectory/StubType.cs new file mode 100644 index 000000000..eee405d3e --- /dev/null +++ b/Emby.Dlna/ContentDirectory/StubType.cs @@ -0,0 +1,28 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1602 + +namespace Emby.Dlna.ContentDirectory +{ + public enum StubType + { + Folder = 0, + Latest = 2, + Playlists = 3, + Albums = 4, + AlbumArtists = 5, + Artists = 6, + Songs = 7, + Genres = 8, + FavoriteSongs = 9, + FavoriteArtists = 10, + FavoriteAlbums = 11, + ContinueWatching = 12, + Movies = 13, + Collections = 14, + Favorites = 15, + NextUp = 16, + Series = 17, + FavoriteSeries = 18, + FavoriteEpisodes = 19 + } +} diff --git a/Emby.Dlna/ControlRequest.cs b/Emby.Dlna/ControlRequest.cs index c82e002ce..4ea4e4e48 100644 --- a/Emby.Dlna/ControlRequest.cs +++ b/Emby.Dlna/ControlRequest.cs @@ -7,12 +7,12 @@ namespace Emby.Dlna { public class ControlRequest { - public ControlRequest() + public ControlRequest(IHeaderDictionary headers) { - Headers = new HeaderDictionary(); + Headers = headers; } - public IHeaderDictionary Headers { get; set; } + public IHeaderDictionary Headers { get; } public Stream InputXml { get; set; } diff --git a/Emby.Dlna/ControlResponse.cs b/Emby.Dlna/ControlResponse.cs index 243b09786..d827eef26 100644 --- a/Emby.Dlna/ControlResponse.cs +++ b/Emby.Dlna/ControlResponse.cs @@ -11,7 +11,7 @@ namespace Emby.Dlna Headers = new Dictionary(); } - public IDictionary Headers { get; set; } + public IDictionary Headers { get; } public string Xml { get; set; } diff --git a/Emby.Dlna/Didl/DidlBuilder.cs b/Emby.Dlna/Didl/DidlBuilder.cs index 74007c92b..bd09a8051 100644 --- a/Emby.Dlna/Didl/DidlBuilder.cs +++ b/Emby.Dlna/Didl/DidlBuilder.cs @@ -977,12 +977,12 @@ namespace Emby.Dlna.Didl writer.WriteStartElement("upnp", "albumArtURI", NsUpnp); writer.WriteAttributeString("dlna", "profileID", NsDlna, _profile.AlbumArtPn); - writer.WriteString(albumartUrlInfo.Url); + writer.WriteString(albumartUrlInfo.url); writer.WriteFullEndElement(); // TOOD: Remove these default values var iconUrlInfo = GetImageUrl(imageInfo, _profile.MaxIconWidth ?? 48, _profile.MaxIconHeight ?? 48, "jpg"); - writer.WriteElementString("upnp", "icon", NsUpnp, iconUrlInfo.Url); + writer.WriteElementString("upnp", "icon", NsUpnp, iconUrlInfo.url); if (!_profile.EnableAlbumArtInDidl) { @@ -1029,8 +1029,8 @@ namespace Emby.Dlna.Didl // Images must have a reported size or many clients (Bubble upnp), will only use the first thumbnail // rather than using a larger one when available - var width = albumartUrlInfo.Width ?? maxWidth; - var height = albumartUrlInfo.Height ?? maxHeight; + var width = albumartUrlInfo.width ?? maxWidth; + var height = albumartUrlInfo.height ?? maxHeight; var contentFeatures = new ContentFeatureBuilder(_profile) .BuildImageHeader(format, width, height, imageInfo.IsDirectStream, org_Pn); @@ -1047,7 +1047,7 @@ namespace Emby.Dlna.Didl "resolution", string.Format(CultureInfo.InvariantCulture, "{0}x{1}", width, height)); - writer.WriteString(albumartUrlInfo.Url); + writer.WriteString(albumartUrlInfo.url); writer.WriteFullEndElement(); } @@ -1143,7 +1143,6 @@ namespace Emby.Dlna.Didl if (width == 0 || height == 0) { - // _imageProcessor.GetImageSize(item, imageInfo); width = null; height = null; } @@ -1153,18 +1152,6 @@ namespace Emby.Dlna.Didl height = null; } - // try - // { - // var size = _imageProcessor.GetImageSize(imageInfo); - - // width = size.Width; - // height = size.Height; - // } - // catch - // { - - // } - var inputFormat = (Path.GetExtension(imageInfo.Path) ?? string.Empty) .TrimStart('.') .Replace("jpeg", "jpg", StringComparison.OrdinalIgnoreCase); @@ -1181,30 +1168,6 @@ namespace Emby.Dlna.Didl }; } - private class ImageDownloadInfo - { - internal Guid ItemId; - internal string ImageTag; - internal ImageType Type; - - internal int? Width; - internal int? Height; - - internal bool IsDirectStream; - - internal string Format; - - internal ItemImageInfo ItemImageInfo; - } - - private class ImageUrlInfo - { - internal string Url; - - internal int? Width; - internal int? Height; - } - public static string GetClientId(BaseItem item, StubType? stubType) { return GetClientId(item.Id, stubType); @@ -1222,7 +1185,7 @@ namespace Emby.Dlna.Didl return id; } - private ImageUrlInfo GetImageUrl(ImageDownloadInfo info, int maxWidth, int maxHeight, string format) + private (string url, int? width, int? height) GetImageUrl(ImageDownloadInfo info, int maxWidth, int maxHeight, string format) { var url = string.Format( CultureInfo.InvariantCulture, @@ -1260,12 +1223,26 @@ namespace Emby.Dlna.Didl // just lie info.IsDirectStream = true; - return new ImageUrlInfo - { - Url = url, - Width = width, - Height = height - }; + return (url, width, height); + } + + private class ImageDownloadInfo + { + internal Guid ItemId { get; set; } + + internal string ImageTag { get; set; } + + internal ImageType Type { get; set; } + + internal int? Width { get; set; } + + internal int? Height { get; set; } + + internal bool IsDirectStream { get; set; } + + internal string Format { get; set; } + + internal ItemImageInfo ItemImageInfo { get; set; } } } } diff --git a/Emby.Dlna/Didl/Filter.cs b/Emby.Dlna/Didl/Filter.cs index b730d9db2..b58fdff2c 100644 --- a/Emby.Dlna/Didl/Filter.cs +++ b/Emby.Dlna/Didl/Filter.cs @@ -23,9 +23,7 @@ namespace Emby.Dlna.Didl public bool Contains(string field) { - // Don't bother with this. Some clients (media monkey) use the filter and then don't display very well when very little data comes back. - return true; - // return _all || ListHelper.ContainsIgnoreCase(_fields, field); + return _all || Array.Exists(_fields, x => x.Equals(field, StringComparison.OrdinalIgnoreCase)); } } } diff --git a/Emby.Dlna/DlnaConfigurationFactory.cs b/Emby.Dlna/DlnaConfigurationFactory.cs new file mode 100644 index 000000000..4c6ca869a --- /dev/null +++ b/Emby.Dlna/DlnaConfigurationFactory.cs @@ -0,0 +1,24 @@ +#nullable enable +#pragma warning disable CS1591 + +using System.Collections.Generic; +using Emby.Dlna.Configuration; +using MediaBrowser.Common.Configuration; + +namespace Emby.Dlna +{ + public class DlnaConfigurationFactory : IConfigurationFactory + { + public IEnumerable GetConfigurations() + { + return new[] + { + new ConfigurationStore + { + Key = "dlna", + ConfigurationType = typeof(DlnaOptions) + } + }; + } + } +} diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs index a6a4740d7..d5629684c 100644 --- a/Emby.Dlna/DlnaManager.cs +++ b/Emby.Dlna/DlnaManager.cs @@ -495,8 +495,8 @@ namespace Emby.Dlna /// Recreates the object using serialization, to ensure it's not a subclass. /// If it's a subclass it may not serlialize properly to xml (different root element tag name). /// - /// - /// + /// The device profile. + /// The reserialized device profile. private DeviceProfile ReserializeProfile(DeviceProfile profile) { if (profile.GetType() == typeof(DeviceProfile)) @@ -509,13 +509,6 @@ namespace Emby.Dlna return _jsonSerializer.DeserializeFromString(json); } - private class InternalProfileInfo - { - internal DeviceProfileInfo Info { get; set; } - - internal string Path { get; set; } - } - public string GetServerDescriptionXml(IHeaderDictionary headers, string serverUuId, string serverAddress) { var profile = GetProfile(headers) ?? @@ -540,6 +533,13 @@ namespace Emby.Dlna Stream = _assembly.GetManifestResourceStream(resource) }; } + + private class InternalProfileInfo + { + internal DeviceProfileInfo Info { get; set; } + + internal string Path { get; set; } + } } /* diff --git a/Emby.Dlna/Emby.Dlna.csproj b/Emby.Dlna/Emby.Dlna.csproj index 42a5f95c1..8538f580c 100644 --- a/Emby.Dlna/Emby.Dlna.csproj +++ b/Emby.Dlna/Emby.Dlna.csproj @@ -20,7 +20,7 @@ netstandard2.1 false true - true + true diff --git a/Emby.Dlna/EventSubscriptionResponse.cs b/Emby.Dlna/EventSubscriptionResponse.cs index fd18343e6..1b1bd426c 100644 --- a/Emby.Dlna/EventSubscriptionResponse.cs +++ b/Emby.Dlna/EventSubscriptionResponse.cs @@ -15,6 +15,6 @@ namespace Emby.Dlna public string ContentType { get; set; } - public Dictionary Headers { get; set; } + public Dictionary Headers { get; } } } diff --git a/Emby.Dlna/IEventManager.cs b/Emby.Dlna/IEventManager.cs index 287203389..de87b0709 100644 --- a/Emby.Dlna/IEventManager.cs +++ b/Emby.Dlna/IEventManager.cs @@ -8,16 +8,26 @@ namespace Emby.Dlna /// Cancels the event subscription. /// /// The subscription identifier. + /// The response. EventSubscriptionResponse CancelEventSubscription(string subscriptionId); /// /// Renews the event subscription. /// + /// The subscription identifier. + /// The notification type. + /// The requested timeout as a sting. + /// The callback url. + /// The response. EventSubscriptionResponse RenewEventSubscription(string subscriptionId, string notificationType, string requestedTimeoutString, string callbackUrl); /// /// Creates the event subscription. /// + /// The notification type. + /// The requested timeout as a sting. + /// The callback url. + /// The response. EventSubscriptionResponse CreateEventSubscription(string notificationType, string requestedTimeoutString, string callbackUrl); } } diff --git a/Emby.Dlna/Main/DlnaEntryPoint.cs b/Emby.Dlna/Main/DlnaEntryPoint.cs index 344f85f6b..0ad82022d 100644 --- a/Emby.Dlna/Main/DlnaEntryPoint.cs +++ b/Emby.Dlna/Main/DlnaEntryPoint.cs @@ -93,14 +93,14 @@ namespace Emby.Dlna.Main _networkManager = networkManager; _logger = loggerFactory.CreateLogger(); - ContentDirectory = new ContentDirectory.ContentDirectory( + ContentDirectory = new ContentDirectory.ContentDirectoryService( dlnaManager, userDataManager, imageProcessor, libraryManager, config, userManager, - loggerFactory.CreateLogger(), + loggerFactory.CreateLogger(), httpClient, localizationManager, mediaSourceManager, @@ -108,19 +108,19 @@ namespace Emby.Dlna.Main mediaEncoder, tvSeriesManager); - ConnectionManager = new ConnectionManager.ConnectionManager( + ConnectionManager = new ConnectionManager.ConnectionManagerService( dlnaManager, config, - loggerFactory.CreateLogger(), + loggerFactory.CreateLogger(), httpClient); - MediaReceiverRegistrar = new MediaReceiverRegistrar.MediaReceiverRegistrar( - loggerFactory.CreateLogger(), + MediaReceiverRegistrar = new MediaReceiverRegistrar.MediaReceiverRegistrarService( + loggerFactory.CreateLogger(), httpClient, config); Current = this; } - + public static DlnaEntryPoint Current { get; private set; } public IContentDirectory ContentDirectory { get; private set; } @@ -413,28 +413,15 @@ namespace Emby.Dlna.Main /// public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Releases unmanaged and optionally managed resources. - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected virtual void Dispose(bool disposing) { if (_disposed) { return; } - if (disposing) - { - DisposeDevicePublisher(); - DisposePlayToManager(); - DisposeDeviceDiscovery(); - } + DisposeDevicePublisher(); + DisposePlayToManager(); + DisposeDeviceDiscovery(); if (_communicationsServer != null) { diff --git a/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrar.cs b/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrar.cs deleted file mode 100644 index 64dfc840a..000000000 --- a/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrar.cs +++ /dev/null @@ -1,39 +0,0 @@ -#pragma warning disable CS1591 - -using System.Threading.Tasks; -using Emby.Dlna.Service; -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; -using Microsoft.Extensions.Logging; - -namespace Emby.Dlna.MediaReceiverRegistrar -{ - public class MediaReceiverRegistrar : BaseService, IMediaReceiverRegistrar - { - private readonly IServerConfigurationManager _config; - - public MediaReceiverRegistrar( - ILogger logger, - IHttpClient httpClient, - IServerConfigurationManager config) - : base(logger, httpClient) - { - _config = config; - } - - /// - public string GetServiceXml() - { - return new MediaReceiverRegistrarXmlBuilder().GetXml(); - } - - /// - public Task ProcessControlRequestAsync(ControlRequest request) - { - return new ControlHandler( - _config, - Logger) - .ProcessControlRequestAsync(request); - } - } -} diff --git a/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarService.cs b/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarService.cs new file mode 100644 index 000000000..28de2fef5 --- /dev/null +++ b/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarService.cs @@ -0,0 +1,39 @@ +#pragma warning disable CS1591 + +using System.Threading.Tasks; +using Emby.Dlna.Service; +using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Configuration; +using Microsoft.Extensions.Logging; + +namespace Emby.Dlna.MediaReceiverRegistrar +{ + public class MediaReceiverRegistrarService : BaseService, IMediaReceiverRegistrar + { + private readonly IServerConfigurationManager _config; + + public MediaReceiverRegistrarService( + ILogger logger, + IHttpClient httpClient, + IServerConfigurationManager config) + : base(logger, httpClient) + { + _config = config; + } + + /// + public string GetServiceXml() + { + return new MediaReceiverRegistrarXmlBuilder().GetXml(); + } + + /// + public Task ProcessControlRequestAsync(ControlRequest request) + { + return new ControlHandler( + _config, + Logger) + .ProcessControlRequestAsync(request); + } + } +} diff --git a/Emby.Dlna/PlayTo/Device.cs b/Emby.Dlna/PlayTo/Device.cs index 9f925eed5..5462e7abc 100644 --- a/Emby.Dlna/PlayTo/Device.cs +++ b/Emby.Dlna/PlayTo/Device.cs @@ -21,15 +21,38 @@ namespace Emby.Dlna.PlayTo { private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); + private readonly IHttpClient _httpClient; + + private readonly ILogger _logger; + + private readonly object _timerLock = new object(); private Timer _timer; + private int _muteVol; + private int _volume; + private DateTime _lastVolumeRefresh; + private bool _volumeRefreshActive; + private int _connectFailureCount; + private bool _disposed; + + public Device(DeviceInfo deviceProperties, IHttpClient httpClient, ILogger logger) + { + Properties = deviceProperties; + _httpClient = httpClient; + _logger = logger; + } + + public event EventHandler PlaybackStart; + + public event EventHandler PlaybackProgress; + + public event EventHandler PlaybackStopped; + + public event EventHandler MediaChanged; public DeviceInfo Properties { get; set; } - private int _muteVol; public bool IsMuted { get; set; } - private int _volume; - public int Volume { get @@ -53,18 +76,13 @@ namespace Emby.Dlna.PlayTo public bool IsStopped => TransportState == TransportState.Stopped; - private readonly IHttpClient _httpClient; + public Action OnDeviceUnavailable { get; set; } - private readonly ILogger _logger; + private TransportCommands AvCommands { get; set; } - public Action OnDeviceUnavailable { get; set; } + private TransportCommands RendererCommands { get; set; } - public Device(DeviceInfo deviceProperties, IHttpClient httpClient, ILogger logger) - { - Properties = deviceProperties; - _httpClient = httpClient; - _logger = logger; - } + public UBaseObject CurrentMediaInfo { get; private set; } public void Start() { @@ -72,8 +90,6 @@ namespace Emby.Dlna.PlayTo _timer = new Timer(TimerCallback, null, 1000, Timeout.Infinite); } - private DateTime _lastVolumeRefresh; - private bool _volumeRefreshActive; private Task RefreshVolumeIfNeeded() { if (_volumeRefreshActive @@ -104,7 +120,6 @@ namespace Emby.Dlna.PlayTo } } - private readonly object _timerLock = new object(); private void RestartTimer(bool immediate = false) { lock (_timerLock) @@ -232,6 +247,9 @@ namespace Emby.Dlna.PlayTo /// /// Sets volume on a scale of 0-100. /// + /// The volume on a scale of 0-100. + /// The cancellation token to cancel operation. + /// A representing the asynchronous operation. public async Task SetVolume(int value, CancellationToken cancellationToken) { var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false); @@ -405,8 +423,6 @@ namespace Emby.Dlna.PlayTo RestartTimer(true); } - private int _connectFailureCount; - private async void TimerCallback(object sender) { if (_disposed) @@ -538,7 +554,7 @@ namespace Emby.Dlna.PlayTo return; } - var volume = result.Document.Descendants(uPnpNamespaces.RenderingControl + "GetVolumeResponse").Select(i => i.Element("CurrentVolume")).FirstOrDefault(i => i != null); + var volume = result.Document.Descendants(UPnpNamespaces.RenderingControl + "GetVolumeResponse").Select(i => i.Element("CurrentVolume")).FirstOrDefault(i => i != null); var volumeValue = volume?.Value; if (string.IsNullOrWhiteSpace(volumeValue)) @@ -588,7 +604,7 @@ namespace Emby.Dlna.PlayTo return; } - var valueNode = result.Document.Descendants(uPnpNamespaces.RenderingControl + "GetMuteResponse") + var valueNode = result.Document.Descendants(UPnpNamespaces.RenderingControl + "GetMuteResponse") .Select(i => i.Element("CurrentMute")) .FirstOrDefault(i => i != null); @@ -622,7 +638,7 @@ namespace Emby.Dlna.PlayTo } var transportState = - result.Document.Descendants(uPnpNamespaces.AvTransport + "GetTransportInfoResponse").Select(i => i.Element("CurrentTransportState")).FirstOrDefault(i => i != null); + result.Document.Descendants(UPnpNamespaces.AvTransport + "GetTransportInfoResponse").Select(i => i.Element("CurrentTransportState")).FirstOrDefault(i => i != null); var transportStateValue = transportState?.Value; @@ -635,7 +651,7 @@ namespace Emby.Dlna.PlayTo return null; } - private async Task GetMediaInfo(TransportCommands avCommands, CancellationToken cancellationToken) + private async Task GetMediaInfo(TransportCommands avCommands, CancellationToken cancellationToken) { var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetMediaInfo"); if (command == null) @@ -670,7 +686,7 @@ namespace Emby.Dlna.PlayTo return null; } - var e = track.Element(uPnpNamespaces.items) ?? track; + var e = track.Element(UPnpNamespaces.Items) ?? track; var elementString = (string)e; @@ -686,13 +702,13 @@ namespace Emby.Dlna.PlayTo return null; } - e = track.Element(uPnpNamespaces.items) ?? track; + e = track.Element(UPnpNamespaces.Items) ?? track; elementString = (string)e; if (!string.IsNullOrWhiteSpace(elementString)) { - return new uBaseObject + return new UBaseObject { Url = elementString }; @@ -701,7 +717,7 @@ namespace Emby.Dlna.PlayTo return null; } - private async Task<(bool, uBaseObject)> GetPositionInfo(TransportCommands avCommands, CancellationToken cancellationToken) + private async Task<(bool, UBaseObject)> GetPositionInfo(TransportCommands avCommands, CancellationToken cancellationToken) { var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetPositionInfo"); if (command == null) @@ -730,10 +746,10 @@ namespace Emby.Dlna.PlayTo return (false, null); } - var trackUriElem = result.Document.Descendants(uPnpNamespaces.AvTransport + "GetPositionInfoResponse").Select(i => i.Element("TrackURI")).FirstOrDefault(i => i != null); + var trackUriElem = result.Document.Descendants(UPnpNamespaces.AvTransport + "GetPositionInfoResponse").Select(i => i.Element("TrackURI")).FirstOrDefault(i => i != null); var trackUri = trackUriElem?.Value; - var durationElem = result.Document.Descendants(uPnpNamespaces.AvTransport + "GetPositionInfoResponse").Select(i => i.Element("TrackDuration")).FirstOrDefault(i => i != null); + var durationElem = result.Document.Descendants(UPnpNamespaces.AvTransport + "GetPositionInfoResponse").Select(i => i.Element("TrackDuration")).FirstOrDefault(i => i != null); var duration = durationElem?.Value; if (!string.IsNullOrWhiteSpace(duration) @@ -746,7 +762,7 @@ namespace Emby.Dlna.PlayTo Duration = null; } - var positionElem = result.Document.Descendants(uPnpNamespaces.AvTransport + "GetPositionInfoResponse").Select(i => i.Element("RelTime")).FirstOrDefault(i => i != null); + var positionElem = result.Document.Descendants(UPnpNamespaces.AvTransport + "GetPositionInfoResponse").Select(i => i.Element("RelTime")).FirstOrDefault(i => i != null); var position = positionElem?.Value; if (!string.IsNullOrWhiteSpace(position) && !string.Equals(position, "NOT_IMPLEMENTED", StringComparison.OrdinalIgnoreCase)) @@ -786,7 +802,7 @@ namespace Emby.Dlna.PlayTo return (true, null); } - var e = uPnpResponse.Element(uPnpNamespaces.items); + var e = uPnpResponse.Element(UPnpNamespaces.Items); var uTrack = CreateUBaseObject(e, trackUri); @@ -827,26 +843,26 @@ namespace Emby.Dlna.PlayTo return null; } - private static uBaseObject CreateUBaseObject(XElement container, string trackUri) + private static UBaseObject CreateUBaseObject(XElement container, string trackUri) { if (container == null) { throw new ArgumentNullException(nameof(container)); } - var url = container.GetValue(uPnpNamespaces.Res); + var url = container.GetValue(UPnpNamespaces.Res); if (string.IsNullOrWhiteSpace(url)) { url = trackUri; } - return new uBaseObject + return new UBaseObject { - Id = container.GetAttributeValue(uPnpNamespaces.Id), - ParentId = container.GetAttributeValue(uPnpNamespaces.ParentId), - Title = container.GetValue(uPnpNamespaces.title), - IconUrl = container.GetValue(uPnpNamespaces.Artwork), + Id = container.GetAttributeValue(UPnpNamespaces.Id), + ParentId = container.GetAttributeValue(UPnpNamespaces.ParentId), + Title = container.GetValue(UPnpNamespaces.Title), + IconUrl = container.GetValue(UPnpNamespaces.Artwork), SecondText = string.Empty, Url = url, ProtocolInfo = GetProtocolInfo(container), @@ -861,11 +877,11 @@ namespace Emby.Dlna.PlayTo throw new ArgumentNullException(nameof(container)); } - var resElement = container.Element(uPnpNamespaces.Res); + var resElement = container.Element(UPnpNamespaces.Res); if (resElement != null) { - var info = resElement.Attribute(uPnpNamespaces.ProtocolInfo); + var info = resElement.Attribute(UPnpNamespaces.ProtocolInfo); if (info != null && !string.IsNullOrWhiteSpace(info.Value)) { @@ -953,11 +969,7 @@ namespace Emby.Dlna.PlayTo return baseUrl + url; } - private TransportCommands AvCommands { get; set; } - - private TransportCommands RendererCommands { get; set; } - - public static async Task CreateuPnpDeviceAsync(Uri url, IHttpClient httpClient, IServerConfigurationManager config, ILogger logger, CancellationToken cancellationToken) + public static async Task CreateuPnpDeviceAsync(Uri url, IHttpClient httpClient, ILogger logger, CancellationToken cancellationToken) { var ssdpHttpClient = new SsdpHttpClient(httpClient); @@ -965,13 +977,13 @@ namespace Emby.Dlna.PlayTo var friendlyNames = new List(); - var name = document.Descendants(uPnpNamespaces.ud.GetName("friendlyName")).FirstOrDefault(); + var name = document.Descendants(UPnpNamespaces.Ud.GetName("friendlyName")).FirstOrDefault(); if (name != null && !string.IsNullOrWhiteSpace(name.Value)) { friendlyNames.Add(name.Value); } - var room = document.Descendants(uPnpNamespaces.ud.GetName("roomName")).FirstOrDefault(); + var room = document.Descendants(UPnpNamespaces.Ud.GetName("roomName")).FirstOrDefault(); if (room != null && !string.IsNullOrWhiteSpace(room.Value)) { friendlyNames.Add(room.Value); @@ -983,74 +995,74 @@ namespace Emby.Dlna.PlayTo BaseUrl = string.Format(CultureInfo.InvariantCulture, "http://{0}:{1}", url.Host, url.Port) }; - var model = document.Descendants(uPnpNamespaces.ud.GetName("modelName")).FirstOrDefault(); + var model = document.Descendants(UPnpNamespaces.Ud.GetName("modelName")).FirstOrDefault(); if (model != null) { deviceProperties.ModelName = model.Value; } - var modelNumber = document.Descendants(uPnpNamespaces.ud.GetName("modelNumber")).FirstOrDefault(); + var modelNumber = document.Descendants(UPnpNamespaces.Ud.GetName("modelNumber")).FirstOrDefault(); if (modelNumber != null) { deviceProperties.ModelNumber = modelNumber.Value; } - var uuid = document.Descendants(uPnpNamespaces.ud.GetName("UDN")).FirstOrDefault(); + var uuid = document.Descendants(UPnpNamespaces.Ud.GetName("UDN")).FirstOrDefault(); if (uuid != null) { deviceProperties.UUID = uuid.Value; } - var manufacturer = document.Descendants(uPnpNamespaces.ud.GetName("manufacturer")).FirstOrDefault(); + var manufacturer = document.Descendants(UPnpNamespaces.Ud.GetName("manufacturer")).FirstOrDefault(); if (manufacturer != null) { deviceProperties.Manufacturer = manufacturer.Value; } - var manufacturerUrl = document.Descendants(uPnpNamespaces.ud.GetName("manufacturerURL")).FirstOrDefault(); + var manufacturerUrl = document.Descendants(UPnpNamespaces.Ud.GetName("manufacturerURL")).FirstOrDefault(); if (manufacturerUrl != null) { deviceProperties.ManufacturerUrl = manufacturerUrl.Value; } - var presentationUrl = document.Descendants(uPnpNamespaces.ud.GetName("presentationURL")).FirstOrDefault(); + var presentationUrl = document.Descendants(UPnpNamespaces.Ud.GetName("presentationURL")).FirstOrDefault(); if (presentationUrl != null) { deviceProperties.PresentationUrl = presentationUrl.Value; } - var modelUrl = document.Descendants(uPnpNamespaces.ud.GetName("modelURL")).FirstOrDefault(); + var modelUrl = document.Descendants(UPnpNamespaces.Ud.GetName("modelURL")).FirstOrDefault(); if (modelUrl != null) { deviceProperties.ModelUrl = modelUrl.Value; } - var serialNumber = document.Descendants(uPnpNamespaces.ud.GetName("serialNumber")).FirstOrDefault(); + var serialNumber = document.Descendants(UPnpNamespaces.Ud.GetName("serialNumber")).FirstOrDefault(); if (serialNumber != null) { deviceProperties.SerialNumber = serialNumber.Value; } - var modelDescription = document.Descendants(uPnpNamespaces.ud.GetName("modelDescription")).FirstOrDefault(); + var modelDescription = document.Descendants(UPnpNamespaces.Ud.GetName("modelDescription")).FirstOrDefault(); if (modelDescription != null) { deviceProperties.ModelDescription = modelDescription.Value; } - var icon = document.Descendants(uPnpNamespaces.ud.GetName("icon")).FirstOrDefault(); + var icon = document.Descendants(UPnpNamespaces.Ud.GetName("icon")).FirstOrDefault(); if (icon != null) { deviceProperties.Icon = CreateIcon(icon); } - foreach (var services in document.Descendants(uPnpNamespaces.ud.GetName("serviceList"))) + foreach (var services in document.Descendants(UPnpNamespaces.Ud.GetName("serviceList"))) { if (services == null) { continue; } - var servicesList = services.Descendants(uPnpNamespaces.ud.GetName("service")); + var servicesList = services.Descendants(UPnpNamespaces.Ud.GetName("service")); if (servicesList == null) { continue; @@ -1077,11 +1089,11 @@ namespace Emby.Dlna.PlayTo throw new ArgumentNullException(nameof(element)); } - var mimeType = element.GetDescendantValue(uPnpNamespaces.ud.GetName("mimetype")); - var width = element.GetDescendantValue(uPnpNamespaces.ud.GetName("width")); - var height = element.GetDescendantValue(uPnpNamespaces.ud.GetName("height")); - var depth = element.GetDescendantValue(uPnpNamespaces.ud.GetName("depth")); - var url = element.GetDescendantValue(uPnpNamespaces.ud.GetName("url")); + var mimeType = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("mimetype")); + var width = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("width")); + var height = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("height")); + var depth = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("depth")); + var url = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("url")); var widthValue = int.Parse(width, NumberStyles.Integer, UsCulture); var heightValue = int.Parse(height, NumberStyles.Integer, UsCulture); @@ -1098,11 +1110,11 @@ namespace Emby.Dlna.PlayTo private static DeviceService Create(XElement element) { - var type = element.GetDescendantValue(uPnpNamespaces.ud.GetName("serviceType")); - var id = element.GetDescendantValue(uPnpNamespaces.ud.GetName("serviceId")); - var scpdUrl = element.GetDescendantValue(uPnpNamespaces.ud.GetName("SCPDURL")); - var controlURL = element.GetDescendantValue(uPnpNamespaces.ud.GetName("controlURL")); - var eventSubURL = element.GetDescendantValue(uPnpNamespaces.ud.GetName("eventSubURL")); + var type = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("serviceType")); + var id = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("serviceId")); + var scpdUrl = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("SCPDURL")); + var controlURL = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("controlURL")); + var eventSubURL = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("eventSubURL")); return new DeviceService { @@ -1114,14 +1126,7 @@ namespace Emby.Dlna.PlayTo }; } - public event EventHandler PlaybackStart; - public event EventHandler PlaybackProgress; - public event EventHandler PlaybackStopped; - public event EventHandler MediaChanged; - - public uBaseObject CurrentMediaInfo { get; private set; } - - private void UpdateMediaInfo(uBaseObject mediaInfo, TransportState state) + private void UpdateMediaInfo(UBaseObject mediaInfo, TransportState state) { TransportState = state; @@ -1149,7 +1154,7 @@ namespace Emby.Dlna.PlayTo } } - private void OnPlaybackStart(uBaseObject mediaInfo) + private void OnPlaybackStart(UBaseObject mediaInfo) { if (string.IsNullOrWhiteSpace(mediaInfo.Url)) { @@ -1162,7 +1167,7 @@ namespace Emby.Dlna.PlayTo }); } - private void OnPlaybackProgress(uBaseObject mediaInfo) + private void OnPlaybackProgress(UBaseObject mediaInfo) { if (string.IsNullOrWhiteSpace(mediaInfo.Url)) { @@ -1175,7 +1180,7 @@ namespace Emby.Dlna.PlayTo }); } - private void OnPlaybackStop(uBaseObject mediaInfo) + private void OnPlaybackStop(UBaseObject mediaInfo) { PlaybackStopped?.Invoke(this, new PlaybackStoppedEventArgs { @@ -1183,7 +1188,7 @@ namespace Emby.Dlna.PlayTo }); } - private void OnMediaChanged(uBaseObject old, uBaseObject newMedia) + private void OnMediaChanged(UBaseObject old, UBaseObject newMedia) { MediaChanged?.Invoke(this, new MediaChangedEventArgs { @@ -1192,8 +1197,7 @@ namespace Emby.Dlna.PlayTo }); } - bool _disposed; - + /// public void Dispose() { Dispose(true); @@ -1222,6 +1226,7 @@ namespace Emby.Dlna.PlayTo _disposed = true; } + /// public override string ToString() { return string.Format(CultureInfo.InvariantCulture, "{0} - {1}", Properties.Name, Properties.BaseUrl); diff --git a/Emby.Dlna/PlayTo/MediaChangedEventArgs.cs b/Emby.Dlna/PlayTo/MediaChangedEventArgs.cs index a8043d4ea..dabd079af 100644 --- a/Emby.Dlna/PlayTo/MediaChangedEventArgs.cs +++ b/Emby.Dlna/PlayTo/MediaChangedEventArgs.cs @@ -6,8 +6,8 @@ namespace Emby.Dlna.PlayTo { public class MediaChangedEventArgs : EventArgs { - public uBaseObject OldMediaInfo { get; set; } + public UBaseObject OldMediaInfo { get; set; } - public uBaseObject NewMediaInfo { get; set; } + public UBaseObject NewMediaInfo { get; set; } } } diff --git a/Emby.Dlna/PlayTo/PlayToController.cs b/Emby.Dlna/PlayTo/PlayToController.cs index f8ce19757..d9e10f459 100644 --- a/Emby.Dlna/PlayTo/PlayToController.cs +++ b/Emby.Dlna/PlayTo/PlayToController.cs @@ -377,7 +377,8 @@ namespace Emby.Dlna.PlayTo _session.ApplicationVersion, _session.DeviceId, _session.DeviceName, - _session.RemoteEndPoint, user); + _session.RemoteEndPoint, + user); } return PlayItems(playlist, cancellationToken); @@ -502,42 +503,44 @@ namespace Emby.Dlna.PlayTo if (streamInfo.MediaType == DlnaProfileType.Audio) { return new ContentFeatureBuilder(profile) - .BuildAudioHeader(streamInfo.Container, - streamInfo.TargetAudioCodec.FirstOrDefault(), - streamInfo.TargetAudioBitrate, - streamInfo.TargetAudioSampleRate, - streamInfo.TargetAudioChannels, - streamInfo.TargetAudioBitDepth, - streamInfo.IsDirectStream, - streamInfo.RunTimeTicks ?? 0, - streamInfo.TranscodeSeekInfo); + .BuildAudioHeader( + streamInfo.Container, + streamInfo.TargetAudioCodec.FirstOrDefault(), + streamInfo.TargetAudioBitrate, + streamInfo.TargetAudioSampleRate, + streamInfo.TargetAudioChannels, + streamInfo.TargetAudioBitDepth, + streamInfo.IsDirectStream, + streamInfo.RunTimeTicks ?? 0, + streamInfo.TranscodeSeekInfo); } if (streamInfo.MediaType == DlnaProfileType.Video) { var list = new ContentFeatureBuilder(profile) - .BuildVideoHeader(streamInfo.Container, - streamInfo.TargetVideoCodec.FirstOrDefault(), - streamInfo.TargetAudioCodec.FirstOrDefault(), - streamInfo.TargetWidth, - streamInfo.TargetHeight, - streamInfo.TargetVideoBitDepth, - streamInfo.TargetVideoBitrate, - streamInfo.TargetTimestamp, - streamInfo.IsDirectStream, - streamInfo.RunTimeTicks ?? 0, - streamInfo.TargetVideoProfile, - streamInfo.TargetVideoLevel, - streamInfo.TargetFramerate ?? 0, - streamInfo.TargetPacketLength, - streamInfo.TranscodeSeekInfo, - streamInfo.IsTargetAnamorphic, - streamInfo.IsTargetInterlaced, - streamInfo.TargetRefFrames, - streamInfo.TargetVideoStreamCount, - streamInfo.TargetAudioStreamCount, - streamInfo.TargetVideoCodecTag, - streamInfo.IsTargetAVC); + .BuildVideoHeader( + streamInfo.Container, + streamInfo.TargetVideoCodec.FirstOrDefault(), + streamInfo.TargetAudioCodec.FirstOrDefault(), + streamInfo.TargetWidth, + streamInfo.TargetHeight, + streamInfo.TargetVideoBitDepth, + streamInfo.TargetVideoBitrate, + streamInfo.TargetTimestamp, + streamInfo.IsDirectStream, + streamInfo.RunTimeTicks ?? 0, + streamInfo.TargetVideoProfile, + streamInfo.TargetVideoLevel, + streamInfo.TargetFramerate ?? 0, + streamInfo.TargetPacketLength, + streamInfo.TranscodeSeekInfo, + streamInfo.IsTargetAnamorphic, + streamInfo.IsTargetInterlaced, + streamInfo.TargetRefFrames, + streamInfo.TargetVideoStreamCount, + streamInfo.TargetAudioStreamCount, + streamInfo.TargetVideoCodecTag, + streamInfo.IsTargetAVC); return list.Count == 0 ? null : list[0]; } @@ -681,48 +684,41 @@ namespace Emby.Dlna.PlayTo case GeneralCommandType.ToggleMute: return _device.ToggleMute(cancellationToken); case GeneralCommandType.SetAudioStreamIndex: + if (command.Arguments.TryGetValue("Index", out string index)) { - if (command.Arguments.TryGetValue("Index", out string arg)) + if (int.TryParse(index, NumberStyles.Integer, _usCulture, out var val)) { - if (int.TryParse(arg, NumberStyles.Integer, _usCulture, out var val)) - { - return SetAudioStreamIndex(val); - } - - throw new ArgumentException("Unsupported SetAudioStreamIndex value supplied."); + return SetAudioStreamIndex(val); } - throw new ArgumentException("SetAudioStreamIndex argument cannot be null"); + throw new ArgumentException("Unsupported SetAudioStreamIndex value supplied."); } + + throw new ArgumentException("SetAudioStreamIndex argument cannot be null"); case GeneralCommandType.SetSubtitleStreamIndex: + if (command.Arguments.TryGetValue("Index", out index)) { - if (command.Arguments.TryGetValue("Index", out string arg)) + if (int.TryParse(index, NumberStyles.Integer, _usCulture, out var val)) { - if (int.TryParse(arg, NumberStyles.Integer, _usCulture, out var val)) - { - return SetSubtitleStreamIndex(val); - } - - throw new ArgumentException("Unsupported SetSubtitleStreamIndex value supplied."); + return SetSubtitleStreamIndex(val); } - throw new ArgumentException("SetSubtitleStreamIndex argument cannot be null"); + throw new ArgumentException("Unsupported SetSubtitleStreamIndex value supplied."); } + + throw new ArgumentException("SetSubtitleStreamIndex argument cannot be null"); case GeneralCommandType.SetVolume: + if (command.Arguments.TryGetValue("Volume", out string vol)) { - if (command.Arguments.TryGetValue("Volume", out string arg)) + if (int.TryParse(vol, NumberStyles.Integer, _usCulture, out var volume)) { - if (int.TryParse(arg, NumberStyles.Integer, _usCulture, out var volume)) - { - return _device.SetVolume(volume, cancellationToken); - } - - throw new ArgumentException("Unsupported volume value supplied."); + return _device.SetVolume(volume, cancellationToken); } - throw new ArgumentException("Volume argument cannot be null"); + throw new ArgumentException("Unsupported volume value supplied."); } + throw new ArgumentException("Volume argument cannot be null"); default: return Task.CompletedTask; } @@ -795,6 +791,62 @@ namespace Emby.Dlna.PlayTo await _device.Seek(TimeSpan.FromTicks(positionTicks), cancellationToken).ConfigureAwait(false); } + private static int? GetIntValue(IReadOnlyDictionary values, string name) + { + var value = values.GetValueOrDefault(name); + + if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) + { + return result; + } + + return null; + } + + private static long GetLongValue(IReadOnlyDictionary values, string name) + { + var value = values.GetValueOrDefault(name); + + if (long.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) + { + return result; + } + + return 0; + } + + /// + public Task SendMessage(string name, Guid messageId, T data, CancellationToken cancellationToken) + { + if (_disposed) + { + throw new ObjectDisposedException(GetType().Name); + } + + if (_device == null) + { + return Task.CompletedTask; + } + + if (string.Equals(name, "Play", StringComparison.OrdinalIgnoreCase)) + { + return SendPlayCommand(data as PlayRequest, cancellationToken); + } + + if (string.Equals(name, "PlayState", StringComparison.OrdinalIgnoreCase)) + { + return SendPlaystateCommand(data as PlaystateRequest, cancellationToken); + } + + if (string.Equals(name, "GeneralCommand", StringComparison.OrdinalIgnoreCase)) + { + return SendGeneralCommand(data as GeneralCommand, cancellationToken); + } + + // Not supported or needed right now + return Task.CompletedTask; + } + private class StreamParams { private MediaSourceInfo mediaSource; @@ -908,61 +960,5 @@ namespace Emby.Dlna.PlayTo return request; } } - - private static int? GetIntValue(IReadOnlyDictionary values, string name) - { - var value = values.GetValueOrDefault(name); - - if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) - { - return result; - } - - return null; - } - - private static long GetLongValue(IReadOnlyDictionary values, string name) - { - var value = values.GetValueOrDefault(name); - - if (long.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) - { - return result; - } - - return 0; - } - - /// - public Task SendMessage(string name, Guid messageId, T data, CancellationToken cancellationToken) - { - if (_disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - - if (_device == null) - { - return Task.CompletedTask; - } - - if (string.Equals(name, "Play", StringComparison.OrdinalIgnoreCase)) - { - return SendPlayCommand(data as PlayRequest, cancellationToken); - } - - if (string.Equals(name, "PlayState", StringComparison.OrdinalIgnoreCase)) - { - return SendPlaystateCommand(data as PlaystateRequest, cancellationToken); - } - - if (string.Equals(name, "GeneralCommand", StringComparison.OrdinalIgnoreCase)) - { - return SendGeneralCommand(data as GeneralCommand, cancellationToken); - } - - // Not supported or needed right now - return Task.CompletedTask; - } } } diff --git a/Emby.Dlna/PlayTo/PlayToManager.cs b/Emby.Dlna/PlayTo/PlayToManager.cs index 99b9105cc..54f4fee05 100644 --- a/Emby.Dlna/PlayTo/PlayToManager.cs +++ b/Emby.Dlna/PlayTo/PlayToManager.cs @@ -174,7 +174,7 @@ namespace Emby.Dlna.PlayTo if (controller == null) { - var device = await Device.CreateuPnpDeviceAsync(uri, _httpClient, _config, _logger, cancellationToken).ConfigureAwait(false); + var device = await Device.CreateuPnpDeviceAsync(uri, _httpClient, _logger, cancellationToken).ConfigureAwait(false); string deviceName = device.Properties.Name; diff --git a/Emby.Dlna/PlayTo/PlaybackProgressEventArgs.cs b/Emby.Dlna/PlayTo/PlaybackProgressEventArgs.cs index 795618df2..d14617c8a 100644 --- a/Emby.Dlna/PlayTo/PlaybackProgressEventArgs.cs +++ b/Emby.Dlna/PlayTo/PlaybackProgressEventArgs.cs @@ -6,6 +6,6 @@ namespace Emby.Dlna.PlayTo { public class PlaybackProgressEventArgs : EventArgs { - public uBaseObject MediaInfo { get; set; } + public UBaseObject MediaInfo { get; set; } } } diff --git a/Emby.Dlna/PlayTo/PlaybackStartEventArgs.cs b/Emby.Dlna/PlayTo/PlaybackStartEventArgs.cs index 27883ca32..3f8d55263 100644 --- a/Emby.Dlna/PlayTo/PlaybackStartEventArgs.cs +++ b/Emby.Dlna/PlayTo/PlaybackStartEventArgs.cs @@ -6,6 +6,6 @@ namespace Emby.Dlna.PlayTo { public class PlaybackStartEventArgs : EventArgs { - public uBaseObject MediaInfo { get; set; } + public UBaseObject MediaInfo { get; set; } } } diff --git a/Emby.Dlna/PlayTo/PlaybackStoppedEventArgs.cs b/Emby.Dlna/PlayTo/PlaybackStoppedEventArgs.cs index 5944d936c..deeb47918 100644 --- a/Emby.Dlna/PlayTo/PlaybackStoppedEventArgs.cs +++ b/Emby.Dlna/PlayTo/PlaybackStoppedEventArgs.cs @@ -6,6 +6,6 @@ namespace Emby.Dlna.PlayTo { public class PlaybackStoppedEventArgs : EventArgs { - public uBaseObject MediaInfo { get; set; } + public UBaseObject MediaInfo { get; set; } } } diff --git a/Emby.Dlna/PlayTo/TransportCommands.cs b/Emby.Dlna/PlayTo/TransportCommands.cs index dc797a691..fda17a8b4 100644 --- a/Emby.Dlna/PlayTo/TransportCommands.cs +++ b/Emby.Dlna/PlayTo/TransportCommands.cs @@ -16,34 +16,26 @@ namespace Emby.Dlna.PlayTo private List _stateVariables = new List(); private List _serviceActions = new List(); - public List StateVariables - { - get => _stateVariables; - set => _stateVariables = value; - } + public List StateVariables => _stateVariables; - public List ServiceActions - { - get => _serviceActions; - set => _serviceActions = value; - } + public List ServiceActions => _serviceActions; public static TransportCommands Create(XDocument document) { var command = new TransportCommands(); - var actionList = document.Descendants(uPnpNamespaces.svc + "actionList"); + var actionList = document.Descendants(UPnpNamespaces.Svc + "actionList"); - foreach (var container in actionList.Descendants(uPnpNamespaces.svc + "action")) + foreach (var container in actionList.Descendants(UPnpNamespaces.Svc + "action")) { command.ServiceActions.Add(ServiceActionFromXml(container)); } - var stateValues = document.Descendants(uPnpNamespaces.ServiceStateTable).FirstOrDefault(); + var stateValues = document.Descendants(UPnpNamespaces.ServiceStateTable).FirstOrDefault(); if (stateValues != null) { - foreach (var container in stateValues.Elements(uPnpNamespaces.svc + "stateVariable")) + foreach (var container in stateValues.Elements(UPnpNamespaces.Svc + "stateVariable")) { command.StateVariables.Add(FromXml(container)); } @@ -54,19 +46,19 @@ namespace Emby.Dlna.PlayTo private static ServiceAction ServiceActionFromXml(XElement container) { - var argumentList = new List(); + var serviceAction = new ServiceAction + { + Name = container.GetValue(UPnpNamespaces.Svc + "name"), + }; + + var argumentList = serviceAction.ArgumentList; - foreach (var arg in container.Descendants(uPnpNamespaces.svc + "argument")) + foreach (var arg in container.Descendants(UPnpNamespaces.Svc + "argument")) { argumentList.Add(ArgumentFromXml(arg)); } - return new ServiceAction - { - Name = container.GetValue(uPnpNamespaces.svc + "name"), - - ArgumentList = argumentList - }; + return serviceAction; } private static Argument ArgumentFromXml(XElement container) @@ -78,29 +70,29 @@ namespace Emby.Dlna.PlayTo return new Argument { - Name = container.GetValue(uPnpNamespaces.svc + "name"), - Direction = container.GetValue(uPnpNamespaces.svc + "direction"), - RelatedStateVariable = container.GetValue(uPnpNamespaces.svc + "relatedStateVariable") + Name = container.GetValue(UPnpNamespaces.Svc + "name"), + Direction = container.GetValue(UPnpNamespaces.Svc + "direction"), + RelatedStateVariable = container.GetValue(UPnpNamespaces.Svc + "relatedStateVariable") }; } private static StateVariable FromXml(XElement container) { var allowedValues = new List(); - var element = container.Descendants(uPnpNamespaces.svc + "allowedValueList") + var element = container.Descendants(UPnpNamespaces.Svc + "allowedValueList") .FirstOrDefault(); if (element != null) { - var values = element.Descendants(uPnpNamespaces.svc + "allowedValue"); + var values = element.Descendants(UPnpNamespaces.Svc + "allowedValue"); allowedValues.AddRange(values.Select(child => child.Value)); } return new StateVariable { - Name = container.GetValue(uPnpNamespaces.svc + "name"), - DataType = container.GetValue(uPnpNamespaces.svc + "dataType"), + Name = container.GetValue(UPnpNamespaces.Svc + "name"), + DataType = container.GetValue(UPnpNamespaces.Svc + "dataType"), AllowedValues = allowedValues.ToArray() }; } @@ -183,8 +175,7 @@ namespace Emby.Dlna.PlayTo if (state != null) { var sendValue = state.AllowedValues.FirstOrDefault(a => string.Equals(a, commandParameter, StringComparison.OrdinalIgnoreCase)) ?? - state.AllowedValues.FirstOrDefault() ?? - value; + (state.AllowedValues.Count > 0 ? state.AllowedValues[0] : value); return string.Format(CultureInfo.InvariantCulture, "<{0} xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"{1}\">{2}", argument.Name, state.DataType ?? "string", sendValue); } diff --git a/Emby.Dlna/PlayTo/TransportState.cs b/Emby.Dlna/PlayTo/TransportState.cs index 2058e9dc7..7068a5d24 100644 --- a/Emby.Dlna/PlayTo/TransportState.cs +++ b/Emby.Dlna/PlayTo/TransportState.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +#pragma warning disable SA1602 namespace Emby.Dlna.PlayTo { diff --git a/Emby.Dlna/PlayTo/UpnpContainer.cs b/Emby.Dlna/PlayTo/UpnpContainer.cs index e2d7a10f0..05f27603f 100644 --- a/Emby.Dlna/PlayTo/UpnpContainer.cs +++ b/Emby.Dlna/PlayTo/UpnpContainer.cs @@ -6,22 +6,22 @@ using Emby.Dlna.Ssdp; namespace Emby.Dlna.PlayTo { - public class UpnpContainer : uBaseObject + public class UpnpContainer : UBaseObject { - public static uBaseObject Create(XElement container) + public static UBaseObject Create(XElement container) { if (container == null) { throw new ArgumentNullException(nameof(container)); } - return new uBaseObject + return new UBaseObject { - Id = container.GetAttributeValue(uPnpNamespaces.Id), - ParentId = container.GetAttributeValue(uPnpNamespaces.ParentId), - Title = container.GetValue(uPnpNamespaces.title), - IconUrl = container.GetValue(uPnpNamespaces.Artwork), - UpnpClass = container.GetValue(uPnpNamespaces.uClass) + Id = container.GetAttributeValue(UPnpNamespaces.Id), + ParentId = container.GetAttributeValue(UPnpNamespaces.ParentId), + Title = container.GetValue(UPnpNamespaces.Title), + IconUrl = container.GetValue(UPnpNamespaces.Artwork), + UpnpClass = container.GetValue(UPnpNamespaces.Class) }; } } diff --git a/Emby.Dlna/PlayTo/uBaseObject.cs b/Emby.Dlna/PlayTo/uBaseObject.cs index 1f0b06f68..0d9478e42 100644 --- a/Emby.Dlna/PlayTo/uBaseObject.cs +++ b/Emby.Dlna/PlayTo/uBaseObject.cs @@ -1,10 +1,11 @@ #pragma warning disable CS1591 using System; +using System.Collections.Generic; namespace Emby.Dlna.PlayTo { - public class uBaseObject + public class UBaseObject { public string Id { get; set; } @@ -20,7 +21,7 @@ namespace Emby.Dlna.PlayTo public string Url { get; set; } - public string[] ProtocolInfo { get; set; } + public IReadOnlyList ProtocolInfo { get; set; } public string UpnpClass { get; set; } @@ -49,7 +50,7 @@ namespace Emby.Dlna.PlayTo } } - public bool Equals(uBaseObject obj) + public bool Equals(UBaseObject obj) { if (obj == null) { diff --git a/Emby.Dlna/PlayTo/uPnpNamespaces.cs b/Emby.Dlna/PlayTo/uPnpNamespaces.cs index 6ea7dc9cf..5042d4493 100644 --- a/Emby.Dlna/PlayTo/uPnpNamespaces.cs +++ b/Emby.Dlna/PlayTo/uPnpNamespaces.cs @@ -4,38 +4,64 @@ using System.Xml.Linq; namespace Emby.Dlna.PlayTo { - public static class uPnpNamespaces + public static class UPnpNamespaces { - public static XNamespace dc = "http://purl.org/dc/elements/1.1/"; - public static XNamespace ns = "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"; - public static XNamespace svc = "urn:schemas-upnp-org:service-1-0"; - public static XNamespace ud = "urn:schemas-upnp-org:device-1-0"; - public static XNamespace upnp = "urn:schemas-upnp-org:metadata-1-0/upnp/"; - public static XNamespace RenderingControl = "urn:schemas-upnp-org:service:RenderingControl:1"; - public static XNamespace AvTransport = "urn:schemas-upnp-org:service:AVTransport:1"; - public static XNamespace ContentDirectory = "urn:schemas-upnp-org:service:ContentDirectory:1"; - - public static XName containers = ns + "container"; - public static XName items = ns + "item"; - public static XName title = dc + "title"; - public static XName creator = dc + "creator"; - public static XName artist = upnp + "artist"; - public static XName Id = "id"; - public static XName ParentId = "parentID"; - public static XName uClass = upnp + "class"; - public static XName Artwork = upnp + "albumArtURI"; - public static XName Description = dc + "description"; - public static XName LongDescription = upnp + "longDescription"; - public static XName Album = upnp + "album"; - public static XName Author = upnp + "author"; - public static XName Director = upnp + "director"; - public static XName PlayCount = upnp + "playbackCount"; - public static XName Tracknumber = upnp + "originalTrackNumber"; - public static XName Res = ns + "res"; - public static XName Duration = "duration"; - public static XName ProtocolInfo = "protocolInfo"; - - public static XName ServiceStateTable = svc + "serviceStateTable"; - public static XName StateVariable = svc + "stateVariable"; + public static XNamespace Dc { get; } = "http://purl.org/dc/elements/1.1/"; + + public static XNamespace Ns { get; } = "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"; + + public static XNamespace Svc { get; } = "urn:schemas-upnp-org:service-1-0"; + + public static XNamespace Ud { get; } = "urn:schemas-upnp-org:device-1-0"; + + public static XNamespace UPnp { get; } = "urn:schemas-upnp-org:metadata-1-0/upnp/"; + + public static XNamespace RenderingControl { get; } = "urn:schemas-upnp-org:service:RenderingControl:1"; + + public static XNamespace AvTransport { get; } = "urn:schemas-upnp-org:service:AVTransport:1"; + + public static XNamespace ContentDirectory { get; } = "urn:schemas-upnp-org:service:ContentDirectory:1"; + + public static XName Containers { get; } = Ns + "container"; + + public static XName Items { get; } = Ns + "item"; + + public static XName Title { get; } = Dc + "title"; + + public static XName Creator { get; } = Dc + "creator"; + + public static XName Artist { get; } = UPnp + "artist"; + + public static XName Id { get; } = "id"; + + public static XName ParentId { get; } = "parentID"; + + public static XName Class { get; } = UPnp + "class"; + + public static XName Artwork { get; } = UPnp + "albumArtURI"; + + public static XName Description { get; } = Dc + "description"; + + public static XName LongDescription { get; } = UPnp + "longDescription"; + + public static XName Album { get; } = UPnp + "album"; + + public static XName Author { get; } = UPnp + "author"; + + public static XName Director { get; } = UPnp + "director"; + + public static XName PlayCount { get; } = UPnp + "playbackCount"; + + public static XName Tracknumber { get; } = UPnp + "originalTrackNumber"; + + public static XName Res { get; } = Ns + "res"; + + public static XName Duration { get; } = "duration"; + + public static XName ProtocolInfo { get; } = "protocolInfo"; + + public static XName ServiceStateTable { get; } = Svc + "serviceStateTable"; + + public static XName StateVariable { get; } = Svc + "stateVariable"; } } diff --git a/Emby.Dlna/Service/BaseControlHandler.cs b/Emby.Dlna/Service/BaseControlHandler.cs index 91a21a21d..d160e3339 100644 --- a/Emby.Dlna/Service/BaseControlHandler.cs +++ b/Emby.Dlna/Service/BaseControlHandler.cs @@ -210,15 +210,6 @@ namespace Emby.Dlna.Service } } - private class ControlRequestInfo - { - public string LocalName { get; set; } - - public string NamespaceURI { get; set; } - - public Dictionary Headers { get; } = new Dictionary(StringComparer.OrdinalIgnoreCase); - } - protected abstract void WriteResult(string methodName, IDictionary methodParams, XmlWriter xmlWriter); private void LogRequest(ControlRequest request) @@ -240,5 +231,14 @@ namespace Emby.Dlna.Service Logger.LogDebug("Control response. Headers: {@Headers}\n{Xml}", response.Headers, response.Xml); } + + private class ControlRequestInfo + { + public string LocalName { get; set; } + + public string NamespaceURI { get; set; } + + public Dictionary Headers { get; } = new Dictionary(StringComparer.OrdinalIgnoreCase); + } } } diff --git a/Emby.Dlna/Service/BaseService.cs b/Emby.Dlna/Service/BaseService.cs index 88c62f86c..3ad1ea9e0 100644 --- a/Emby.Dlna/Service/BaseService.cs +++ b/Emby.Dlna/Service/BaseService.cs @@ -8,31 +8,33 @@ namespace Emby.Dlna.Service { public class BaseService : IEventManager { - protected IEventManager _eventManager; - protected IHttpClient _httpClient; - protected ILogger Logger; - protected BaseService(ILogger logger, IHttpClient httpClient) { Logger = logger; - _httpClient = httpClient; + HttpClient = httpClient; - _eventManager = new EventManager(logger, _httpClient); + EventManager = new EventManager(logger, HttpClient); } + protected IEventManager EventManager { get; } + + protected IHttpClient HttpClient { get; } + + protected ILogger Logger { get; } + public EventSubscriptionResponse CancelEventSubscription(string subscriptionId) { - return _eventManager.CancelEventSubscription(subscriptionId); + return EventManager.CancelEventSubscription(subscriptionId); } public EventSubscriptionResponse RenewEventSubscription(string subscriptionId, string notificationType, string timeoutString, string callbackUrl) { - return _eventManager.RenewEventSubscription(subscriptionId, notificationType, timeoutString, callbackUrl); + return EventManager.RenewEventSubscription(subscriptionId, notificationType, timeoutString, callbackUrl); } public EventSubscriptionResponse CreateEventSubscription(string notificationType, string timeoutString, string callbackUrl) { - return _eventManager.CreateEventSubscription(notificationType, timeoutString, callbackUrl); + return EventManager.CreateEventSubscription(notificationType, timeoutString, callbackUrl); } } } diff --git a/Emby.Dlna/Service/ServiceXmlBuilder.cs b/Emby.Dlna/Service/ServiceXmlBuilder.cs index 6c7d6f846..1e56d09b2 100644 --- a/Emby.Dlna/Service/ServiceXmlBuilder.cs +++ b/Emby.Dlna/Service/ServiceXmlBuilder.cs @@ -87,7 +87,7 @@ namespace Emby.Dlna.Service .Append(SecurityElement.Escape(item.DataType ?? string.Empty)) .Append(""); - if (item.AllowedValues.Length > 0) + if (item.AllowedValues.Count > 0) { builder.Append(""); foreach (var allowedValue in item.AllowedValues) diff --git a/Emby.Dlna/Ssdp/Extensions.cs b/Emby.Dlna/Ssdp/Extensions.cs deleted file mode 100644 index 613d332b2..000000000 --- a/Emby.Dlna/Ssdp/Extensions.cs +++ /dev/null @@ -1,27 +0,0 @@ -#pragma warning disable CS1591 - -using System.Linq; -using System.Xml.Linq; - -namespace Emby.Dlna.Ssdp -{ - public static class Extensions - { - public static string GetValue(this XElement container, XName name) - { - var node = container.Element(name); - - return node?.Value; - } - - public static string GetAttributeValue(this XElement container, XName name) - { - var node = container.Attribute(name); - - return node?.Value; - } - - public static string GetDescendantValue(this XElement container, XName name) - => container.Descendants(name).FirstOrDefault()?.Value; - } -} diff --git a/Emby.Dlna/Ssdp/SsdpExtensions.cs b/Emby.Dlna/Ssdp/SsdpExtensions.cs new file mode 100644 index 000000000..e7a52f168 --- /dev/null +++ b/Emby.Dlna/Ssdp/SsdpExtensions.cs @@ -0,0 +1,27 @@ +#pragma warning disable CS1591 + +using System.Linq; +using System.Xml.Linq; + +namespace Emby.Dlna.Ssdp +{ + public static class SsdpExtensions + { + public static string GetValue(this XElement container, XName name) + { + var node = container.Element(name); + + return node?.Value; + } + + public static string GetAttributeValue(this XElement container, XName name) + { + var node = container.Attribute(name); + + return node?.Value; + } + + public static string GetDescendantValue(this XElement container, XName name) + => container.Descendants(name).FirstOrDefault()?.Value; + } +} diff --git a/Jellyfin.Api/Controllers/DlnaServerController.cs b/Jellyfin.Api/Controllers/DlnaServerController.cs index 0100d642b..bbaf39eb1 100644 --- a/Jellyfin.Api/Controllers/DlnaServerController.cs +++ b/Jellyfin.Api/Controllers/DlnaServerController.cs @@ -221,9 +221,8 @@ namespace Jellyfin.Api.Controllers private Task ProcessControlRequestInternalAsync(string id, Stream requestStream, IUpnpService service) { - return service.ProcessControlRequestAsync(new ControlRequest + return service.ProcessControlRequestAsync(new ControlRequest(Request.Headers) { - Headers = Request.Headers, InputXml = requestStream, TargetServerUuId = id, RequestedUrl = GetAbsoluteUri() -- cgit v1.2.3 From 4a64819277b98ac98ba0051c49fcf0cbfe16319c Mon Sep 17 00:00:00 2001 From: crobibero Date: Fri, 21 Aug 2020 11:10:29 -0600 Subject: Fix dlna produces type --- Jellyfin.Api/Controllers/DlnaServerController.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'Jellyfin.Api/Controllers/DlnaServerController.cs') diff --git a/Jellyfin.Api/Controllers/DlnaServerController.cs b/Jellyfin.Api/Controllers/DlnaServerController.cs index 0100d642b..e775c05a0 100644 --- a/Jellyfin.Api/Controllers/DlnaServerController.cs +++ b/Jellyfin.Api/Controllers/DlnaServerController.cs @@ -1,6 +1,7 @@ using System; using System.Diagnostics.CodeAnalysis; using System.IO; +using System.Net.Mime; using System.Threading.Tasks; using Emby.Dlna; using Emby.Dlna.Main; @@ -17,8 +18,6 @@ namespace Jellyfin.Api.Controllers [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; @@ -44,7 +43,7 @@ namespace Jellyfin.Api.Controllers /// An containing the description xml. [HttpGet("{serverId}/description")] [HttpGet("{serverId}/description.xml", Name = "GetDescriptionXml_2")] - [Produces(XMLContentType)] + [Produces(MediaTypeNames.Text.Xml)] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult GetDescriptionXml([FromRoute] string serverId) { @@ -62,7 +61,7 @@ namespace Jellyfin.Api.Controllers /// An containing the dlna content directory xml. [HttpGet("{serverId}/ContentDirectory")] [HttpGet("{serverId}/ContentDirectory.xml", Name = "GetContentDirectory_2")] - [Produces(XMLContentType)] + [Produces(MediaTypeNames.Text.Xml)] [ProducesResponseType(StatusCodes.Status200OK)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")] public ActionResult GetContentDirectory([FromRoute] string serverId) @@ -77,7 +76,7 @@ namespace Jellyfin.Api.Controllers /// Dlna media receiver registrar xml. [HttpGet("{serverId}/MediaReceiverRegistrar")] [HttpGet("{serverId}/MediaReceiverRegistrar.xml", Name = "GetMediaReceiverRegistrar_2")] - [Produces(XMLContentType)] + [Produces(MediaTypeNames.Text.Xml)] [ProducesResponseType(StatusCodes.Status200OK)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")] public ActionResult GetMediaReceiverRegistrar([FromRoute] string serverId) @@ -92,7 +91,7 @@ namespace Jellyfin.Api.Controllers /// Dlna media receiver registrar xml. [HttpGet("{serverId}/ConnectionManager")] [HttpGet("{serverId}/ConnectionManager.xml", Name = "GetConnectionManager_2")] - [Produces(XMLContentType)] + [Produces(MediaTypeNames.Text.Xml)] [ProducesResponseType(StatusCodes.Status200OK)] [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "serverId", Justification = "Required for DLNA")] public ActionResult GetConnectionManager([FromRoute] string serverId) -- cgit v1.2.3