Jelajahi Sumber

Remove Emby.Dlna

Patrick Barron 1 tahun lalu
induk
melakukan
f1aba6b952
100 mengubah file dengan 0 tambahan dan 12540 penghapusan
  1. 0 23
      Emby.Dlna/Common/Argument.cs
  2. 0 41
      Emby.Dlna/Common/DeviceIcon.cs
  3. 0 36
      Emby.Dlna/Common/DeviceService.cs
  4. 0 31
      Emby.Dlna/Common/ServiceAction.cs
  5. 0 34
      Emby.Dlna/Common/StateVariable.cs
  6. 0 92
      Emby.Dlna/Configuration/DlnaOptions.cs
  7. 0 15
      Emby.Dlna/ConfigurationExtension.cs
  8. 0 53
      Emby.Dlna/ConnectionManager/ConnectionManagerService.cs
  9. 0 119
      Emby.Dlna/ConnectionManager/ConnectionManagerXmlBuilder.cs
  10. 0 55
      Emby.Dlna/ConnectionManager/ControlHandler.cs
  11. 0 234
      Emby.Dlna/ConnectionManager/ServiceActionListBuilder.cs
  12. 0 173
      Emby.Dlna/ContentDirectory/ContentDirectoryService.cs
  13. 0 159
      Emby.Dlna/ContentDirectory/ContentDirectoryXmlBuilder.cs
  14. 0 1250
      Emby.Dlna/ContentDirectory/ControlHandler.cs
  15. 0 39
      Emby.Dlna/ContentDirectory/ServerItem.cs
  16. 0 415
      Emby.Dlna/ContentDirectory/ServiceActionListBuilder.cs
  17. 0 30
      Emby.Dlna/ContentDirectory/StubType.cs
  18. 0 25
      Emby.Dlna/ControlRequest.cs
  19. 0 28
      Emby.Dlna/ControlResponse.cs
  20. 0 1266
      Emby.Dlna/Didl/DidlBuilder.cs
  21. 0 28
      Emby.Dlna/Didl/Filter.cs
  22. 0 58
      Emby.Dlna/Didl/StringWriterWithEncoding.cs
  23. 0 23
      Emby.Dlna/DlnaConfigurationFactory.cs
  24. 0 491
      Emby.Dlna/DlnaManager.cs
  25. 0 90
      Emby.Dlna/Emby.Dlna.csproj
  26. 0 22
      Emby.Dlna/EventSubscriptionResponse.cs
  27. 0 183
      Emby.Dlna/Eventing/DlnaEventManager.cs
  28. 0 35
      Emby.Dlna/Eventing/EventSubscription.cs
  29. 0 72
      Emby.Dlna/Extensions/DlnaServiceCollectionExtensions.cs
  30. 0 8
      Emby.Dlna/IConnectionManager.cs
  31. 0 8
      Emby.Dlna/IContentDirectory.cs
  32. 0 34
      Emby.Dlna/IDlnaEventManager.cs
  33. 0 8
      Emby.Dlna/IMediaReceiverRegistrar.cs
  34. 0 22
      Emby.Dlna/IUpnpService.cs
  35. TEMPAT SAMPAH
      Emby.Dlna/Images/logo120.jpg
  36. TEMPAT SAMPAH
      Emby.Dlna/Images/logo120.png
  37. TEMPAT SAMPAH
      Emby.Dlna/Images/logo240.jpg
  38. TEMPAT SAMPAH
      Emby.Dlna/Images/logo240.png
  39. TEMPAT SAMPAH
      Emby.Dlna/Images/logo48.jpg
  40. TEMPAT SAMPAH
      Emby.Dlna/Images/logo48.png
  41. TEMPAT SAMPAH
      Emby.Dlna/Images/people48.jpg
  42. TEMPAT SAMPAH
      Emby.Dlna/Images/people48.png
  43. TEMPAT SAMPAH
      Emby.Dlna/Images/people480.jpg
  44. TEMPAT SAMPAH
      Emby.Dlna/Images/people480.png
  45. 0 387
      Emby.Dlna/Main/DlnaHost.cs
  46. 0 58
      Emby.Dlna/MediaReceiverRegistrar/ControlHandler.cs
  47. 0 46
      Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarService.cs
  48. 0 90
      Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarXmlBuilder.cs
  49. 0 187
      Emby.Dlna/MediaReceiverRegistrar/ServiceActionListBuilder.cs
  50. 0 1264
      Emby.Dlna/PlayTo/Device.cs
  51. 0 66
      Emby.Dlna/PlayTo/DeviceInfo.cs
  52. 0 137
      Emby.Dlna/PlayTo/DlnaHttpClient.cs
  53. 0 19
      Emby.Dlna/PlayTo/MediaChangedEventArgs.cs
  54. 0 980
      Emby.Dlna/PlayTo/PlayToController.cs
  55. 0 258
      Emby.Dlna/PlayTo/PlayToManager.cs
  56. 0 16
      Emby.Dlna/PlayTo/PlaybackProgressEventArgs.cs
  57. 0 16
      Emby.Dlna/PlayTo/PlaybackStartEventArgs.cs
  58. 0 16
      Emby.Dlna/PlayTo/PlaybackStoppedEventArgs.cs
  59. 0 19
      Emby.Dlna/PlayTo/PlaylistItem.cs
  60. 0 70
      Emby.Dlna/PlayTo/PlaylistItemFactory.cs
  61. 0 181
      Emby.Dlna/PlayTo/TransportCommands.cs
  62. 0 16
      Emby.Dlna/PlayTo/TransportState.cs
  63. 0 25
      Emby.Dlna/PlayTo/UpnpContainer.cs
  64. 0 63
      Emby.Dlna/PlayTo/uBaseObject.cs
  65. 0 67
      Emby.Dlna/PlayTo/uPnpNamespaces.cs
  66. 0 179
      Emby.Dlna/Profiles/DefaultProfile.cs
  67. 0 61
      Emby.Dlna/Profiles/Xml/Default.xml
  68. 0 68
      Emby.Dlna/Profiles/Xml/Denon AVR.xml
  69. 0 67
      Emby.Dlna/Profiles/Xml/DirecTV HD-DVR.xml
  70. 0 96
      Emby.Dlna/Profiles/Xml/Dish Hopper-Joey.xml
  71. 0 92
      Emby.Dlna/Profiles/Xml/LG Smart TV.xml
  72. 0 54
      Emby.Dlna/Profiles/Xml/Linksys DMA2100.xml
  73. 0 62
      Emby.Dlna/Profiles/Xml/Marantz.xml
  74. 0 62
      Emby.Dlna/Profiles/Xml/MediaMonkey.xml
  75. 0 87
      Emby.Dlna/Profiles/Xml/Panasonic Viera.xml
  76. 0 92
      Emby.Dlna/Profiles/Xml/Popcorn Hour.xml
  77. 0 128
      Emby.Dlna/Profiles/Xml/Samsung Smart TV.xml
  78. 0 60
      Emby.Dlna/Profiles/Xml/Sharp Smart TV.xml
  79. 0 33
      Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml
  80. 0 33
      Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2014.xml
  81. 0 31
      Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2015.xml
  82. 0 31
      Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2016.xml
  83. 0 31
      Emby.Dlna/Profiles/Xml/Sony Blu-ray Player.xml
  84. 0 133
      Emby.Dlna/Profiles/Xml/Sony Bravia (2010).xml
  85. 0 139
      Emby.Dlna/Profiles/Xml/Sony Bravia (2011).xml
  86. 0 115
      Emby.Dlna/Profiles/Xml/Sony Bravia (2012).xml
  87. 0 114
      Emby.Dlna/Profiles/Xml/Sony Bravia (2013).xml
  88. 0 114
      Emby.Dlna/Profiles/Xml/Sony Bravia (2014).xml
  89. 0 105
      Emby.Dlna/Profiles/Xml/Sony PlayStation 3.xml
  90. 0 108
      Emby.Dlna/Profiles/Xml/Sony PlayStation 4.xml
  91. 0 94
      Emby.Dlna/Profiles/Xml/WDTV Live.xml
  92. 0 126
      Emby.Dlna/Profiles/Xml/Xbox One.xml
  93. 0 67
      Emby.Dlna/Profiles/Xml/foobar2000.xml
  94. 0 28
      Emby.Dlna/Properties/AssemblyInfo.cs
  95. 0 358
      Emby.Dlna/Server/DescriptionXmlBuilder.cs
  96. 0 242
      Emby.Dlna/Service/BaseControlHandler.cs
  97. 0 37
      Emby.Dlna/Service/BaseService.cs
  98. 0 52
      Emby.Dlna/Service/ControlErrorHandler.cs
  99. 0 109
      Emby.Dlna/Service/ServiceXmlBuilder.cs
  100. 0 151
      Emby.Dlna/Ssdp/DeviceDiscovery.cs

+ 0 - 23
Emby.Dlna/Common/Argument.cs

@@ -1,23 +0,0 @@
-namespace Emby.Dlna.Common
-{
-    /// <summary>
-    /// DLNA Query parameter type, used when querying DLNA devices via SOAP.
-    /// </summary>
-    public class Argument
-    {
-        /// <summary>
-        /// Gets or sets name of the DLNA argument.
-        /// </summary>
-        public string Name { get; set; } = string.Empty;
-
-        /// <summary>
-        /// Gets or sets the direction of the parameter.
-        /// </summary>
-        public string Direction { get; set; } = string.Empty;
-
-        /// <summary>
-        /// Gets or sets the related DLNA state variable for this argument.
-        /// </summary>
-        public string RelatedStateVariable { get; set; } = string.Empty;
-    }
-}

+ 0 - 41
Emby.Dlna/Common/DeviceIcon.cs

@@ -1,41 +0,0 @@
-using System.Globalization;
-
-namespace Emby.Dlna.Common
-{
-    /// <summary>
-    /// Defines the <see cref="DeviceIcon" />.
-    /// </summary>
-    public class DeviceIcon
-    {
-        /// <summary>
-        /// Gets or sets the Url.
-        /// </summary>
-        public string Url { get; set; } = string.Empty;
-
-        /// <summary>
-        /// Gets or sets the MimeType.
-        /// </summary>
-        public string MimeType { get; set; } = string.Empty;
-
-        /// <summary>
-        /// Gets or sets the Width.
-        /// </summary>
-        public int Width { get; set; }
-
-        /// <summary>
-        /// Gets or sets the Height.
-        /// </summary>
-        public int Height { get; set; }
-
-        /// <summary>
-        /// Gets or sets the Depth.
-        /// </summary>
-        public string Depth { get; set; } = string.Empty;
-
-        /// <inheritdoc />
-        public override string ToString()
-        {
-            return string.Format(CultureInfo.InvariantCulture, "{0}x{1}", Height, Width);
-        }
-    }
-}

+ 0 - 36
Emby.Dlna/Common/DeviceService.cs

@@ -1,36 +0,0 @@
-namespace Emby.Dlna.Common
-{
-    /// <summary>
-    /// Defines the <see cref="DeviceService" />.
-    /// </summary>
-    public class DeviceService
-    {
-        /// <summary>
-        /// Gets or sets the Service Type.
-        /// </summary>
-        public string ServiceType { get; set; } = string.Empty;
-
-        /// <summary>
-        /// Gets or sets the Service Id.
-        /// </summary>
-        public string ServiceId { get; set; } = string.Empty;
-
-        /// <summary>
-        /// Gets or sets the Scpd Url.
-        /// </summary>
-        public string ScpdUrl { get; set; } = string.Empty;
-
-        /// <summary>
-        /// Gets or sets the Control Url.
-        /// </summary>
-        public string ControlUrl { get; set; } = string.Empty;
-
-        /// <summary>
-        /// Gets or sets the EventSubUrl.
-        /// </summary>
-        public string EventSubUrl { get; set; } = string.Empty;
-
-        /// <inheritdoc />
-        public override string ToString() => ServiceId;
-    }
-}

+ 0 - 31
Emby.Dlna/Common/ServiceAction.cs

@@ -1,31 +0,0 @@
-using System.Collections.Generic;
-
-namespace Emby.Dlna.Common
-{
-    /// <summary>
-    /// Defines the <see cref="ServiceAction" />.
-    /// </summary>
-    public class ServiceAction
-    {
-        /// <summary>
-        /// Initializes a new instance of the <see cref="ServiceAction"/> class.
-        /// </summary>
-        public ServiceAction()
-        {
-            ArgumentList = new List<Argument>();
-        }
-
-        /// <summary>
-        /// Gets or sets the name of the action.
-        /// </summary>
-        public string Name { get; set; } = string.Empty;
-
-        /// <summary>
-        /// Gets the ArgumentList.
-        /// </summary>
-        public List<Argument> ArgumentList { get; }
-
-        /// <inheritdoc />
-        public override string ToString() => Name;
-    }
-}

+ 0 - 34
Emby.Dlna/Common/StateVariable.cs

@@ -1,34 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace Emby.Dlna.Common
-{
-    /// <summary>
-    /// Defines the <see cref="StateVariable" />.
-    /// </summary>
-    public class StateVariable
-    {
-        /// <summary>
-        /// Gets or sets the name of the state variable.
-        /// </summary>
-        public string Name { get; set; } = string.Empty;
-
-        /// <summary>
-        /// Gets or sets the data type of the state variable.
-        /// </summary>
-        public string DataType { get; set; } = string.Empty;
-
-        /// <summary>
-        /// Gets or sets a value indicating whether it sends events.
-        /// </summary>
-        public bool SendsEvents { get; set; }
-
-        /// <summary>
-        /// Gets or sets the allowed values range.
-        /// </summary>
-        public IReadOnlyList<string> AllowedValues { get; set; } = Array.Empty<string>();
-
-        /// <inheritdoc />
-        public override string ToString() => Name;
-    }
-}

+ 0 - 92
Emby.Dlna/Configuration/DlnaOptions.cs

@@ -1,92 +0,0 @@
-#pragma warning disable CS1591
-
-namespace Emby.Dlna.Configuration
-{
-    /// <summary>
-    /// The DlnaOptions class contains the user definable parameters for the dlna subsystems.
-    /// </summary>
-    public class DlnaOptions
-    {
-        /// <summary>
-        /// Initializes a new instance of the <see cref="DlnaOptions"/> class.
-        /// </summary>
-        public DlnaOptions()
-        {
-            EnablePlayTo = true;
-            EnableServer = false;
-            BlastAliveMessages = true;
-            SendOnlyMatchedHost = true;
-            ClientDiscoveryIntervalSeconds = 60;
-            AliveMessageIntervalSeconds = 180;
-        }
-
-        /// <summary>
-        /// Gets or sets a value indicating whether gets or sets a value to indicate the status of the dlna playTo subsystem.
-        /// </summary>
-        public bool EnablePlayTo { get; set; }
-
-        /// <summary>
-        /// Gets or sets a value indicating whether gets or sets a value to indicate the status of the dlna server subsystem.
-        /// </summary>
-        public bool EnableServer { get; set; }
-
-        /// <summary>
-        /// Gets or sets a value indicating whether detailed dlna server logs are sent to the console/log.
-        /// If the setting "Emby.Dlna": "Debug" msut be set in logging.default.json for this property to work.
-        /// </summary>
-        public bool EnableDebugLog { get; set; }
-
-        /// <summary>
-        /// Gets or sets a value indicating whether whether detailed playTo debug logs are sent to the console/log.
-        /// If the setting "Emby.Dlna.PlayTo": "Debug" msut be set in logging.default.json for this property to work.
-        /// </summary>
-        public bool EnablePlayToTracing { get; set; }
-
-        /// <summary>
-        /// Gets or sets the ssdp client discovery interval time (in seconds).
-        /// This is the time after which the server will send a ssdp search request.
-        /// </summary>
-        public int ClientDiscoveryIntervalSeconds { get; set; }
-
-        /// <summary>
-        /// Gets or sets the frequency at which ssdp alive notifications are transmitted.
-        /// </summary>
-        public int AliveMessageIntervalSeconds { get; set; }
-
-        /// <summary>
-        /// Gets or sets the frequency at which ssdp alive notifications are transmitted. MIGRATING - TO BE REMOVED ONCE WEB HAS BEEN ALTERED.
-        /// </summary>
-        public int BlastAliveMessageIntervalSeconds
-        {
-            get
-            {
-                return AliveMessageIntervalSeconds;
-            }
-
-            set
-            {
-                AliveMessageIntervalSeconds = value;
-            }
-        }
-
-        /// <summary>
-        /// Gets or sets the default user account that the dlna server uses.
-        /// </summary>
-        public string? DefaultUserId { get; set; }
-
-        /// <summary>
-        /// Gets or sets a value indicating whether playTo device profiles should be created.
-        /// </summary>
-        public bool AutoCreatePlayToProfiles { get; set; }
-
-        /// <summary>
-        /// Gets or sets a value indicating whether to blast alive messages.
-        /// </summary>
-        public bool BlastAliveMessages { get; set; } = true;
-
-        /// <summary>
-        /// gets or sets a value indicating whether to send only matched host.
-        /// </summary>
-        public bool SendOnlyMatchedHost { get; set; } = true;
-    }
-}

+ 0 - 15
Emby.Dlna/ConfigurationExtension.cs

@@ -1,15 +0,0 @@
-#pragma warning disable CS1591
-
-using Emby.Dlna.Configuration;
-using MediaBrowser.Common.Configuration;
-
-namespace Emby.Dlna
-{
-    public static class ConfigurationExtension
-    {
-        public static DlnaOptions GetDlnaConfiguration(this IConfigurationManager manager)
-        {
-            return manager.GetConfiguration<DlnaOptions>("dlna");
-        }
-    }
-}

+ 0 - 53
Emby.Dlna/ConnectionManager/ConnectionManagerService.cs

@@ -1,53 +0,0 @@
-#pragma warning disable CS1591
-
-using System.Net.Http;
-using System.Threading.Tasks;
-using Emby.Dlna.Service;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Dlna;
-using Microsoft.Extensions.Logging;
-
-namespace Emby.Dlna.ConnectionManager
-{
-    /// <summary>
-    /// Defines the <see cref="ConnectionManagerService" />.
-    /// </summary>
-    public class ConnectionManagerService : BaseService, IConnectionManager
-    {
-        private readonly IDlnaManager _dlna;
-        private readonly IServerConfigurationManager _config;
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="ConnectionManagerService"/> class.
-        /// </summary>
-        /// <param name="dlna">The <see cref="IDlnaManager"/> for use with the <see cref="ConnectionManagerService"/> instance.</param>
-        /// <param name="config">The <see cref="IServerConfigurationManager"/> for use with the <see cref="ConnectionManagerService"/> instance.</param>
-        /// <param name="logger">The <see cref="ILogger{ConnectionManagerService}"/> for use with the <see cref="ConnectionManagerService"/> instance..</param>
-        /// <param name="httpClientFactory">The <see cref="IHttpClientFactory"/> for use with the <see cref="ConnectionManagerService"/> instance..</param>
-        public ConnectionManagerService(
-            IDlnaManager dlna,
-            IServerConfigurationManager config,
-            ILogger<ConnectionManagerService> logger,
-            IHttpClientFactory httpClientFactory)
-            : base(logger, httpClientFactory)
-        {
-            _dlna = dlna;
-            _config = config;
-        }
-
-        /// <inheritdoc />
-        public string GetServiceXml()
-        {
-            return ConnectionManagerXmlBuilder.GetXml();
-        }
-
-        /// <inheritdoc />
-        public Task<ControlResponse> ProcessControlRequestAsync(ControlRequest request)
-        {
-            var profile = _dlna.GetProfile(request.Headers) ??
-                         _dlna.GetDefaultProfile();
-
-            return new ControlHandler(_config, Logger, profile).ProcessControlRequestAsync(request);
-        }
-    }
-}

+ 0 - 119
Emby.Dlna/ConnectionManager/ConnectionManagerXmlBuilder.cs

@@ -1,119 +0,0 @@
-#pragma warning disable CS1591
-
-using System.Collections.Generic;
-using Emby.Dlna.Common;
-using Emby.Dlna.Service;
-
-namespace Emby.Dlna.ConnectionManager
-{
-    /// <summary>
-    /// Defines the <see cref="ConnectionManagerXmlBuilder" />.
-    /// </summary>
-    public static class ConnectionManagerXmlBuilder
-    {
-        /// <summary>
-        /// Gets the ConnectionManager:1 service template.
-        /// See http://upnp.org/specs/av/UPnP-av-ConnectionManager-v1-Service.pdf.
-        /// </summary>
-        /// <returns>An XML description of this service.</returns>
-        public static string GetXml()
-        {
-            return new ServiceXmlBuilder().GetXml(ServiceActionListBuilder.GetActions(), GetStateVariables());
-        }
-
-        /// <summary>
-        /// Get the list of state variables for this invocation.
-        /// </summary>
-        /// <returns>The <see cref="IEnumerable{StateVariable}"/>.</returns>
-        private static IEnumerable<StateVariable> GetStateVariables()
-        {
-            return new StateVariable[]
-            {
-                new StateVariable
-                {
-                    Name = "SourceProtocolInfo",
-                    DataType = "string",
-                    SendsEvents = true
-                },
-
-                new StateVariable
-                {
-                    Name = "SinkProtocolInfo",
-                    DataType = "string",
-                    SendsEvents = true
-                },
-
-                new StateVariable
-                {
-                    Name = "CurrentConnectionIDs",
-                    DataType = "string",
-                    SendsEvents = true
-                },
-
-                new StateVariable
-                {
-                    Name = "A_ARG_TYPE_ConnectionStatus",
-                    DataType = "string",
-                    SendsEvents = false,
-
-                    AllowedValues = new[]
-                {
-                    "OK",
-                    "ContentFormatMismatch",
-                    "InsufficientBandwidth",
-                    "UnreliableChannel",
-                    "Unknown"
-                }
-                },
-
-                new StateVariable
-                {
-                    Name = "A_ARG_TYPE_ConnectionManager",
-                    DataType = "string",
-                    SendsEvents = false
-                },
-
-                new StateVariable
-                {
-                    Name = "A_ARG_TYPE_Direction",
-                    DataType = "string",
-                    SendsEvents = false,
-
-                    AllowedValues = new[]
-                {
-                    "Output",
-                    "Input"
-                }
-                },
-
-                new StateVariable
-                {
-                    Name = "A_ARG_TYPE_ProtocolInfo",
-                    DataType = "string",
-                    SendsEvents = false
-                },
-
-                new StateVariable
-                {
-                    Name = "A_ARG_TYPE_ConnectionID",
-                    DataType = "ui4",
-                    SendsEvents = false
-                },
-
-                new StateVariable
-                {
-                    Name = "A_ARG_TYPE_AVTransportID",
-                    DataType = "ui4",
-                    SendsEvents = false
-                },
-
-                new StateVariable
-                {
-                    Name = "A_ARG_TYPE_RcsID",
-                    DataType = "ui4",
-                    SendsEvents = false
-                }
-            };
-        }
-    }
-}

+ 0 - 55
Emby.Dlna/ConnectionManager/ControlHandler.cs

@@ -1,55 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using System.Collections.Generic;
-using System.Xml;
-using Emby.Dlna.Service;
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Model.Dlna;
-using Microsoft.Extensions.Logging;
-
-namespace Emby.Dlna.ConnectionManager
-{
-    /// <summary>
-    /// Defines the <see cref="ControlHandler" />.
-    /// </summary>
-    public class ControlHandler : BaseControlHandler
-    {
-        private readonly DeviceProfile _profile;
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="ControlHandler"/> class.
-        /// </summary>
-        /// <param name="config">The <see cref="IServerConfigurationManager"/> for use with the <see cref="ControlHandler"/> instance.</param>
-        /// <param name="logger">The <see cref="ILogger"/> for use with the <see cref="ControlHandler"/> instance.</param>
-        /// <param name="profile">The <see cref="DeviceProfile"/> for use with the <see cref="ControlHandler"/> instance.</param>
-        public ControlHandler(IServerConfigurationManager config, ILogger logger, DeviceProfile profile)
-            : base(config, logger)
-        {
-            _profile = profile;
-        }
-
-        /// <inheritdoc />
-        protected override void WriteResult(string methodName, IReadOnlyDictionary<string, string> methodParams, XmlWriter xmlWriter)
-        {
-            if (string.Equals(methodName, "GetProtocolInfo", StringComparison.OrdinalIgnoreCase))
-            {
-                HandleGetProtocolInfo(xmlWriter);
-                return;
-            }
-
-            throw new ResourceNotFoundException("Unexpected control request name: " + methodName);
-        }
-
-        /// <summary>
-        /// Builds the response to the GetProtocolInfo request.
-        /// </summary>
-        /// <param name="xmlWriter">The <see cref="XmlWriter"/>.</param>
-        private void HandleGetProtocolInfo(XmlWriter xmlWriter)
-        {
-            xmlWriter.WriteElementString("Source", _profile.ProtocolInfo);
-            xmlWriter.WriteElementString("Sink", string.Empty);
-        }
-    }
-}

+ 0 - 234
Emby.Dlna/ConnectionManager/ServiceActionListBuilder.cs

@@ -1,234 +0,0 @@
-#pragma warning disable CS1591
-
-using System.Collections.Generic;
-using Emby.Dlna.Common;
-
-namespace Emby.Dlna.ConnectionManager
-{
-    /// <summary>
-    /// Defines the <see cref="ServiceActionListBuilder" />.
-    /// </summary>
-    public static class ServiceActionListBuilder
-    {
-        /// <summary>
-        /// Returns an enumerable of the ConnectionManagar:1 DLNA actions.
-        /// </summary>
-        /// <returns>An <see cref="IEnumerable{ServiceAction}"/>.</returns>
-        public static IEnumerable<ServiceAction> GetActions()
-        {
-            var list = new List<ServiceAction>
-            {
-                GetCurrentConnectionInfo(),
-                GetProtocolInfo(),
-                GetCurrentConnectionIDs(),
-                ConnectionComplete(),
-                PrepareForConnection()
-            };
-
-            return list;
-        }
-
-        /// <summary>
-        /// Returns the action details for "PrepareForConnection".
-        /// </summary>
-        /// <returns>The <see cref="ServiceAction"/>.</returns>
-        private static ServiceAction PrepareForConnection()
-        {
-            var action = new ServiceAction
-            {
-                Name = "PrepareForConnection"
-            };
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "RemoteProtocolInfo",
-                Direction = "in",
-                RelatedStateVariable = "A_ARG_TYPE_ProtocolInfo"
-            });
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "PeerConnectionManager",
-                Direction = "in",
-                RelatedStateVariable = "A_ARG_TYPE_ConnectionManager"
-            });
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "PeerConnectionID",
-                Direction = "in",
-                RelatedStateVariable = "A_ARG_TYPE_ConnectionID"
-            });
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "Direction",
-                Direction = "in",
-                RelatedStateVariable = "A_ARG_TYPE_Direction"
-            });
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "ConnectionID",
-                Direction = "out",
-                RelatedStateVariable = "A_ARG_TYPE_ConnectionID"
-            });
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "AVTransportID",
-                Direction = "out",
-                RelatedStateVariable = "A_ARG_TYPE_AVTransportID"
-            });
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "RcsID",
-                Direction = "out",
-                RelatedStateVariable = "A_ARG_TYPE_RcsID"
-            });
-
-            return action;
-        }
-
-        /// <summary>
-        /// Returns the action details for "GetCurrentConnectionInfo".
-        /// </summary>
-        /// <returns>The <see cref="ServiceAction"/>.</returns>
-        private static ServiceAction GetCurrentConnectionInfo()
-        {
-            var action = new ServiceAction
-            {
-                Name = "GetCurrentConnectionInfo"
-            };
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "ConnectionID",
-                Direction = "in",
-                RelatedStateVariable = "A_ARG_TYPE_ConnectionID"
-            });
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "RcsID",
-                Direction = "out",
-                RelatedStateVariable = "A_ARG_TYPE_RcsID"
-            });
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "AVTransportID",
-                Direction = "out",
-                RelatedStateVariable = "A_ARG_TYPE_AVTransportID"
-            });
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "ProtocolInfo",
-                Direction = "out",
-                RelatedStateVariable = "A_ARG_TYPE_ProtocolInfo"
-            });
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "PeerConnectionManager",
-                Direction = "out",
-                RelatedStateVariable = "A_ARG_TYPE_ConnectionManager"
-            });
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "PeerConnectionID",
-                Direction = "out",
-                RelatedStateVariable = "A_ARG_TYPE_ConnectionID"
-            });
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "Direction",
-                Direction = "out",
-                RelatedStateVariable = "A_ARG_TYPE_Direction"
-            });
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "Status",
-                Direction = "out",
-                RelatedStateVariable = "A_ARG_TYPE_ConnectionStatus"
-            });
-
-            return action;
-        }
-
-        /// <summary>
-        /// Returns the action details for "GetProtocolInfo".
-        /// </summary>
-        /// <returns>The <see cref="ServiceAction"/>.</returns>
-        private static ServiceAction GetProtocolInfo()
-        {
-            var action = new ServiceAction
-            {
-                Name = "GetProtocolInfo"
-            };
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "Source",
-                Direction = "out",
-                RelatedStateVariable = "SourceProtocolInfo"
-            });
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "Sink",
-                Direction = "out",
-                RelatedStateVariable = "SinkProtocolInfo"
-            });
-
-            return action;
-        }
-
-        /// <summary>
-        /// Returns the action details for "GetCurrentConnectionIDs".
-        /// </summary>
-        /// <returns>The <see cref="ServiceAction"/>.</returns>
-        private static ServiceAction GetCurrentConnectionIDs()
-        {
-            var action = new ServiceAction
-            {
-                Name = "GetCurrentConnectionIDs"
-            };
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "ConnectionIDs",
-                Direction = "out",
-                RelatedStateVariable = "CurrentConnectionIDs"
-            });
-
-            return action;
-        }
-
-        /// <summary>
-        /// Returns the action details for "ConnectionComplete".
-        /// </summary>
-        /// <returns>The <see cref="ServiceAction"/>.</returns>
-        private static ServiceAction ConnectionComplete()
-        {
-            var action = new ServiceAction
-            {
-                Name = "ConnectionComplete"
-            };
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "ConnectionID",
-                Direction = "in",
-                RelatedStateVariable = "A_ARG_TYPE_ConnectionID"
-            });
-
-            return action;
-        }
-    }
-}

+ 0 - 173
Emby.Dlna/ContentDirectory/ContentDirectoryService.cs

@@ -1,173 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using System.Linq;
-using System.Net.Http;
-using System.Threading.Tasks;
-using Emby.Dlna.Service;
-using Jellyfin.Data.Entities;
-using Jellyfin.Data.Enums;
-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
-{
-    /// <summary>
-    /// Defines the <see cref="ContentDirectoryService" />.
-    /// </summary>
-    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;
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="ContentDirectoryService"/> class.
-        /// </summary>
-        /// <param name="dlna">The <see cref="IDlnaManager"/> to use in the <see cref="ContentDirectoryService"/> instance.</param>
-        /// <param name="userDataManager">The <see cref="IUserDataManager"/> to use in the <see cref="ContentDirectoryService"/> instance.</param>
-        /// <param name="imageProcessor">The <see cref="IImageProcessor"/> to use in the <see cref="ContentDirectoryService"/> instance.</param>
-        /// <param name="libraryManager">The <see cref="ILibraryManager"/> to use in the <see cref="ContentDirectoryService"/> instance.</param>
-        /// <param name="config">The <see cref="IServerConfigurationManager"/> to use in the <see cref="ContentDirectoryService"/> instance.</param>
-        /// <param name="userManager">The <see cref="IUserManager"/> to use in the <see cref="ContentDirectoryService"/> instance.</param>
-        /// <param name="logger">The <see cref="ILogger{ContentDirectoryService}"/> to use in the <see cref="ContentDirectoryService"/> instance.</param>
-        /// <param name="httpClient">The <see cref="IHttpClientFactory"/> to use in the <see cref="ContentDirectoryService"/> instance.</param>
-        /// <param name="localization">The <see cref="ILocalizationManager"/> to use in the <see cref="ContentDirectoryService"/> instance.</param>
-        /// <param name="mediaSourceManager">The <see cref="IMediaSourceManager"/> to use in the <see cref="ContentDirectoryService"/> instance.</param>
-        /// <param name="userViewManager">The <see cref="IUserViewManager"/> to use in the <see cref="ContentDirectoryService"/> instance.</param>
-        /// <param name="mediaEncoder">The <see cref="IMediaEncoder"/> to use in the <see cref="ContentDirectoryService"/> instance.</param>
-        /// <param name="tvSeriesManager">The <see cref="ITVSeriesManager"/> to use in the <see cref="ContentDirectoryService"/> instance.</param>
-        public ContentDirectoryService(
-            IDlnaManager dlna,
-            IUserDataManager userDataManager,
-            IImageProcessor imageProcessor,
-            ILibraryManager libraryManager,
-            IServerConfigurationManager config,
-            IUserManager userManager,
-            ILogger<ContentDirectoryService> logger,
-            IHttpClientFactory 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;
-        }
-
-        /// <summary>
-        /// Gets the system id. (A unique id which changes on when our definition changes.)
-        /// </summary>
-        private static int SystemUpdateId
-        {
-            get
-            {
-                var now = DateTime.UtcNow;
-
-                return now.Year + now.DayOfYear + now.Hour;
-            }
-        }
-
-        /// <inheritdoc />
-        public string GetServiceXml()
-        {
-            return ContentDirectoryXmlBuilder.GetXml();
-        }
-
-        /// <inheritdoc />
-        public Task<ControlResponse> ProcessControlRequestAsync(ControlRequest request)
-        {
-            ArgumentNullException.ThrowIfNull(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);
-        }
-
-        /// <summary>
-        /// Get the user stored in the device profile.
-        /// </summary>
-        /// <param name="profile">The <see cref="DeviceProfile"/>.</param>
-        /// <returns>The <see cref="User"/>.</returns>
-        private User? GetUser(DeviceProfile profile)
-        {
-            if (!string.IsNullOrEmpty(profile.UserId))
-            {
-                var user = _userManager.GetUserById(Guid.Parse(profile.UserId));
-
-                if (user is not null)
-                {
-                    return user;
-                }
-            }
-
-            var userId = _config.GetDlnaConfiguration().DefaultUserId;
-
-            if (!string.IsNullOrEmpty(userId))
-            {
-                var user = _userManager.GetUserById(Guid.Parse(userId));
-
-                if (user is not null)
-                {
-                    return user;
-                }
-            }
-
-            foreach (var user in _userManager.Users)
-            {
-                if (user.HasPermission(PermissionKind.IsAdministrator))
-                {
-                    return user;
-                }
-            }
-
-            return _userManager.Users.FirstOrDefault();
-        }
-    }
-}

+ 0 - 159
Emby.Dlna/ContentDirectory/ContentDirectoryXmlBuilder.cs

@@ -1,159 +0,0 @@
-#pragma warning disable CS1591
-
-using System.Collections.Generic;
-using Emby.Dlna.Common;
-using Emby.Dlna.Service;
-
-namespace Emby.Dlna.ContentDirectory
-{
-    /// <summary>
-    /// Defines the <see cref="ContentDirectoryXmlBuilder" />.
-    /// </summary>
-    public static class ContentDirectoryXmlBuilder
-    {
-        /// <summary>
-        /// Gets the ContentDirectory:1 service template.
-        /// See http://upnp.org/specs/av/UPnP-av-ContentDirectory-v1-Service.pdf.
-        /// </summary>
-        /// <returns>An XML description of this service.</returns>
-        public static string GetXml()
-        {
-            return new ServiceXmlBuilder().GetXml(ServiceActionListBuilder.GetActions(), GetStateVariables());
-        }
-
-        /// <summary>
-        /// Get the list of state variables for this invocation.
-        /// </summary>
-        /// <returns>The <see cref="IEnumerable{StateVariable}"/>.</returns>
-        private static IEnumerable<StateVariable> GetStateVariables()
-        {
-            return new StateVariable[]
-            {
-                new StateVariable
-                {
-                    Name = "A_ARG_TYPE_Filter",
-                    DataType = "string",
-                    SendsEvents = false
-                },
-
-                new StateVariable
-                {
-                    Name = "A_ARG_TYPE_SortCriteria",
-                    DataType = "string",
-                    SendsEvents = false
-                },
-
-                new StateVariable
-                {
-                    Name = "A_ARG_TYPE_Index",
-                    DataType = "ui4",
-                    SendsEvents = false
-                },
-
-                new StateVariable
-                {
-                    Name = "A_ARG_TYPE_Count",
-                    DataType = "ui4",
-                    SendsEvents = false
-                },
-
-                new StateVariable
-                {
-                    Name = "A_ARG_TYPE_UpdateID",
-                    DataType = "ui4",
-                    SendsEvents = false
-                },
-
-                new StateVariable
-                {
-                    Name = "SearchCapabilities",
-                    DataType = "string",
-                    SendsEvents = false
-                },
-
-                new StateVariable
-                {
-                    Name = "SortCapabilities",
-                    DataType = "string",
-                    SendsEvents = false
-                },
-
-                new StateVariable
-                {
-                    Name = "SystemUpdateID",
-                    DataType = "ui4",
-                    SendsEvents = true
-                },
-
-                new StateVariable
-                {
-                    Name = "A_ARG_TYPE_SearchCriteria",
-                    DataType = "string",
-                    SendsEvents = false
-                },
-
-                new StateVariable
-                {
-                    Name = "A_ARG_TYPE_Result",
-                    DataType = "string",
-                    SendsEvents = false
-                },
-
-                new StateVariable
-                {
-                    Name = "A_ARG_TYPE_ObjectID",
-                    DataType = "string",
-                    SendsEvents = false
-                },
-
-                new StateVariable
-                {
-                    Name = "A_ARG_TYPE_BrowseFlag",
-                    DataType = "string",
-                    SendsEvents = false,
-
-                    AllowedValues = new[]
-                {
-                    "BrowseMetadata",
-                    "BrowseDirectChildren"
-                }
-                },
-
-                new StateVariable
-                {
-                    Name = "A_ARG_TYPE_BrowseLetter",
-                    DataType = "string",
-                    SendsEvents = false
-                },
-
-                new StateVariable
-                {
-                    Name = "A_ARG_TYPE_CategoryType",
-                    DataType = "ui4",
-                    SendsEvents = false
-                },
-
-                new StateVariable
-                {
-                    Name = "A_ARG_TYPE_RID",
-                    DataType = "ui4",
-                    SendsEvents = false
-                },
-
-                new StateVariable
-                {
-                    Name = "A_ARG_TYPE_PosSec",
-                    DataType = "ui4",
-                    SendsEvents = false
-                },
-
-                new StateVariable
-                {
-                    Name = "A_ARG_TYPE_Featurelist",
-                    DataType = "string",
-                    SendsEvents = false
-                }
-            };
-        }
-    }
-}

+ 0 - 1250
Emby.Dlna/ContentDirectory/ControlHandler.cs

@@ -1,1250 +0,0 @@
-#nullable disable
-
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Text;
-using System.Threading;
-using System.Xml;
-using Emby.Dlna.Didl;
-using Emby.Dlna.Service;
-using Jellyfin.Data.Entities;
-using Jellyfin.Data.Enums;
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Drawing;
-using MediaBrowser.Controller.Dto;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Controller.TV;
-using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Globalization;
-using MediaBrowser.Model.Querying;
-using Microsoft.Extensions.Logging;
-using Genre = MediaBrowser.Controller.Entities.Genre;
-
-namespace Emby.Dlna.ContentDirectory
-{
-    /// <summary>
-    /// Defines the <see cref="ControlHandler" />.
-    /// </summary>
-    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 User _user;
-        private readonly IUserViewManager _userViewManager;
-        private readonly ITVSeriesManager _tvSeriesManager;
-
-        private readonly int _systemUpdateId;
-
-        private readonly DidlBuilder _didlBuilder;
-
-        private readonly DeviceProfile _profile;
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="ControlHandler"/> class.
-        /// </summary>
-        /// <param name="logger">The <see cref="ILogger"/> for use with the <see cref="ControlHandler"/> instance.</param>
-        /// <param name="libraryManager">The <see cref="ILibraryManager"/> for use with the <see cref="ControlHandler"/> instance.</param>
-        /// <param name="profile">The <see cref="DeviceProfile"/> for use with the <see cref="ControlHandler"/> instance.</param>
-        /// <param name="serverAddress">The server address to use in this instance> for use with the <see cref="ControlHandler"/> instance.</param>
-        /// <param name="accessToken">The <see cref="string"/> for use with the <see cref="ControlHandler"/> instance.</param>
-        /// <param name="imageProcessor">The <see cref="IImageProcessor"/> for use with the <see cref="ControlHandler"/> instance.</param>
-        /// <param name="userDataManager">The <see cref="IUserDataManager"/> for use with the <see cref="ControlHandler"/> instance.</param>
-        /// <param name="user">The <see cref="User"/> for use with the <see cref="ControlHandler"/> instance.</param>
-        /// <param name="systemUpdateId">The system id for use with the <see cref="ControlHandler"/> instance.</param>
-        /// <param name="config">The <see cref="IServerConfigurationManager"/> for use with the <see cref="ControlHandler"/> instance.</param>
-        /// <param name="localization">The <see cref="ILocalizationManager"/> for use with the <see cref="ControlHandler"/> instance.</param>
-        /// <param name="mediaSourceManager">The <see cref="IMediaSourceManager"/> for use with the <see cref="ControlHandler"/> instance.</param>
-        /// <param name="userViewManager">The <see cref="IUserViewManager"/> for use with the <see cref="ControlHandler"/> instance.</param>
-        /// <param name="mediaEncoder">The <see cref="IMediaEncoder"/> for use with the <see cref="ControlHandler"/> instance.</param>
-        /// <param name="tvSeriesManager">The <see cref="ITVSeriesManager"/> for use with the <see cref="ControlHandler"/> instance.</param>
-        public ControlHandler(
-            ILogger logger,
-            ILibraryManager libraryManager,
-            DeviceProfile profile,
-            string serverAddress,
-            string accessToken,
-            IImageProcessor imageProcessor,
-            IUserDataManager userDataManager,
-            User user,
-            int systemUpdateId,
-            IServerConfigurationManager config,
-            ILocalizationManager localization,
-            IMediaSourceManager mediaSourceManager,
-            IUserViewManager userViewManager,
-            IMediaEncoder mediaEncoder,
-            ITVSeriesManager tvSeriesManager)
-            : base(config, logger)
-        {
-            _libraryManager = libraryManager;
-            _userDataManager = userDataManager;
-            _user = user;
-            _systemUpdateId = systemUpdateId;
-            _userViewManager = userViewManager;
-            _tvSeriesManager = tvSeriesManager;
-            _profile = profile;
-
-            _didlBuilder = new DidlBuilder(
-                profile,
-                user,
-                imageProcessor,
-                serverAddress,
-                accessToken,
-                userDataManager,
-                localization,
-                mediaSourceManager,
-                Logger,
-                mediaEncoder,
-                libraryManager);
-        }
-
-        /// <inheritdoc />
-        protected override void WriteResult(string methodName, IReadOnlyDictionary<string, string> methodParams, XmlWriter xmlWriter)
-        {
-            ArgumentNullException.ThrowIfNull(xmlWriter);
-
-            ArgumentNullException.ThrowIfNull(methodParams);
-
-            const string DeviceId = "test";
-
-            if (string.Equals(methodName, "GetSearchCapabilities", StringComparison.OrdinalIgnoreCase))
-            {
-                HandleGetSearchCapabilities(xmlWriter);
-                return;
-            }
-
-            if (string.Equals(methodName, "GetSortCapabilities", StringComparison.OrdinalIgnoreCase))
-            {
-                HandleGetSortCapabilities(xmlWriter);
-                return;
-            }
-
-            if (string.Equals(methodName, "GetSortExtensionCapabilities", StringComparison.OrdinalIgnoreCase))
-            {
-                HandleGetSortExtensionCapabilities(xmlWriter);
-                return;
-            }
-
-            if (string.Equals(methodName, "GetSystemUpdateID", StringComparison.OrdinalIgnoreCase))
-            {
-                HandleGetSystemUpdateID(xmlWriter);
-                return;
-            }
-
-            if (string.Equals(methodName, "Browse", StringComparison.OrdinalIgnoreCase))
-            {
-                HandleBrowse(xmlWriter, methodParams, DeviceId);
-                return;
-            }
-
-            if (string.Equals(methodName, "X_GetFeatureList", StringComparison.OrdinalIgnoreCase))
-            {
-                HandleXGetFeatureList(xmlWriter);
-                return;
-            }
-
-            if (string.Equals(methodName, "GetFeatureList", StringComparison.OrdinalIgnoreCase))
-            {
-                HandleGetFeatureList(xmlWriter);
-                return;
-            }
-
-            if (string.Equals(methodName, "X_SetBookmark", StringComparison.OrdinalIgnoreCase))
-            {
-                HandleXSetBookmark(methodParams);
-                return;
-            }
-
-            if (string.Equals(methodName, "Search", StringComparison.OrdinalIgnoreCase))
-            {
-                HandleSearch(xmlWriter, methodParams, DeviceId);
-                return;
-            }
-
-            if (string.Equals(methodName, "X_BrowseByLetter", StringComparison.OrdinalIgnoreCase))
-            {
-                HandleXBrowseByLetter(xmlWriter, methodParams, DeviceId);
-                return;
-            }
-
-            throw new ResourceNotFoundException("Unexpected control request name: " + methodName);
-        }
-
-        /// <summary>
-        /// Adds a "XSetBookmark" element to the xml document.
-        /// </summary>
-        /// <param name="sparams">The method parameters.</param>
-        private void HandleXSetBookmark(IReadOnlyDictionary<string, string> sparams)
-        {
-            var id = sparams["ObjectID"];
-
-            var serverItem = GetItemFromObjectId(id);
-
-            var item = serverItem.Item;
-
-            var newbookmark = int.Parse(sparams["PosSecond"], CultureInfo.InvariantCulture);
-
-            var userdata = _userDataManager.GetUserData(_user, item);
-
-            userdata.PlaybackPositionTicks = TimeSpan.FromSeconds(newbookmark).Ticks;
-
-            _userDataManager.SaveUserData(
-                _user,
-                item,
-                userdata,
-                UserDataSaveReason.TogglePlayed,
-                CancellationToken.None);
-        }
-
-        /// <summary>
-        /// Adds the "SearchCaps" element to the xml document.
-        /// </summary>
-        /// <param name="xmlWriter">The <see cref="XmlWriter"/>.</param>
-        private static void HandleGetSearchCapabilities(XmlWriter xmlWriter)
-        {
-            xmlWriter.WriteElementString(
-                "SearchCaps",
-                "res@resolution,res@size,res@duration,dc:title,dc:creator,upnp:actor,upnp:artist,upnp:genre,upnp:album,dc:date,upnp:class,@id,@refID,@protocolInfo,upnp:author,dc:description,pv:avKeywords");
-        }
-
-        /// <summary>
-        /// Adds the "SortCaps" element to the xml document.
-        /// </summary>
-        /// <param name="xmlWriter">The <see cref="XmlWriter"/>.</param>
-        private static void HandleGetSortCapabilities(XmlWriter xmlWriter)
-        {
-            xmlWriter.WriteElementString(
-                "SortCaps",
-                "res@duration,res@size,res@bitrate,dc:date,dc:title,dc:size,upnp:album,upnp:artist,upnp:albumArtist,upnp:episodeNumber,upnp:genre,upnp:originalTrackNumber,upnp:rating");
-        }
-
-        /// <summary>
-        /// Adds the "SortExtensionCaps" element to the xml document.
-        /// </summary>
-        /// <param name="xmlWriter">The <see cref="XmlWriter"/>.</param>
-        private static void HandleGetSortExtensionCapabilities(XmlWriter xmlWriter)
-        {
-            xmlWriter.WriteElementString(
-                "SortExtensionCaps",
-                "res@duration,res@size,res@bitrate,dc:date,dc:title,dc:size,upnp:album,upnp:artist,upnp:albumArtist,upnp:episodeNumber,upnp:genre,upnp:originalTrackNumber,upnp:rating");
-        }
-
-        /// <summary>
-        /// Adds the "Id" element to the xml document.
-        /// </summary>
-        /// <param name="xmlWriter">The <see cref="XmlWriter"/>.</param>
-        private void HandleGetSystemUpdateID(XmlWriter xmlWriter)
-        {
-            xmlWriter.WriteElementString("Id", _systemUpdateId.ToString(CultureInfo.InvariantCulture));
-        }
-
-        /// <summary>
-        /// Adds the "FeatureList" element to the xml document.
-        /// </summary>
-        /// <param name="xmlWriter">The <see cref="XmlWriter"/>.</param>
-        private static void HandleGetFeatureList(XmlWriter xmlWriter)
-        {
-            xmlWriter.WriteElementString("FeatureList", WriteFeatureListXml());
-        }
-
-        /// <summary>
-        /// Adds the "FeatureList" element to the xml document.
-        /// </summary>
-        /// <param name="xmlWriter">The <see cref="XmlWriter"/>.</param>
-        private static void HandleXGetFeatureList(XmlWriter xmlWriter)
-            => HandleGetFeatureList(xmlWriter);
-
-        /// <summary>
-        /// Builds a static feature list.
-        /// </summary>
-        /// <returns>The xml feature list.</returns>
-        private static string WriteFeatureListXml()
-        {
-            return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
-                + "<Features xmlns=\"urn:schemas-upnp-org:av:avs\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"urn:schemas-upnp-org:av:avs http://www.upnp.org/schemas/av/avs.xsd\">"
-                + "<Feature name=\"samsung.com_BASICVIEW\" version=\"1\">"
-                + "<container id=\"0\" type=\"object.item.imageItem\"/>"
-                + "<container id=\"0\" type=\"object.item.audioItem\"/>"
-                + "<container id=\"0\" type=\"object.item.videoItem\"/>"
-                + "</Feature>"
-                + "</Features>";
-        }
-
-        /// <summary>
-        /// Builds the "Browse" xml response.
-        /// </summary>
-        /// <param name="xmlWriter">The <see cref="XmlWriter"/>.</param>
-        /// <param name="sparams">The method parameters.</param>
-        /// <param name="deviceId">The device Id to use.</param>
-        private void HandleBrowse(XmlWriter xmlWriter, IReadOnlyDictionary<string, string> sparams, string deviceId)
-        {
-            var id = sparams["ObjectID"];
-            var flag = sparams["BrowseFlag"];
-            var filter = new Filter(sparams.GetValueOrDefault("Filter", "*"));
-            var sortCriteria = new SortCriteria(sparams.GetValueOrDefault("SortCriteria", string.Empty));
-
-            var provided = 0;
-
-            // Default to null instead of 0
-            // Upnp inspector sends 0 as requestedCount when it wants everything
-            int? requestedCount = null;
-            int? start = 0;
-
-            if (sparams.ContainsKey("RequestedCount") && int.TryParse(sparams["RequestedCount"], out var requestedVal) && requestedVal > 0)
-            {
-                requestedCount = requestedVal;
-            }
-
-            if (sparams.ContainsKey("StartingIndex") && int.TryParse(sparams["StartingIndex"], out var startVal) && startVal > 0)
-            {
-                start = startVal;
-            }
-
-            int totalCount;
-
-            var settings = new XmlWriterSettings
-            {
-                Encoding = Encoding.UTF8,
-                CloseOutput = false,
-                OmitXmlDeclaration = true,
-                ConformanceLevel = ConformanceLevel.Fragment
-            };
-
-            using (StringWriter builder = new StringWriterWithEncoding(Encoding.UTF8))
-            using (var writer = XmlWriter.Create(builder, settings))
-            {
-                writer.WriteStartElement(string.Empty, "DIDL-Lite", NsDidl);
-
-                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;
-
-                    if (item.IsDisplayedAsFolder || serverItem.StubType.HasValue)
-                    {
-                        var childrenResult = GetUserItems(item, serverItem.StubType, _user, sortCriteria, start, requestedCount);
-
-                        _didlBuilder.WriteFolderElement(writer, item, serverItem.StubType, null, childrenResult.TotalRecordCount, filter, id);
-                    }
-                    else
-                    {
-                        _didlBuilder.WriteItemElement(writer, item, _user, null, null, deviceId, filter);
-                    }
-
-                    provided++;
-                }
-                else
-                {
-                    var childrenResult = GetUserItems(item, serverItem.StubType, _user, sortCriteria, start, requestedCount);
-                    totalCount = childrenResult.TotalRecordCount;
-
-                    provided = childrenResult.Items.Count;
-
-                    foreach (var i in childrenResult.Items)
-                    {
-                        var childItem = i.Item;
-                        var displayStubType = i.StubType;
-
-                        if (childItem.IsDisplayedAsFolder || displayStubType.HasValue)
-                        {
-                            var childCount = GetUserItems(childItem, displayStubType, _user, sortCriteria, null, 0)
-                                .TotalRecordCount;
-
-                            _didlBuilder.WriteFolderElement(writer, childItem, displayStubType, item, childCount, filter);
-                        }
-                        else
-                        {
-                            _didlBuilder.WriteItemElement(writer, childItem, _user, item, serverItem.StubType, deviceId, filter);
-                        }
-                    }
-                }
-
-                writer.WriteFullEndElement();
-                writer.Flush();
-                xmlWriter.WriteElementString("Result", builder.ToString());
-            }
-
-            xmlWriter.WriteElementString("NumberReturned", provided.ToString(CultureInfo.InvariantCulture));
-            xmlWriter.WriteElementString("TotalMatches", totalCount.ToString(CultureInfo.InvariantCulture));
-            xmlWriter.WriteElementString("UpdateID", _systemUpdateId.ToString(CultureInfo.InvariantCulture));
-        }
-
-        /// <summary>
-        /// Builds the response to the "X_BrowseByLetter request.
-        /// </summary>
-        /// <param name="xmlWriter">The <see cref="XmlWriter"/>.</param>
-        /// <param name="sparams">The method parameters.</param>
-        /// <param name="deviceId">The device id.</param>
-        private void HandleXBrowseByLetter(XmlWriter xmlWriter, IReadOnlyDictionary<string, string> sparams, string deviceId)
-        {
-            // TODO: Implement this method
-            HandleSearch(xmlWriter, sparams, deviceId);
-        }
-
-        /// <summary>
-        /// Builds a response to the "Search" request.
-        /// </summary>
-        /// <param name="xmlWriter">The xmlWriter<see cref="XmlWriter"/>.</param>
-        /// <param name="sparams">The method parameters.</param>
-        /// <param name="deviceId">The deviceId<see cref="string"/>.</param>
-        private void HandleSearch(XmlWriter xmlWriter, IReadOnlyDictionary<string, string> sparams, string deviceId)
-        {
-            var searchCriteria = new SearchCriteria(sparams.GetValueOrDefault("SearchCriteria", string.Empty));
-            var sortCriteria = new SortCriteria(sparams.GetValueOrDefault("SortCriteria", string.Empty));
-            var filter = new Filter(sparams.GetValueOrDefault("Filter", "*"));
-
-            // sort example: dc:title, dc:date
-
-            // Default to null instead of 0
-            // Upnp inspector sends 0 as requestedCount when it wants everything
-            int? requestedCount = null;
-            int? start = 0;
-
-            if (sparams.ContainsKey("RequestedCount") && int.TryParse(sparams["RequestedCount"], out var requestedVal) && requestedVal > 0)
-            {
-                requestedCount = requestedVal;
-            }
-
-            if (sparams.ContainsKey("StartingIndex") && int.TryParse(sparams["StartingIndex"], out var startVal) && startVal > 0)
-            {
-                start = startVal;
-            }
-
-            QueryResult<BaseItem> childrenResult;
-            var settings = new XmlWriterSettings
-            {
-                Encoding = Encoding.UTF8,
-                CloseOutput = false,
-                OmitXmlDeclaration = true,
-                ConformanceLevel = ConformanceLevel.Fragment
-            };
-
-            using (StringWriter builder = new StringWriterWithEncoding(Encoding.UTF8))
-            using (var writer = XmlWriter.Create(builder, settings))
-            {
-                writer.WriteStartElement(string.Empty, "DIDL-Lite", NsDidl);
-                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(sparams["ContainerID"]);
-
-                var item = serverItem.Item;
-
-                childrenResult = GetChildrenSorted(item, _user, searchCriteria, sortCriteria, start, requestedCount);
-                foreach (var i in childrenResult.Items)
-                {
-                    if (i.IsDisplayedAsFolder)
-                    {
-                        var childCount = GetChildrenSorted(i, _user, searchCriteria, sortCriteria, null, 0)
-                            .TotalRecordCount;
-
-                        _didlBuilder.WriteFolderElement(writer, i, null, item, childCount, filter);
-                    }
-                    else
-                    {
-                        _didlBuilder.WriteItemElement(writer, i, _user, item, serverItem.StubType, deviceId, filter);
-                    }
-                }
-
-                writer.WriteFullEndElement();
-                writer.Flush();
-                xmlWriter.WriteElementString("Result", builder.ToString());
-            }
-
-            xmlWriter.WriteElementString("NumberReturned", childrenResult.Items.Count.ToString(CultureInfo.InvariantCulture));
-            xmlWriter.WriteElementString("TotalMatches", childrenResult.TotalRecordCount.ToString(CultureInfo.InvariantCulture));
-            xmlWriter.WriteElementString("UpdateID", _systemUpdateId.ToString(CultureInfo.InvariantCulture));
-        }
-
-        /// <summary>
-        /// Returns the child items meeting the criteria.
-        /// </summary>
-        /// <param name="item">The <see cref="BaseItem"/>.</param>
-        /// <param name="user">The <see cref="User"/>.</param>
-        /// <param name="search">The <see cref="SearchCriteria"/>.</param>
-        /// <param name="sort">The <see cref="SortCriteria"/>.</param>
-        /// <param name="startIndex">The start index.</param>
-        /// <param name="limit">The maximum number to return.</param>
-        /// <returns>The <see cref="QueryResult{BaseItem}"/>.</returns>
-        private static QueryResult<BaseItem> GetChildrenSorted(BaseItem item, User user, SearchCriteria search, SortCriteria sort, int? startIndex, int? limit)
-        {
-            var folder = (Folder)item;
-
-            MediaType[] mediaTypes = Array.Empty<MediaType>();
-            bool? isFolder = null;
-
-            switch (search.SearchType)
-            {
-                case SearchType.Audio:
-                    mediaTypes = new[] { MediaType.Audio };
-                    isFolder = false;
-                    break;
-                case SearchType.Video:
-                    mediaTypes = new[] { MediaType.Video };
-                    isFolder = false;
-                    break;
-                case SearchType.Image:
-                    mediaTypes = new[] { MediaType.Photo };
-                    isFolder = false;
-                    break;
-                case SearchType.Playlist:
-                case SearchType.MusicAlbum:
-                    isFolder = true;
-                    break;
-            }
-
-            return folder.GetItems(new InternalItemsQuery
-            {
-                Limit = limit,
-                StartIndex = startIndex,
-                OrderBy = GetOrderBy(sort, folder.IsPreSorted),
-                User = user,
-                Recursive = true,
-                IsMissing = false,
-                ExcludeItemTypes = new[] { BaseItemKind.Book },
-                IsFolder = isFolder,
-                MediaTypes = mediaTypes,
-                DtoOptions = GetDtoOptions()
-            });
-        }
-
-        /// <summary>
-        /// Returns a new DtoOptions object.
-        /// </summary>
-        /// <returns>The <see cref="DtoOptions"/>.</returns>
-        private static DtoOptions GetDtoOptions()
-        {
-            return new DtoOptions(true);
-        }
-
-        /// <summary>
-        /// Returns the User items meeting the criteria.
-        /// </summary>
-        /// <param name="item">The <see cref="BaseItem"/>.</param>
-        /// <param name="stubType">The <see cref="StubType"/>.</param>
-        /// <param name="user">The <see cref="User"/>.</param>
-        /// <param name="sort">The <see cref="SortCriteria"/>.</param>
-        /// <param name="startIndex">The start index.</param>
-        /// <param name="limit">The maximum number to return.</param>
-        /// <returns>The <see cref="QueryResult{ServerItem}"/>.</returns>
-        private QueryResult<ServerItem> GetUserItems(BaseItem item, StubType? stubType, User user, SortCriteria sort, int? startIndex, int? limit)
-        {
-            switch (item)
-            {
-                case MusicGenre:
-                    return GetMusicGenreItems(item, user, sort, startIndex, limit);
-                case MusicArtist:
-                    return GetMusicArtistItems(item, user, sort, startIndex, limit);
-                case Genre:
-                    return GetGenreItems(item, user, sort, startIndex, limit);
-            }
-
-            if (stubType != StubType.Folder && item is IHasCollectionType collectionFolder)
-            {
-                switch (collectionFolder.CollectionType)
-                {
-                    case CollectionType.Music:
-                        return GetMusicFolders(item, user, stubType, sort, startIndex, limit);
-                    case CollectionType.Movies:
-                        return GetMovieFolders(item, user, stubType, sort, startIndex, limit);
-                    case CollectionType.TvShows:
-                        return GetTvFolders(item, user, stubType, sort, startIndex, limit);
-                    case CollectionType.Folders:
-                        return GetFolders(user, startIndex, limit);
-                    case CollectionType.LiveTv:
-                        return GetLiveTvChannels(user, sort, startIndex, limit);
-                }
-            }
-
-            if (stubType.HasValue && stubType.Value != StubType.Folder)
-            {
-                // TODO should this be doing something?
-                return new QueryResult<ServerItem>();
-            }
-
-            var folder = (Folder)item;
-
-            var query = new InternalItemsQuery(user)
-            {
-                Limit = limit,
-                StartIndex = startIndex,
-                IsVirtualItem = false,
-                ExcludeItemTypes = new[] { BaseItemKind.Book },
-                IsPlaceHolder = false,
-                DtoOptions = GetDtoOptions(),
-                OrderBy = GetOrderBy(sort, folder.IsPreSorted)
-            };
-
-            var queryResult = folder.GetItems(query);
-
-            return ToResult(startIndex, queryResult);
-        }
-
-        /// <summary>
-        /// Returns the Live Tv Channels meeting the criteria.
-        /// </summary>
-        /// <param name="user">The <see cref="User"/>.</param>
-        /// <param name="sort">The <see cref="SortCriteria"/>.</param>
-        /// <param name="startIndex">The start index.</param>
-        /// <param name="limit">The maximum number to return.</param>
-        /// <returns>The <see cref="QueryResult{ServerItem}"/>.</returns>
-        private QueryResult<ServerItem> GetLiveTvChannels(User user, SortCriteria sort, int? startIndex, int? limit)
-        {
-            var query = new InternalItemsQuery(user)
-            {
-                StartIndex = startIndex,
-                Limit = limit,
-                IncludeItemTypes = new[] { BaseItemKind.LiveTvChannel },
-                OrderBy = GetOrderBy(sort, false)
-            };
-
-            var result = _libraryManager.GetItemsResult(query);
-
-            return ToResult(startIndex, result);
-        }
-
-        /// <summary>
-        /// Returns the music folders meeting the criteria.
-        /// </summary>
-        /// <param name="item">The <see cref="BaseItem"/>.</param>
-        /// <param name="user">The <see cref="User"/>.</param>
-        /// <param name="stubType">The <see cref="StubType"/>.</param>
-        /// <param name="sort">The <see cref="SortCriteria"/>.</param>
-        /// <param name="startIndex">The start index.</param>
-        /// <param name="limit">The maximum number to return.</param>
-        /// <returns>The <see cref="QueryResult{ServerItem}"/>.</returns>
-        private QueryResult<ServerItem> GetMusicFolders(BaseItem item, User user, StubType? stubType, SortCriteria sort, int? startIndex, int? limit)
-        {
-            var query = new InternalItemsQuery(user)
-            {
-                StartIndex = startIndex,
-                Limit = limit,
-                OrderBy = GetOrderBy(sort, false)
-            };
-
-            switch (stubType)
-            {
-                case StubType.Latest:
-                    return GetLatest(item, query, BaseItemKind.Audio);
-                case StubType.Playlists:
-                    return GetMusicPlaylists(query);
-                case StubType.Albums:
-                    return GetChildrenOfItem(item, query, BaseItemKind.MusicAlbum);
-                case StubType.Artists:
-                    return GetMusicArtists(item, query);
-                case StubType.AlbumArtists:
-                    return GetMusicAlbumArtists(item, query);
-                case StubType.FavoriteAlbums:
-                    return GetChildrenOfItem(item, query, BaseItemKind.MusicAlbum, true);
-                case StubType.FavoriteArtists:
-                    return GetFavoriteArtists(item, query);
-                case StubType.FavoriteSongs:
-                    return GetChildrenOfItem(item, query, BaseItemKind.Audio, true);
-                case StubType.Songs:
-                    return GetChildrenOfItem(item, query, BaseItemKind.Audio);
-                case StubType.Genres:
-                    return GetMusicGenres(item, query);
-            }
-
-            var serverItems = new ServerItem[]
-            {
-                new(item, StubType.Latest),
-                new(item, StubType.Playlists),
-                new(item, StubType.Albums),
-                new(item, StubType.AlbumArtists),
-                new(item, StubType.Artists),
-                new(item, StubType.Songs),
-                new(item, StubType.Genres),
-                new(item, StubType.FavoriteArtists),
-                new(item, StubType.FavoriteAlbums),
-                new(item, StubType.FavoriteSongs)
-            };
-
-            if (limit < serverItems.Length)
-            {
-                serverItems = serverItems[..limit.Value];
-            }
-
-            return new QueryResult<ServerItem>(
-                startIndex,
-                serverItems.Length,
-                serverItems);
-        }
-
-        /// <summary>
-        /// Returns the movie folders meeting the criteria.
-        /// </summary>
-        /// <param name="item">The <see cref="BaseItem"/>.</param>
-        /// <param name="user">The <see cref="User"/>.</param>
-        /// <param name="stubType">The <see cref="StubType"/>.</param>
-        /// <param name="sort">The <see cref="SortCriteria"/>.</param>
-        /// <param name="startIndex">The start index.</param>
-        /// <param name="limit">The maximum number to return.</param>
-        /// <returns>The <see cref="QueryResult{ServerItem}"/>.</returns>
-        private QueryResult<ServerItem> GetMovieFolders(BaseItem item, User user, StubType? stubType, SortCriteria sort, int? startIndex, int? limit)
-        {
-            var query = new InternalItemsQuery(user)
-            {
-                StartIndex = startIndex,
-                Limit = limit,
-                OrderBy = GetOrderBy(sort, false)
-            };
-
-            switch (stubType)
-            {
-                case StubType.ContinueWatching:
-                    return GetMovieContinueWatching(item, query);
-                case StubType.Latest:
-                    return GetLatest(item, query, BaseItemKind.Movie);
-                case StubType.Movies:
-                    return GetChildrenOfItem(item, query, BaseItemKind.Movie);
-                case StubType.Collections:
-                    return GetMovieCollections(query);
-                case StubType.Favorites:
-                    return GetChildrenOfItem(item, query, BaseItemKind.Movie, true);
-                case StubType.Genres:
-                    return GetGenres(item, query);
-            }
-
-            var array = new ServerItem[]
-            {
-                new(item, StubType.ContinueWatching),
-                new(item, StubType.Latest),
-                new(item, StubType.Movies),
-                new(item, StubType.Collections),
-                new(item, StubType.Favorites),
-                new(item, StubType.Genres)
-            };
-
-            if (limit < array.Length)
-            {
-                array = array[..limit.Value];
-            }
-
-            return new QueryResult<ServerItem>(
-                startIndex,
-                array.Length,
-                array);
-        }
-
-        /// <summary>
-        /// Returns the folders meeting the criteria.
-        /// </summary>
-        /// <param name="user">The <see cref="User"/>.</param>
-        /// <param name="startIndex">The start index.</param>
-        /// <param name="limit">The maximum number to return.</param>
-        /// <returns>The <see cref="QueryResult{ServerItem}"/>.</returns>
-        private QueryResult<ServerItem> GetFolders(User user, int? startIndex, int? limit)
-        {
-            var folders = _libraryManager.GetUserRootFolder().GetChildren(user, true);
-            var totalRecordCount = folders.Count;
-            // Handle paging
-            var items = folders
-                .OrderBy(i => i.SortName)
-                .Skip(startIndex ?? 0)
-                .Take(limit ?? int.MaxValue)
-                .Select(i => new ServerItem(i, StubType.Folder))
-                .ToArray();
-
-            return new QueryResult<ServerItem>(
-                startIndex,
-                totalRecordCount,
-                items);
-        }
-
-        /// <summary>
-        /// Returns the TV folders meeting the criteria.
-        /// </summary>
-        /// <param name="item">The <see cref="BaseItem"/>.</param>
-        /// <param name="user">The <see cref="User"/>.</param>
-        /// <param name="stubType">The <see cref="StubType"/>.</param>
-        /// <param name="sort">The <see cref="SortCriteria"/>.</param>
-        /// <param name="startIndex">The start index.</param>
-        /// <param name="limit">The maximum number to return.</param>
-        /// <returns>The <see cref="QueryResult{ServerItem}"/>.</returns>
-        private QueryResult<ServerItem> GetTvFolders(BaseItem item, User user, StubType? stubType, SortCriteria sort, int? startIndex, int? limit)
-        {
-            var query = new InternalItemsQuery(user)
-            {
-                StartIndex = startIndex,
-                Limit = limit,
-                OrderBy = GetOrderBy(sort, false)
-            };
-
-            switch (stubType)
-            {
-                case StubType.ContinueWatching:
-                    return GetMovieContinueWatching(item, query);
-                case StubType.NextUp:
-                    return GetNextUp(item, query);
-                case StubType.Latest:
-                    return GetLatest(item, query, BaseItemKind.Episode);
-                case StubType.Series:
-                    return GetChildrenOfItem(item, query, BaseItemKind.Series);
-                case StubType.FavoriteSeries:
-                    return GetChildrenOfItem(item, query, BaseItemKind.Series, true);
-                case StubType.FavoriteEpisodes:
-                    return GetChildrenOfItem(item, query, BaseItemKind.Episode, true);
-                case StubType.Genres:
-                    return GetGenres(item, query);
-            }
-
-            var serverItems = new ServerItem[]
-            {
-                new(item, StubType.ContinueWatching),
-                new(item, StubType.NextUp),
-                new(item, StubType.Latest),
-                new(item, StubType.Series),
-                new(item, StubType.FavoriteSeries),
-                new(item, StubType.FavoriteEpisodes),
-                new(item, StubType.Genres)
-            };
-
-            if (limit < serverItems.Length)
-            {
-                serverItems = serverItems[..limit.Value];
-            }
-
-            return new QueryResult<ServerItem>(
-                startIndex,
-                serverItems.Length,
-                serverItems);
-        }
-
-        /// <summary>
-        /// Returns the Movies that are part watched that meet the criteria.
-        /// </summary>
-        /// <param name="parent">The <see cref="BaseItem"/>.</param>
-        /// <param name="query">The <see cref="InternalItemsQuery"/>.</param>
-        /// <returns>The <see cref="QueryResult{ServerItem}"/>.</returns>
-        private QueryResult<ServerItem> GetMovieContinueWatching(BaseItem parent, InternalItemsQuery query)
-        {
-            query.Recursive = true;
-            query.Parent = parent;
-
-            query.OrderBy = new[]
-            {
-                (ItemSortBy.DatePlayed, SortOrder.Descending),
-                (ItemSortBy.SortName, SortOrder.Ascending)
-            };
-
-            query.IsResumable = true;
-            query.Limit ??= 10;
-
-            var result = _libraryManager.GetItemsResult(query);
-
-            return ToResult(query.StartIndex, result);
-        }
-
-        /// <summary>
-        /// Returns the Movie collections meeting the criteria.
-        /// </summary>
-        /// <param name="query">The see cref="InternalItemsQuery"/>.</param>
-        /// <returns>The <see cref="QueryResult{ServerItem}"/>.</returns>
-        private QueryResult<ServerItem> GetMovieCollections(InternalItemsQuery query)
-        {
-            query.Recursive = true;
-            query.IncludeItemTypes = new[] { BaseItemKind.BoxSet };
-
-            var result = _libraryManager.GetItemsResult(query);
-
-            return ToResult(query.StartIndex, result);
-        }
-
-        /// <summary>
-        /// Returns the children that meet the criteria.
-        /// </summary>
-        /// <param name="parent">The <see cref="BaseItem"/>.</param>
-        /// <param name="query">The <see cref="InternalItemsQuery"/>.</param>
-        /// <param name="itemType">The item type.</param>
-        /// <param name="isFavorite">A value indicating whether to only fetch favorite items.</param>
-        /// <returns>The <see cref="QueryResult{ServerItem}"/>.</returns>
-        private QueryResult<ServerItem> GetChildrenOfItem(BaseItem parent, InternalItemsQuery query, BaseItemKind itemType, bool isFavorite = false)
-        {
-            query.Recursive = true;
-            query.Parent = parent;
-            query.IsFavorite = isFavorite;
-            query.IncludeItemTypes = new[] { itemType };
-
-            var result = _libraryManager.GetItemsResult(query);
-
-            return ToResult(query.StartIndex, result);
-        }
-
-        /// <summary>
-        /// Returns the genres meeting the criteria.
-        /// The GetGenres.
-        /// </summary>
-        /// <param name="parent">The <see cref="BaseItem"/>.</param>
-        /// <param name="query">The <see cref="InternalItemsQuery"/>.</param>
-        /// <returns>The <see cref="QueryResult{ServerItem}"/>.</returns>
-        private QueryResult<ServerItem> GetGenres(BaseItem parent, InternalItemsQuery query)
-        {
-            // Don't sort
-            query.OrderBy = Array.Empty<(ItemSortBy, SortOrder)>();
-            query.AncestorIds = new[] { parent.Id };
-            var genresResult = _libraryManager.GetGenres(query);
-
-            return ToResult(query.StartIndex, genresResult);
-        }
-
-        /// <summary>
-        /// Returns the music genres meeting the criteria.
-        /// </summary>
-        /// <param name="parent">The <see cref="BaseItem"/>.</param>
-        /// <param name="query">The <see cref="InternalItemsQuery"/>.</param>
-        /// <returns>The <see cref="QueryResult{ServerItem}"/>.</returns>
-        private QueryResult<ServerItem> GetMusicGenres(BaseItem parent, InternalItemsQuery query)
-        {
-            // Don't sort
-            query.OrderBy = Array.Empty<(ItemSortBy, SortOrder)>();
-            query.AncestorIds = new[] { parent.Id };
-            var genresResult = _libraryManager.GetMusicGenres(query);
-
-            return ToResult(query.StartIndex, genresResult);
-        }
-
-        /// <summary>
-        /// Returns the music albums by artist that meet the criteria.
-        /// </summary>
-        /// <param name="parent">The <see cref="BaseItem"/>.</param>
-        /// <param name="query">The <see cref="InternalItemsQuery"/>.</param>
-        /// <returns>The <see cref="QueryResult{ServerItem}"/>.</returns>
-        private QueryResult<ServerItem> GetMusicAlbumArtists(BaseItem parent, InternalItemsQuery query)
-        {
-            // Don't sort
-            query.OrderBy = Array.Empty<(ItemSortBy, SortOrder)>();
-            query.AncestorIds = new[] { parent.Id };
-            var artists = _libraryManager.GetAlbumArtists(query);
-
-            return ToResult(query.StartIndex, artists);
-        }
-
-        /// <summary>
-        /// Returns the music artists meeting the criteria.
-        /// </summary>
-        /// <param name="parent">The <see cref="BaseItem"/>.</param>
-        /// <param name="query">The <see cref="InternalItemsQuery"/>.</param>
-        /// <returns>The <see cref="QueryResult{ServerItem}"/>.</returns>
-        private QueryResult<ServerItem> GetMusicArtists(BaseItem parent, InternalItemsQuery query)
-        {
-            // Don't sort
-            query.OrderBy = Array.Empty<(ItemSortBy, SortOrder)>();
-            query.AncestorIds = new[] { parent.Id };
-            var artists = _libraryManager.GetArtists(query);
-            return ToResult(query.StartIndex, artists);
-        }
-
-        /// <summary>
-        /// Returns the artists tagged as favourite that meet the criteria.
-        /// </summary>
-        /// <param name="parent">The <see cref="BaseItem"/>.</param>
-        /// <param name="query">The <see cref="InternalItemsQuery"/>.</param>
-        /// <returns>The <see cref="QueryResult{ServerItem}"/>.</returns>
-        private QueryResult<ServerItem> GetFavoriteArtists(BaseItem parent, InternalItemsQuery query)
-        {
-            // Don't sort
-            query.OrderBy = Array.Empty<(ItemSortBy, SortOrder)>();
-            query.AncestorIds = new[] { parent.Id };
-            query.IsFavorite = true;
-            var artists = _libraryManager.GetArtists(query);
-            return ToResult(query.StartIndex, artists);
-        }
-
-        /// <summary>
-        /// Returns the music playlists meeting the criteria.
-        /// </summary>
-        /// <param name="query">The query<see cref="InternalItemsQuery"/>.</param>
-        /// <returns>The <see cref="QueryResult{ServerItem}"/>.</returns>
-        private QueryResult<ServerItem> GetMusicPlaylists(InternalItemsQuery query)
-        {
-            query.Parent = null;
-            query.IncludeItemTypes = new[] { BaseItemKind.Playlist };
-            query.Recursive = true;
-
-            var result = _libraryManager.GetItemsResult(query);
-
-            return ToResult(query.StartIndex, result);
-        }
-
-        /// <summary>
-        /// Returns the next up item meeting the criteria.
-        /// </summary>
-        /// <param name="parent">The <see cref="BaseItem"/>.</param>
-        /// <param name="query">The <see cref="InternalItemsQuery"/>.</param>
-        /// <returns>The <see cref="QueryResult{ServerItem}"/>.</returns>
-        private QueryResult<ServerItem> GetNextUp(BaseItem parent, InternalItemsQuery query)
-        {
-            query.OrderBy = Array.Empty<(ItemSortBy, SortOrder)>();
-
-            var result = _tvSeriesManager.GetNextUp(
-                new NextUpQuery
-                {
-                    Limit = query.Limit,
-                    StartIndex = query.StartIndex,
-                    // User cannot be null here as the caller has set it
-                    UserId = query.User!.Id
-                },
-                new[] { parent },
-                query.DtoOptions);
-
-            return ToResult(query.StartIndex, result);
-        }
-
-        /// <summary>
-        /// Returns the latest items of [itemType] meeting the criteria.
-        /// </summary>
-        /// <param name="parent">The <see cref="BaseItem"/>.</param>
-        /// <param name="query">The <see cref="InternalItemsQuery"/>.</param>
-        /// <param name="itemType">The item type.</param>
-        /// <returns>The <see cref="QueryResult{ServerItem}"/>.</returns>
-        private QueryResult<ServerItem> GetLatest(BaseItem parent, InternalItemsQuery query, BaseItemKind itemType)
-        {
-            query.OrderBy = Array.Empty<(ItemSortBy, SortOrder)>();
-
-            var items = _userViewManager.GetLatestItems(
-                new LatestItemsQuery
-                {
-                    // User cannot be null here as the caller has set it
-                    UserId = query.User!.Id,
-                    Limit = query.Limit ?? 50,
-                    IncludeItemTypes = new[] { itemType },
-                    ParentId = parent?.Id ?? Guid.Empty,
-                    GroupItems = true
-                },
-                query.DtoOptions).Select(i => i.Item1 ?? i.Item2.FirstOrDefault()).Where(i => i is not null).ToArray();
-
-            return ToResult(query.StartIndex, items);
-        }
-
-        /// <summary>
-        /// Returns music artist items that meet the criteria.
-        /// </summary>
-        /// <param name="item">The <see cref="BaseItem"/>.</param>
-        /// <param name="user">The <see cref="User"/>.</param>
-        /// <param name="sort">The <see cref="SortCriteria"/>.</param>
-        /// <param name="startIndex">The start index.</param>
-        /// <param name="limit">The maximum number to return.</param>
-        /// <returns>The <see cref="QueryResult{ServerItem}"/>.</returns>
-        private QueryResult<ServerItem> GetMusicArtistItems(BaseItem item, User user, SortCriteria sort, int? startIndex, int? limit)
-        {
-            var query = new InternalItemsQuery(user)
-            {
-                Recursive = true,
-                ArtistIds = new[] { item.Id },
-                IncludeItemTypes = new[] { BaseItemKind.MusicAlbum },
-                Limit = limit,
-                StartIndex = startIndex,
-                DtoOptions = GetDtoOptions(),
-                OrderBy = GetOrderBy(sort, false)
-            };
-
-            var result = _libraryManager.GetItemsResult(query);
-
-            return ToResult(startIndex, result);
-        }
-
-        /// <summary>
-        /// Returns the genre items meeting the criteria.
-        /// </summary>
-        /// <param name="item">The <see cref="BaseItem"/>.</param>
-        /// <param name="user">The <see cref="User"/>.</param>
-        /// <param name="sort">The <see cref="SortCriteria"/>.</param>
-        /// <param name="startIndex">The start index.</param>
-        /// <param name="limit">The maximum number to return.</param>
-        /// <returns>The <see cref="QueryResult{ServerItem}"/>.</returns>
-        private QueryResult<ServerItem> GetGenreItems(BaseItem item, User user, SortCriteria sort, int? startIndex, int? limit)
-        {
-            var query = new InternalItemsQuery(user)
-            {
-                Recursive = true,
-                GenreIds = new[] { item.Id },
-                IncludeItemTypes = new[]
-                {
-                    BaseItemKind.Movie,
-                    BaseItemKind.Series
-                },
-                Limit = limit,
-                StartIndex = startIndex,
-                DtoOptions = GetDtoOptions(),
-                OrderBy = GetOrderBy(sort, false)
-            };
-
-            var result = _libraryManager.GetItemsResult(query);
-
-            return ToResult(startIndex, result);
-        }
-
-        /// <summary>
-        /// Returns the music genre items meeting the criteria.
-        /// </summary>
-        /// <param name="item">The <see cref="BaseItem"/>.</param>
-        /// <param name="user">The <see cref="User"/>.</param>
-        /// <param name="sort">The <see cref="SortCriteria"/>.</param>
-        /// <param name="startIndex">The start index.</param>
-        /// <param name="limit">The maximum number to return.</param>
-        /// <returns>The <see cref="QueryResult{ServerItem}"/>.</returns>
-        private QueryResult<ServerItem> GetMusicGenreItems(BaseItem item, User user, SortCriteria sort, int? startIndex, int? limit)
-        {
-            var query = new InternalItemsQuery(user)
-            {
-                Recursive = true,
-                GenreIds = new[] { item.Id },
-                IncludeItemTypes = new[] { BaseItemKind.MusicAlbum },
-                Limit = limit,
-                StartIndex = startIndex,
-                DtoOptions = GetDtoOptions(),
-                OrderBy = GetOrderBy(sort, false)
-            };
-
-            var result = _libraryManager.GetItemsResult(query);
-
-            return ToResult(startIndex, result);
-        }
-
-        /// <summary>
-        /// Converts <see cref="IReadOnlyCollection{BaseItem}"/> into a <see cref="QueryResult{ServerItem}"/>.
-        /// </summary>
-        /// <param name="startIndex">The start index.</param>
-        /// <param name="result">An array of <see cref="BaseItem"/>.</param>
-        /// <returns>A <see cref="QueryResult{ServerItem}"/>.</returns>
-        private static QueryResult<ServerItem> ToResult(int? startIndex, IReadOnlyCollection<BaseItem> result)
-        {
-            var serverItems = result
-                .Select(i => new ServerItem(i, null))
-                .ToArray();
-
-            return new QueryResult<ServerItem>(
-                startIndex,
-                result.Count,
-                serverItems);
-        }
-
-        /// <summary>
-        /// Converts a <see cref="QueryResult{BaseItem}"/> to a <see cref="QueryResult{ServerItem}"/>.
-        /// </summary>
-        /// <param name="startIndex">The index the result started at.</param>
-        /// <param name="result">A <see cref="QueryResult{BaseItem}"/>.</param>
-        /// <returns>The <see cref="QueryResult{ServerItem}"/>.</returns>
-        private static QueryResult<ServerItem> ToResult(int? startIndex, QueryResult<BaseItem> result)
-        {
-            var length = result.Items.Count;
-            var serverItems = new ServerItem[length];
-            for (var i = 0; i < length; i++)
-            {
-                serverItems[i] = new ServerItem(result.Items[i], null);
-            }
-
-            return new QueryResult<ServerItem>(
-                startIndex,
-                result.TotalRecordCount,
-                serverItems);
-        }
-
-        /// <summary>
-        /// Converts a query result to a <see cref="QueryResult{ServerItem}"/>.
-        /// </summary>
-        /// <param name="startIndex">The start index.</param>
-        /// <param name="result">A <see cref="QueryResult{BaseItem}"/>.</param>
-        /// <returns>The <see cref="QueryResult{ServerItem}"/>.</returns>
-        private static QueryResult<ServerItem> ToResult(int? startIndex, QueryResult<(BaseItem Item, ItemCounts ItemCounts)> result)
-        {
-            var length = result.Items.Count;
-            var serverItems = new ServerItem[length];
-            for (var i = 0; i < length; i++)
-            {
-                serverItems[i] = new ServerItem(result.Items[i].Item, null);
-            }
-
-            return new QueryResult<ServerItem>(
-                startIndex,
-                result.TotalRecordCount,
-                serverItems);
-        }
-
-        /// <summary>
-        /// Gets the sorting method on a query.
-        /// </summary>
-        /// <param name="sort">The <see cref="SortCriteria"/>.</param>
-        /// <param name="isPreSorted">True if pre-sorted.</param>
-        private static (ItemSortBy SortName, SortOrder SortOrder)[] GetOrderBy(SortCriteria sort, bool isPreSorted)
-        {
-            return isPreSorted ? Array.Empty<(ItemSortBy, SortOrder)>() : new[] { (ItemSortBy.SortName, sort.SortOrder) };
-        }
-
-        /// <summary>
-        /// Retrieves the ServerItem id.
-        /// </summary>
-        /// <param name="id">The id<see cref="string"/>.</param>
-        /// <returns>The <see cref="ServerItem"/>.</returns>
-        private ServerItem GetItemFromObjectId(string id)
-        {
-            return DidlBuilder.IsIdRoot(id)
-                 ? new ServerItem(_libraryManager.GetUserRootFolder(), null)
-                 : ParseItemId(id);
-        }
-
-        /// <summary>
-        /// Parses the item id into a <see cref="ServerItem"/>.
-        /// </summary>
-        /// <param name="id">The <see cref="string"/>.</param>
-        /// <returns>The corresponding <see cref="ServerItem"/>.</returns>
-        private ServerItem ParseItemId(string id)
-        {
-            StubType? stubType = null;
-
-            // After using PlayTo, MediaMonkey sends a request to the server trying to get item info
-            const string ParamsSrch = "Params=";
-            var paramsIndex = id.IndexOf(ParamsSrch, StringComparison.OrdinalIgnoreCase);
-            if (paramsIndex != -1)
-            {
-                id = id[(paramsIndex + ParamsSrch.Length)..];
-
-                var parts = id.Split(';');
-                id = parts[23];
-            }
-
-            var dividerIndex = id.IndexOf('_', StringComparison.Ordinal);
-            if (dividerIndex != -1 && Enum.TryParse<StubType>(id.AsSpan(0, dividerIndex), true, out var parsedStubType))
-            {
-                id = id[(dividerIndex + 1)..];
-                stubType = parsedStubType;
-            }
-
-            if (Guid.TryParse(id, out var itemId))
-            {
-                var item = _libraryManager.GetItemById(itemId);
-
-                return new ServerItem(item, stubType);
-            }
-
-            Logger.LogError("Error parsing item Id: {Id}. Returning user root folder.", id);
-
-            return new ServerItem(_libraryManager.GetUserRootFolder(), null);
-        }
-    }
-}

+ 0 - 39
Emby.Dlna/ContentDirectory/ServerItem.cs

@@ -1,39 +0,0 @@
-using MediaBrowser.Controller.Entities;
-
-namespace Emby.Dlna.ContentDirectory
-{
-    /// <summary>
-    /// Defines the <see cref="ServerItem" />.
-    /// </summary>
-    internal class ServerItem
-    {
-        /// <summary>
-        /// Initializes a new instance of the <see cref="ServerItem"/> class.
-        /// </summary>
-        /// <param name="item">The <see cref="BaseItem"/>.</param>
-        /// <param name="stubType">The stub type.</param>
-        public ServerItem(BaseItem item, StubType? stubType)
-        {
-            Item = item;
-
-            if (stubType.HasValue)
-            {
-                StubType = stubType;
-            }
-            else if (item is IItemByName and not Folder)
-            {
-                StubType = Dlna.ContentDirectory.StubType.Folder;
-            }
-        }
-
-        /// <summary>
-        /// Gets the underlying base item.
-        /// </summary>
-        public BaseItem Item { get; }
-
-        /// <summary>
-        /// Gets the DLNA item type.
-        /// </summary>
-        public StubType? StubType { get; }
-    }
-}

+ 0 - 415
Emby.Dlna/ContentDirectory/ServiceActionListBuilder.cs

@@ -1,415 +0,0 @@
-using System.Collections.Generic;
-using Emby.Dlna.Common;
-
-namespace Emby.Dlna.ContentDirectory
-{
-    /// <summary>
-    /// Defines the <see cref="ServiceActionListBuilder" />.
-    /// </summary>
-    public static class ServiceActionListBuilder
-    {
-        /// <summary>
-        /// Returns a list of services that this instance provides.
-        /// </summary>
-        /// <returns>An <see cref="IEnumerable{ServiceAction}"/>.</returns>
-        public static IEnumerable<ServiceAction> GetActions()
-        {
-            return new[]
-            {
-                GetSearchCapabilitiesAction(),
-                GetSortCapabilitiesAction(),
-                GetGetSystemUpdateIDAction(),
-                GetBrowseAction(),
-                GetSearchAction(),
-                GetX_GetFeatureListAction(),
-                GetXSetBookmarkAction(),
-                GetBrowseByLetterAction()
-            };
-        }
-
-        /// <summary>
-        /// Returns the action details for "GetSystemUpdateID".
-        /// </summary>
-        /// <returns>The <see cref="ServiceAction"/>.</returns>
-        private static ServiceAction GetGetSystemUpdateIDAction()
-        {
-            var action = new ServiceAction
-            {
-                Name = "GetSystemUpdateID"
-            };
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "Id",
-                Direction = "out",
-                RelatedStateVariable = "SystemUpdateID"
-            });
-
-            return action;
-        }
-
-        /// <summary>
-        /// Returns the action details for "GetSearchCapabilities".
-        /// </summary>
-        /// <returns>The <see cref="ServiceAction"/>.</returns>
-        private static ServiceAction GetSearchCapabilitiesAction()
-        {
-            var action = new ServiceAction
-            {
-                Name = "GetSearchCapabilities"
-            };
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "SearchCaps",
-                Direction = "out",
-                RelatedStateVariable = "SearchCapabilities"
-            });
-
-            return action;
-        }
-
-        /// <summary>
-        /// Returns the action details for "GetSortCapabilities".
-        /// </summary>
-        /// <returns>The <see cref="ServiceAction"/>.</returns>
-        private static ServiceAction GetSortCapabilitiesAction()
-        {
-            var action = new ServiceAction
-            {
-                Name = "GetSortCapabilities"
-            };
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "SortCaps",
-                Direction = "out",
-                RelatedStateVariable = "SortCapabilities"
-            });
-
-            return action;
-        }
-
-        /// <summary>
-        /// Returns the action details for "X_GetFeatureList".
-        /// </summary>
-        /// <returns>The <see cref="ServiceAction"/>.</returns>
-        private static ServiceAction GetX_GetFeatureListAction()
-        {
-            var action = new ServiceAction
-            {
-                Name = "X_GetFeatureList"
-            };
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "FeatureList",
-                Direction = "out",
-                RelatedStateVariable = "A_ARG_TYPE_Featurelist"
-            });
-
-            return action;
-        }
-
-        /// <summary>
-        /// Returns the action details for "Search".
-        /// </summary>
-        /// <returns>The <see cref="ServiceAction"/>.</returns>
-        private static ServiceAction GetSearchAction()
-        {
-            var action = new ServiceAction
-            {
-                Name = "Search"
-            };
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "ContainerID",
-                Direction = "in",
-                RelatedStateVariable = "A_ARG_TYPE_ObjectID"
-            });
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "SearchCriteria",
-                Direction = "in",
-                RelatedStateVariable = "A_ARG_TYPE_SearchCriteria"
-            });
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "Filter",
-                Direction = "in",
-                RelatedStateVariable = "A_ARG_TYPE_Filter"
-            });
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "StartingIndex",
-                Direction = "in",
-                RelatedStateVariable = "A_ARG_TYPE_Index"
-            });
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "RequestedCount",
-                Direction = "in",
-                RelatedStateVariable = "A_ARG_TYPE_Count"
-            });
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "SortCriteria",
-                Direction = "in",
-                RelatedStateVariable = "A_ARG_TYPE_SortCriteria"
-            });
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "Result",
-                Direction = "out",
-                RelatedStateVariable = "A_ARG_TYPE_Result"
-            });
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "NumberReturned",
-                Direction = "out",
-                RelatedStateVariable = "A_ARG_TYPE_Count"
-            });
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "TotalMatches",
-                Direction = "out",
-                RelatedStateVariable = "A_ARG_TYPE_Count"
-            });
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "UpdateID",
-                Direction = "out",
-                RelatedStateVariable = "A_ARG_TYPE_UpdateID"
-            });
-
-            return action;
-        }
-
-        /// <summary>
-        /// Returns the action details for "Browse".
-        /// </summary>
-        /// <returns>The <see cref="ServiceAction"/>.</returns>
-        private static ServiceAction GetBrowseAction()
-        {
-            var action = new ServiceAction
-            {
-                Name = "Browse"
-            };
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "ObjectID",
-                Direction = "in",
-                RelatedStateVariable = "A_ARG_TYPE_ObjectID"
-            });
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "BrowseFlag",
-                Direction = "in",
-                RelatedStateVariable = "A_ARG_TYPE_BrowseFlag"
-            });
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "Filter",
-                Direction = "in",
-                RelatedStateVariable = "A_ARG_TYPE_Filter"
-            });
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "StartingIndex",
-                Direction = "in",
-                RelatedStateVariable = "A_ARG_TYPE_Index"
-            });
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "RequestedCount",
-                Direction = "in",
-                RelatedStateVariable = "A_ARG_TYPE_Count"
-            });
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "SortCriteria",
-                Direction = "in",
-                RelatedStateVariable = "A_ARG_TYPE_SortCriteria"
-            });
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "Result",
-                Direction = "out",
-                RelatedStateVariable = "A_ARG_TYPE_Result"
-            });
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "NumberReturned",
-                Direction = "out",
-                RelatedStateVariable = "A_ARG_TYPE_Count"
-            });
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "TotalMatches",
-                Direction = "out",
-                RelatedStateVariable = "A_ARG_TYPE_Count"
-            });
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "UpdateID",
-                Direction = "out",
-                RelatedStateVariable = "A_ARG_TYPE_UpdateID"
-            });
-
-            return action;
-        }
-
-        /// <summary>
-        /// Returns the action details for "X_BrowseByLetter".
-        /// </summary>
-        /// <returns>The <see cref="ServiceAction"/>.</returns>
-        private static ServiceAction GetBrowseByLetterAction()
-        {
-            var action = new ServiceAction
-            {
-                Name = "X_BrowseByLetter"
-            };
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "ObjectID",
-                Direction = "in",
-                RelatedStateVariable = "A_ARG_TYPE_ObjectID"
-            });
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "BrowseFlag",
-                Direction = "in",
-                RelatedStateVariable = "A_ARG_TYPE_BrowseFlag"
-            });
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "Filter",
-                Direction = "in",
-                RelatedStateVariable = "A_ARG_TYPE_Filter"
-            });
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "StartingLetter",
-                Direction = "in",
-                RelatedStateVariable = "A_ARG_TYPE_BrowseLetter"
-            });
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "RequestedCount",
-                Direction = "in",
-                RelatedStateVariable = "A_ARG_TYPE_Count"
-            });
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "SortCriteria",
-                Direction = "in",
-                RelatedStateVariable = "A_ARG_TYPE_SortCriteria"
-            });
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "Result",
-                Direction = "out",
-                RelatedStateVariable = "A_ARG_TYPE_Result"
-            });
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "NumberReturned",
-                Direction = "out",
-                RelatedStateVariable = "A_ARG_TYPE_Count"
-            });
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "TotalMatches",
-                Direction = "out",
-                RelatedStateVariable = "A_ARG_TYPE_Count"
-            });
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "UpdateID",
-                Direction = "out",
-                RelatedStateVariable = "A_ARG_TYPE_UpdateID"
-            });
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "StartingIndex",
-                Direction = "out",
-                RelatedStateVariable = "A_ARG_TYPE_Index"
-            });
-
-            return action;
-        }
-
-        /// <summary>
-        /// Returns the action details for "X_SetBookmark".
-        /// </summary>
-        /// <returns>The <see cref="ServiceAction"/>.</returns>
-        private static ServiceAction GetXSetBookmarkAction()
-        {
-            var action = new ServiceAction
-            {
-                Name = "X_SetBookmark"
-            };
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "CategoryType",
-                Direction = "in",
-                RelatedStateVariable = "A_ARG_TYPE_CategoryType"
-            });
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "RID",
-                Direction = "in",
-                RelatedStateVariable = "A_ARG_TYPE_RID"
-            });
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "ObjectID",
-                Direction = "in",
-                RelatedStateVariable = "A_ARG_TYPE_ObjectID"
-            });
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "PosSecond",
-                Direction = "in",
-                RelatedStateVariable = "A_ARG_TYPE_PosSec"
-            });
-
-            return action;
-        }
-    }
-}

+ 0 - 30
Emby.Dlna/ContentDirectory/StubType.cs

@@ -1,30 +0,0 @@
-#pragma warning disable CS1591
-
-namespace Emby.Dlna.ContentDirectory
-{
-    /// <summary>
-    /// Defines the DLNA item types.
-    /// </summary>
-    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
-    }
-}

+ 0 - 25
Emby.Dlna/ControlRequest.cs

@@ -1,25 +0,0 @@
-#nullable disable
-
-#pragma warning disable CS1591
-
-using System.IO;
-using Microsoft.AspNetCore.Http;
-
-namespace Emby.Dlna
-{
-    public class ControlRequest
-    {
-        public ControlRequest(IHeaderDictionary headers)
-        {
-            Headers = headers;
-        }
-
-        public IHeaderDictionary Headers { get; }
-
-        public Stream InputXml { get; set; }
-
-        public string TargetServerUuId { get; set; }
-
-        public string RequestedUrl { get; set; }
-    }
-}

+ 0 - 28
Emby.Dlna/ControlResponse.cs

@@ -1,28 +0,0 @@
-#pragma warning disable CS1591
-
-using System.Collections.Generic;
-
-namespace Emby.Dlna
-{
-    public class ControlResponse
-    {
-        public ControlResponse(string xml, bool isSuccessful)
-        {
-            Headers = new Dictionary<string, string>();
-            Xml = xml;
-            IsSuccessful = isSuccessful;
-        }
-
-        public IDictionary<string, string> Headers { get; }
-
-        public string Xml { get; set; }
-
-        public bool IsSuccessful { get; set; }
-
-        /// <inheritdoc />
-        public override string ToString()
-        {
-            return Xml;
-        }
-    }
-}

+ 0 - 1266
Emby.Dlna/Didl/DidlBuilder.cs

@@ -1,1266 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Text;
-using System.Xml;
-using Emby.Dlna.ContentDirectory;
-using Jellyfin.Data.Entities;
-using Jellyfin.Data.Enums;
-using MediaBrowser.Controller.Channels;
-using MediaBrowser.Controller.Drawing;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Entities.Movies;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Controller.Playlists;
-using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.Drawing;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Globalization;
-using MediaBrowser.Model.Net;
-using Microsoft.Extensions.Logging;
-using Episode = MediaBrowser.Controller.Entities.TV.Episode;
-using Genre = MediaBrowser.Controller.Entities.Genre;
-using Movie = MediaBrowser.Controller.Entities.Movies.Movie;
-using MusicAlbum = MediaBrowser.Controller.Entities.Audio.MusicAlbum;
-using Season = MediaBrowser.Controller.Entities.TV.Season;
-using Series = MediaBrowser.Controller.Entities.TV.Series;
-using XmlAttribute = MediaBrowser.Model.Dlna.XmlAttribute;
-
-namespace Emby.Dlna.Didl
-{
-    public class DidlBuilder
-    {
-        private const string NsDidl = "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/";
-        private const string NsDc = "http://purl.org/dc/elements/1.1/";
-        private const string NsUpnp = "urn:schemas-upnp-org:metadata-1-0/upnp/";
-        private const string NsDlna = "urn:schemas-dlna-org:metadata-1-0/";
-
-        private readonly DeviceProfile _profile;
-        private readonly IImageProcessor _imageProcessor;
-        private readonly string _serverAddress;
-        private readonly string? _accessToken;
-        private readonly User? _user;
-        private readonly IUserDataManager _userDataManager;
-        private readonly ILocalizationManager _localization;
-        private readonly IMediaSourceManager _mediaSourceManager;
-        private readonly ILogger _logger;
-        private readonly IMediaEncoder _mediaEncoder;
-        private readonly ILibraryManager _libraryManager;
-
-        public DidlBuilder(
-            DeviceProfile profile,
-            User? user,
-            IImageProcessor imageProcessor,
-            string serverAddress,
-            string? accessToken,
-            IUserDataManager userDataManager,
-            ILocalizationManager localization,
-            IMediaSourceManager mediaSourceManager,
-            ILogger logger,
-            IMediaEncoder mediaEncoder,
-            ILibraryManager libraryManager)
-        {
-            _profile = profile;
-            _user = user;
-            _imageProcessor = imageProcessor;
-            _serverAddress = serverAddress;
-            _accessToken = accessToken;
-            _userDataManager = userDataManager;
-            _localization = localization;
-            _mediaSourceManager = mediaSourceManager;
-            _logger = logger;
-            _mediaEncoder = mediaEncoder;
-            _libraryManager = libraryManager;
-        }
-
-        public static string NormalizeDlnaMediaUrl(string url)
-        {
-            return url + "&dlnaheaders=true";
-        }
-
-        public string GetItemDidl(BaseItem item, User? user, BaseItem? context, string deviceId, Filter filter, StreamInfo streamInfo)
-        {
-            var settings = new XmlWriterSettings
-            {
-                Encoding = Encoding.UTF8,
-                CloseOutput = false,
-                OmitXmlDeclaration = true,
-                ConformanceLevel = ConformanceLevel.Fragment
-            };
-
-            using (StringWriter builder = new StringWriterWithEncoding(Encoding.UTF8))
-            {
-                // If this using are changed to single lines, then write.Flush needs to be appended before the return.
-                using (var writer = XmlWriter.Create(builder, settings))
-                {
-                    // writer.WriteStartDocument();
-
-                    writer.WriteStartElement(string.Empty, "DIDL-Lite", NsDidl);
-
-                    writer.WriteAttributeString("xmlns", "dc", null, NsDc);
-                    writer.WriteAttributeString("xmlns", "dlna", null, NsDlna);
-                    writer.WriteAttributeString("xmlns", "upnp", null, NsUpnp);
-                    // didl.SetAttribute("xmlns:sec", NS_SEC);
-
-                    WriteXmlRootAttributes(_profile, writer);
-
-                    WriteItemElement(writer, item, user, context, null, deviceId, filter, streamInfo);
-
-                    writer.WriteFullEndElement();
-                    // writer.WriteEndDocument();
-                }
-
-                return builder.ToString();
-            }
-        }
-
-        public static void WriteXmlRootAttributes(DeviceProfile profile, XmlWriter writer)
-        {
-            foreach (var att in profile.XmlRootAttributes)
-            {
-                var parts = att.Name.Split(':', StringSplitOptions.RemoveEmptyEntries);
-                if (parts.Length == 2)
-                {
-                    writer.WriteAttributeString(parts[0], parts[1], null, att.Value);
-                }
-                else
-                {
-                    writer.WriteAttributeString(att.Name, att.Value);
-                }
-            }
-        }
-
-        public void WriteItemElement(
-            XmlWriter writer,
-            BaseItem item,
-            User? user,
-            BaseItem? context,
-            StubType? contextStubType,
-            string deviceId,
-            Filter filter,
-            StreamInfo? streamInfo = null)
-        {
-            var clientId = GetClientId(item, null);
-
-            writer.WriteStartElement(string.Empty, "item", NsDidl);
-
-            writer.WriteAttributeString("restricted", "1");
-            writer.WriteAttributeString("id", clientId);
-
-            if (context is not null)
-            {
-                writer.WriteAttributeString("parentID", GetClientId(context, contextStubType));
-            }
-            else
-            {
-                var parent = item.DisplayParentId;
-                if (!parent.Equals(default))
-                {
-                    writer.WriteAttributeString("parentID", GetClientId(parent, null));
-                }
-            }
-
-            AddGeneralProperties(item, null, context, writer, filter);
-
-            AddSamsungBookmarkInfo(item, user, writer, streamInfo);
-
-            // refID?
-            // storeAttribute(itemNode, object, ClassProperties.REF_ID, false);
-
-            if (item is IHasMediaSources)
-            {
-                switch (item.MediaType)
-                {
-                    case MediaType.Audio:
-                        AddAudioResource(writer, item, deviceId, filter, streamInfo);
-                        break;
-                    case MediaType.Video:
-                        AddVideoResource(writer, item, deviceId, filter, streamInfo);
-                        break;
-                }
-            }
-
-            AddCover(item, null, writer);
-            writer.WriteFullEndElement();
-        }
-
-        private void AddVideoResource(XmlWriter writer, BaseItem video, string deviceId, Filter filter, StreamInfo? streamInfo = null)
-        {
-            if (streamInfo is null)
-            {
-                var sources = _mediaSourceManager.GetStaticMediaSources(video, true, _user);
-
-                streamInfo = new StreamBuilder(_mediaEncoder, _logger).GetOptimalVideoStream(new MediaOptions
-                {
-                    ItemId = video.Id,
-                    MediaSources = sources.ToArray(),
-                    Profile = _profile,
-                    DeviceId = deviceId,
-                    MaxBitrate = _profile.MaxStreamingBitrate
-                }) ?? throw new InvalidOperationException("No optimal video stream found");
-            }
-
-            var targetWidth = streamInfo.TargetWidth;
-            var targetHeight = streamInfo.TargetHeight;
-
-            var contentFeatureList = ContentFeatureBuilder.BuildVideoHeader(
-                _profile,
-                streamInfo.Container,
-                streamInfo.TargetVideoCodec.FirstOrDefault(),
-                streamInfo.TargetAudioCodec.FirstOrDefault(),
-                targetWidth,
-                targetHeight,
-                streamInfo.TargetVideoBitDepth,
-                streamInfo.TargetVideoBitrate,
-                streamInfo.TargetTimestamp,
-                streamInfo.IsDirectStream,
-                streamInfo.RunTimeTicks ?? 0,
-                streamInfo.TargetVideoProfile,
-                streamInfo.TargetVideoRangeType,
-                streamInfo.TargetVideoLevel,
-                streamInfo.TargetFramerate ?? 0,
-                streamInfo.TargetPacketLength,
-                streamInfo.TranscodeSeekInfo,
-                streamInfo.IsTargetAnamorphic,
-                streamInfo.IsTargetInterlaced,
-                streamInfo.TargetRefFrames,
-                streamInfo.TargetVideoStreamCount,
-                streamInfo.TargetAudioStreamCount,
-                streamInfo.TargetVideoCodecTag,
-                streamInfo.IsTargetAVC);
-
-            foreach (var contentFeature in contentFeatureList)
-            {
-                AddVideoResource(writer, filter, contentFeature, streamInfo);
-            }
-
-            var subtitleProfiles = streamInfo.GetSubtitleProfiles(_mediaEncoder, false, _serverAddress, _accessToken);
-
-            foreach (var subtitle in subtitleProfiles)
-            {
-                if (subtitle.DeliveryMethod != SubtitleDeliveryMethod.External)
-                {
-                    continue;
-                }
-
-                var subtitleAdded = AddSubtitleElement(writer, subtitle);
-
-                if (subtitleAdded && _profile.EnableSingleSubtitleLimit)
-                {
-                    break;
-                }
-            }
-        }
-
-        private bool AddSubtitleElement(XmlWriter writer, SubtitleStreamInfo info)
-        {
-            var subtitleProfile = _profile.SubtitleProfiles
-                .FirstOrDefault(i => string.Equals(info.Format, i.Format, StringComparison.OrdinalIgnoreCase)
-                                    && i.Method == SubtitleDeliveryMethod.External);
-
-            if (subtitleProfile is null)
-            {
-                return false;
-            }
-
-            var subtitleMode = subtitleProfile.DidlMode;
-
-            if (string.Equals(subtitleMode, "CaptionInfoEx", StringComparison.OrdinalIgnoreCase))
-            {
-                // <sec:CaptionInfoEx sec:type="srt">http://192.168.1.3:9999/video.srt</sec:CaptionInfoEx>
-                // <sec:CaptionInfo sec:type="srt">http://192.168.1.3:9999/video.srt</sec:CaptionInfo>
-
-                writer.WriteStartElement("sec", "CaptionInfoEx", null);
-                writer.WriteAttributeString("sec", "type", null, info.Format.ToLowerInvariant());
-
-                writer.WriteString(info.Url);
-                writer.WriteFullEndElement();
-            }
-            else if (string.Equals(subtitleMode, "smi", StringComparison.OrdinalIgnoreCase))
-            {
-                writer.WriteStartElement(string.Empty, "res", NsDidl);
-
-                writer.WriteAttributeString("protocolInfo", "http-get:*:smi/caption:*");
-
-                writer.WriteString(info.Url);
-                writer.WriteFullEndElement();
-            }
-            else
-            {
-                writer.WriteStartElement(string.Empty, "res", NsDidl);
-                var protocolInfo = string.Format(
-                    CultureInfo.InvariantCulture,
-                    "http-get:*:text/{0}:*",
-                    info.Format.ToLowerInvariant());
-                writer.WriteAttributeString("protocolInfo", protocolInfo);
-
-                writer.WriteString(info.Url);
-                writer.WriteFullEndElement();
-            }
-
-            return true;
-        }
-
-        private void AddVideoResource(XmlWriter writer, Filter filter, string contentFeatures, StreamInfo streamInfo)
-        {
-            writer.WriteStartElement(string.Empty, "res", NsDidl);
-
-            var url = NormalizeDlnaMediaUrl(streamInfo.ToUrl(_serverAddress, _accessToken));
-
-            var mediaSource = streamInfo.MediaSource;
-
-            if (mediaSource?.RunTimeTicks.HasValue == true)
-            {
-                writer.WriteAttributeString("duration", TimeSpan.FromTicks(mediaSource.RunTimeTicks.Value).ToString("c", CultureInfo.InvariantCulture));
-            }
-
-            if (filter.Contains("res@size"))
-            {
-                if (streamInfo.IsDirectStream || streamInfo.EstimateContentLength)
-                {
-                    var size = streamInfo.TargetSize;
-
-                    if (size.HasValue)
-                    {
-                        writer.WriteAttributeString("size", size.Value.ToString(CultureInfo.InvariantCulture));
-                    }
-                }
-            }
-
-            var totalBitrate = streamInfo.TargetTotalBitrate;
-            var targetSampleRate = streamInfo.TargetAudioSampleRate;
-            var targetChannels = streamInfo.TargetAudioChannels;
-
-            var targetWidth = streamInfo.TargetWidth;
-            var targetHeight = streamInfo.TargetHeight;
-
-            if (targetChannels.HasValue)
-            {
-                writer.WriteAttributeString("nrAudioChannels", targetChannels.Value.ToString(CultureInfo.InvariantCulture));
-            }
-
-            if (filter.Contains("res@resolution"))
-            {
-                if (targetWidth.HasValue && targetHeight.HasValue)
-                {
-                    writer.WriteAttributeString(
-                        "resolution",
-                        string.Format(
-                            CultureInfo.InvariantCulture,
-                            "{0}x{1}",
-                            targetWidth.Value,
-                            targetHeight.Value));
-                }
-            }
-
-            if (targetSampleRate.HasValue)
-            {
-                writer.WriteAttributeString("sampleFrequency", targetSampleRate.Value.ToString(CultureInfo.InvariantCulture));
-            }
-
-            if (totalBitrate.HasValue)
-            {
-                writer.WriteAttributeString("bitrate", totalBitrate.Value.ToString(CultureInfo.InvariantCulture));
-            }
-
-            var mediaProfile = _profile.GetVideoMediaProfile(
-                streamInfo.Container,
-                streamInfo.TargetAudioCodec.FirstOrDefault(),
-                streamInfo.TargetVideoCodec.FirstOrDefault(),
-                streamInfo.TargetAudioBitrate,
-                targetWidth,
-                targetHeight,
-                streamInfo.TargetVideoBitDepth,
-                streamInfo.TargetVideoProfile,
-                streamInfo.TargetVideoRangeType,
-                streamInfo.TargetVideoLevel,
-                streamInfo.TargetFramerate ?? 0,
-                streamInfo.TargetPacketLength,
-                streamInfo.TargetTimestamp,
-                streamInfo.IsTargetAnamorphic,
-                streamInfo.IsTargetInterlaced,
-                streamInfo.TargetRefFrames,
-                streamInfo.TargetVideoStreamCount,
-                streamInfo.TargetAudioStreamCount,
-                streamInfo.TargetVideoCodecTag,
-                streamInfo.IsTargetAVC);
-
-            var filename = url.Substring(0, url.IndexOf('?', StringComparison.Ordinal));
-
-            var mimeType = mediaProfile is null || string.IsNullOrEmpty(mediaProfile.MimeType)
-               ? MimeTypes.GetMimeType(filename)
-               : mediaProfile.MimeType;
-
-            writer.WriteAttributeString(
-                "protocolInfo",
-                string.Format(
-                    CultureInfo.InvariantCulture,
-                    "http-get:*:{0}:{1}",
-                    mimeType,
-                    contentFeatures));
-
-            writer.WriteString(url);
-
-            writer.WriteFullEndElement();
-        }
-
-        private string GetDisplayName(BaseItem item, StubType? itemStubType, BaseItem? context)
-        {
-            if (itemStubType.HasValue)
-            {
-                switch (itemStubType.Value)
-                {
-                    case StubType.Latest: return _localization.GetLocalizedString("Latest");
-                    case StubType.Playlists: return _localization.GetLocalizedString("Playlists");
-                    case StubType.AlbumArtists: return _localization.GetLocalizedString("HeaderAlbumArtists");
-                    case StubType.Albums: return _localization.GetLocalizedString("Albums");
-                    case StubType.Artists: return _localization.GetLocalizedString("Artists");
-                    case StubType.Songs: return _localization.GetLocalizedString("Songs");
-                    case StubType.Genres: return _localization.GetLocalizedString("Genres");
-                    case StubType.FavoriteAlbums: return _localization.GetLocalizedString("HeaderFavoriteAlbums");
-                    case StubType.FavoriteArtists: return _localization.GetLocalizedString("HeaderFavoriteArtists");
-                    case StubType.FavoriteSongs: return _localization.GetLocalizedString("HeaderFavoriteSongs");
-                    case StubType.ContinueWatching: return _localization.GetLocalizedString("HeaderContinueWatching");
-                    case StubType.Movies: return _localization.GetLocalizedString("Movies");
-                    case StubType.Collections: return _localization.GetLocalizedString("Collections");
-                    case StubType.Favorites: return _localization.GetLocalizedString("Favorites");
-                    case StubType.NextUp: return _localization.GetLocalizedString("HeaderNextUp");
-                    case StubType.FavoriteSeries: return _localization.GetLocalizedString("HeaderFavoriteShows");
-                    case StubType.FavoriteEpisodes: return _localization.GetLocalizedString("HeaderFavoriteEpisodes");
-                    case StubType.Series: return _localization.GetLocalizedString("Shows");
-                }
-            }
-
-            return item is Episode episode
-                ? GetEpisodeDisplayName(episode, context)
-                : item.Name;
-        }
-
-        /// <summary>
-        /// Gets episode display name appropriate for the given context.
-        /// </summary>
-        /// <remarks>
-        /// If context is a season, this will return a string containing just episode number and name.
-        /// Otherwise the result will include series names and season number.
-        /// </remarks>
-        /// <param name="episode">The episode.</param>
-        /// <param name="context">Current context.</param>
-        /// <returns>Formatted name of the episode.</returns>
-        private string GetEpisodeDisplayName(Episode episode, BaseItem? context)
-        {
-            string[] components;
-
-            if (context is Season season)
-            {
-                // This is a special embedded within a season
-                if (episode.ParentIndexNumber.HasValue && episode.ParentIndexNumber.Value == 0
-                    && season.IndexNumber.HasValue && season.IndexNumber.Value != 0)
-                {
-                    return string.Format(
-                        CultureInfo.InvariantCulture,
-                        _localization.GetLocalizedString("ValueSpecialEpisodeName"),
-                        episode.Name);
-                }
-
-                // inside a season use simple format (ex. '12 - Episode Name')
-                var epNumberName = GetEpisodeIndexFullName(episode);
-                components = new[] { epNumberName, episode.Name };
-            }
-            else
-            {
-                // outside a season include series and season details (ex. 'TV Show - S05E11 - Episode Name')
-                var epNumberName = GetEpisodeNumberDisplayName(episode);
-                components = new[] { episode.SeriesName, epNumberName, episode.Name };
-            }
-
-            return string.Join(" - ", components.Where(NotNullOrWhiteSpace));
-        }
-
-        /// <summary>
-        /// Gets complete episode number.
-        /// </summary>
-        /// <param name="episode">The episode.</param>
-        /// <returns>For single episodes returns just the number. For double episodes - current and ending numbers.</returns>
-        private string GetEpisodeIndexFullName(Episode episode)
-        {
-            var name = string.Empty;
-            if (episode.IndexNumber.HasValue)
-            {
-                name += episode.IndexNumber.Value.ToString("00", CultureInfo.InvariantCulture);
-
-                if (episode.IndexNumberEnd.HasValue)
-                {
-                    name += "-" + episode.IndexNumberEnd.Value.ToString("00", CultureInfo.InvariantCulture);
-                }
-            }
-
-            return name;
-        }
-
-        /// <summary>
-        /// Gets episode number formatted as 'S##E##'.
-        /// </summary>
-        /// <param name="episode">The episode.</param>
-        /// <returns>Formatted episode number.</returns>
-        private string GetEpisodeNumberDisplayName(Episode episode)
-        {
-            var name = string.Empty;
-            var seasonNumber = episode.Season?.IndexNumber;
-
-            if (seasonNumber.HasValue)
-            {
-                name = "S" + seasonNumber.Value.ToString("00", CultureInfo.InvariantCulture);
-            }
-
-            var indexName = GetEpisodeIndexFullName(episode);
-
-            if (!string.IsNullOrWhiteSpace(indexName))
-            {
-                name += "E" + indexName;
-            }
-
-            return name;
-        }
-
-        private bool NotNullOrWhiteSpace(string s) => !string.IsNullOrWhiteSpace(s);
-
-        private void AddAudioResource(XmlWriter writer, BaseItem audio, string deviceId, Filter filter, StreamInfo? streamInfo = null)
-        {
-            writer.WriteStartElement(string.Empty, "res", NsDidl);
-
-            if (streamInfo is null)
-            {
-                var sources = _mediaSourceManager.GetStaticMediaSources(audio, true, _user);
-
-                streamInfo = new StreamBuilder(_mediaEncoder, _logger).GetOptimalAudioStream(new MediaOptions
-                {
-                    ItemId = audio.Id,
-                    MediaSources = sources.ToArray(),
-                    Profile = _profile,
-                    DeviceId = deviceId
-                }) ?? throw new InvalidOperationException("No optimal audio stream found");
-            }
-
-            var url = NormalizeDlnaMediaUrl(streamInfo.ToUrl(_serverAddress, _accessToken));
-
-            var mediaSource = streamInfo.MediaSource;
-
-            if (mediaSource?.RunTimeTicks is not null)
-            {
-                writer.WriteAttributeString("duration", TimeSpan.FromTicks(mediaSource.RunTimeTicks.Value).ToString("c", CultureInfo.InvariantCulture));
-            }
-
-            if (filter.Contains("res@size"))
-            {
-                if (streamInfo.IsDirectStream || streamInfo.EstimateContentLength)
-                {
-                    var size = streamInfo.TargetSize;
-
-                    if (size.HasValue)
-                    {
-                        writer.WriteAttributeString("size", size.Value.ToString(CultureInfo.InvariantCulture));
-                    }
-                }
-            }
-
-            var targetAudioBitrate = streamInfo.TargetAudioBitrate;
-            var targetSampleRate = streamInfo.TargetAudioSampleRate;
-            var targetChannels = streamInfo.TargetAudioChannels;
-            var targetAudioBitDepth = streamInfo.TargetAudioBitDepth;
-
-            if (targetChannels.HasValue)
-            {
-                writer.WriteAttributeString("nrAudioChannels", targetChannels.Value.ToString(CultureInfo.InvariantCulture));
-            }
-
-            if (targetSampleRate.HasValue)
-            {
-                writer.WriteAttributeString("sampleFrequency", targetSampleRate.Value.ToString(CultureInfo.InvariantCulture));
-            }
-
-            if (targetAudioBitrate.HasValue)
-            {
-                writer.WriteAttributeString("bitrate", targetAudioBitrate.Value.ToString(CultureInfo.InvariantCulture));
-            }
-
-            var mediaProfile = _profile.GetAudioMediaProfile(
-                streamInfo.Container,
-                streamInfo.TargetAudioCodec.FirstOrDefault(),
-                targetChannels,
-                targetAudioBitrate,
-                targetSampleRate,
-                targetAudioBitDepth);
-
-            var filename = url.Substring(0, url.IndexOf('?', StringComparison.Ordinal));
-
-            var mimeType = mediaProfile is null || string.IsNullOrEmpty(mediaProfile.MimeType)
-                ? MimeTypes.GetMimeType(filename)
-                : mediaProfile.MimeType;
-
-            var contentFeatures = ContentFeatureBuilder.BuildAudioHeader(
-                _profile,
-                streamInfo.Container,
-                streamInfo.TargetAudioCodec.FirstOrDefault(),
-                targetAudioBitrate,
-                targetSampleRate,
-                targetChannels,
-                targetAudioBitDepth,
-                streamInfo.IsDirectStream,
-                streamInfo.RunTimeTicks ?? 0,
-                streamInfo.TranscodeSeekInfo);
-
-            writer.WriteAttributeString(
-                "protocolInfo",
-                string.Format(
-                    CultureInfo.InvariantCulture,
-                    "http-get:*:{0}:{1}",
-                    mimeType,
-                    contentFeatures));
-
-            writer.WriteString(url);
-
-            writer.WriteFullEndElement();
-        }
-
-        public static bool IsIdRoot(string id)
-            => string.IsNullOrWhiteSpace(id)
-                || string.Equals(id, "0", StringComparison.OrdinalIgnoreCase)
-                // Samsung sometimes uses 1 as root
-                || string.Equals(id, "1", StringComparison.OrdinalIgnoreCase);
-
-        public void WriteFolderElement(XmlWriter writer, BaseItem folder, StubType? stubType, BaseItem context, int childCount, Filter filter, string? requestedId = null)
-        {
-            writer.WriteStartElement(string.Empty, "container", NsDidl);
-
-            writer.WriteAttributeString("restricted", "1");
-            writer.WriteAttributeString("searchable", "1");
-            writer.WriteAttributeString("childCount", childCount.ToString(CultureInfo.InvariantCulture));
-
-            var clientId = GetClientId(folder, stubType);
-
-            if (string.Equals(requestedId, "0", StringComparison.Ordinal))
-            {
-                writer.WriteAttributeString("id", "0");
-                writer.WriteAttributeString("parentID", "-1");
-            }
-            else
-            {
-                writer.WriteAttributeString("id", clientId);
-
-                if (context is not null)
-                {
-                    writer.WriteAttributeString("parentID", GetClientId(context, null));
-                }
-                else
-                {
-                    var parent = folder.DisplayParentId;
-                    if (parent.Equals(default))
-                    {
-                        writer.WriteAttributeString("parentID", "0");
-                    }
-                    else
-                    {
-                        writer.WriteAttributeString("parentID", GetClientId(parent, null));
-                    }
-                }
-            }
-
-            AddGeneralProperties(folder, stubType, context, writer, filter);
-
-            AddCover(folder, stubType, writer);
-
-            writer.WriteFullEndElement();
-        }
-
-        private void AddSamsungBookmarkInfo(BaseItem item, User? user, XmlWriter writer, StreamInfo? streamInfo)
-        {
-            if (!item.SupportsPositionTicksResume || item is Folder)
-            {
-                return;
-            }
-
-            XmlAttribute? secAttribute = null;
-            foreach (var attribute in _profile.XmlRootAttributes)
-            {
-                if (string.Equals(attribute.Name, "xmlns:sec", StringComparison.OrdinalIgnoreCase))
-                {
-                    secAttribute = attribute;
-                    break;
-                }
-            }
-
-            // Not a samsung device or no user data
-            if (secAttribute is null || user is null)
-            {
-                return;
-            }
-
-            var userdata = _userDataManager.GetUserData(user, item);
-            var playbackPositionTicks = (streamInfo is not null && streamInfo.StartPositionTicks > 0) ? streamInfo.StartPositionTicks : userdata.PlaybackPositionTicks;
-
-            if (playbackPositionTicks > 0)
-            {
-                var elementValue = string.Format(
-                    CultureInfo.InvariantCulture,
-                    "BM={0}",
-                    Convert.ToInt32(TimeSpan.FromTicks(playbackPositionTicks).TotalSeconds));
-                AddValue(writer, "sec", "dcmInfo", elementValue, secAttribute.Value);
-            }
-        }
-
-        /// <summary>
-        /// Adds fields used by both items and folders.
-        /// </summary>
-        private void AddCommonFields(BaseItem item, StubType? itemStubType, BaseItem? context, XmlWriter writer, Filter filter)
-        {
-            // Don't filter on dc:title because not all devices will include it in the filter
-            // MediaMonkey for example won't display content without a title
-            // if (filter.Contains("dc:title"))
-            {
-                AddValue(writer, "dc", "title", GetDisplayName(item, itemStubType, context), NsDc);
-            }
-
-            WriteObjectClass(writer, item, itemStubType);
-
-            if (filter.Contains("dc:date"))
-            {
-                if (item.PremiereDate.HasValue)
-                {
-                    AddValue(writer, "dc", "date", item.PremiereDate.Value.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture), NsDc);
-                }
-            }
-
-            if (filter.Contains("upnp:genre"))
-            {
-                foreach (var genre in item.Genres)
-                {
-                    AddValue(writer, "upnp", "genre", genre, NsUpnp);
-                }
-            }
-
-            foreach (var studio in item.Studios)
-            {
-                AddValue(writer, "upnp", "publisher", studio, NsUpnp);
-            }
-
-            if (item is not Folder)
-            {
-                if (filter.Contains("dc:description"))
-                {
-                    var desc = item.Overview;
-
-                    if (!string.IsNullOrWhiteSpace(desc))
-                    {
-                        AddValue(writer, "dc", "description", desc, NsDc);
-                    }
-                }
-
-                // if (filter.Contains("upnp:longDescription"))
-                // {
-                //    if (!string.IsNullOrWhiteSpace(item.Overview))
-                //    {
-                //        AddValue(writer, "upnp", "longDescription", item.Overview, NsUpnp);
-                //    }
-                // }
-            }
-
-            if (!string.IsNullOrEmpty(item.OfficialRating))
-            {
-                if (filter.Contains("dc:rating"))
-                {
-                    AddValue(writer, "dc", "rating", item.OfficialRating, NsDc);
-                }
-
-                if (filter.Contains("upnp:rating"))
-                {
-                    AddValue(writer, "upnp", "rating", item.OfficialRating, NsUpnp);
-                }
-            }
-
-            AddPeople(item, writer);
-        }
-
-        private void WriteObjectClass(XmlWriter writer, BaseItem item, StubType? stubType)
-        {
-            // More types here
-            // http://oss.linn.co.uk/repos/Public/LibUpnpCil/DidlLite/UpnpAv/Test/TestDidlLite.cs
-
-            writer.WriteStartElement("upnp", "class", NsUpnp);
-
-            if (item.IsDisplayedAsFolder || stubType.HasValue)
-            {
-                string? classType = null;
-
-                if (!_profile.RequiresPlainFolders)
-                {
-                    if (item is MusicAlbum)
-                    {
-                        classType = "object.container.album.musicAlbum";
-                    }
-                    else if (item is MusicArtist)
-                    {
-                        classType = "object.container.person.musicArtist";
-                    }
-                    else if (item is Series || item is Season || item is BoxSet || item is Video)
-                    {
-                        classType = "object.container.album.videoAlbum";
-                    }
-                    else if (item is Playlist)
-                    {
-                        classType = "object.container.playlistContainer";
-                    }
-                    else if (item is PhotoAlbum)
-                    {
-                        classType = "object.container.album.photoAlbum";
-                    }
-                }
-
-                writer.WriteString(classType ?? "object.container.storageFolder");
-            }
-            else if (item.MediaType == MediaType.Audio)
-            {
-                writer.WriteString("object.item.audioItem.musicTrack");
-            }
-            else if (item.MediaType == MediaType.Photo)
-            {
-                writer.WriteString("object.item.imageItem.photo");
-            }
-            else if (item.MediaType == MediaType.Video)
-            {
-                if (!_profile.RequiresPlainVideoItems && item is Movie)
-                {
-                    writer.WriteString("object.item.videoItem.movie");
-                }
-                else if (!_profile.RequiresPlainVideoItems && item is MusicVideo)
-                {
-                    writer.WriteString("object.item.videoItem.musicVideoClip");
-                }
-                else
-                {
-                    writer.WriteString("object.item.videoItem");
-                }
-            }
-            else if (item is MusicGenre)
-            {
-                writer.WriteString(_profile.RequiresPlainFolders ? "object.container.storageFolder" : "object.container.genre.musicGenre");
-            }
-            else if (item is Genre)
-            {
-                writer.WriteString(_profile.RequiresPlainFolders ? "object.container.storageFolder" : "object.container.genre");
-            }
-            else
-            {
-                writer.WriteString("object.item");
-            }
-
-            writer.WriteFullEndElement();
-        }
-
-        private void AddPeople(BaseItem item, XmlWriter writer)
-        {
-            if (!item.SupportsPeople)
-            {
-                return;
-            }
-
-            var types = new[]
-            {
-                PersonKind.Director,
-                PersonKind.Writer,
-                PersonKind.Producer,
-                PersonKind.Composer,
-                PersonKind.Creator
-            };
-
-            // Seeing some LG models locking up due content with large lists of people
-            // The actual issue might just be due to processing a more metadata than it can handle
-            var people = _libraryManager.GetPeople(
-                new InternalPeopleQuery
-                {
-                    ItemId = item.Id,
-                    Limit = 6
-                });
-
-            foreach (var actor in people)
-            {
-                var type = types.FirstOrDefault(i => i == actor.Type || string.Equals(actor.Role, i.ToString(), StringComparison.OrdinalIgnoreCase));
-                if (type == PersonKind.Unknown)
-                {
-                    type = PersonKind.Actor;
-                }
-
-                AddValue(writer, "upnp", type.ToString().ToLowerInvariant(), actor.Name, NsUpnp);
-            }
-        }
-
-        private void AddGeneralProperties(BaseItem item, StubType? itemStubType, BaseItem? context, XmlWriter writer, Filter filter)
-        {
-            AddCommonFields(item, itemStubType, context, writer, filter);
-
-            var hasAlbumArtists = item as IHasAlbumArtist;
-
-            if (item is IHasArtist hasArtists)
-            {
-                foreach (var artist in hasArtists.Artists)
-                {
-                    AddValue(writer, "upnp", "artist", artist, NsUpnp);
-                    AddValue(writer, "dc", "creator", artist, NsDc);
-
-                    // If it doesn't support album artists (musicvideo), then tag as both
-                    if (hasAlbumArtists is null)
-                    {
-                        AddAlbumArtist(writer, artist);
-                    }
-                }
-            }
-
-            if (hasAlbumArtists is not null)
-            {
-                foreach (var albumArtist in hasAlbumArtists.AlbumArtists)
-                {
-                    AddAlbumArtist(writer, albumArtist);
-                }
-            }
-
-            if (!string.IsNullOrWhiteSpace(item.Album))
-            {
-                AddValue(writer, "upnp", "album", item.Album, NsUpnp);
-            }
-
-            if (item.IndexNumber.HasValue)
-            {
-                AddValue(writer, "upnp", "originalTrackNumber", item.IndexNumber.Value.ToString(CultureInfo.InvariantCulture), NsUpnp);
-
-                if (item is Episode)
-                {
-                    AddValue(writer, "upnp", "episodeNumber", item.IndexNumber.Value.ToString(CultureInfo.InvariantCulture), NsUpnp);
-                }
-            }
-        }
-
-        private void AddAlbumArtist(XmlWriter writer, string name)
-        {
-            try
-            {
-                writer.WriteStartElement("upnp", "artist", NsUpnp);
-                writer.WriteAttributeString("role", "AlbumArtist");
-
-                writer.WriteString(name);
-
-                writer.WriteFullEndElement();
-            }
-            catch (XmlException ex)
-            {
-                _logger.LogError(ex, "Error adding xml value: {Value}", name);
-            }
-        }
-
-        private void AddValue(XmlWriter writer, string prefix, string name, string value, string namespaceUri)
-        {
-            try
-            {
-                writer.WriteElementString(prefix, name, namespaceUri, value);
-            }
-            catch (XmlException ex)
-            {
-                _logger.LogError(ex, "Error adding xml value: {Value}", value);
-            }
-        }
-
-        private void AddCover(BaseItem item, StubType? stubType, XmlWriter writer)
-        {
-            ImageDownloadInfo? imageInfo = GetImageInfo(item);
-
-            if (imageInfo is null)
-            {
-                return;
-            }
-
-            // TODO: Remove these default values
-            var albumArtUrlInfo = GetImageUrl(
-                imageInfo,
-                _profile.MaxAlbumArtWidth ?? 10000,
-                _profile.MaxAlbumArtHeight ?? 10000,
-                "jpg");
-
-            writer.WriteStartElement("upnp", "albumArtURI", NsUpnp);
-            if (!string.IsNullOrEmpty(_profile.AlbumArtPn))
-            {
-                writer.WriteAttributeString("dlna", "profileID", NsDlna, _profile.AlbumArtPn);
-            }
-
-            writer.WriteString(albumArtUrlInfo.Url);
-            writer.WriteFullEndElement();
-
-            // TODO: Remove these default values
-            var iconUrlInfo = GetImageUrl(
-                imageInfo,
-                _profile.MaxIconWidth ?? 48,
-                _profile.MaxIconHeight ?? 48,
-                "jpg");
-            writer.WriteElementString("upnp", "icon", NsUpnp, iconUrlInfo.Url);
-
-            if (!_profile.EnableAlbumArtInDidl)
-            {
-                if (item.MediaType == MediaType.Audio || item.MediaType == MediaType.Video)
-                {
-                    if (!stubType.HasValue)
-                    {
-                        return;
-                    }
-                }
-            }
-
-            if (!_profile.EnableSingleAlbumArtLimit || item.MediaType == MediaType.Photo)
-            {
-                AddImageResElement(item, writer, 4096, 4096, "jpg", "JPEG_LRG");
-                AddImageResElement(item, writer, 1024, 768, "jpg", "JPEG_MED");
-                AddImageResElement(item, writer, 640, 480, "jpg", "JPEG_SM");
-                AddImageResElement(item, writer, 4096, 4096, "png", "PNG_LRG");
-                AddImageResElement(item, writer, 160, 160, "png", "PNG_TN");
-            }
-
-            AddImageResElement(item, writer, 160, 160, "jpg", "JPEG_TN");
-        }
-
-        private void AddImageResElement(
-            BaseItem item,
-            XmlWriter writer,
-            int maxWidth,
-            int maxHeight,
-            string format,
-            string org_Pn)
-        {
-            var imageInfo = GetImageInfo(item);
-
-            if (imageInfo is null)
-            {
-                return;
-            }
-
-            var albumartUrlInfo = GetImageUrl(imageInfo, maxWidth, maxHeight, format);
-
-            writer.WriteStartElement(string.Empty, "res", NsDidl);
-
-            // 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 contentFeatures = ContentFeatureBuilder.BuildImageHeader(_profile, format, width, height, imageInfo.IsDirectStream, org_Pn);
-
-            writer.WriteAttributeString(
-                "protocolInfo",
-                string.Format(
-                    CultureInfo.InvariantCulture,
-                    "http-get:*:{0}:{1}",
-                    MimeTypes.GetMimeType("file." + format),
-                    contentFeatures));
-
-            writer.WriteAttributeString(
-                "resolution",
-                string.Format(CultureInfo.InvariantCulture, "{0}x{1}", width, height));
-
-            writer.WriteString(albumartUrlInfo.Url);
-
-            writer.WriteFullEndElement();
-        }
-
-        private ImageDownloadInfo? GetImageInfo(BaseItem item)
-        {
-            if (item.HasImage(ImageType.Primary))
-            {
-                return GetImageInfo(item, ImageType.Primary);
-            }
-
-            if (item.HasImage(ImageType.Thumb))
-            {
-                return GetImageInfo(item, ImageType.Thumb);
-            }
-
-            if (item.HasImage(ImageType.Backdrop))
-            {
-                if (item is Channel)
-                {
-                    return GetImageInfo(item, ImageType.Backdrop);
-                }
-            }
-
-            // For audio tracks without art use album art if available.
-            if (item is Audio audioItem)
-            {
-                var album = audioItem.AlbumEntity;
-                return album is not null && album.HasImage(ImageType.Primary)
-                    ? GetImageInfo(album, ImageType.Primary)
-                    : null;
-            }
-
-            // Don't look beyond album/playlist level. Metadata service may assign an image from a different album/show to the parent folder.
-            if (item is MusicAlbum || item is Playlist)
-            {
-                return null;
-            }
-
-            // For other item types check parents, but be aware that image retrieved from a parent may be not suitable for this media item.
-            var parentWithImage = GetFirstParentWithImageBelowUserRoot(item);
-            if (parentWithImage is not null)
-            {
-                return GetImageInfo(parentWithImage, ImageType.Primary);
-            }
-
-            return null;
-        }
-
-        private BaseItem? GetFirstParentWithImageBelowUserRoot(BaseItem item)
-        {
-            if (item is null)
-            {
-                return null;
-            }
-
-            if (item.HasImage(ImageType.Primary))
-            {
-                return item;
-            }
-
-            var parent = item.GetParent();
-            if (parent is UserRootFolder)
-            {
-                return null;
-            }
-
-            // terminate in case we went past user root folder (unlikely?)
-            if (parent is Folder folder && folder.IsRoot)
-            {
-                return null;
-            }
-
-            return GetFirstParentWithImageBelowUserRoot(parent);
-        }
-
-        private ImageDownloadInfo GetImageInfo(BaseItem item, ImageType type)
-        {
-            var imageInfo = item.GetImageInfo(type, 0);
-            string? tag = null;
-
-            try
-            {
-                tag = _imageProcessor.GetImageCacheTag(item, type);
-            }
-            catch (Exception ex)
-            {
-                _logger.LogError(ex, "Error getting image cache tag");
-            }
-
-            int? width = imageInfo.Width;
-            int? height = imageInfo.Height;
-
-            if (width == 0 || height == 0)
-            {
-                width = null;
-                height = null;
-            }
-            else if (width == -1 || height == -1)
-            {
-                width = null;
-                height = null;
-            }
-
-            var inputFormat = (Path.GetExtension(imageInfo.Path) ?? string.Empty)
-                .TrimStart('.')
-                .Replace("jpeg", "jpg", StringComparison.OrdinalIgnoreCase);
-
-            return new ImageDownloadInfo
-            {
-                ItemId = item.Id,
-                Type = type,
-                ImageTag = tag,
-                Width = width,
-                Height = height,
-                Format = inputFormat,
-                ItemImageInfo = imageInfo
-            };
-        }
-
-        public static string GetClientId(BaseItem item, StubType? stubType)
-        {
-            return GetClientId(item.Id, stubType);
-        }
-
-        public static string GetClientId(Guid idValue, StubType? stubType)
-        {
-            var id = idValue.ToString("N", CultureInfo.InvariantCulture);
-
-            if (stubType.HasValue)
-            {
-                id = stubType.Value.ToString().ToLowerInvariant() + "_" + id;
-            }
-
-            return id;
-        }
-
-        private (string Url, int? Width, int? Height) GetImageUrl(ImageDownloadInfo info, int maxWidth, int maxHeight, string format)
-        {
-            var url = string.Format(
-                CultureInfo.InvariantCulture,
-                "{0}/Items/{1}/Images/{2}/0/{3}/{4}/{5}/{6}/0/0",
-                _serverAddress,
-                info.ItemId.ToString("N", CultureInfo.InvariantCulture),
-                info.Type,
-                info.ImageTag,
-                format,
-                maxWidth.ToString(CultureInfo.InvariantCulture),
-                maxHeight.ToString(CultureInfo.InvariantCulture));
-
-            var width = info.Width;
-            var height = info.Height;
-
-            info.IsDirectStream = false;
-
-            if (width.HasValue && height.HasValue)
-            {
-                var newSize = DrawingUtils.Resize(new ImageDimensions(width.Value, height.Value), 0, 0, maxWidth, maxHeight);
-
-                width = newSize.Width;
-                height = newSize.Height;
-
-                var normalizedFormat = format
-                    .Replace("jpeg", "jpg", StringComparison.OrdinalIgnoreCase);
-
-                if (string.Equals(info.Format, normalizedFormat, StringComparison.OrdinalIgnoreCase))
-                {
-                    info.IsDirectStream = maxWidth >= width.Value && maxHeight >= height.Value;
-                }
-            }
-
-            // just lie
-            info.IsDirectStream = true;
-
-            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 required string Format { get; set; }
-
-            internal required ItemImageInfo ItemImageInfo { get; set; }
-        }
-    }
-}

+ 0 - 28
Emby.Dlna/Didl/Filter.cs

@@ -1,28 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-
-namespace Emby.Dlna.Didl
-{
-    public class Filter
-    {
-        private readonly string[] _fields;
-        private readonly bool _all;
-
-        public Filter()
-            : this("*")
-        {
-        }
-
-        public Filter(string filter)
-        {
-            _all = string.Equals(filter, "*", StringComparison.OrdinalIgnoreCase);
-            _fields = filter.Split(',', StringSplitOptions.RemoveEmptyEntries);
-        }
-
-        public bool Contains(string field)
-        {
-            return _all || Array.Exists(_fields, x => x.Equals(field, StringComparison.OrdinalIgnoreCase));
-        }
-    }
-}

+ 0 - 58
Emby.Dlna/Didl/StringWriterWithEncoding.cs

@@ -1,58 +0,0 @@
-#pragma warning disable CS1591
-#pragma warning disable CA1305
-
-using System;
-using System.IO;
-using System.Text;
-
-namespace Emby.Dlna.Didl
-{
-    public class StringWriterWithEncoding : StringWriter
-    {
-        private readonly Encoding? _encoding;
-
-        public StringWriterWithEncoding()
-        {
-        }
-
-        public StringWriterWithEncoding(IFormatProvider formatProvider)
-            : base(formatProvider)
-        {
-        }
-
-        public StringWriterWithEncoding(StringBuilder sb)
-            : base(sb)
-        {
-        }
-
-        public StringWriterWithEncoding(StringBuilder sb, IFormatProvider formatProvider)
-            : base(sb, formatProvider)
-        {
-        }
-
-        public StringWriterWithEncoding(Encoding encoding)
-        {
-            _encoding = encoding;
-        }
-
-        public StringWriterWithEncoding(IFormatProvider formatProvider, Encoding encoding)
-            : base(formatProvider)
-        {
-            _encoding = encoding;
-        }
-
-        public StringWriterWithEncoding(StringBuilder sb, Encoding encoding)
-            : base(sb)
-        {
-            _encoding = encoding;
-        }
-
-        public StringWriterWithEncoding(StringBuilder sb, IFormatProvider formatProvider, Encoding encoding)
-            : base(sb, formatProvider)
-        {
-            _encoding = encoding;
-        }
-
-        public override Encoding Encoding => _encoding ?? base.Encoding;
-    }
-}

+ 0 - 23
Emby.Dlna/DlnaConfigurationFactory.cs

@@ -1,23 +0,0 @@
-#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<ConfigurationStore> GetConfigurations()
-        {
-            return new[]
-            {
-                new ConfigurationStore
-                {
-                    Key = "dlna",
-                    ConfigurationType = typeof(DlnaOptions)
-                }
-            };
-        }
-    }
-}

+ 0 - 491
Emby.Dlna/DlnaManager.cs

@@ -1,491 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Reflection;
-using System.Text.Json;
-using System.Text.RegularExpressions;
-using System.Threading.Tasks;
-using Emby.Dlna.Profiles;
-using Emby.Dlna.Server;
-using Jellyfin.Extensions.Json;
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Dlna;
-using MediaBrowser.Controller.Drawing;
-using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.Drawing;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Serialization;
-using Microsoft.AspNetCore.Http;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Primitives;
-
-namespace Emby.Dlna
-{
-    public class DlnaManager : IDlnaManager
-    {
-        private readonly IApplicationPaths _appPaths;
-        private readonly IXmlSerializer _xmlSerializer;
-        private readonly IFileSystem _fileSystem;
-        private readonly ILogger<DlnaManager> _logger;
-        private readonly IServerApplicationHost _appHost;
-        private static readonly Assembly _assembly = typeof(DlnaManager).Assembly;
-        private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options;
-
-        private readonly Dictionary<string, Tuple<InternalProfileInfo, DeviceProfile>> _profiles = new Dictionary<string, Tuple<InternalProfileInfo, DeviceProfile>>(StringComparer.Ordinal);
-
-        public DlnaManager(
-            IXmlSerializer xmlSerializer,
-            IFileSystem fileSystem,
-            IApplicationPaths appPaths,
-            ILoggerFactory loggerFactory,
-            IServerApplicationHost appHost)
-        {
-            _xmlSerializer = xmlSerializer;
-            _fileSystem = fileSystem;
-            _appPaths = appPaths;
-            _logger = loggerFactory.CreateLogger<DlnaManager>();
-            _appHost = appHost;
-        }
-
-        private string UserProfilesPath => Path.Combine(_appPaths.ConfigurationDirectoryPath, "dlna", "user");
-
-        private string SystemProfilesPath => Path.Combine(_appPaths.ConfigurationDirectoryPath, "dlna", "system");
-
-        public async Task InitProfilesAsync()
-        {
-            try
-            {
-                await ExtractSystemProfilesAsync().ConfigureAwait(false);
-                Directory.CreateDirectory(UserProfilesPath);
-                LoadProfiles();
-            }
-            catch (Exception ex)
-            {
-                _logger.LogError(ex, "Error extracting DLNA profiles.");
-            }
-        }
-
-        private void LoadProfiles()
-        {
-            var list = GetProfiles(UserProfilesPath, DeviceProfileType.User)
-                .OrderBy(i => i.Name)
-                .ToList();
-
-            list.AddRange(GetProfiles(SystemProfilesPath, DeviceProfileType.System)
-                .OrderBy(i => i.Name));
-        }
-
-        public IEnumerable<DeviceProfile> GetProfiles()
-        {
-            lock (_profiles)
-            {
-                return _profiles.Values
-                    .OrderBy(i => i.Item1.Info.Type == DeviceProfileType.User ? 0 : 1)
-                    .ThenBy(i => i.Item1.Info.Name)
-                    .Select(i => i.Item2)
-                    .ToList();
-            }
-        }
-
-        /// <inheritdoc />
-        public DeviceProfile GetDefaultProfile()
-        {
-            return new DefaultProfile();
-        }
-
-        /// <inheritdoc />
-        public DeviceProfile? GetProfile(DeviceIdentification deviceInfo)
-        {
-            ArgumentNullException.ThrowIfNull(deviceInfo);
-
-            var profile = GetProfiles()
-                .FirstOrDefault(i => i.Identification is not null && IsMatch(deviceInfo, i.Identification));
-
-            if (profile is null)
-            {
-                _logger.LogInformation("No matching device profile found. The default will need to be used. \n{@Profile}", deviceInfo);
-            }
-            else
-            {
-                _logger.LogDebug("Found matching device profile: {ProfileName}", profile.Name);
-            }
-
-            return profile;
-        }
-
-        /// <summary>
-        /// Attempts to match a device with a profile.
-        /// Rules:
-        /// - If the profile field has no value, the field matches regardless of its contents.
-        /// - the profile field can be an exact match, or a reg exp.
-        /// </summary>
-        /// <param name="deviceInfo">The <see cref="DeviceIdentification"/> of the device.</param>
-        /// <param name="profileInfo">The <see cref="DeviceIdentification"/> of the profile.</param>
-        /// <returns><b>True</b> if they match.</returns>
-        public bool IsMatch(DeviceIdentification deviceInfo, DeviceIdentification profileInfo)
-        {
-            return IsRegexOrSubstringMatch(deviceInfo.FriendlyName, profileInfo.FriendlyName)
-                && IsRegexOrSubstringMatch(deviceInfo.Manufacturer, profileInfo.Manufacturer)
-                && IsRegexOrSubstringMatch(deviceInfo.ManufacturerUrl, profileInfo.ManufacturerUrl)
-                && IsRegexOrSubstringMatch(deviceInfo.ModelDescription, profileInfo.ModelDescription)
-                && IsRegexOrSubstringMatch(deviceInfo.ModelName, profileInfo.ModelName)
-                && IsRegexOrSubstringMatch(deviceInfo.ModelNumber, profileInfo.ModelNumber)
-                && IsRegexOrSubstringMatch(deviceInfo.ModelUrl, profileInfo.ModelUrl)
-                && IsRegexOrSubstringMatch(deviceInfo.SerialNumber, profileInfo.SerialNumber);
-        }
-
-        private bool IsRegexOrSubstringMatch(string input, string pattern)
-        {
-            if (string.IsNullOrEmpty(pattern))
-            {
-                // In profile identification: An empty pattern matches anything.
-                return true;
-            }
-
-            if (string.IsNullOrEmpty(input))
-            {
-                // The profile contains a value, and the device doesn't.
-                return false;
-            }
-
-            try
-            {
-                return input.Equals(pattern, StringComparison.OrdinalIgnoreCase)
-                    || Regex.IsMatch(input, pattern, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
-            }
-            catch (ArgumentException ex)
-            {
-                _logger.LogError(ex, "Error evaluating regex pattern {Pattern}", pattern);
-                return false;
-            }
-        }
-
-        /// <inheritdoc />
-        public DeviceProfile? GetProfile(IHeaderDictionary headers)
-        {
-            ArgumentNullException.ThrowIfNull(headers);
-
-            var profile = GetProfiles().FirstOrDefault(i => i.Identification is not null && IsMatch(headers, i.Identification));
-            if (profile is null)
-            {
-                _logger.LogDebug("No matching device profile found. {@Headers}", headers);
-            }
-            else
-            {
-                _logger.LogDebug("Found matching device profile: {0}", profile.Name);
-            }
-
-            return profile;
-        }
-
-        private bool IsMatch(IHeaderDictionary headers, DeviceIdentification profileInfo)
-        {
-            return profileInfo.Headers.Any(i => IsMatch(headers, i));
-        }
-
-        private bool IsMatch(IHeaderDictionary headers, HttpHeaderInfo header)
-        {
-            // Handle invalid user setup
-            if (string.IsNullOrEmpty(header.Name))
-            {
-                return false;
-            }
-
-            if (headers.TryGetValue(header.Name, out StringValues value))
-            {
-                if (StringValues.IsNullOrEmpty(value))
-                {
-                    return false;
-                }
-
-                switch (header.Match)
-                {
-                    case HeaderMatchType.Equals:
-                        return string.Equals(value, header.Value, StringComparison.OrdinalIgnoreCase);
-                    case HeaderMatchType.Substring:
-                        var isMatch = value.ToString().IndexOf(header.Value, StringComparison.OrdinalIgnoreCase) != -1;
-                        // _logger.LogDebug("IsMatch-Substring value: {0} testValue: {1} isMatch: {2}", value, header.Value, isMatch);
-                        return isMatch;
-                    case HeaderMatchType.Regex:
-                        // Can't be null, we checked above the switch statement
-                        return Regex.IsMatch(value!, header.Value, RegexOptions.IgnoreCase);
-                    default:
-                        throw new ArgumentException("Unrecognized HeaderMatchType");
-                }
-            }
-
-            return false;
-        }
-
-        private IEnumerable<DeviceProfile> GetProfiles(string path, DeviceProfileType type)
-        {
-            try
-            {
-                return _fileSystem.GetFilePaths(path)
-                    .Where(i => Path.GetExtension(i.AsSpan()).Equals(".xml", StringComparison.OrdinalIgnoreCase))
-                    .Select(i => ParseProfileFile(i, type))
-                    .Where(i => i is not null)
-                    .ToList()!; // We just filtered out all the nulls
-            }
-            catch (IOException)
-            {
-                return Array.Empty<DeviceProfile>();
-            }
-        }
-
-        private DeviceProfile? ParseProfileFile(string path, DeviceProfileType type)
-        {
-            lock (_profiles)
-            {
-                if (_profiles.TryGetValue(path, out Tuple<InternalProfileInfo, DeviceProfile>? profileTuple))
-                {
-                    return profileTuple.Item2;
-                }
-
-                try
-                {
-                    var tempProfile = (DeviceProfile)_xmlSerializer.DeserializeFromFile(typeof(DeviceProfile), path);
-                    var profile = ReserializeProfile(tempProfile);
-
-                    profile.Id = path.ToLowerInvariant().GetMD5().ToString("N", CultureInfo.InvariantCulture);
-
-                    _profiles[path] = new Tuple<InternalProfileInfo, DeviceProfile>(GetInternalProfileInfo(_fileSystem.GetFileInfo(path), type), profile);
-
-                    return profile;
-                }
-                catch (Exception ex)
-                {
-                    _logger.LogError(ex, "Error parsing profile file: {Path}", path);
-
-                    return null;
-                }
-            }
-        }
-
-        /// <inheritdoc />
-        public DeviceProfile? GetProfile(string id)
-        {
-            ArgumentException.ThrowIfNullOrEmpty(id);
-
-            var info = GetProfileInfosInternal().FirstOrDefault(i => string.Equals(i.Info.Id, id, StringComparison.OrdinalIgnoreCase));
-
-            if (info is null)
-            {
-                return null;
-            }
-
-            return ParseProfileFile(info.Path, info.Info.Type);
-        }
-
-        private IEnumerable<InternalProfileInfo> GetProfileInfosInternal()
-        {
-            lock (_profiles)
-            {
-                return _profiles.Values
-                    .Select(i => i.Item1)
-                    .OrderBy(i => i.Info.Type == DeviceProfileType.User ? 0 : 1)
-                    .ThenBy(i => i.Info.Name);
-            }
-        }
-
-        /// <inheritdoc />
-        public IEnumerable<DeviceProfileInfo> GetProfileInfos()
-        {
-            return GetProfileInfosInternal().Select(i => i.Info);
-        }
-
-        private InternalProfileInfo GetInternalProfileInfo(FileSystemMetadata file, DeviceProfileType type)
-        {
-            return new InternalProfileInfo(
-                new DeviceProfileInfo
-                {
-                    Id = file.FullName.ToLowerInvariant().GetMD5().ToString("N", CultureInfo.InvariantCulture),
-                    Name = _fileSystem.GetFileNameWithoutExtension(file),
-                    Type = type
-                },
-                file.FullName);
-        }
-
-        private async Task ExtractSystemProfilesAsync()
-        {
-            var namespaceName = GetType().Namespace + ".Profiles.Xml.";
-
-            var systemProfilesPath = SystemProfilesPath;
-
-            foreach (var name in _assembly.GetManifestResourceNames())
-            {
-                if (!name.StartsWith(namespaceName, StringComparison.Ordinal))
-                {
-                    continue;
-                }
-
-                var path = Path.Join(
-                    systemProfilesPath,
-                    Path.GetFileName(name.AsSpan())[namespaceName.Length..]);
-
-                if (File.Exists(path))
-                {
-                    continue;
-                }
-
-                // The stream should exist as we just got its name from GetManifestResourceNames
-                using (var stream = _assembly.GetManifestResourceStream(name)!)
-                {
-                    Directory.CreateDirectory(systemProfilesPath);
-
-                    var fileOptions = AsyncFile.WriteOptions;
-                    fileOptions.Mode = FileMode.CreateNew;
-                    fileOptions.PreallocationSize = stream.Length;
-                    var fileStream = new FileStream(path, fileOptions);
-                    await using (fileStream.ConfigureAwait(false))
-                    {
-                        await stream.CopyToAsync(fileStream).ConfigureAwait(false);
-                    }
-                }
-            }
-        }
-
-        /// <inheritdoc />
-        public void DeleteProfile(string id)
-        {
-            var info = GetProfileInfosInternal().First(i => string.Equals(id, i.Info.Id, StringComparison.OrdinalIgnoreCase));
-
-            if (info.Info.Type == DeviceProfileType.System)
-            {
-                throw new ArgumentException("System profiles cannot be deleted.");
-            }
-
-            _fileSystem.DeleteFile(info.Path);
-
-            lock (_profiles)
-            {
-                _profiles.Remove(info.Path);
-            }
-        }
-
-        /// <inheritdoc />
-        public void CreateProfile(DeviceProfile profile)
-        {
-            profile = ReserializeProfile(profile);
-
-            ArgumentException.ThrowIfNullOrEmpty(profile.Name);
-
-            var newFilename = _fileSystem.GetValidFilename(profile.Name) + ".xml";
-            var path = Path.Combine(UserProfilesPath, newFilename);
-
-            SaveProfile(profile, path, DeviceProfileType.User);
-        }
-
-        /// <inheritdoc />
-        public void UpdateProfile(string profileId, DeviceProfile profile)
-        {
-            profile = ReserializeProfile(profile);
-
-            ArgumentException.ThrowIfNullOrEmpty(profile.Id);
-
-            ArgumentException.ThrowIfNullOrEmpty(profile.Name);
-
-            var current = GetProfileInfosInternal().First(i => string.Equals(i.Info.Id, profileId, StringComparison.OrdinalIgnoreCase));
-            if (current.Info.Type == DeviceProfileType.System)
-            {
-                throw new ArgumentException("System profiles can't be edited");
-            }
-
-            var newFilename = _fileSystem.GetValidFilename(profile.Name) + ".xml";
-            var path = Path.Join(UserProfilesPath, newFilename);
-
-            if (!string.Equals(path, current.Path, StringComparison.Ordinal))
-            {
-                lock (_profiles)
-                {
-                    _profiles.Remove(current.Path);
-                }
-            }
-
-            SaveProfile(profile, path, DeviceProfileType.User);
-        }
-
-        private void SaveProfile(DeviceProfile profile, string path, DeviceProfileType type)
-        {
-            lock (_profiles)
-            {
-                _profiles[path] = new Tuple<InternalProfileInfo, DeviceProfile>(GetInternalProfileInfo(_fileSystem.GetFileInfo(path), type), profile);
-            }
-
-            SerializeToXml(profile, path);
-        }
-
-        internal void SerializeToXml(DeviceProfile profile, string path)
-        {
-            _xmlSerializer.SerializeToFile(profile, path);
-        }
-
-        /// <summary>
-        /// Recreates the object using serialization, to ensure it's not a subclass.
-        /// If it's a subclass it may not serialize properly to xml (different root element tag name).
-        /// </summary>
-        /// <param name="profile">The device profile.</param>
-        /// <returns>The re-serialized device profile.</returns>
-        private DeviceProfile ReserializeProfile(DeviceProfile profile)
-        {
-            if (profile.GetType() == typeof(DeviceProfile))
-            {
-                return profile;
-            }
-
-            var json = JsonSerializer.Serialize(profile, _jsonOptions);
-
-            // Output can't be null if the input isn't null
-            return JsonSerializer.Deserialize<DeviceProfile>(json, _jsonOptions)!;
-        }
-
-        /// <inheritdoc />
-        public string GetServerDescriptionXml(IHeaderDictionary headers, string serverUuId, string serverAddress)
-        {
-            var profile = GetProfile(headers) ?? GetDefaultProfile();
-
-            var serverId = _appHost.SystemId;
-
-            return new DescriptionXmlBuilder(profile, serverUuId, serverAddress, _appHost.FriendlyName, serverId).GetXml();
-        }
-
-        /// <inheritdoc />
-        public ImageStream? GetIcon(string filename)
-        {
-            var format = filename.EndsWith(".png", StringComparison.OrdinalIgnoreCase)
-                ? ImageFormat.Png
-                : ImageFormat.Jpg;
-
-            var resource = GetType().Namespace + ".Images." + filename.ToLowerInvariant();
-            var stream = _assembly.GetManifestResourceStream(resource);
-            if (stream is null)
-            {
-                return null;
-            }
-
-            return new ImageStream(stream)
-            {
-                Format = format
-            };
-        }
-
-        private class InternalProfileInfo
-        {
-            internal InternalProfileInfo(DeviceProfileInfo info, string path)
-            {
-                Info = info;
-                Path = path;
-            }
-
-            internal DeviceProfileInfo Info { get; }
-
-            internal string Path { get; }
-        }
-    }
-}

+ 0 - 90
Emby.Dlna/Emby.Dlna.csproj

@@ -1,90 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-
-  <!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
-  <PropertyGroup>
-    <ProjectGuid>{805844AB-E92F-45E6-9D99-4F6D48D129A5}</ProjectGuid>
-  </PropertyGroup>
-
-  <ItemGroup>
-    <Compile Include="..\SharedVersion.cs" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
-    <ProjectReference Include="..\MediaBrowser.Controller\MediaBrowser.Controller.csproj" />
-    <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj" />
-    <ProjectReference Include="..\RSSDP\RSSDP.csproj" />
-  </ItemGroup>
-
-  <PropertyGroup>
-    <TargetFramework>net8.0</TargetFramework>
-    <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
-    <GenerateDocumentationFile>true</GenerateDocumentationFile>
-  </PropertyGroup>
-
-  <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
-    <CodeAnalysisTreatWarningsAsErrors>false</CodeAnalysisTreatWarningsAsErrors>
-  </PropertyGroup>
-
-  <!-- Code Analyzers -->
-  <ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
-    <PackageReference Include="IDisposableAnalyzers">
-      <PrivateAssets>all</PrivateAssets>
-      <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
-    </PackageReference>
-    <PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers">
-      <PrivateAssets>all</PrivateAssets>
-      <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
-    </PackageReference>
-    <PackageReference Include="SerilogAnalyzer" PrivateAssets="All" />
-    <PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />
-    <PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" PrivateAssets="All" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <EmbeddedResource Include="Images\logo120.jpg" />
-    <EmbeddedResource Include="Images\logo120.png" />
-    <EmbeddedResource Include="Images\logo240.jpg" />
-    <EmbeddedResource Include="Images\logo240.png" />
-    <EmbeddedResource Include="Images\logo48.jpg" />
-    <EmbeddedResource Include="Images\logo48.png" />
-    <EmbeddedResource Include="Images\people48.jpg" />
-    <EmbeddedResource Include="Images\people48.png" />
-    <EmbeddedResource Include="Images\people480.jpg" />
-    <EmbeddedResource Include="Images\people480.png" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <EmbeddedResource Include="Profiles\Xml\Default.xml" />
-    <EmbeddedResource Include="Profiles\Xml\Denon AVR.xml" />
-    <EmbeddedResource Include="Profiles\Xml\DirecTV HD-DVR.xml" />
-    <EmbeddedResource Include="Profiles\Xml\Dish Hopper-Joey.xml" />
-    <EmbeddedResource Include="Profiles\Xml\foobar2000.xml" />
-    <EmbeddedResource Include="Profiles\Xml\LG Smart TV.xml" />
-    <EmbeddedResource Include="Profiles\Xml\Linksys DMA2100.xml" />
-    <EmbeddedResource Include="Profiles\Xml\Marantz.xml" />
-    <EmbeddedResource Include="Profiles\Xml\MediaMonkey.xml" />
-    <EmbeddedResource Include="Profiles\Xml\Panasonic Viera.xml" />
-    <EmbeddedResource Include="Profiles\Xml\Popcorn Hour.xml" />
-    <EmbeddedResource Include="Profiles\Xml\Samsung Smart TV.xml" />
-    <EmbeddedResource Include="Profiles\Xml\Sony Blu-ray Player 2013.xml" />
-    <EmbeddedResource Include="Profiles\Xml\Sony Blu-ray Player 2014.xml" />
-    <EmbeddedResource Include="Profiles\Xml\Sony Blu-ray Player 2015.xml" />
-    <EmbeddedResource Include="Profiles\Xml\Sony Blu-ray Player 2016.xml" />
-    <EmbeddedResource Include="Profiles\Xml\Sony Blu-ray Player.xml" />
-    <EmbeddedResource Include="Profiles\Xml\Sony Bravia %282010%29.xml" />
-    <EmbeddedResource Include="Profiles\Xml\Sony Bravia %282011%29.xml" />
-    <EmbeddedResource Include="Profiles\Xml\Sony Bravia %282012%29.xml" />
-    <EmbeddedResource Include="Profiles\Xml\Sony Bravia %282013%29.xml" />
-    <EmbeddedResource Include="Profiles\Xml\Sony Bravia %282014%29.xml" />
-    <EmbeddedResource Include="Profiles\Xml\Sony PlayStation 3.xml" />
-    <EmbeddedResource Include="Profiles\Xml\Sony PlayStation 4.xml" />
-    <EmbeddedResource Include="Profiles\Xml\WDTV Live.xml" />
-    <EmbeddedResource Include="Profiles\Xml\Xbox One.xml" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <PackageReference Include="Microsoft.Extensions.Http" />
-  </ItemGroup>
-
-</Project>

+ 0 - 22
Emby.Dlna/EventSubscriptionResponse.cs

@@ -1,22 +0,0 @@
-#pragma warning disable CS1591
-
-using System.Collections.Generic;
-
-namespace Emby.Dlna
-{
-    public class EventSubscriptionResponse
-    {
-        public EventSubscriptionResponse(string content, string contentType)
-        {
-            Content = content;
-            ContentType = contentType;
-            Headers = new Dictionary<string, string>();
-        }
-
-        public string Content { get; set; }
-
-        public string ContentType { get; set; }
-
-        public Dictionary<string, string> Headers { get; }
-    }
-}

+ 0 - 183
Emby.Dlna/Eventing/DlnaEventManager.cs

@@ -1,183 +0,0 @@
-#nullable disable
-
-#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.Net.Mime;
-using System.Text;
-using System.Threading.Tasks;
-using Jellyfin.Extensions;
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.Net;
-using Microsoft.Extensions.Logging;
-
-namespace Emby.Dlna.Eventing
-{
-    public class DlnaEventManager : IDlnaEventManager
-    {
-        private readonly ConcurrentDictionary<string, EventSubscription> _subscriptions =
-            new ConcurrentDictionary<string, EventSubscription>(StringComparer.OrdinalIgnoreCase);
-
-        private readonly ILogger _logger;
-        private readonly IHttpClientFactory _httpClientFactory;
-
-        public DlnaEventManager(ILogger logger, IHttpClientFactory httpClientFactory)
-        {
-            _httpClientFactory = httpClientFactory;
-            _logger = logger;
-        }
-
-        public EventSubscriptionResponse RenewEventSubscription(string subscriptionId, string notificationType, string requestedTimeoutString, string callbackUrl)
-        {
-            var subscription = GetSubscription(subscriptionId, false);
-            if (subscription is not 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(string.Empty, "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,
-                NotificationType = notificationType
-            });
-
-            return GetEventSubscriptionResponse(id, requestedTimeoutString, timeout);
-        }
-
-        private int? ParseTimeout(string header)
-        {
-            if (!string.IsNullOrEmpty(header))
-            {
-                // Starts with SECOND-
-                if (int.TryParse(header.AsSpan().RightPart('-'), NumberStyles.Integer, CultureInfo.InvariantCulture, out var val))
-                {
-                    return val;
-                }
-            }
-
-            return null;
-        }
-
-        public EventSubscriptionResponse CancelEventSubscription(string subscriptionId)
-        {
-            _logger.LogDebug("Cancelling event subscription {0}", subscriptionId);
-
-            _subscriptions.TryRemove(subscriptionId, out _);
-
-            return new EventSubscriptionResponse(string.Empty, "text/plain");
-        }
-
-        private EventSubscriptionResponse GetEventSubscriptionResponse(string subscriptionId, string requestedTimeoutString, int timeoutSeconds)
-        {
-            var response = new EventSubscriptionResponse(string.Empty, "text/plain");
-
-            response.Headers["SID"] = subscriptionId;
-            response.Headers["TIMEOUT"] = string.IsNullOrEmpty(requestedTimeoutString) ? ("SECOND-" + timeoutSeconds.ToString(CultureInfo.InvariantCulture)) : 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<string, string> stateVariables)
-        {
-            var subs = _subscriptions.Values
-                .Where(i => !i.IsExpired && string.Equals(notificationType, i.NotificationType, StringComparison.OrdinalIgnoreCase));
-
-            var tasks = subs.Select(i => TriggerEvent(i, stateVariables));
-
-            return Task.WhenAll(tasks);
-        }
-
-        private async Task TriggerEvent(EventSubscription subscription, IDictionary<string, string> stateVariables)
-        {
-            var builder = new StringBuilder();
-
-            builder.Append("<?xml version=\"1.0\"?>");
-            builder.Append("<e:propertyset xmlns:e=\"urn:schemas-upnp-org:event-1-0\">");
-            foreach (var key in stateVariables.Keys)
-            {
-                builder.Append("<e:property>")
-                    .Append('<')
-                    .Append(key)
-                    .Append('>')
-                    .Append(stateVariables[key])
-                    .Append("</")
-                    .Append(key)
-                    .Append('>')
-                    .Append("</e:property>");
-            }
-
-            builder.Append("</e:propertyset>");
-
-            using var options = new HttpRequestMessage(new HttpMethod("NOTIFY"), subscription.CallbackUrl);
-            options.Content = new StringContent(builder.ToString(), Encoding.UTF8, MediaTypeNames.Text.Xml);
-            options.Headers.TryAddWithoutValidation("NT", subscription.NotificationType);
-            options.Headers.TryAddWithoutValidation("NTS", "upnp:propchange");
-            options.Headers.TryAddWithoutValidation("SID", subscription.Id);
-            options.Headers.TryAddWithoutValidation("SEQ", subscription.TriggerCount.ToString(CultureInfo.InvariantCulture));
-
-            try
-            {
-                using var response = await _httpClientFactory.CreateClient(NamedClient.DirectIp)
-                    .SendAsync(options, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);
-            }
-            catch (OperationCanceledException)
-            {
-            }
-            catch
-            {
-                // Already logged at lower levels
-            }
-            finally
-            {
-                subscription.IncrementTriggerCount();
-            }
-        }
-    }
-}

+ 0 - 35
Emby.Dlna/Eventing/EventSubscription.cs

@@ -1,35 +0,0 @@
-#nullable disable
-
-#pragma warning disable CS1591
-
-using System;
-
-namespace Emby.Dlna.Eventing
-{
-    public class EventSubscription
-    {
-        public string Id { get; set; }
-
-        public string CallbackUrl { get; set; }
-
-        public string NotificationType { get; set; }
-
-        public DateTime SubscriptionTime { get; set; }
-
-        public int TimeoutSeconds { get; set; }
-
-        public long TriggerCount { get; set; }
-
-        public bool IsExpired => SubscriptionTime.AddSeconds(TimeoutSeconds) >= DateTime.UtcNow;
-
-        public void IncrementTriggerCount()
-        {
-            if (TriggerCount == long.MaxValue)
-            {
-                TriggerCount = 0;
-            }
-
-            TriggerCount++;
-        }
-    }
-}

+ 0 - 72
Emby.Dlna/Extensions/DlnaServiceCollectionExtensions.cs

@@ -1,72 +0,0 @@
-using System;
-using System.Globalization;
-using System.Net;
-using System.Net.Http;
-using System.Text;
-using Emby.Dlna.ConnectionManager;
-using Emby.Dlna.ContentDirectory;
-using Emby.Dlna.Main;
-using Emby.Dlna.MediaReceiverRegistrar;
-using Emby.Dlna.Ssdp;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Dlna;
-using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.Net;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-using Rssdp.Infrastructure;
-
-namespace Emby.Dlna.Extensions;
-
-/// <summary>
-/// Extension methods for adding DLNA services.
-/// </summary>
-public static class DlnaServiceCollectionExtensions
-{
-    /// <summary>
-    /// Adds DLNA services to the provided <see cref="IServiceCollection"/>.
-    /// </summary>
-    /// <param name="services">The <see cref="IServiceCollection"/>.</param>
-    /// <param name="applicationHost">The <see cref="IServerApplicationHost"/>.</param>
-    public static void AddDlnaServices(
-        this IServiceCollection services,
-        IServerApplicationHost applicationHost)
-    {
-        services.AddHttpClient(NamedClient.Dlna, c =>
-            {
-                c.DefaultRequestHeaders.UserAgent.ParseAdd(
-                    string.Format(
-                        CultureInfo.InvariantCulture,
-                        "{0}/{1} UPnP/1.0 {2}/{3}",
-                        Environment.OSVersion.Platform,
-                        Environment.OSVersion,
-                        applicationHost.Name,
-                        applicationHost.ApplicationVersionString));
-
-                c.DefaultRequestHeaders.Add("CPFN.UPNP.ORG", applicationHost.FriendlyName); // Required for UPnP DeviceArchitecture v2.0
-                c.DefaultRequestHeaders.Add("FriendlyName.DLNA.ORG", applicationHost.FriendlyName); // REVIEW: where does this come from?
-            })
-            .ConfigurePrimaryHttpMessageHandler(_ => new SocketsHttpHandler
-            {
-                AutomaticDecompression = DecompressionMethods.All,
-                RequestHeaderEncodingSelector = (_, _) => Encoding.UTF8
-            });
-
-        services.AddSingleton<IDlnaManager, DlnaManager>();
-        services.AddSingleton<IDeviceDiscovery, DeviceDiscovery>();
-        services.AddSingleton<IContentDirectory, ContentDirectoryService>();
-        services.AddSingleton<IConnectionManager, ConnectionManagerService>();
-        services.AddSingleton<IMediaReceiverRegistrar, MediaReceiverRegistrarService>();
-
-        services.AddSingleton<ISsdpCommunicationsServer>(provider => new SsdpCommunicationsServer(
-            provider.GetRequiredService<ISocketFactory>(),
-            provider.GetRequiredService<INetworkManager>(),
-            provider.GetRequiredService<ILogger<SsdpCommunicationsServer>>())
-        {
-            IsShared = true
-        });
-
-        services.AddHostedService<DlnaHost>();
-    }
-}

+ 0 - 8
Emby.Dlna/IConnectionManager.cs

@@ -1,8 +0,0 @@
-#pragma warning disable CS1591
-
-namespace Emby.Dlna
-{
-    public interface IConnectionManager : IDlnaEventManager, IUpnpService
-    {
-    }
-}

+ 0 - 8
Emby.Dlna/IContentDirectory.cs

@@ -1,8 +0,0 @@
-#pragma warning disable CS1591
-
-namespace Emby.Dlna
-{
-    public interface IContentDirectory : IDlnaEventManager, IUpnpService
-    {
-    }
-}

+ 0 - 34
Emby.Dlna/IDlnaEventManager.cs

@@ -1,34 +0,0 @@
-#nullable disable
-#pragma warning disable CS1591
-
-namespace Emby.Dlna
-{
-    public interface IDlnaEventManager
-    {
-        /// <summary>
-        /// Cancels the event subscription.
-        /// </summary>
-        /// <param name="subscriptionId">The subscription identifier.</param>
-        /// <returns>The response.</returns>
-        EventSubscriptionResponse CancelEventSubscription(string subscriptionId);
-
-        /// <summary>
-        /// Renews the event subscription.
-        /// </summary>
-        /// <param name="subscriptionId">The subscription identifier.</param>
-        /// <param name="notificationType">The notification type.</param>
-        /// <param name="requestedTimeoutString">The requested timeout as a string.</param>
-        /// <param name="callbackUrl">The callback url.</param>
-        /// <returns>The response.</returns>
-        EventSubscriptionResponse RenewEventSubscription(string subscriptionId, string notificationType, string requestedTimeoutString, string callbackUrl);
-
-        /// <summary>
-        /// Creates the event subscription.
-        /// </summary>
-        /// <param name="notificationType">The notification type.</param>
-        /// <param name="requestedTimeoutString">The requested timeout as a string.</param>
-        /// <param name="callbackUrl">The callback url.</param>
-        /// <returns>The response.</returns>
-        EventSubscriptionResponse CreateEventSubscription(string notificationType, string requestedTimeoutString, string callbackUrl);
-    }
-}

+ 0 - 8
Emby.Dlna/IMediaReceiverRegistrar.cs

@@ -1,8 +0,0 @@
-#pragma warning disable CS1591
-
-namespace Emby.Dlna
-{
-    public interface IMediaReceiverRegistrar : IDlnaEventManager, IUpnpService
-    {
-    }
-}

+ 0 - 22
Emby.Dlna/IUpnpService.cs

@@ -1,22 +0,0 @@
-#pragma warning disable CS1591
-
-using System.Threading.Tasks;
-
-namespace Emby.Dlna
-{
-    public interface IUpnpService
-    {
-        /// <summary>
-        /// Gets the content directory XML.
-        /// </summary>
-        /// <returns>System.String.</returns>
-        string GetServiceXml();
-
-        /// <summary>
-        /// Processes the control request.
-        /// </summary>
-        /// <param name="request">The request.</param>
-        /// <returns>ControlResponse.</returns>
-        Task<ControlResponse> ProcessControlRequestAsync(ControlRequest request);
-    }
-}

TEMPAT SAMPAH
Emby.Dlna/Images/logo120.jpg


TEMPAT SAMPAH
Emby.Dlna/Images/logo120.png


TEMPAT SAMPAH
Emby.Dlna/Images/logo240.jpg


TEMPAT SAMPAH
Emby.Dlna/Images/logo240.png


TEMPAT SAMPAH
Emby.Dlna/Images/logo48.jpg


TEMPAT SAMPAH
Emby.Dlna/Images/logo48.png


TEMPAT SAMPAH
Emby.Dlna/Images/people48.jpg


TEMPAT SAMPAH
Emby.Dlna/Images/people48.png


TEMPAT SAMPAH
Emby.Dlna/Images/people480.jpg


TEMPAT SAMPAH
Emby.Dlna/Images/people480.png


+ 0 - 387
Emby.Dlna/Main/DlnaHost.cs

@@ -1,387 +0,0 @@
-#pragma warning disable CA1031 // Do not catch general exception types.
-
-using System;
-using System.Globalization;
-using System.Linq;
-using System.Net.Http;
-using System.Net.Sockets;
-using System.Threading;
-using System.Threading.Tasks;
-using Emby.Dlna.PlayTo;
-using Emby.Dlna.Ssdp;
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Dlna;
-using MediaBrowser.Controller.Drawing;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Controller.Session;
-using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.Globalization;
-using Microsoft.Extensions.Hosting;
-using Microsoft.Extensions.Logging;
-using Rssdp;
-using Rssdp.Infrastructure;
-
-namespace Emby.Dlna.Main;
-
-/// <summary>
-/// A <see cref="IHostedService"/> that manages a DLNA server.
-/// </summary>
-public sealed class DlnaHost : IHostedService, IDisposable
-{
-    private readonly ILogger<DlnaHost> _logger;
-    private readonly IServerConfigurationManager _config;
-    private readonly IServerApplicationHost _appHost;
-    private readonly ISessionManager _sessionManager;
-    private readonly IHttpClientFactory _httpClientFactory;
-    private readonly ILibraryManager _libraryManager;
-    private readonly IUserManager _userManager;
-    private readonly IDlnaManager _dlnaManager;
-    private readonly IImageProcessor _imageProcessor;
-    private readonly IUserDataManager _userDataManager;
-    private readonly ILocalizationManager _localization;
-    private readonly IMediaSourceManager _mediaSourceManager;
-    private readonly IMediaEncoder _mediaEncoder;
-    private readonly IDeviceDiscovery _deviceDiscovery;
-    private readonly ISsdpCommunicationsServer _communicationsServer;
-    private readonly INetworkManager _networkManager;
-    private readonly object _syncLock = new();
-
-    private SsdpDevicePublisher? _publisher;
-    private PlayToManager? _manager;
-    private bool _disposed;
-
-    /// <summary>
-    /// Initializes a new instance of the <see cref="DlnaHost"/> class.
-    /// </summary>
-    /// <param name="config">The <see cref="IServerConfigurationManager"/>.</param>
-    /// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
-    /// <param name="appHost">The <see cref="IServerApplicationHost"/>.</param>
-    /// <param name="sessionManager">The <see cref="ISessionManager"/>.</param>
-    /// <param name="httpClientFactory">The <see cref="IHttpClientFactory"/>.</param>
-    /// <param name="libraryManager">The <see cref="ILibraryManager"/>.</param>
-    /// <param name="userManager">The <see cref="IUserManager"/>.</param>
-    /// <param name="dlnaManager">The <see cref="IDlnaManager"/>.</param>
-    /// <param name="imageProcessor">The <see cref="IImageProcessor"/>.</param>
-    /// <param name="userDataManager">The <see cref="IUserDataManager"/>.</param>
-    /// <param name="localizationManager">The <see cref="ILocalizationManager"/>.</param>
-    /// <param name="mediaSourceManager">The <see cref="IMediaSourceManager"/>.</param>
-    /// <param name="deviceDiscovery">The <see cref="IDeviceDiscovery"/>.</param>
-    /// <param name="mediaEncoder">The <see cref="IMediaEncoder"/>.</param>
-    /// <param name="communicationsServer">The <see cref="ISsdpCommunicationsServer"/>.</param>
-    /// <param name="networkManager">The <see cref="INetworkManager"/>.</param>
-    public DlnaHost(
-        IServerConfigurationManager config,
-        ILoggerFactory loggerFactory,
-        IServerApplicationHost appHost,
-        ISessionManager sessionManager,
-        IHttpClientFactory httpClientFactory,
-        ILibraryManager libraryManager,
-        IUserManager userManager,
-        IDlnaManager dlnaManager,
-        IImageProcessor imageProcessor,
-        IUserDataManager userDataManager,
-        ILocalizationManager localizationManager,
-        IMediaSourceManager mediaSourceManager,
-        IDeviceDiscovery deviceDiscovery,
-        IMediaEncoder mediaEncoder,
-        ISsdpCommunicationsServer communicationsServer,
-        INetworkManager networkManager)
-    {
-        _config = config;
-        _appHost = appHost;
-        _sessionManager = sessionManager;
-        _httpClientFactory = httpClientFactory;
-        _libraryManager = libraryManager;
-        _userManager = userManager;
-        _dlnaManager = dlnaManager;
-        _imageProcessor = imageProcessor;
-        _userDataManager = userDataManager;
-        _localization = localizationManager;
-        _mediaSourceManager = mediaSourceManager;
-        _deviceDiscovery = deviceDiscovery;
-        _mediaEncoder = mediaEncoder;
-        _communicationsServer = communicationsServer;
-        _networkManager = networkManager;
-        _logger = loggerFactory.CreateLogger<DlnaHost>();
-    }
-
-    /// <inheritdoc />
-    public async Task StartAsync(CancellationToken cancellationToken)
-    {
-        var netConfig = _config.GetConfiguration<NetworkConfiguration>(NetworkConfigurationStore.StoreKey);
-        if (_appHost.ListenWithHttps && netConfig.RequireHttps)
-        {
-            if (_config.GetDlnaConfiguration().EnableServer)
-            {
-                _logger.LogError("The DLNA specification does not support HTTPS.");
-            }
-
-            // No use starting as dlna won't work, as we're running purely on HTTPS.
-            return;
-        }
-
-        await ((DlnaManager)_dlnaManager).InitProfilesAsync().ConfigureAwait(false);
-        ReloadComponents();
-
-        _config.NamedConfigurationUpdated += OnNamedConfigurationUpdated;
-    }
-
-    /// <inheritdoc />
-    public Task StopAsync(CancellationToken cancellationToken)
-    {
-        Stop();
-
-        return Task.CompletedTask;
-    }
-
-    /// <inheritdoc />
-    public void Dispose()
-    {
-        if (!_disposed)
-        {
-            Stop();
-            _disposed = true;
-        }
-    }
-
-    private void OnNamedConfigurationUpdated(object? sender, ConfigurationUpdateEventArgs e)
-    {
-        if (string.Equals(e.Key, "dlna", StringComparison.OrdinalIgnoreCase))
-        {
-            ReloadComponents();
-        }
-    }
-
-    private void ReloadComponents()
-    {
-        var options = _config.GetDlnaConfiguration();
-        StartDeviceDiscovery();
-
-        if (options.EnableServer)
-        {
-            StartDevicePublisher(options);
-        }
-        else
-        {
-            DisposeDevicePublisher();
-        }
-
-        if (options.EnablePlayTo)
-        {
-            StartPlayToManager();
-        }
-        else
-        {
-            DisposePlayToManager();
-        }
-    }
-
-    private static string CreateUuid(string text)
-    {
-        if (!Guid.TryParse(text, out var guid))
-        {
-            guid = text.GetMD5();
-        }
-
-        return guid.ToString("D", CultureInfo.InvariantCulture);
-    }
-
-    private static void SetProperties(SsdpDevice device, string fullDeviceType)
-    {
-        var serviceParts = fullDeviceType
-            .Replace("urn:", string.Empty, StringComparison.OrdinalIgnoreCase)
-            .Replace(":1", string.Empty, StringComparison.OrdinalIgnoreCase)
-            .Split(':');
-
-        device.DeviceTypeNamespace = serviceParts[0].Replace('.', '-');
-        device.DeviceClass = serviceParts[1];
-        device.DeviceType = serviceParts[2];
-    }
-
-    private void StartDeviceDiscovery()
-    {
-        try
-        {
-            ((DeviceDiscovery)_deviceDiscovery).Start(_communicationsServer);
-        }
-        catch (Exception ex)
-        {
-            _logger.LogError(ex, "Error starting device discovery");
-        }
-    }
-
-    private void StartDevicePublisher(Configuration.DlnaOptions options)
-    {
-        if (_publisher is not null)
-        {
-            return;
-        }
-
-        try
-        {
-            _publisher = new SsdpDevicePublisher(
-                _communicationsServer,
-                Environment.OSVersion.Platform.ToString(),
-                // Can not use VersionString here since that includes OS and version
-                Environment.OSVersion.Version.ToString(),
-                _config.GetDlnaConfiguration().SendOnlyMatchedHost)
-            {
-                LogFunction = msg => _logger.LogDebug("{Msg}", msg),
-                SupportPnpRootDevice = false
-            };
-
-            RegisterServerEndpoints();
-
-            if (options.BlastAliveMessages)
-            {
-                _publisher.StartSendingAliveNotifications(TimeSpan.FromSeconds(options.BlastAliveMessageIntervalSeconds));
-            }
-        }
-        catch (Exception ex)
-        {
-            _logger.LogError(ex, "Error registering endpoint");
-        }
-    }
-
-    private void RegisterServerEndpoints()
-    {
-        var udn = CreateUuid(_appHost.SystemId);
-        var descriptorUri = "/dlna/" + udn + "/description.xml";
-
-        // Only get bind addresses in LAN
-        // IPv6 is currently unsupported
-        var validInterfaces = _networkManager.GetInternalBindAddresses()
-            .Where(x => x.AddressFamily != AddressFamily.InterNetworkV6)
-            .ToList();
-
-        if (validInterfaces.Count == 0)
-        {
-            // No interfaces returned, fall back to loopback
-            validInterfaces = _networkManager.GetLoopbacks().ToList();
-        }
-
-        foreach (var intf in validInterfaces)
-        {
-            var fullService = "urn:schemas-upnp-org:device:MediaServer:1";
-
-            _logger.LogInformation("Registering publisher for {ResourceName} on {DeviceAddress}", fullService, intf.Address);
-
-            var uri = new UriBuilder(_appHost.GetApiUrlForLocalAccess(intf.Address, false) + descriptorUri);
-
-            var device = new SsdpRootDevice
-            {
-                CacheLifetime = TimeSpan.FromSeconds(1800), // How long SSDP clients can cache this info.
-                Location = uri.Uri, // Must point to the URL that serves your devices UPnP description document.
-                Address = intf.Address,
-                PrefixLength = NetworkUtils.MaskToCidr(intf.Subnet.Prefix),
-                FriendlyName = "Jellyfin",
-                Manufacturer = "Jellyfin",
-                ModelName = "Jellyfin Server",
-                Uuid = udn
-                // This must be a globally unique value that survives reboots etc. Get from storage or embedded hardware etc.
-            };
-
-            SetProperties(device, fullService);
-            _publisher!.AddDevice(device);
-
-            var embeddedDevices = new[]
-            {
-                "urn:schemas-upnp-org:service:ContentDirectory:1",
-                "urn:schemas-upnp-org:service:ConnectionManager:1",
-                // "urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1"
-            };
-
-            foreach (var subDevice in embeddedDevices)
-            {
-                var embeddedDevice = new SsdpEmbeddedDevice
-                {
-                    FriendlyName = device.FriendlyName,
-                    Manufacturer = device.Manufacturer,
-                    ModelName = device.ModelName,
-                    Uuid = udn
-                    // This must be a globally unique value that survives reboots etc. Get from storage or embedded hardware etc.
-                };
-
-                SetProperties(embeddedDevice, subDevice);
-                device.AddDevice(embeddedDevice);
-            }
-        }
-    }
-
-    private void StartPlayToManager()
-    {
-        lock (_syncLock)
-        {
-            if (_manager is not null)
-            {
-                return;
-            }
-
-            try
-            {
-                _manager = new PlayToManager(
-                    _logger,
-                    _sessionManager,
-                    _libraryManager,
-                    _userManager,
-                    _dlnaManager,
-                    _appHost,
-                    _imageProcessor,
-                    _deviceDiscovery,
-                    _httpClientFactory,
-                    _userDataManager,
-                    _localization,
-                    _mediaSourceManager,
-                    _mediaEncoder);
-
-                _manager.Start();
-            }
-            catch (Exception ex)
-            {
-                _logger.LogError(ex, "Error starting PlayTo manager");
-            }
-        }
-    }
-
-    private void DisposePlayToManager()
-    {
-        lock (_syncLock)
-        {
-            if (_manager is not null)
-            {
-                try
-                {
-                    _logger.LogInformation("Disposing PlayToManager");
-                    _manager.Dispose();
-                }
-                catch (Exception ex)
-                {
-                    _logger.LogError(ex, "Error disposing PlayTo manager");
-                }
-
-                _manager = null;
-            }
-        }
-    }
-
-    private void DisposeDevicePublisher()
-    {
-        if (_publisher is not null)
-        {
-            _logger.LogInformation("Disposing SsdpDevicePublisher");
-            _publisher.Dispose();
-            _publisher = null;
-        }
-    }
-
-    private void Stop()
-    {
-        DisposeDevicePublisher();
-        DisposePlayToManager();
-    }
-}

+ 0 - 58
Emby.Dlna/MediaReceiverRegistrar/ControlHandler.cs

@@ -1,58 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Xml;
-using Emby.Dlna.Service;
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Controller.Configuration;
-using Microsoft.Extensions.Logging;
-
-namespace Emby.Dlna.MediaReceiverRegistrar
-{
-    /// <summary>
-    /// Defines the <see cref="ControlHandler" />.
-    /// </summary>
-    public class ControlHandler : BaseControlHandler
-    {
-        /// <summary>
-        /// Initializes a new instance of the <see cref="ControlHandler"/> class.
-        /// </summary>
-        /// <param name="config">The <see cref="IServerConfigurationManager"/> for use with the <see cref="ControlHandler"/> instance.</param>
-        /// <param name="logger">The <see cref="ILogger"/> for use with the <see cref="ControlHandler"/> instance.</param>
-        public ControlHandler(IServerConfigurationManager config, ILogger logger)
-            : base(config, logger)
-        {
-        }
-
-        /// <inheritdoc />
-        protected override void WriteResult(string methodName, IReadOnlyDictionary<string, string> methodParams, XmlWriter xmlWriter)
-        {
-            if (string.Equals(methodName, "IsAuthorized", StringComparison.OrdinalIgnoreCase))
-            {
-                HandleIsAuthorized(xmlWriter);
-                return;
-            }
-
-            if (string.Equals(methodName, "IsValidated", StringComparison.OrdinalIgnoreCase))
-            {
-                HandleIsValidated(xmlWriter);
-                return;
-            }
-
-            throw new ResourceNotFoundException("Unexpected control request name: " + methodName);
-        }
-
-        /// <summary>
-        /// Records that the handle is authorized in the xml stream.
-        /// </summary>
-        /// <param name="xmlWriter">The <see cref="XmlWriter"/>.</param>
-        private static void HandleIsAuthorized(XmlWriter xmlWriter)
-            => xmlWriter.WriteElementString("Result", "1");
-
-        /// <summary>
-        /// Records that the handle is validated in the xml stream.
-        /// </summary>
-        /// <param name="xmlWriter">The <see cref="XmlWriter"/>.</param>
-        private static void HandleIsValidated(XmlWriter xmlWriter)
-            => xmlWriter.WriteElementString("Result", "1");
-    }
-}

+ 0 - 46
Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarService.cs

@@ -1,46 +0,0 @@
-using System.Net.Http;
-using System.Threading.Tasks;
-using Emby.Dlna.Service;
-using MediaBrowser.Controller.Configuration;
-using Microsoft.Extensions.Logging;
-
-namespace Emby.Dlna.MediaReceiverRegistrar
-{
-    /// <summary>
-    /// Defines the <see cref="MediaReceiverRegistrarService" />.
-    /// </summary>
-    public class MediaReceiverRegistrarService : BaseService, IMediaReceiverRegistrar
-    {
-        private readonly IServerConfigurationManager _config;
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="MediaReceiverRegistrarService"/> class.
-        /// </summary>
-        /// <param name="logger">The <see cref="ILogger{MediaReceiverRegistrarService}"/> for use with the <see cref="MediaReceiverRegistrarService"/> instance.</param>
-        /// <param name="httpClientFactory">The <see cref="IHttpClientFactory"/> for use with the <see cref="MediaReceiverRegistrarService"/> instance.</param>
-        /// <param name="config">The <see cref="IServerConfigurationManager"/> for use with the <see cref="MediaReceiverRegistrarService"/> instance.</param>
-        public MediaReceiverRegistrarService(
-            ILogger<MediaReceiverRegistrarService> logger,
-            IHttpClientFactory httpClientFactory,
-            IServerConfigurationManager config)
-            : base(logger, httpClientFactory)
-        {
-            _config = config;
-        }
-
-        /// <inheritdoc />
-        public string GetServiceXml()
-        {
-            return MediaReceiverRegistrarXmlBuilder.GetXml();
-        }
-
-        /// <inheritdoc />
-        public Task<ControlResponse> ProcessControlRequestAsync(ControlRequest request)
-        {
-            return new ControlHandler(
-                _config,
-                Logger)
-                .ProcessControlRequestAsync(request);
-        }
-    }
-}

+ 0 - 90
Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarXmlBuilder.cs

@@ -1,90 +0,0 @@
-using System.Collections.Generic;
-using Emby.Dlna.Common;
-using Emby.Dlna.Service;
-
-namespace Emby.Dlna.MediaReceiverRegistrar
-{
-    /// <summary>
-    /// Defines the <see cref="MediaReceiverRegistrarXmlBuilder" />.
-    /// See https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-drmnd/5d37515e-7a63-4709-8258-8fd4e0ed4482.
-    /// </summary>
-    public static class MediaReceiverRegistrarXmlBuilder
-    {
-        /// <summary>
-        /// Retrieves an XML description of the X_MS_MediaReceiverRegistrar.
-        /// </summary>
-        /// <returns>An XML representation of this service.</returns>
-        public static string GetXml()
-        {
-            return new ServiceXmlBuilder().GetXml(ServiceActionListBuilder.GetActions(), GetStateVariables());
-        }
-
-        /// <summary>
-        /// The a list of all the state variables for this invocation.
-        /// </summary>
-        /// <returns>The <see cref="IEnumerable{StateVariable}"/>.</returns>
-        private static IEnumerable<StateVariable> GetStateVariables()
-        {
-            var list = new List<StateVariable>
-            {
-                new StateVariable
-                {
-                    Name = "AuthorizationGrantedUpdateID",
-                    DataType = "ui4",
-                    SendsEvents = true
-                },
-
-                new StateVariable
-                {
-                    Name = "A_ARG_TYPE_DeviceID",
-                    DataType = "string",
-                    SendsEvents = false
-                },
-
-                new StateVariable
-                {
-                    Name = "AuthorizationDeniedUpdateID",
-                    DataType = "ui4",
-                    SendsEvents = true
-                },
-
-                new StateVariable
-                {
-                    Name = "ValidationSucceededUpdateID",
-                    DataType = "ui4",
-                    SendsEvents = true
-                },
-
-                new StateVariable
-                {
-                    Name = "A_ARG_TYPE_RegistrationRespMsg",
-                    DataType = "bin.base64",
-                    SendsEvents = false
-                },
-
-                new StateVariable
-                {
-                    Name = "A_ARG_TYPE_RegistrationReqMsg",
-                    DataType = "bin.base64",
-                    SendsEvents = false
-                },
-
-                new StateVariable
-                {
-                    Name = "ValidationRevokedUpdateID",
-                    DataType = "ui4",
-                    SendsEvents = true
-                },
-
-                new StateVariable
-                {
-                    Name = "A_ARG_TYPE_Result",
-                    DataType = "int",
-                    SendsEvents = false
-                }
-            };
-
-            return list;
-        }
-    }
-}

+ 0 - 187
Emby.Dlna/MediaReceiverRegistrar/ServiceActionListBuilder.cs

@@ -1,187 +0,0 @@
-using System.Collections.Generic;
-using Emby.Dlna.Common;
-
-namespace Emby.Dlna.MediaReceiverRegistrar
-{
-    /// <summary>
-    /// Defines the <see cref="ServiceActionListBuilder" />.
-    /// </summary>
-    public static class ServiceActionListBuilder
-    {
-        /// <summary>
-        /// Returns a list of services that this instance provides.
-        /// </summary>
-        /// <returns>An <see cref="IEnumerable{ServiceAction}"/>.</returns>
-        public static IEnumerable<ServiceAction> GetActions()
-        {
-            return new[]
-            {
-                GetIsValidated(),
-                GetIsAuthorized(),
-                GetRegisterDevice(),
-                GetGetAuthorizationDeniedUpdateID(),
-                GetGetAuthorizationGrantedUpdateID(),
-                GetGetValidationRevokedUpdateID(),
-                GetGetValidationSucceededUpdateID()
-            };
-        }
-
-        /// <summary>
-        /// Returns the action details for "IsValidated".
-        /// </summary>
-        /// <returns>The <see cref="ServiceAction"/>.</returns>
-        private static ServiceAction GetIsValidated()
-        {
-            var action = new ServiceAction
-            {
-                Name = "IsValidated"
-            };
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "DeviceID",
-                Direction = "in"
-            });
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "Result",
-                Direction = "out"
-            });
-
-            return action;
-        }
-
-        /// <summary>
-        /// Returns the action details for "IsAuthorized".
-        /// </summary>
-        /// <returns>The <see cref="ServiceAction"/>.</returns>
-        private static ServiceAction GetIsAuthorized()
-        {
-            var action = new ServiceAction
-            {
-                Name = "IsAuthorized"
-            };
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "DeviceID",
-                Direction = "in"
-            });
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "Result",
-                Direction = "out"
-            });
-
-            return action;
-        }
-
-        /// <summary>
-        /// Returns the action details for "RegisterDevice".
-        /// </summary>
-        /// <returns>The <see cref="ServiceAction"/>.</returns>
-        private static ServiceAction GetRegisterDevice()
-        {
-            var action = new ServiceAction
-            {
-                Name = "RegisterDevice"
-            };
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "RegistrationReqMsg",
-                Direction = "in"
-            });
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "RegistrationRespMsg",
-                Direction = "out"
-            });
-
-            return action;
-        }
-
-        /// <summary>
-        /// Returns the action details for "GetValidationSucceededUpdateID".
-        /// </summary>
-        /// <returns>The <see cref="ServiceAction"/>.</returns>
-        private static ServiceAction GetGetValidationSucceededUpdateID()
-        {
-            var action = new ServiceAction
-            {
-                Name = "GetValidationSucceededUpdateID"
-            };
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "ValidationSucceededUpdateID",
-                Direction = "out"
-            });
-
-            return action;
-        }
-
-        /// <summary>
-        /// Returns the action details for "GetGetAuthorizationDeniedUpdateID".
-        /// </summary>
-        /// <returns>The <see cref="ServiceAction"/>.</returns>
-        private static ServiceAction GetGetAuthorizationDeniedUpdateID()
-        {
-            var action = new ServiceAction
-            {
-                Name = "GetAuthorizationDeniedUpdateID"
-            };
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "AuthorizationDeniedUpdateID",
-                Direction = "out"
-            });
-
-            return action;
-        }
-
-        /// <summary>
-        /// Returns the action details for "GetValidationRevokedUpdateID".
-        /// </summary>
-        /// <returns>The <see cref="ServiceAction"/>.</returns>
-        private static ServiceAction GetGetValidationRevokedUpdateID()
-        {
-            var action = new ServiceAction
-            {
-                Name = "GetValidationRevokedUpdateID"
-            };
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "ValidationRevokedUpdateID",
-                Direction = "out"
-            });
-
-            return action;
-        }
-
-        /// <summary>
-        /// Returns the action details for "GetAuthorizationGrantedUpdateID".
-        /// </summary>
-        /// <returns>The <see cref="ServiceAction"/>.</returns>
-        private static ServiceAction GetGetAuthorizationGrantedUpdateID()
-        {
-            var action = new ServiceAction
-            {
-                Name = "GetAuthorizationGrantedUpdateID"
-            };
-
-            action.ArgumentList.Add(new Argument
-            {
-                Name = "AuthorizationGrantedUpdateID",
-                Direction = "out"
-            });
-
-            return action;
-        }
-    }
-}

+ 0 - 1264
Emby.Dlna/PlayTo/Device.cs

@@ -1,1264 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Linq;
-using System.Net.Http;
-using System.Security;
-using System.Threading;
-using System.Threading.Tasks;
-using System.Xml;
-using System.Xml.Linq;
-using Emby.Dlna.Common;
-using Emby.Dlna.Ssdp;
-using Microsoft.Extensions.Logging;
-
-namespace Emby.Dlna.PlayTo
-{
-    public class Device : IDisposable
-    {
-        private readonly IHttpClientFactory _httpClientFactory;
-
-        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, IHttpClientFactory httpClientFactory, ILogger logger)
-        {
-            Properties = deviceProperties;
-            _httpClientFactory = httpClientFactory;
-            _logger = logger;
-        }
-
-        public event EventHandler<PlaybackStartEventArgs>? PlaybackStart;
-
-        public event EventHandler<PlaybackProgressEventArgs>? PlaybackProgress;
-
-        public event EventHandler<PlaybackStoppedEventArgs>? PlaybackStopped;
-
-        public event EventHandler<MediaChangedEventArgs>? MediaChanged;
-
-        public DeviceInfo Properties { get; set; }
-
-        public bool IsMuted { get; set; }
-
-        public int Volume
-        {
-            get
-            {
-                RefreshVolumeIfNeeded().GetAwaiter().GetResult();
-                return _volume;
-            }
-
-            set => _volume = value;
-        }
-
-        public TimeSpan? Duration { get; set; }
-
-        public TimeSpan Position { get; set; } = TimeSpan.FromSeconds(0);
-
-        public TransportState TransportState { get; private set; }
-
-        public bool IsPlaying => TransportState == TransportState.PLAYING;
-
-        public bool IsPaused => TransportState == TransportState.PAUSED_PLAYBACK;
-
-        public bool IsStopped => TransportState == TransportState.STOPPED;
-
-        public Action? OnDeviceUnavailable { get; set; }
-
-        private TransportCommands? AvCommands { get; set; }
-
-        private TransportCommands? RendererCommands { get; set; }
-
-        public UBaseObject? CurrentMediaInfo { get; private set; }
-
-        public void Start()
-        {
-            _logger.LogDebug("Dlna Device.Start");
-            _timer = new Timer(TimerCallback, null, 1000, Timeout.Infinite);
-        }
-
-        private Task RefreshVolumeIfNeeded()
-        {
-            if (_volumeRefreshActive
-                && DateTime.UtcNow >= _lastVolumeRefresh.AddSeconds(5))
-            {
-                _lastVolumeRefresh = DateTime.UtcNow;
-                return RefreshVolume();
-            }
-
-            return Task.CompletedTask;
-        }
-
-        private async Task RefreshVolume(CancellationToken cancellationToken = default)
-        {
-            if (_disposed)
-            {
-                return;
-            }
-
-            try
-            {
-                await GetVolume(cancellationToken).ConfigureAwait(false);
-                await GetMute(cancellationToken).ConfigureAwait(false);
-            }
-            catch (Exception ex)
-            {
-                _logger.LogError(ex, "Error updating device volume info for {DeviceName}", Properties.Name);
-            }
-        }
-
-        private void RestartTimer(bool immediate = false)
-        {
-            lock (_timerLock)
-            {
-                if (_disposed)
-                {
-                    return;
-                }
-
-                _volumeRefreshActive = true;
-
-                var time = immediate ? 100 : 10000;
-                _timer?.Change(time, Timeout.Infinite);
-            }
-        }
-
-        /// <summary>
-        /// Restarts the timer in inactive mode.
-        /// </summary>
-        private void RestartTimerInactive()
-        {
-            lock (_timerLock)
-            {
-                if (_disposed)
-                {
-                    return;
-                }
-
-                _volumeRefreshActive = false;
-
-                _timer?.Change(Timeout.Infinite, Timeout.Infinite);
-            }
-        }
-
-        public Task VolumeDown(CancellationToken cancellationToken)
-        {
-            var sendVolume = Math.Max(Volume - 5, 0);
-
-            return SetVolume(sendVolume, cancellationToken);
-        }
-
-        public Task VolumeUp(CancellationToken cancellationToken)
-        {
-            var sendVolume = Math.Min(Volume + 5, 100);
-
-            return SetVolume(sendVolume, cancellationToken);
-        }
-
-        public Task ToggleMute(CancellationToken cancellationToken)
-        {
-            if (IsMuted)
-            {
-                return Unmute(cancellationToken);
-            }
-
-            return Mute(cancellationToken);
-        }
-
-        public async Task Mute(CancellationToken cancellationToken)
-        {
-            var success = await SetMute(true, cancellationToken).ConfigureAwait(true);
-
-            if (!success)
-            {
-                await SetVolume(0, cancellationToken).ConfigureAwait(false);
-            }
-        }
-
-        public async Task Unmute(CancellationToken cancellationToken)
-        {
-            var success = await SetMute(false, cancellationToken).ConfigureAwait(true);
-
-            if (!success)
-            {
-                var sendVolume = _muteVol <= 0 ? 20 : _muteVol;
-
-                await SetVolume(sendVolume, cancellationToken).ConfigureAwait(false);
-            }
-        }
-
-        private DeviceService? GetServiceRenderingControl()
-        {
-            var services = Properties.Services;
-
-            return services.FirstOrDefault(s => string.Equals(s.ServiceType, "urn:schemas-upnp-org:service:RenderingControl:1", StringComparison.OrdinalIgnoreCase)) ??
-                services.FirstOrDefault(s => (s.ServiceType ?? string.Empty).StartsWith("urn:schemas-upnp-org:service:RenderingControl", StringComparison.OrdinalIgnoreCase));
-        }
-
-        private DeviceService? GetAvTransportService()
-        {
-            var services = Properties.Services;
-
-            return services.FirstOrDefault(s => string.Equals(s.ServiceType, "urn:schemas-upnp-org:service:AVTransport:1", StringComparison.OrdinalIgnoreCase)) ??
-                services.FirstOrDefault(s => (s.ServiceType ?? string.Empty).StartsWith("urn:schemas-upnp-org:service:AVTransport", StringComparison.OrdinalIgnoreCase));
-        }
-
-        private async Task<bool> SetMute(bool mute, CancellationToken cancellationToken)
-        {
-            var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false);
-
-            var command = rendererCommands?.ServiceActions.FirstOrDefault(c => c.Name == "SetMute");
-            if (command is null)
-            {
-                return false;
-            }
-
-            var service = GetServiceRenderingControl();
-
-            if (service is null)
-            {
-                return false;
-            }
-
-            _logger.LogDebug("Setting mute");
-            var value = mute ? 1 : 0;
-
-            await new DlnaHttpClient(_logger, _httpClientFactory)
-                .SendCommandAsync(
-                    Properties.BaseUrl,
-                    service,
-                    command.Name,
-                    rendererCommands!.BuildPost(command, service.ServiceType, value), // null checked above
-                    cancellationToken: cancellationToken)
-                .ConfigureAwait(false);
-
-            IsMuted = mute;
-
-            return true;
-        }
-
-        /// <summary>
-        /// Sets volume on a scale of 0-100.
-        /// </summary>
-        /// <param name="value">The volume on a scale of 0-100.</param>
-        /// <param name="cancellationToken">The cancellation token to cancel operation.</param>
-        /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
-        public async Task SetVolume(int value, CancellationToken cancellationToken)
-        {
-            var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false);
-
-            var command = rendererCommands?.ServiceActions.FirstOrDefault(c => c.Name == "SetVolume");
-            if (command is null)
-            {
-                return;
-            }
-
-            var service = GetServiceRenderingControl() ?? throw new InvalidOperationException("Unable to find service");
-
-            // Set it early and assume it will succeed
-            // Remote control will perform better
-            Volume = value;
-
-            await new DlnaHttpClient(_logger, _httpClientFactory)
-                .SendCommandAsync(
-                    Properties.BaseUrl,
-                    service,
-                    command.Name,
-                    rendererCommands!.BuildPost(command, service.ServiceType, value), // null checked above
-                    cancellationToken: cancellationToken)
-                .ConfigureAwait(false);
-        }
-
-        public async Task Seek(TimeSpan value, CancellationToken cancellationToken)
-        {
-            var avCommands = await GetAVProtocolAsync(cancellationToken).ConfigureAwait(false);
-
-            var command = avCommands?.ServiceActions.FirstOrDefault(c => c.Name == "Seek");
-            if (command is null)
-            {
-                return;
-            }
-
-            var service = GetAvTransportService() ?? throw new InvalidOperationException("Unable to find service");
-            await new DlnaHttpClient(_logger, _httpClientFactory)
-                .SendCommandAsync(
-                    Properties.BaseUrl,
-                    service,
-                    command.Name,
-                    avCommands!.BuildPost(command, service.ServiceType, string.Format(CultureInfo.InvariantCulture, "{0:hh}:{0:mm}:{0:ss}", value), "REL_TIME"), // null checked above
-                    cancellationToken: cancellationToken)
-                .ConfigureAwait(false);
-
-            RestartTimer(true);
-        }
-
-        public async Task SetAvTransport(string url, string? header, string metaData, CancellationToken cancellationToken)
-        {
-            var avCommands = await GetAVProtocolAsync(cancellationToken).ConfigureAwait(false);
-
-            url = url.Replace("&", "&amp;", StringComparison.Ordinal);
-
-            _logger.LogDebug("{0} - SetAvTransport Uri: {1} DlnaHeaders: {2}", Properties.Name, url, header);
-
-            var command = avCommands?.ServiceActions.FirstOrDefault(c => c.Name == "SetAVTransportURI");
-            if (command is null)
-            {
-                return;
-            }
-
-            var dictionary = new Dictionary<string, string>
-            {
-                { "CurrentURI", url },
-                { "CurrentURIMetaData", CreateDidlMeta(metaData) }
-            };
-
-            var service = GetAvTransportService() ?? throw new InvalidOperationException("Unable to find service");
-            var post = avCommands!.BuildPost(command, service.ServiceType, url, dictionary); // null checked above
-            await new DlnaHttpClient(_logger, _httpClientFactory)
-                .SendCommandAsync(
-                    Properties.BaseUrl,
-                    service,
-                    command.Name,
-                    post,
-                    header: header,
-                    cancellationToken: cancellationToken)
-                .ConfigureAwait(false);
-
-            await Task.Delay(50, cancellationToken).ConfigureAwait(false);
-
-            try
-            {
-                await SetPlay(avCommands, cancellationToken).ConfigureAwait(false);
-            }
-            catch
-            {
-                // Some devices will throw an error if you tell it to play when it's already playing
-                // Others won't
-            }
-
-            RestartTimer(true);
-        }
-
-        /*
-         * SetNextAvTransport is used to specify to the DLNA device what is the next track to play.
-         * Without that information, the next track command on the device does not work.
-         */
-        public async Task SetNextAvTransport(string url, string? header, string metaData, CancellationToken cancellationToken = default)
-        {
-            var avCommands = await GetAVProtocolAsync(cancellationToken).ConfigureAwait(false);
-
-            url = url.Replace("&", "&amp;", StringComparison.Ordinal);
-
-            _logger.LogDebug("{PropertyName} - SetNextAvTransport Uri: {Url} DlnaHeaders: {Header}", Properties.Name, url, header);
-
-            var command = avCommands?.ServiceActions.FirstOrDefault(c => string.Equals(c.Name, "SetNextAVTransportURI", StringComparison.OrdinalIgnoreCase));
-            if (command is null)
-            {
-                return;
-            }
-
-            var dictionary = new Dictionary<string, string>
-            {
-                { "NextURI", url },
-                { "NextURIMetaData", CreateDidlMeta(metaData) }
-            };
-
-            var service = GetAvTransportService() ?? throw new InvalidOperationException("Unable to find service");
-            var post = avCommands!.BuildPost(command, service.ServiceType, url, dictionary); // null checked above
-            await new DlnaHttpClient(_logger, _httpClientFactory)
-                .SendCommandAsync(Properties.BaseUrl, service, command.Name, post, header, cancellationToken)
-                .ConfigureAwait(false);
-        }
-
-        private static string CreateDidlMeta(string value)
-        {
-            if (string.IsNullOrEmpty(value))
-            {
-                return string.Empty;
-            }
-
-            return SecurityElement.Escape(value);
-        }
-
-        private Task SetPlay(TransportCommands avCommands, CancellationToken cancellationToken)
-        {
-            var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "Play");
-            if (command is null)
-            {
-                return Task.CompletedTask;
-            }
-
-            var service = GetAvTransportService() ?? throw new InvalidOperationException("Unable to find service");
-            return new DlnaHttpClient(_logger, _httpClientFactory).SendCommandAsync(
-                Properties.BaseUrl,
-                service,
-                command.Name,
-                avCommands.BuildPost(command, service.ServiceType, 1),
-                cancellationToken: cancellationToken);
-        }
-
-        public async Task SetPlay(CancellationToken cancellationToken)
-        {
-            var avCommands = await GetAVProtocolAsync(cancellationToken).ConfigureAwait(false);
-            if (avCommands is null)
-            {
-                return;
-            }
-
-            await SetPlay(avCommands, cancellationToken).ConfigureAwait(false);
-
-            RestartTimer(true);
-        }
-
-        public async Task SetStop(CancellationToken cancellationToken)
-        {
-            var avCommands = await GetAVProtocolAsync(cancellationToken).ConfigureAwait(false);
-
-            var command = avCommands?.ServiceActions.FirstOrDefault(c => c.Name == "Stop");
-            if (command is null)
-            {
-                return;
-            }
-
-            var service = GetAvTransportService() ?? throw new InvalidOperationException("Unable to find service");
-            await new DlnaHttpClient(_logger, _httpClientFactory)
-                .SendCommandAsync(
-                    Properties.BaseUrl,
-                    service,
-                    command.Name,
-                    avCommands!.BuildPost(command, service.ServiceType, 1), // null checked above
-                    cancellationToken: cancellationToken)
-                .ConfigureAwait(false);
-
-            RestartTimer(true);
-        }
-
-        public async Task SetPause(CancellationToken cancellationToken)
-        {
-            var avCommands = await GetAVProtocolAsync(cancellationToken).ConfigureAwait(false);
-
-            var command = avCommands?.ServiceActions.FirstOrDefault(c => c.Name == "Pause");
-            if (command is null)
-            {
-                return;
-            }
-
-            var service = GetAvTransportService() ?? throw new InvalidOperationException("Unable to find service");
-            await new DlnaHttpClient(_logger, _httpClientFactory)
-                .SendCommandAsync(
-                    Properties.BaseUrl,
-                    service,
-                    command.Name,
-                    avCommands!.BuildPost(command, service.ServiceType, 1), // null checked above
-                    cancellationToken: cancellationToken)
-                .ConfigureAwait(false);
-
-            TransportState = TransportState.PAUSED_PLAYBACK;
-
-            RestartTimer(true);
-        }
-
-        private async void TimerCallback(object? sender)
-        {
-            if (_disposed)
-            {
-                return;
-            }
-
-            try
-            {
-                var cancellationToken = CancellationToken.None;
-
-                var avCommands = await GetAVProtocolAsync(cancellationToken).ConfigureAwait(false);
-
-                if (avCommands is null)
-                {
-                    return;
-                }
-
-                var transportState = await GetTransportInfo(avCommands, cancellationToken).ConfigureAwait(false);
-
-                if (_disposed)
-                {
-                    return;
-                }
-
-                if (transportState.HasValue)
-                {
-                    // If we're not playing anything no need to get additional data
-                    if (transportState.Value == TransportState.STOPPED)
-                    {
-                        UpdateMediaInfo(null, transportState.Value);
-                    }
-                    else
-                    {
-                        var tuple = await GetPositionInfo(avCommands, cancellationToken).ConfigureAwait(false);
-
-                        var currentObject = tuple.Track;
-
-                        if (tuple.Success && currentObject is null)
-                        {
-                            currentObject = await GetMediaInfo(avCommands, cancellationToken).ConfigureAwait(false);
-                        }
-
-                        if (currentObject is not null)
-                        {
-                            UpdateMediaInfo(currentObject, transportState.Value);
-                        }
-                    }
-
-                    _connectFailureCount = 0;
-
-                    if (_disposed)
-                    {
-                        return;
-                    }
-
-                    // If we're not playing anything make sure we don't get data more often than necessary to keep the Session alive
-                    if (transportState.Value == TransportState.STOPPED)
-                    {
-                        RestartTimerInactive();
-                    }
-                    else
-                    {
-                        RestartTimer();
-                    }
-                }
-                else
-                {
-                    RestartTimerInactive();
-                }
-            }
-            catch (Exception ex)
-            {
-                if (_disposed)
-                {
-                    return;
-                }
-
-                _logger.LogError(ex, "Error updating device info for {DeviceName}", Properties.Name);
-
-                _connectFailureCount++;
-
-                if (_connectFailureCount >= 3)
-                {
-                    var action = OnDeviceUnavailable;
-                    if (action is not null)
-                    {
-                        _logger.LogDebug("Disposing device due to loss of connection");
-                        action();
-                        return;
-                    }
-                }
-
-                RestartTimerInactive();
-            }
-        }
-
-        private async Task GetVolume(CancellationToken cancellationToken)
-        {
-            if (_disposed)
-            {
-                return;
-            }
-
-            var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false);
-
-            var command = rendererCommands?.ServiceActions.FirstOrDefault(c => c.Name == "GetVolume");
-            if (command is null)
-            {
-                return;
-            }
-
-            var service = GetServiceRenderingControl();
-
-            if (service is null)
-            {
-                return;
-            }
-
-            var result = await new DlnaHttpClient(_logger, _httpClientFactory).SendCommandAsync(
-                Properties.BaseUrl,
-                service,
-                command.Name,
-                rendererCommands!.BuildPost(command, service.ServiceType), // null checked above
-                cancellationToken: cancellationToken).ConfigureAwait(false);
-
-            if (result is null || result.Document is null)
-            {
-                return;
-            }
-
-            var volume = result.Document.Descendants(UPnpNamespaces.RenderingControl + "GetVolumeResponse").Select(i => i.Element("CurrentVolume")).FirstOrDefault(i => i is not null);
-            var volumeValue = volume?.Value;
-
-            if (string.IsNullOrWhiteSpace(volumeValue))
-            {
-                return;
-            }
-
-            Volume = int.Parse(volumeValue, CultureInfo.InvariantCulture);
-
-            if (Volume > 0)
-            {
-                _muteVol = Volume;
-            }
-        }
-
-        private async Task GetMute(CancellationToken cancellationToken)
-        {
-            if (_disposed)
-            {
-                return;
-            }
-
-            var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false);
-
-            var command = rendererCommands?.ServiceActions.FirstOrDefault(c => c.Name == "GetMute");
-            if (command is null)
-            {
-                return;
-            }
-
-            var service = GetServiceRenderingControl();
-
-            if (service is null)
-            {
-                return;
-            }
-
-            var result = await new DlnaHttpClient(_logger, _httpClientFactory).SendCommandAsync(
-                Properties.BaseUrl,
-                service,
-                command.Name,
-                rendererCommands!.BuildPost(command, service.ServiceType), // null checked above
-                cancellationToken: cancellationToken).ConfigureAwait(false);
-
-            if (result is null || result.Document is null)
-            {
-                return;
-            }
-
-            var valueNode = result.Document.Descendants(UPnpNamespaces.RenderingControl + "GetMuteResponse")
-                                            .Select(i => i.Element("CurrentMute"))
-                                            .FirstOrDefault(i => i is not null);
-
-            IsMuted = string.Equals(valueNode?.Value, "1", StringComparison.OrdinalIgnoreCase);
-        }
-
-        private async Task<TransportState?> GetTransportInfo(TransportCommands avCommands, CancellationToken cancellationToken)
-        {
-            var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetTransportInfo");
-            if (command is null)
-            {
-                return null;
-            }
-
-            var service = GetAvTransportService();
-            if (service is null)
-            {
-                return null;
-            }
-
-            var result = await new DlnaHttpClient(_logger, _httpClientFactory).SendCommandAsync(
-                Properties.BaseUrl,
-                service,
-                command.Name,
-                avCommands.BuildPost(command, service.ServiceType),
-                cancellationToken: cancellationToken).ConfigureAwait(false);
-
-            if (result is null || result.Document is null)
-            {
-                return null;
-            }
-
-            var transportState =
-                result.Document.Descendants(UPnpNamespaces.AvTransport + "GetTransportInfoResponse").Select(i => i.Element("CurrentTransportState")).FirstOrDefault(i => i is not null);
-
-            var transportStateValue = transportState?.Value;
-
-            if (transportStateValue is not null
-                && Enum.TryParse(transportStateValue, true, out TransportState state))
-            {
-                return state;
-            }
-
-            return null;
-        }
-
-        private async Task<UBaseObject?> GetMediaInfo(TransportCommands avCommands, CancellationToken cancellationToken)
-        {
-            var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetMediaInfo");
-            if (command is null)
-            {
-                return null;
-            }
-
-            var service = GetAvTransportService();
-            if (service is null)
-            {
-                throw new InvalidOperationException("Unable to find service");
-            }
-
-            var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false);
-            if (rendererCommands is null)
-            {
-                return null;
-            }
-
-            var result = await new DlnaHttpClient(_logger, _httpClientFactory).SendCommandAsync(
-                Properties.BaseUrl,
-                service,
-                command.Name,
-                rendererCommands.BuildPost(command, service.ServiceType),
-                cancellationToken: cancellationToken).ConfigureAwait(false);
-
-            if (result is null || result.Document is null)
-            {
-                return null;
-            }
-
-            var track = result.Document.Descendants("CurrentURIMetaData").FirstOrDefault();
-
-            if (track is null)
-            {
-                return null;
-            }
-
-            var e = track.Element(UPnpNamespaces.Items) ?? track;
-
-            var elementString = (string)e;
-
-            if (!string.IsNullOrWhiteSpace(elementString))
-            {
-                return UpnpContainer.Create(e);
-            }
-
-            track = result.Document.Descendants("CurrentURI").FirstOrDefault();
-
-            if (track is null)
-            {
-                return null;
-            }
-
-            e = track.Element(UPnpNamespaces.Items) ?? track;
-
-            elementString = (string)e;
-
-            if (!string.IsNullOrWhiteSpace(elementString))
-            {
-                return new UBaseObject
-                {
-                    Url = elementString
-                };
-            }
-
-            return null;
-        }
-
-        private async Task<(bool Success, UBaseObject? Track)> GetPositionInfo(TransportCommands avCommands, CancellationToken cancellationToken)
-        {
-            var command = avCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetPositionInfo");
-            if (command is null)
-            {
-                return (false, null);
-            }
-
-            var service = GetAvTransportService();
-
-            if (service is null)
-            {
-                throw new InvalidOperationException("Unable to find service");
-            }
-
-            var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false);
-
-            if (rendererCommands is null)
-            {
-                return (false, null);
-            }
-
-            var result = await new DlnaHttpClient(_logger, _httpClientFactory).SendCommandAsync(
-                Properties.BaseUrl,
-                service,
-                command.Name,
-                rendererCommands.BuildPost(command, service.ServiceType),
-                cancellationToken: cancellationToken).ConfigureAwait(false);
-
-            if (result is null || result.Document is null)
-            {
-                return (false, null);
-            }
-
-            var trackUriElem = result.Document.Descendants(UPnpNamespaces.AvTransport + "GetPositionInfoResponse").Select(i => i.Element("TrackURI")).FirstOrDefault(i => i is not null);
-            var trackUri = trackUriElem?.Value;
-
-            var durationElem = result.Document.Descendants(UPnpNamespaces.AvTransport + "GetPositionInfoResponse").Select(i => i.Element("TrackDuration")).FirstOrDefault(i => i is not null);
-            var duration = durationElem?.Value;
-
-            if (!string.IsNullOrWhiteSpace(duration)
-                && !string.Equals(duration, "NOT_IMPLEMENTED", StringComparison.OrdinalIgnoreCase))
-            {
-                Duration = TimeSpan.Parse(duration, CultureInfo.InvariantCulture);
-            }
-            else
-            {
-                Duration = null;
-            }
-
-            var positionElem = result.Document.Descendants(UPnpNamespaces.AvTransport + "GetPositionInfoResponse").Select(i => i.Element("RelTime")).FirstOrDefault(i => i is not null);
-            var position = positionElem?.Value;
-
-            if (!string.IsNullOrWhiteSpace(position) && !string.Equals(position, "NOT_IMPLEMENTED", StringComparison.OrdinalIgnoreCase))
-            {
-                Position = TimeSpan.Parse(position, CultureInfo.InvariantCulture);
-            }
-
-            var track = result.Document.Descendants("TrackMetaData").FirstOrDefault();
-
-            if (track is null)
-            {
-                // If track is null, some vendors do this, use GetMediaInfo instead.
-                return (true, null);
-            }
-
-            var trackString = (string)track;
-
-            if (string.IsNullOrWhiteSpace(trackString) || string.Equals(trackString, "NOT_IMPLEMENTED", StringComparison.OrdinalIgnoreCase))
-            {
-                return (true, null);
-            }
-
-            XElement? uPnpResponse = null;
-
-            try
-            {
-                uPnpResponse = ParseResponse(trackString);
-            }
-            catch (Exception ex)
-            {
-                _logger.LogError(ex, "Uncaught exception while parsing xml");
-            }
-
-            if (uPnpResponse is null)
-            {
-                _logger.LogError("Failed to parse xml: \n {Xml}", trackString);
-                return (true, null);
-            }
-
-            var e = uPnpResponse.Element(UPnpNamespaces.Items);
-
-            var uTrack = CreateUBaseObject(e, trackUri);
-
-            return (true, uTrack);
-        }
-
-        private XElement? ParseResponse(string xml)
-        {
-            // Handle different variations sent back by devices.
-            try
-            {
-                return XElement.Parse(xml);
-            }
-            catch (XmlException)
-            {
-            }
-
-            // first try to add a root node with a dlna namespace.
-            try
-            {
-                return XElement.Parse("<data xmlns:dlna=\"urn:schemas-dlna-org:device-1-0\">" + xml + "</data>")
-                                .Descendants()
-                                .First();
-            }
-            catch (XmlException)
-            {
-            }
-
-            // some devices send back invalid xml
-            try
-            {
-                return XElement.Parse(xml.Replace("&", "&amp;", StringComparison.Ordinal));
-            }
-            catch (XmlException)
-            {
-            }
-
-            return null;
-        }
-
-        private static UBaseObject CreateUBaseObject(XElement? container, string? trackUri)
-        {
-            ArgumentNullException.ThrowIfNull(container);
-
-            var url = container.GetValue(UPnpNamespaces.Res);
-
-            if (string.IsNullOrWhiteSpace(url))
-            {
-                url = trackUri;
-            }
-
-            return new UBaseObject
-            {
-                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),
-                MetaData = container.ToString()
-            };
-        }
-
-        private static string[] GetProtocolInfo(XElement container)
-        {
-            ArgumentNullException.ThrowIfNull(container);
-
-            var resElement = container.Element(UPnpNamespaces.Res);
-
-            var info = resElement?.Attribute(UPnpNamespaces.ProtocolInfo);
-
-            if (info is not null && !string.IsNullOrWhiteSpace(info.Value))
-            {
-                return info.Value.Split(':');
-            }
-
-            return new string[4];
-        }
-
-        private async Task<TransportCommands?> GetAVProtocolAsync(CancellationToken cancellationToken)
-        {
-            if (AvCommands is not null)
-            {
-                return AvCommands;
-            }
-
-            if (_disposed)
-            {
-                throw new ObjectDisposedException(GetType().Name);
-            }
-
-            var avService = GetAvTransportService();
-            if (avService is null)
-            {
-                return null;
-            }
-
-            string url = NormalizeUrl(Properties.BaseUrl, avService.ScpdUrl);
-
-            var httpClient = new DlnaHttpClient(_logger, _httpClientFactory);
-
-            var document = await httpClient.GetDataAsync(url, cancellationToken).ConfigureAwait(false);
-            if (document is null)
-            {
-                return null;
-            }
-
-            AvCommands = TransportCommands.Create(document);
-            return AvCommands;
-        }
-
-        private async Task<TransportCommands?> GetRenderingProtocolAsync(CancellationToken cancellationToken)
-        {
-            if (RendererCommands is not null)
-            {
-                return RendererCommands;
-            }
-
-            if (_disposed)
-            {
-                throw new ObjectDisposedException(GetType().Name);
-            }
-
-            var avService = GetServiceRenderingControl();
-            ArgumentNullException.ThrowIfNull(avService);
-
-            string url = NormalizeUrl(Properties.BaseUrl, avService.ScpdUrl);
-
-            var httpClient = new DlnaHttpClient(_logger, _httpClientFactory);
-            _logger.LogDebug("Dlna Device.GetRenderingProtocolAsync");
-            var document = await httpClient.GetDataAsync(url, cancellationToken).ConfigureAwait(false);
-            if (document is null)
-            {
-                return null;
-            }
-
-            RendererCommands = TransportCommands.Create(document);
-            return RendererCommands;
-        }
-
-        private string NormalizeUrl(string baseUrl, string url)
-        {
-            // If it's already a complete url, don't stick anything onto the front of it
-            if (url.StartsWith("http", StringComparison.OrdinalIgnoreCase))
-            {
-                return url;
-            }
-
-            if (!url.Contains('/', StringComparison.Ordinal))
-            {
-                url = "/dmr/" + url;
-            }
-
-            if (!url.StartsWith('/'))
-            {
-                url = "/" + url;
-            }
-
-            return baseUrl + url;
-        }
-
-        public static async Task<Device?> CreateuPnpDeviceAsync(Uri url, IHttpClientFactory httpClientFactory, ILogger logger, CancellationToken cancellationToken)
-        {
-            var ssdpHttpClient = new DlnaHttpClient(logger, httpClientFactory);
-
-            var document = await ssdpHttpClient.GetDataAsync(url.ToString(), cancellationToken).ConfigureAwait(false);
-            if (document is null)
-            {
-                return null;
-            }
-
-            var friendlyNames = new List<string>();
-
-            var name = document.Descendants(UPnpNamespaces.Ud.GetName("friendlyName")).FirstOrDefault();
-            if (name is not null && !string.IsNullOrWhiteSpace(name.Value))
-            {
-                friendlyNames.Add(name.Value);
-            }
-
-            var room = document.Descendants(UPnpNamespaces.Ud.GetName("roomName")).FirstOrDefault();
-            if (room is not null && !string.IsNullOrWhiteSpace(room.Value))
-            {
-                friendlyNames.Add(room.Value);
-            }
-
-            var deviceProperties = new DeviceInfo()
-            {
-                Name = string.Join(' ', friendlyNames),
-                BaseUrl = string.Format(CultureInfo.InvariantCulture, "http://{0}:{1}", url.Host, url.Port)
-            };
-
-            var model = document.Descendants(UPnpNamespaces.Ud.GetName("modelName")).FirstOrDefault();
-            if (model is not null)
-            {
-                deviceProperties.ModelName = model.Value;
-            }
-
-            var modelNumber = document.Descendants(UPnpNamespaces.Ud.GetName("modelNumber")).FirstOrDefault();
-            if (modelNumber is not null)
-            {
-                deviceProperties.ModelNumber = modelNumber.Value;
-            }
-
-            var uuid = document.Descendants(UPnpNamespaces.Ud.GetName("UDN")).FirstOrDefault();
-            if (uuid is not null)
-            {
-                deviceProperties.UUID = uuid.Value;
-            }
-
-            var manufacturer = document.Descendants(UPnpNamespaces.Ud.GetName("manufacturer")).FirstOrDefault();
-            if (manufacturer is not null)
-            {
-                deviceProperties.Manufacturer = manufacturer.Value;
-            }
-
-            var manufacturerUrl = document.Descendants(UPnpNamespaces.Ud.GetName("manufacturerURL")).FirstOrDefault();
-            if (manufacturerUrl is not null)
-            {
-                deviceProperties.ManufacturerUrl = manufacturerUrl.Value;
-            }
-
-            var presentationUrl = document.Descendants(UPnpNamespaces.Ud.GetName("presentationURL")).FirstOrDefault();
-            if (presentationUrl is not null)
-            {
-                deviceProperties.PresentationUrl = presentationUrl.Value;
-            }
-
-            var modelUrl = document.Descendants(UPnpNamespaces.Ud.GetName("modelURL")).FirstOrDefault();
-            if (modelUrl is not null)
-            {
-                deviceProperties.ModelUrl = modelUrl.Value;
-            }
-
-            var serialNumber = document.Descendants(UPnpNamespaces.Ud.GetName("serialNumber")).FirstOrDefault();
-            if (serialNumber is not null)
-            {
-                deviceProperties.SerialNumber = serialNumber.Value;
-            }
-
-            var modelDescription = document.Descendants(UPnpNamespaces.Ud.GetName("modelDescription")).FirstOrDefault();
-            if (modelDescription is not null)
-            {
-                deviceProperties.ModelDescription = modelDescription.Value;
-            }
-
-            var icon = document.Descendants(UPnpNamespaces.Ud.GetName("icon")).FirstOrDefault();
-            if (icon is not null)
-            {
-                deviceProperties.Icon = CreateIcon(icon);
-            }
-
-            foreach (var services in document.Descendants(UPnpNamespaces.Ud.GetName("serviceList")))
-            {
-                if (services is null)
-                {
-                    continue;
-                }
-
-                var servicesList = services.Descendants(UPnpNamespaces.Ud.GetName("service"));
-                if (servicesList is null)
-                {
-                    continue;
-                }
-
-                foreach (var element in servicesList)
-                {
-                    var service = Create(element);
-
-                    if (service is not null)
-                    {
-                        deviceProperties.Services.Add(service);
-                    }
-                }
-            }
-
-            return new Device(deviceProperties, httpClientFactory, logger);
-        }
-
-        private static DeviceIcon CreateIcon(XElement element)
-        {
-            ArgumentNullException.ThrowIfNull(element);
-
-            var width = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("width"));
-            var height = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("height"));
-
-            _ = int.TryParse(width, NumberStyles.Integer, CultureInfo.InvariantCulture, out var widthValue);
-            _ = int.TryParse(height, NumberStyles.Integer, CultureInfo.InvariantCulture, out var heightValue);
-
-            return new DeviceIcon
-            {
-                Depth = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("depth")) ?? string.Empty,
-                Height = heightValue,
-                MimeType = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("mimetype")) ?? string.Empty,
-                Url = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("url")) ?? string.Empty,
-                Width = widthValue
-            };
-        }
-
-        private static DeviceService Create(XElement element)
-            => new DeviceService()
-            {
-                ControlUrl = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("controlURL")) ?? string.Empty,
-                EventSubUrl = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("eventSubURL")) ?? string.Empty,
-                ScpdUrl = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("SCPDURL")) ?? string.Empty,
-                ServiceId = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("serviceId")) ?? string.Empty,
-                ServiceType = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("serviceType")) ?? string.Empty
-            };
-
-        private void UpdateMediaInfo(UBaseObject? mediaInfo, TransportState state)
-        {
-            TransportState = state;
-
-            var previousMediaInfo = CurrentMediaInfo;
-            CurrentMediaInfo = mediaInfo;
-
-            if (mediaInfo is null)
-            {
-                if (previousMediaInfo is not null)
-                {
-                    OnPlaybackStop(previousMediaInfo);
-                }
-            }
-            else if (previousMediaInfo is null)
-            {
-                if (state != TransportState.STOPPED)
-                {
-                    OnPlaybackStart(mediaInfo);
-                }
-            }
-            else if (mediaInfo.Equals(previousMediaInfo))
-            {
-                OnPlaybackProgress(mediaInfo);
-            }
-            else
-            {
-                OnMediaChanged(previousMediaInfo, mediaInfo);
-            }
-        }
-
-        private void OnPlaybackStart(UBaseObject mediaInfo)
-        {
-            if (string.IsNullOrWhiteSpace(mediaInfo.Url))
-            {
-                return;
-            }
-
-            PlaybackStart?.Invoke(this, new PlaybackStartEventArgs(mediaInfo));
-        }
-
-        private void OnPlaybackProgress(UBaseObject mediaInfo)
-        {
-            if (string.IsNullOrWhiteSpace(mediaInfo.Url))
-            {
-                return;
-            }
-
-            PlaybackProgress?.Invoke(this, new PlaybackProgressEventArgs(mediaInfo));
-        }
-
-        private void OnPlaybackStop(UBaseObject mediaInfo)
-        {
-            PlaybackStopped?.Invoke(this, new PlaybackStoppedEventArgs(mediaInfo));
-        }
-
-        private void OnMediaChanged(UBaseObject old, UBaseObject newMedia)
-        {
-            MediaChanged?.Invoke(this, new MediaChangedEventArgs(old, newMedia));
-        }
-
-        /// <inheritdoc />
-        public void Dispose()
-        {
-            Dispose(true);
-            GC.SuppressFinalize(this);
-        }
-
-        /// <summary>
-        /// Releases unmanaged and optionally managed resources.
-        /// </summary>
-        /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
-        protected virtual void Dispose(bool disposing)
-        {
-            if (_disposed)
-            {
-                return;
-            }
-
-            if (disposing)
-            {
-                _timer?.Dispose();
-                _timer = null;
-                Properties = null!;
-            }
-
-            _disposed = true;
-        }
-
-        /// <inheritdoc />
-        public override string ToString()
-        {
-            return string.Format(CultureInfo.InvariantCulture, "{0} - {1}", Properties.Name, Properties.BaseUrl);
-        }
-    }
-}

+ 0 - 66
Emby.Dlna/PlayTo/DeviceInfo.cs

@@ -1,66 +0,0 @@
-#nullable disable
-
-#pragma warning disable CS1591
-
-using System.Collections.Generic;
-using Emby.Dlna.Common;
-using MediaBrowser.Model.Dlna;
-
-namespace Emby.Dlna.PlayTo
-{
-    public class DeviceInfo
-    {
-        private readonly List<DeviceService> _services = new List<DeviceService>();
-        private string _baseUrl = string.Empty;
-
-        public DeviceInfo()
-        {
-            Name = "Generic Device";
-        }
-
-        public string UUID { get; set; }
-
-        public string Name { get; set; }
-
-        public string ModelName { get; set; }
-
-        public string ModelNumber { get; set; }
-
-        public string ModelDescription { get; set; }
-
-        public string ModelUrl { get; set; }
-
-        public string Manufacturer { get; set; }
-
-        public string SerialNumber { get; set; }
-
-        public string ManufacturerUrl { get; set; }
-
-        public string PresentationUrl { get; set; }
-
-        public string BaseUrl
-        {
-            get => _baseUrl;
-            set => _baseUrl = value;
-        }
-
-        public DeviceIcon Icon { get; set; }
-
-        public List<DeviceService> Services => _services;
-
-        public DeviceIdentification ToDeviceIdentification()
-        {
-            return new DeviceIdentification
-            {
-                Manufacturer = Manufacturer,
-                ModelName = ModelName,
-                ModelNumber = ModelNumber,
-                FriendlyName = Name,
-                ManufacturerUrl = ManufacturerUrl,
-                ModelUrl = ModelUrl,
-                ModelDescription = ModelDescription,
-                SerialNumber = SerialNumber
-            };
-        }
-    }
-}

+ 0 - 137
Emby.Dlna/PlayTo/DlnaHttpClient.cs

@@ -1,137 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using System.Globalization;
-using System.IO;
-using System.Net.Http;
-using System.Net.Mime;
-using System.Text;
-using System.Text.RegularExpressions;
-using System.Threading;
-using System.Threading.Tasks;
-using System.Xml;
-using System.Xml.Linq;
-using Emby.Dlna.Common;
-using MediaBrowser.Common.Net;
-using Microsoft.Extensions.Logging;
-
-namespace Emby.Dlna.PlayTo
-{
-    /// <summary>
-    /// Http client for Dlna PlayTo function.
-    /// </summary>
-    public partial class DlnaHttpClient
-    {
-        private readonly ILogger _logger;
-        private readonly IHttpClientFactory _httpClientFactory;
-
-        public DlnaHttpClient(ILogger logger, IHttpClientFactory httpClientFactory)
-        {
-            _logger = logger;
-            _httpClientFactory = httpClientFactory;
-        }
-
-        [GeneratedRegex("(&(?![a-z]*;))")]
-        private static partial Regex EscapeAmpersandRegex();
-
-        private static string NormalizeServiceUrl(string baseUrl, string serviceUrl)
-        {
-            // If it's already a complete url, don't stick anything onto the front of it
-            if (serviceUrl.StartsWith("http", StringComparison.OrdinalIgnoreCase))
-            {
-                return serviceUrl;
-            }
-
-            if (!serviceUrl.StartsWith('/'))
-            {
-                serviceUrl = "/" + serviceUrl;
-            }
-
-            return baseUrl + serviceUrl;
-        }
-
-        private async Task<XDocument?> SendRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken)
-        {
-            var client = _httpClientFactory.CreateClient(NamedClient.Dlna);
-            using var response = await client.SendAsync(request, cancellationToken).ConfigureAwait(false);
-            response.EnsureSuccessStatusCode();
-            Stream stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
-            await using (stream.ConfigureAwait(false))
-            {
-                try
-                {
-                    return await XDocument.LoadAsync(
-                        stream,
-                        LoadOptions.None,
-                        cancellationToken).ConfigureAwait(false);
-                }
-                catch (XmlException)
-                {
-                    // try correcting the Xml response with common errors
-                    stream.Position = 0;
-                    using StreamReader sr = new StreamReader(stream);
-                    var xmlString = await sr.ReadToEndAsync(cancellationToken).ConfigureAwait(false);
-
-                    // find and replace unescaped ampersands (&)
-                    xmlString = EscapeAmpersandRegex().Replace(xmlString, "&amp;");
-
-                    try
-                    {
-                        // retry reading Xml
-                        using var xmlReader = new StringReader(xmlString);
-                        return await XDocument.LoadAsync(
-                            xmlReader,
-                            LoadOptions.None,
-                            cancellationToken).ConfigureAwait(false);
-                    }
-                    catch (XmlException ex)
-                    {
-                        _logger.LogError(ex, "Failed to parse response");
-                        _logger.LogDebug("Malformed response: {Content}\n", xmlString);
-
-                        return null;
-                    }
-                }
-            }
-        }
-
-        public async Task<XDocument?> GetDataAsync(string url, CancellationToken cancellationToken)
-        {
-            using var request = new HttpRequestMessage(HttpMethod.Get, url);
-
-            // Have to await here instead of returning the Task directly, otherwise request would be disposed too soon
-            return await SendRequestAsync(request, cancellationToken).ConfigureAwait(false);
-        }
-
-        public async Task<XDocument?> SendCommandAsync(
-            string baseUrl,
-            DeviceService service,
-            string command,
-            string postData,
-            string? header = null,
-            CancellationToken cancellationToken = default)
-        {
-            using var request = new HttpRequestMessage(HttpMethod.Post, NormalizeServiceUrl(baseUrl, service.ControlUrl))
-            {
-                Content = new StringContent(postData, Encoding.UTF8, MediaTypeNames.Text.Xml)
-            };
-
-            request.Headers.TryAddWithoutValidation(
-                "SOAPACTION",
-                string.Format(
-                    CultureInfo.InvariantCulture,
-                    "\"{0}#{1}\"",
-                    service.ServiceType,
-                    command));
-            request.Headers.Pragma.ParseAdd("no-cache");
-
-            if (!string.IsNullOrEmpty(header))
-            {
-                request.Headers.TryAddWithoutValidation("contentFeatures.dlna.org", header);
-            }
-
-            // Have to await here instead of returning the Task directly, otherwise request would be disposed too soon
-            return await SendRequestAsync(request, cancellationToken).ConfigureAwait(false);
-        }
-    }
-}

+ 0 - 19
Emby.Dlna/PlayTo/MediaChangedEventArgs.cs

@@ -1,19 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-
-namespace Emby.Dlna.PlayTo
-{
-    public class MediaChangedEventArgs : EventArgs
-    {
-        public MediaChangedEventArgs(UBaseObject oldMediaInfo, UBaseObject newMediaInfo)
-        {
-            OldMediaInfo = oldMediaInfo;
-            NewMediaInfo = newMediaInfo;
-        }
-
-        public UBaseObject OldMediaInfo { get; set; }
-
-        public UBaseObject NewMediaInfo { get; set; }
-    }
-}

+ 0 - 980
Emby.Dlna/PlayTo/PlayToController.cs

@@ -1,980 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using Emby.Dlna.Didl;
-using Jellyfin.Data.Entities;
-using Jellyfin.Data.Enums;
-using Jellyfin.Data.Events;
-using MediaBrowser.Controller.Dlna;
-using MediaBrowser.Controller.Drawing;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Controller.Session;
-using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.Globalization;
-using MediaBrowser.Model.Session;
-using Microsoft.AspNetCore.WebUtilities;
-using Microsoft.Extensions.Logging;
-using Photo = MediaBrowser.Controller.Entities.Photo;
-
-namespace Emby.Dlna.PlayTo
-{
-    public class PlayToController : ISessionController, IDisposable
-    {
-        private readonly SessionInfo _session;
-        private readonly ISessionManager _sessionManager;
-        private readonly ILibraryManager _libraryManager;
-        private readonly ILogger _logger;
-        private readonly IDlnaManager _dlnaManager;
-        private readonly IUserManager _userManager;
-        private readonly IImageProcessor _imageProcessor;
-        private readonly IUserDataManager _userDataManager;
-        private readonly ILocalizationManager _localization;
-        private readonly IMediaSourceManager _mediaSourceManager;
-        private readonly IMediaEncoder _mediaEncoder;
-
-        private readonly IDeviceDiscovery _deviceDiscovery;
-        private readonly string _serverAddress;
-        private readonly string? _accessToken;
-
-        private readonly List<PlaylistItem> _playlist = new List<PlaylistItem>();
-        private Device _device;
-        private int _currentPlaylistIndex;
-
-        private bool _disposed;
-
-        public PlayToController(
-            SessionInfo session,
-            ISessionManager sessionManager,
-            ILibraryManager libraryManager,
-            ILogger logger,
-            IDlnaManager dlnaManager,
-            IUserManager userManager,
-            IImageProcessor imageProcessor,
-            string serverAddress,
-            string? accessToken,
-            IDeviceDiscovery deviceDiscovery,
-            IUserDataManager userDataManager,
-            ILocalizationManager localization,
-            IMediaSourceManager mediaSourceManager,
-            IMediaEncoder mediaEncoder,
-            Device device)
-        {
-            _session = session;
-            _sessionManager = sessionManager;
-            _libraryManager = libraryManager;
-            _logger = logger;
-            _dlnaManager = dlnaManager;
-            _userManager = userManager;
-            _imageProcessor = imageProcessor;
-            _serverAddress = serverAddress;
-            _accessToken = accessToken;
-            _deviceDiscovery = deviceDiscovery;
-            _userDataManager = userDataManager;
-            _localization = localization;
-            _mediaSourceManager = mediaSourceManager;
-            _mediaEncoder = mediaEncoder;
-
-            _device = device;
-            _device.OnDeviceUnavailable = OnDeviceUnavailable;
-            _device.PlaybackStart += OnDevicePlaybackStart;
-            _device.PlaybackProgress += OnDevicePlaybackProgress;
-            _device.PlaybackStopped += OnDevicePlaybackStopped;
-            _device.MediaChanged += OnDeviceMediaChanged;
-
-            _device.Start();
-
-            _deviceDiscovery.DeviceLeft += OnDeviceDiscoveryDeviceLeft;
-        }
-
-        public bool IsSessionActive => !_disposed;
-
-        public bool SupportsMediaControl => IsSessionActive;
-
-        /*
-         * Send a message to the DLNA device to notify what is the next track in the playlist.
-         */
-        private async Task SendNextTrackMessage(int currentPlayListItemIndex, CancellationToken cancellationToken)
-        {
-            if (currentPlayListItemIndex >= 0 && currentPlayListItemIndex < _playlist.Count - 1)
-            {
-                // The current playing item is indeed in the play list and we are not yet at the end of the playlist.
-                var nextItemIndex = currentPlayListItemIndex + 1;
-                var nextItem = _playlist[nextItemIndex];
-
-                // Send the SetNextAvTransport message.
-                await _device.SetNextAvTransport(nextItem.StreamUrl, GetDlnaHeaders(nextItem), nextItem.Didl, cancellationToken).ConfigureAwait(false);
-            }
-        }
-
-        private void OnDeviceUnavailable()
-        {
-            try
-            {
-                _sessionManager.ReportSessionEnded(_session.Id);
-            }
-            catch (Exception ex)
-            {
-                // Could throw if the session is already gone
-                _logger.LogError(ex, "Error reporting the end of session {Id}", _session.Id);
-            }
-        }
-
-        private void OnDeviceDiscoveryDeviceLeft(object? sender, GenericEventArgs<UpnpDeviceInfo> e)
-        {
-            var info = e.Argument;
-
-            if (!_disposed
-                && info.Headers.TryGetValue("USN", out string? usn)
-                && usn.IndexOf(_device.Properties.UUID, StringComparison.OrdinalIgnoreCase) != -1
-                && (usn.IndexOf("MediaRenderer:", StringComparison.OrdinalIgnoreCase) != -1
-                    || (info.Headers.TryGetValue("NT", out string? nt)
-                        && nt.IndexOf("MediaRenderer:", StringComparison.OrdinalIgnoreCase) != -1)))
-            {
-                OnDeviceUnavailable();
-            }
-        }
-
-        private async void OnDeviceMediaChanged(object? sender, MediaChangedEventArgs e)
-        {
-            if (_disposed || string.IsNullOrEmpty(e.OldMediaInfo.Url))
-            {
-                return;
-            }
-
-            try
-            {
-                var streamInfo = StreamParams.ParseFromUrl(e.OldMediaInfo.Url, _libraryManager, _mediaSourceManager);
-                if (streamInfo.Item is not null)
-                {
-                    var positionTicks = GetProgressPositionTicks(streamInfo);
-
-                    await ReportPlaybackStopped(streamInfo, positionTicks).ConfigureAwait(false);
-                }
-
-                streamInfo = StreamParams.ParseFromUrl(e.NewMediaInfo.Url, _libraryManager, _mediaSourceManager);
-                if (streamInfo.Item is null)
-                {
-                    return;
-                }
-
-                var newItemProgress = GetProgressInfo(streamInfo);
-
-                await _sessionManager.OnPlaybackStart(newItemProgress).ConfigureAwait(false);
-
-                // Send a message to the DLNA device to notify what is the next track in the playlist.
-                var currentItemIndex = _playlist.FindIndex(item => item.StreamInfo.ItemId.Equals(streamInfo.ItemId));
-                if (currentItemIndex >= 0)
-                {
-                    _currentPlaylistIndex = currentItemIndex;
-                }
-
-                await SendNextTrackMessage(currentItemIndex, CancellationToken.None).ConfigureAwait(false);
-            }
-            catch (Exception ex)
-            {
-                _logger.LogError(ex, "Error reporting progress");
-            }
-        }
-
-        private async void OnDevicePlaybackStopped(object? sender, PlaybackStoppedEventArgs e)
-        {
-            if (_disposed)
-            {
-                return;
-            }
-
-            try
-            {
-                var streamInfo = StreamParams.ParseFromUrl(e.MediaInfo.Url, _libraryManager, _mediaSourceManager);
-
-                if (streamInfo.Item is null)
-                {
-                    return;
-                }
-
-                var positionTicks = GetProgressPositionTicks(streamInfo);
-
-                await ReportPlaybackStopped(streamInfo, positionTicks).ConfigureAwait(false);
-
-                var mediaSource = await streamInfo.GetMediaSource(CancellationToken.None).ConfigureAwait(false);
-
-                var duration = mediaSource is null
-                    ? _device.Duration?.Ticks
-                    : mediaSource.RunTimeTicks;
-
-                var playedToCompletion = positionTicks.HasValue && positionTicks.Value == 0;
-
-                if (!playedToCompletion && duration.HasValue && positionTicks.HasValue)
-                {
-                    double percent = positionTicks.Value;
-                    percent /= duration.Value;
-
-                    playedToCompletion = Math.Abs(1 - percent) <= .1;
-                }
-
-                if (playedToCompletion)
-                {
-                    await SetPlaylistIndex(_currentPlaylistIndex + 1).ConfigureAwait(false);
-                }
-                else
-                {
-                    _playlist.Clear();
-                }
-            }
-            catch (Exception ex)
-            {
-                _logger.LogError(ex, "Error reporting playback stopped");
-            }
-        }
-
-        private async Task ReportPlaybackStopped(StreamParams streamInfo, long? positionTicks)
-        {
-            try
-            {
-                await _sessionManager.OnPlaybackStopped(new PlaybackStopInfo
-                {
-                    ItemId = streamInfo.ItemId,
-                    SessionId = _session.Id,
-                    PositionTicks = positionTicks,
-                    MediaSourceId = streamInfo.MediaSourceId
-                }).ConfigureAwait(false);
-            }
-            catch (Exception ex)
-            {
-                _logger.LogError(ex, "Error reporting progress");
-            }
-        }
-
-        private async void OnDevicePlaybackStart(object? sender, PlaybackStartEventArgs e)
-        {
-            if (_disposed)
-            {
-                return;
-            }
-
-            try
-            {
-                var info = StreamParams.ParseFromUrl(e.MediaInfo.Url, _libraryManager, _mediaSourceManager);
-
-                if (info.Item is not null)
-                {
-                    var progress = GetProgressInfo(info);
-
-                    await _sessionManager.OnPlaybackStart(progress).ConfigureAwait(false);
-                }
-            }
-            catch (Exception ex)
-            {
-                _logger.LogError(ex, "Error reporting progress");
-            }
-        }
-
-        private async void OnDevicePlaybackProgress(object? sender, PlaybackProgressEventArgs e)
-        {
-            if (_disposed)
-            {
-                return;
-            }
-
-            try
-            {
-                var mediaUrl = e.MediaInfo.Url;
-
-                if (string.IsNullOrWhiteSpace(mediaUrl))
-                {
-                    return;
-                }
-
-                var info = StreamParams.ParseFromUrl(mediaUrl, _libraryManager, _mediaSourceManager);
-
-                if (info.Item is not null)
-                {
-                    var progress = GetProgressInfo(info);
-
-                    await _sessionManager.OnPlaybackProgress(progress).ConfigureAwait(false);
-                }
-            }
-            catch (Exception ex)
-            {
-                _logger.LogError(ex, "Error reporting progress");
-            }
-        }
-
-        private long? GetProgressPositionTicks(StreamParams info)
-        {
-            var ticks = _device.Position.Ticks;
-
-            if (!EnableClientSideSeek(info))
-            {
-                ticks += info.StartPositionTicks;
-            }
-
-            return ticks;
-        }
-
-        private PlaybackStartInfo GetProgressInfo(StreamParams info)
-        {
-            return new PlaybackStartInfo
-            {
-                ItemId = info.ItemId,
-                SessionId = _session.Id,
-                PositionTicks = GetProgressPositionTicks(info),
-                IsMuted = _device.IsMuted,
-                IsPaused = _device.IsPaused,
-                MediaSourceId = info.MediaSourceId,
-                AudioStreamIndex = info.AudioStreamIndex,
-                SubtitleStreamIndex = info.SubtitleStreamIndex,
-                VolumeLevel = _device.Volume,
-
-                CanSeek = true,
-
-                PlayMethod = info.IsDirectStream ? PlayMethod.DirectStream : PlayMethod.Transcode
-            };
-        }
-
-        public Task SendPlayCommand(PlayRequest command, CancellationToken cancellationToken)
-        {
-            _logger.LogDebug("{0} - Received PlayRequest: {1}", _session.DeviceName, command.PlayCommand);
-
-            var user = command.ControllingUserId.Equals(default)
-                ? null :
-                _userManager.GetUserById(command.ControllingUserId);
-
-            var items = new List<BaseItem>();
-            foreach (var id in command.ItemIds)
-            {
-                AddItemFromId(id, items);
-            }
-
-            var startIndex = command.StartIndex ?? 0;
-            int len = items.Count - startIndex;
-            if (startIndex > 0)
-            {
-                items = items.GetRange(startIndex, len);
-            }
-
-            var playlist = new PlaylistItem[len];
-
-            // Not nullable enabled - so this is required.
-            playlist[0] = CreatePlaylistItem(
-                items[0],
-                user,
-                command.StartPositionTicks ?? 0,
-                command.MediaSourceId ?? string.Empty,
-                command.AudioStreamIndex,
-                command.SubtitleStreamIndex);
-
-            for (int i = 1; i < len; i++)
-            {
-                playlist[i] = CreatePlaylistItem(items[i], user, 0, string.Empty, null, null);
-            }
-
-            _logger.LogDebug("{0} - Playlist created", _session.DeviceName);
-
-            if (command.PlayCommand == PlayCommand.PlayLast)
-            {
-                _playlist.AddRange(playlist);
-            }
-
-            if (command.PlayCommand == PlayCommand.PlayNext)
-            {
-                _playlist.AddRange(playlist);
-            }
-
-            if (!command.ControllingUserId.Equals(default))
-            {
-                _sessionManager.LogSessionActivity(
-                    _session.Client,
-                    _session.ApplicationVersion,
-                    _session.DeviceId,
-                    _session.DeviceName,
-                    _session.RemoteEndPoint,
-                    user);
-            }
-
-            return PlayItems(playlist, cancellationToken);
-        }
-
-        private Task SendPlaystateCommand(PlaystateRequest command, CancellationToken cancellationToken)
-        {
-            switch (command.Command)
-            {
-                case PlaystateCommand.Stop:
-                    _playlist.Clear();
-                    return _device.SetStop(CancellationToken.None);
-
-                case PlaystateCommand.Pause:
-                    return _device.SetPause(CancellationToken.None);
-
-                case PlaystateCommand.Unpause:
-                    return _device.SetPlay(CancellationToken.None);
-
-                case PlaystateCommand.PlayPause:
-                    return _device.IsPaused ? _device.SetPlay(CancellationToken.None) : _device.SetPause(CancellationToken.None);
-
-                case PlaystateCommand.Seek:
-                    return Seek(command.SeekPositionTicks ?? 0);
-
-                case PlaystateCommand.NextTrack:
-                    return SetPlaylistIndex(_currentPlaylistIndex + 1, cancellationToken);
-
-                case PlaystateCommand.PreviousTrack:
-                    return SetPlaylistIndex(_currentPlaylistIndex - 1, cancellationToken);
-            }
-
-            return Task.CompletedTask;
-        }
-
-        private async Task Seek(long newPosition)
-        {
-            var media = _device.CurrentMediaInfo;
-
-            if (media is not null)
-            {
-                var info = StreamParams.ParseFromUrl(media.Url, _libraryManager, _mediaSourceManager);
-
-                if (info.Item is not null && !EnableClientSideSeek(info))
-                {
-                    var user = _session.UserId.Equals(default)
-                        ? null
-                        : _userManager.GetUserById(_session.UserId);
-                    var newItem = CreatePlaylistItem(info.Item, user, newPosition, info.MediaSourceId, info.AudioStreamIndex, info.SubtitleStreamIndex);
-
-                    await _device.SetAvTransport(newItem.StreamUrl, GetDlnaHeaders(newItem), newItem.Didl, CancellationToken.None).ConfigureAwait(false);
-
-                    // Send a message to the DLNA device to notify what is the next track in the play list.
-                    var newItemIndex = _playlist.FindIndex(item => item.StreamUrl == newItem.StreamUrl);
-                    await SendNextTrackMessage(newItemIndex, CancellationToken.None).ConfigureAwait(false);
-
-                    return;
-                }
-
-                await SeekAfterTransportChange(newPosition, CancellationToken.None).ConfigureAwait(false);
-            }
-        }
-
-        private bool EnableClientSideSeek(StreamParams info)
-        {
-            return info.IsDirectStream;
-        }
-
-        private bool EnableClientSideSeek(StreamInfo info)
-        {
-            return info.IsDirectStream;
-        }
-
-        private void AddItemFromId(Guid id, List<BaseItem> list)
-        {
-            var item = _libraryManager.GetItemById(id);
-            if (item.MediaType == MediaType.Audio || item.MediaType == MediaType.Video)
-            {
-                list.Add(item);
-            }
-        }
-
-        private PlaylistItem CreatePlaylistItem(
-            BaseItem item,
-            User? user,
-            long startPostionTicks,
-            string? mediaSourceId,
-            int? audioStreamIndex,
-            int? subtitleStreamIndex)
-        {
-            var deviceInfo = _device.Properties;
-
-            var profile = _dlnaManager.GetProfile(deviceInfo.ToDeviceIdentification()) ??
-                _dlnaManager.GetDefaultProfile();
-
-            var mediaSources = item is IHasMediaSources
-                ? _mediaSourceManager.GetStaticMediaSources(item, true, user).ToArray()
-                : Array.Empty<MediaSourceInfo>();
-
-            var playlistItem = GetPlaylistItem(item, mediaSources, profile, _session.DeviceId, mediaSourceId, audioStreamIndex, subtitleStreamIndex);
-            playlistItem.StreamInfo.StartPositionTicks = startPostionTicks;
-
-            playlistItem.StreamUrl = DidlBuilder.NormalizeDlnaMediaUrl(playlistItem.StreamInfo.ToUrl(_serverAddress, _accessToken));
-
-            var itemXml = new DidlBuilder(
-                profile,
-                user,
-                _imageProcessor,
-                _serverAddress,
-                _accessToken,
-                _userDataManager,
-                _localization,
-                _mediaSourceManager,
-                _logger,
-                _mediaEncoder,
-                _libraryManager)
-                .GetItemDidl(item, user, null, _session.DeviceId, new Filter(), playlistItem.StreamInfo);
-
-            playlistItem.Didl = itemXml;
-
-            return playlistItem;
-        }
-
-        private string? GetDlnaHeaders(PlaylistItem item)
-        {
-            var profile = item.Profile;
-            var streamInfo = item.StreamInfo;
-
-            if (streamInfo.MediaType == DlnaProfileType.Audio)
-            {
-                return ContentFeatureBuilder.BuildAudioHeader(
-                        profile,
-                        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 = ContentFeatureBuilder.BuildVideoHeader(
-                        profile,
-                        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.TargetVideoRangeType,
-                        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.FirstOrDefault();
-            }
-
-            return null;
-        }
-
-        private PlaylistItem GetPlaylistItem(BaseItem item, MediaSourceInfo[] mediaSources, DeviceProfile profile, string deviceId, string? mediaSourceId, int? audioStreamIndex, int? subtitleStreamIndex)
-        {
-            if (item.MediaType == MediaType.Video)
-            {
-                return new PlaylistItem
-                {
-                    StreamInfo = new StreamBuilder(_mediaEncoder, _logger).GetOptimalVideoStream(new MediaOptions
-                    {
-                        ItemId = item.Id,
-                        MediaSources = mediaSources,
-                        Profile = profile,
-                        DeviceId = deviceId,
-                        MaxBitrate = profile.MaxStreamingBitrate,
-                        MediaSourceId = mediaSourceId,
-                        AudioStreamIndex = audioStreamIndex,
-                        SubtitleStreamIndex = subtitleStreamIndex
-                    }),
-
-                    Profile = profile
-                };
-            }
-
-            if (item.MediaType == MediaType.Audio)
-            {
-                return new PlaylistItem
-                {
-                    StreamInfo = new StreamBuilder(_mediaEncoder, _logger).GetOptimalAudioStream(new MediaOptions
-                    {
-                        ItemId = item.Id,
-                        MediaSources = mediaSources,
-                        Profile = profile,
-                        DeviceId = deviceId,
-                        MaxBitrate = profile.MaxStreamingBitrate,
-                        MediaSourceId = mediaSourceId
-                    }),
-
-                    Profile = profile
-                };
-            }
-
-            if (item.MediaType == MediaType.Photo)
-            {
-                return PlaylistItemFactory.Create((Photo)item, profile);
-            }
-
-            throw new ArgumentException("Unrecognized item type.");
-        }
-
-        /// <summary>
-        /// Plays the items.
-        /// </summary>
-        /// <param name="items">The items.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns><c>true</c> on success.</returns>
-        private async Task<bool> PlayItems(IEnumerable<PlaylistItem> items, CancellationToken cancellationToken = default)
-        {
-            _playlist.Clear();
-            _playlist.AddRange(items);
-            _logger.LogDebug("{0} - Playing {1} items", _session.DeviceName, _playlist.Count);
-
-            await SetPlaylistIndex(0, cancellationToken).ConfigureAwait(false);
-            return true;
-        }
-
-        private async Task SetPlaylistIndex(int index, CancellationToken cancellationToken = default)
-        {
-            if (index < 0 || index >= _playlist.Count)
-            {
-                _playlist.Clear();
-                await _device.SetStop(cancellationToken).ConfigureAwait(false);
-                return;
-            }
-
-            _currentPlaylistIndex = index;
-            var currentitem = _playlist[index];
-
-            await _device.SetAvTransport(currentitem.StreamUrl, GetDlnaHeaders(currentitem), currentitem.Didl, cancellationToken).ConfigureAwait(false);
-
-            // Send a message to the DLNA device to notify what is the next track in the play list.
-            await SendNextTrackMessage(index, cancellationToken).ConfigureAwait(false);
-
-            var streamInfo = currentitem.StreamInfo;
-            if (streamInfo.StartPositionTicks > 0 && EnableClientSideSeek(streamInfo))
-            {
-                await SeekAfterTransportChange(streamInfo.StartPositionTicks, CancellationToken.None).ConfigureAwait(false);
-            }
-        }
-
-        /// <inheritdoc />
-        public void Dispose()
-        {
-            Dispose(true);
-            GC.SuppressFinalize(this);
-        }
-
-        /// <summary>
-        /// Releases unmanaged and optionally managed resources.
-        /// </summary>
-        /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
-        protected virtual void Dispose(bool disposing)
-        {
-            if (_disposed)
-            {
-                return;
-            }
-
-            if (disposing)
-            {
-                _device.PlaybackStart -= OnDevicePlaybackStart;
-                _device.PlaybackProgress -= OnDevicePlaybackProgress;
-                _device.PlaybackStopped -= OnDevicePlaybackStopped;
-                _device.MediaChanged -= OnDeviceMediaChanged;
-                _deviceDiscovery.DeviceLeft -= OnDeviceDiscoveryDeviceLeft;
-                _device.OnDeviceUnavailable = null;
-                _device.Dispose();
-            }
-
-            _disposed = true;
-        }
-
-        private Task SendGeneralCommand(GeneralCommand command, CancellationToken cancellationToken)
-        {
-            switch (command.Name)
-            {
-                case GeneralCommandType.VolumeDown:
-                    return _device.VolumeDown(cancellationToken);
-                case GeneralCommandType.VolumeUp:
-                    return _device.VolumeUp(cancellationToken);
-                case GeneralCommandType.Mute:
-                    return _device.Mute(cancellationToken);
-                case GeneralCommandType.Unmute:
-                    return _device.Unmute(cancellationToken);
-                case GeneralCommandType.ToggleMute:
-                    return _device.ToggleMute(cancellationToken);
-                case GeneralCommandType.SetAudioStreamIndex:
-                    if (command.Arguments.TryGetValue("Index", out string? index))
-                    {
-                        if (int.TryParse(index, NumberStyles.Integer, CultureInfo.InvariantCulture, out var val))
-                        {
-                            return SetAudioStreamIndex(val);
-                        }
-
-                        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 (int.TryParse(index, NumberStyles.Integer, CultureInfo.InvariantCulture, out var val))
-                        {
-                            return SetSubtitleStreamIndex(val);
-                        }
-
-                        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 (int.TryParse(vol, NumberStyles.Integer, CultureInfo.InvariantCulture, out var volume))
-                        {
-                            return _device.SetVolume(volume, cancellationToken);
-                        }
-
-                        throw new ArgumentException("Unsupported volume value supplied.");
-                    }
-
-                    throw new ArgumentException("Volume argument cannot be null");
-                default:
-                    return Task.CompletedTask;
-            }
-        }
-
-        private async Task SetAudioStreamIndex(int? newIndex)
-        {
-            var media = _device.CurrentMediaInfo;
-
-            if (media is not null)
-            {
-                var info = StreamParams.ParseFromUrl(media.Url, _libraryManager, _mediaSourceManager);
-
-                if (info.Item is not null)
-                {
-                    var newPosition = GetProgressPositionTicks(info) ?? 0;
-
-                    var user = _session.UserId.Equals(default)
-                        ? null
-                        : _userManager.GetUserById(_session.UserId);
-                    var newItem = CreatePlaylistItem(info.Item, user, newPosition, info.MediaSourceId, newIndex, info.SubtitleStreamIndex);
-
-                    await _device.SetAvTransport(newItem.StreamUrl, GetDlnaHeaders(newItem), newItem.Didl, CancellationToken.None).ConfigureAwait(false);
-
-                    // Send a message to the DLNA device to notify what is the next track in the play list.
-                    var newItemIndex = _playlist.FindIndex(item => item.StreamUrl == newItem.StreamUrl);
-                    await SendNextTrackMessage(newItemIndex, CancellationToken.None).ConfigureAwait(false);
-
-                    if (EnableClientSideSeek(newItem.StreamInfo))
-                    {
-                        await SeekAfterTransportChange(newPosition, CancellationToken.None).ConfigureAwait(false);
-                    }
-                }
-            }
-        }
-
-        private async Task SetSubtitleStreamIndex(int? newIndex)
-        {
-            var media = _device.CurrentMediaInfo;
-
-            if (media is not null)
-            {
-                var info = StreamParams.ParseFromUrl(media.Url, _libraryManager, _mediaSourceManager);
-
-                if (info.Item is not null)
-                {
-                    var newPosition = GetProgressPositionTicks(info) ?? 0;
-
-                    var user = _session.UserId.Equals(default)
-                        ? null
-                        : _userManager.GetUserById(_session.UserId);
-                    var newItem = CreatePlaylistItem(info.Item, user, newPosition, info.MediaSourceId, info.AudioStreamIndex, newIndex);
-
-                    await _device.SetAvTransport(newItem.StreamUrl, GetDlnaHeaders(newItem), newItem.Didl, CancellationToken.None).ConfigureAwait(false);
-
-                    // Send a message to the DLNA device to notify what is the next track in the play list.
-                    var newItemIndex = _playlist.FindIndex(item => item.StreamUrl == newItem.StreamUrl);
-                    await SendNextTrackMessage(newItemIndex, CancellationToken.None).ConfigureAwait(false);
-
-                    if (EnableClientSideSeek(newItem.StreamInfo) && newPosition > 0)
-                    {
-                        await SeekAfterTransportChange(newPosition, CancellationToken.None).ConfigureAwait(false);
-                    }
-                }
-            }
-        }
-
-        private async Task SeekAfterTransportChange(long positionTicks, CancellationToken cancellationToken)
-        {
-            const int MaxWait = 15000000;
-            const int Interval = 500;
-
-            var currentWait = 0;
-            while (_device.TransportState != TransportState.PLAYING && currentWait < MaxWait)
-            {
-                await Task.Delay(Interval, cancellationToken).ConfigureAwait(false);
-                currentWait += Interval;
-            }
-
-            await _device.Seek(TimeSpan.FromTicks(positionTicks), cancellationToken).ConfigureAwait(false);
-        }
-
-        private static int? GetIntValue(IReadOnlyDictionary<string, string> 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<string, string> values, string name)
-        {
-            var value = values.GetValueOrDefault(name);
-
-            if (long.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
-            {
-                return result;
-            }
-
-            return 0;
-        }
-
-        /// <inheritdoc />
-        public Task SendMessage<T>(SessionMessageType name, Guid messageId, T data, CancellationToken cancellationToken)
-        {
-            if (_disposed)
-            {
-                throw new ObjectDisposedException(GetType().Name);
-            }
-
-            return name switch
-            {
-                SessionMessageType.Play => SendPlayCommand((data as PlayRequest)!, cancellationToken),
-                SessionMessageType.Playstate => SendPlaystateCommand((data as PlaystateRequest)!, cancellationToken),
-                SessionMessageType.GeneralCommand => SendGeneralCommand((data as GeneralCommand)!, cancellationToken),
-                _ => Task.CompletedTask // Not supported or needed right now
-            };
-        }
-
-        private class StreamParams
-        {
-            private MediaSourceInfo? _mediaSource;
-            private IMediaSourceManager? _mediaSourceManager;
-
-            public Guid ItemId { get; set; }
-
-            public bool IsDirectStream { get; set; }
-
-            public long StartPositionTicks { get; set; }
-
-            public int? AudioStreamIndex { get; set; }
-
-            public int? SubtitleStreamIndex { get; set; }
-
-            public string? DeviceProfileId { get; set; }
-
-            public string? DeviceId { get; set; }
-
-            public string? MediaSourceId { get; set; }
-
-            public string? LiveStreamId { get; set; }
-
-            public BaseItem? Item { get; set; }
-
-            public async Task<MediaSourceInfo?> GetMediaSource(CancellationToken cancellationToken)
-            {
-                if (_mediaSource is not null)
-                {
-                    return _mediaSource;
-                }
-
-                if (Item is not IHasMediaSources)
-                {
-                    return null;
-                }
-
-                if (_mediaSourceManager is not null)
-                {
-                    _mediaSource = await _mediaSourceManager.GetMediaSource(Item, MediaSourceId, LiveStreamId, false, cancellationToken).ConfigureAwait(false);
-                }
-
-                return _mediaSource;
-            }
-
-            private static Guid GetItemId(string url)
-            {
-                ArgumentException.ThrowIfNullOrEmpty(url);
-
-                var parts = url.Split('/');
-
-                for (var i = 0; i < parts.Length - 1; i++)
-                {
-                    var part = parts[i];
-
-                    if (string.Equals(part, "audio", StringComparison.OrdinalIgnoreCase)
-                        || string.Equals(part, "videos", StringComparison.OrdinalIgnoreCase))
-                    {
-                        if (Guid.TryParse(parts[i + 1], out var result))
-                        {
-                            return result;
-                        }
-                    }
-                }
-
-                return default;
-            }
-
-            public static StreamParams ParseFromUrl(string url, ILibraryManager libraryManager, IMediaSourceManager mediaSourceManager)
-            {
-                ArgumentException.ThrowIfNullOrEmpty(url);
-
-                var request = new StreamParams
-                {
-                    ItemId = GetItemId(url)
-                };
-
-                if (request.ItemId.Equals(default))
-                {
-                    return request;
-                }
-
-                var index = url.IndexOf('?', StringComparison.Ordinal);
-                if (index == -1)
-                {
-                    return request;
-                }
-
-                var query = url.Substring(index + 1);
-                Dictionary<string, string> values = QueryHelpers.ParseQuery(query).ToDictionary(kv => kv.Key, kv => kv.Value.ToString());
-
-                request.DeviceProfileId = values.GetValueOrDefault("DeviceProfileId");
-                request.DeviceId = values.GetValueOrDefault("DeviceId");
-                request.MediaSourceId = values.GetValueOrDefault("MediaSourceId");
-                request.LiveStreamId = values.GetValueOrDefault("LiveStreamId");
-                request.IsDirectStream = string.Equals("true", values.GetValueOrDefault("Static"), StringComparison.OrdinalIgnoreCase);
-                request.AudioStreamIndex = GetIntValue(values, "AudioStreamIndex");
-                request.SubtitleStreamIndex = GetIntValue(values, "SubtitleStreamIndex");
-                request.StartPositionTicks = GetLongValue(values, "StartPositionTicks");
-
-                request.Item = libraryManager.GetItemById(request.ItemId);
-
-                request._mediaSourceManager = mediaSourceManager;
-
-                return request;
-            }
-        }
-    }
-}

+ 0 - 258
Emby.Dlna/PlayTo/PlayToManager.cs

@@ -1,258 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using System.Globalization;
-using System.Linq;
-using System.Net.Http;
-using System.Threading;
-using System.Threading.Tasks;
-using Jellyfin.Data.Events;
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Controller;
-using MediaBrowser.Controller.Dlna;
-using MediaBrowser.Controller.Drawing;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.MediaEncoding;
-using MediaBrowser.Controller.Session;
-using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.Globalization;
-using MediaBrowser.Model.Session;
-using Microsoft.Extensions.Logging;
-
-namespace Emby.Dlna.PlayTo
-{
-    public sealed class PlayToManager : IDisposable
-    {
-        private readonly ILogger _logger;
-        private readonly ISessionManager _sessionManager;
-
-        private readonly ILibraryManager _libraryManager;
-        private readonly IUserManager _userManager;
-        private readonly IDlnaManager _dlnaManager;
-        private readonly IServerApplicationHost _appHost;
-        private readonly IImageProcessor _imageProcessor;
-        private readonly IHttpClientFactory _httpClientFactory;
-        private readonly IUserDataManager _userDataManager;
-        private readonly ILocalizationManager _localization;
-
-        private readonly IDeviceDiscovery _deviceDiscovery;
-        private readonly IMediaSourceManager _mediaSourceManager;
-        private readonly IMediaEncoder _mediaEncoder;
-
-        private readonly SemaphoreSlim _sessionLock = new SemaphoreSlim(1, 1);
-        private readonly CancellationTokenSource _disposeCancellationTokenSource = new CancellationTokenSource();
-        private bool _disposed;
-
-        public PlayToManager(ILogger logger, ISessionManager sessionManager, ILibraryManager libraryManager, IUserManager userManager, IDlnaManager dlnaManager, IServerApplicationHost appHost, IImageProcessor imageProcessor, IDeviceDiscovery deviceDiscovery, IHttpClientFactory httpClientFactory, IUserDataManager userDataManager, ILocalizationManager localization, IMediaSourceManager mediaSourceManager, IMediaEncoder mediaEncoder)
-        {
-            _logger = logger;
-            _sessionManager = sessionManager;
-            _libraryManager = libraryManager;
-            _userManager = userManager;
-            _dlnaManager = dlnaManager;
-            _appHost = appHost;
-            _imageProcessor = imageProcessor;
-            _deviceDiscovery = deviceDiscovery;
-            _httpClientFactory = httpClientFactory;
-            _userDataManager = userDataManager;
-            _localization = localization;
-            _mediaSourceManager = mediaSourceManager;
-            _mediaEncoder = mediaEncoder;
-        }
-
-        public void Start()
-        {
-            _deviceDiscovery.DeviceDiscovered += OnDeviceDiscoveryDeviceDiscovered;
-        }
-
-        private async void OnDeviceDiscoveryDeviceDiscovered(object? sender, GenericEventArgs<UpnpDeviceInfo> e)
-        {
-            if (_disposed)
-            {
-                return;
-            }
-
-            var info = e.Argument;
-
-            if (!info.Headers.TryGetValue("USN", out string? usn))
-            {
-                usn = string.Empty;
-            }
-
-            if (!info.Headers.TryGetValue("NT", out string? nt))
-            {
-                nt = string.Empty;
-            }
-
-            // It has to report that it's a media renderer
-            if (!usn.Contains("MediaRenderer:", StringComparison.OrdinalIgnoreCase)
-                && !nt.Contains("MediaRenderer:", StringComparison.OrdinalIgnoreCase))
-            {
-                return;
-            }
-
-            var cancellationToken = _disposeCancellationTokenSource.Token;
-
-            await _sessionLock.WaitAsync(cancellationToken).ConfigureAwait(false);
-
-            try
-            {
-                if (_disposed)
-                {
-                    return;
-                }
-
-                if (_sessionManager.Sessions.Any(i => usn.IndexOf(i.DeviceId, StringComparison.OrdinalIgnoreCase) != -1))
-                {
-                    return;
-                }
-
-                await AddDevice(info, cancellationToken).ConfigureAwait(false);
-            }
-            catch (OperationCanceledException)
-            {
-            }
-            catch (Exception ex)
-            {
-                _logger.LogError(ex, "Error creating PlayTo device.");
-            }
-            finally
-            {
-                _sessionLock.Release();
-            }
-        }
-
-        internal static string GetUuid(string usn)
-        {
-            const string UuidStr = "uuid:";
-            const string UuidColonStr = "::";
-
-            var index = usn.IndexOf(UuidStr, StringComparison.OrdinalIgnoreCase);
-            if (index == -1)
-            {
-                return usn.GetMD5().ToString("N", CultureInfo.InvariantCulture);
-            }
-
-            ReadOnlySpan<char> tmp = usn.AsSpan()[(index + UuidStr.Length)..];
-
-            index = tmp.IndexOf(UuidColonStr, StringComparison.OrdinalIgnoreCase);
-            if (index != -1)
-            {
-                tmp = tmp[..index];
-            }
-
-            index = tmp.IndexOf('{');
-            if (index != -1)
-            {
-                int endIndex = tmp.IndexOf('}');
-                if (endIndex != -1)
-                {
-                    tmp = tmp[(index + 1)..endIndex];
-                }
-            }
-
-            return tmp.ToString();
-        }
-
-        private async Task AddDevice(UpnpDeviceInfo info, CancellationToken cancellationToken)
-        {
-            var uri = info.Location;
-            _logger.LogDebug("Attempting to create PlayToController from location {0}", uri);
-
-            if (info.Headers.TryGetValue("USN", out string? uuid))
-            {
-                uuid = GetUuid(uuid);
-            }
-            else
-            {
-                uuid = uri.ToString().GetMD5().ToString("N", CultureInfo.InvariantCulture);
-            }
-
-            var sessionInfo = await _sessionManager
-                .LogSessionActivity("DLNA", _appHost.ApplicationVersionString, uuid, null, uri.OriginalString, null)
-                .ConfigureAwait(false);
-
-            var controller = sessionInfo.SessionControllers.OfType<PlayToController>().FirstOrDefault();
-
-            if (controller is null)
-            {
-                var device = await Device.CreateuPnpDeviceAsync(uri, _httpClientFactory, _logger, cancellationToken).ConfigureAwait(false);
-                if (device is null)
-                {
-                    _logger.LogError("Ignoring device as xml response is invalid.");
-                    return;
-                }
-
-                string deviceName = device.Properties.Name;
-
-                _sessionManager.UpdateDeviceName(sessionInfo.Id, deviceName);
-
-                string serverAddress = _appHost.GetSmartApiUrl(info.RemoteIPAddress);
-
-                controller = new PlayToController(
-                    sessionInfo,
-                    _sessionManager,
-                    _libraryManager,
-                    _logger,
-                    _dlnaManager,
-                    _userManager,
-                    _imageProcessor,
-                    serverAddress,
-                    null,
-                    _deviceDiscovery,
-                    _userDataManager,
-                    _localization,
-                    _mediaSourceManager,
-                    _mediaEncoder,
-                    device);
-
-                sessionInfo.AddController(controller);
-
-                var profile = _dlnaManager.GetProfile(device.Properties.ToDeviceIdentification()) ??
-                              _dlnaManager.GetDefaultProfile();
-
-                _sessionManager.ReportCapabilities(sessionInfo.Id, new ClientCapabilities
-                {
-                    PlayableMediaTypes = profile.GetSupportedMediaTypes(),
-
-                    SupportedCommands = new[]
-                    {
-                        GeneralCommandType.VolumeDown,
-                        GeneralCommandType.VolumeUp,
-                        GeneralCommandType.Mute,
-                        GeneralCommandType.Unmute,
-                        GeneralCommandType.ToggleMute,
-                        GeneralCommandType.SetVolume,
-                        GeneralCommandType.SetAudioStreamIndex,
-                        GeneralCommandType.SetSubtitleStreamIndex,
-                        GeneralCommandType.PlayMediaSource
-                    },
-
-                    SupportsMediaControl = true
-                });
-
-                _logger.LogInformation("DLNA Session created for {0} - {1}", device.Properties.Name, device.Properties.ModelName);
-            }
-        }
-
-        /// <inheritdoc />
-        public void Dispose()
-        {
-            _deviceDiscovery.DeviceDiscovered -= OnDeviceDiscoveryDeviceDiscovered;
-
-            try
-            {
-                _disposeCancellationTokenSource.Cancel();
-            }
-            catch (Exception ex)
-            {
-                _logger.LogDebug(ex, "Error while disposing PlayToManager");
-            }
-
-            _sessionLock.Dispose();
-            _disposeCancellationTokenSource.Dispose();
-
-            _disposed = true;
-        }
-    }
-}

+ 0 - 16
Emby.Dlna/PlayTo/PlaybackProgressEventArgs.cs

@@ -1,16 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-
-namespace Emby.Dlna.PlayTo
-{
-    public class PlaybackProgressEventArgs : EventArgs
-    {
-        public PlaybackProgressEventArgs(UBaseObject mediaInfo)
-        {
-            MediaInfo = mediaInfo;
-        }
-
-        public UBaseObject MediaInfo { get; set; }
-    }
-}

+ 0 - 16
Emby.Dlna/PlayTo/PlaybackStartEventArgs.cs

@@ -1,16 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-
-namespace Emby.Dlna.PlayTo
-{
-    public class PlaybackStartEventArgs : EventArgs
-    {
-        public PlaybackStartEventArgs(UBaseObject mediaInfo)
-        {
-            MediaInfo = mediaInfo;
-        }
-
-        public UBaseObject MediaInfo { get; set; }
-    }
-}

+ 0 - 16
Emby.Dlna/PlayTo/PlaybackStoppedEventArgs.cs

@@ -1,16 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-
-namespace Emby.Dlna.PlayTo
-{
-    public class PlaybackStoppedEventArgs : EventArgs
-    {
-        public PlaybackStoppedEventArgs(UBaseObject mediaInfo)
-        {
-            MediaInfo = mediaInfo;
-        }
-
-        public UBaseObject MediaInfo { get; set; }
-    }
-}

+ 0 - 19
Emby.Dlna/PlayTo/PlaylistItem.cs

@@ -1,19 +0,0 @@
-#nullable disable
-
-#pragma warning disable CS1591
-
-using MediaBrowser.Model.Dlna;
-
-namespace Emby.Dlna.PlayTo
-{
-    public class PlaylistItem
-    {
-        public string StreamUrl { get; set; }
-
-        public string Didl { get; set; }
-
-        public StreamInfo StreamInfo { get; set; }
-
-        public DeviceProfile Profile { get; set; }
-    }
-}

+ 0 - 70
Emby.Dlna/PlayTo/PlaylistItemFactory.cs

@@ -1,70 +0,0 @@
-#nullable disable
-
-#pragma warning disable CS1591
-
-using System.IO;
-using System.Linq;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Model.Dlna;
-using MediaBrowser.Model.Session;
-
-namespace Emby.Dlna.PlayTo
-{
-    public static class PlaylistItemFactory
-    {
-        public static PlaylistItem Create(Photo item, DeviceProfile profile)
-        {
-            var playlistItem = new PlaylistItem
-            {
-                StreamInfo = new StreamInfo
-                {
-                    ItemId = item.Id,
-                    MediaType = DlnaProfileType.Photo,
-                    DeviceProfile = profile
-                },
-
-                Profile = profile
-            };
-
-            var directPlay = profile.DirectPlayProfiles
-                .FirstOrDefault(i => i.Type == DlnaProfileType.Photo && IsSupported(i, item));
-
-            if (directPlay is not null)
-            {
-                playlistItem.StreamInfo.PlayMethod = PlayMethod.DirectStream;
-                playlistItem.StreamInfo.Container = Path.GetExtension(item.Path);
-
-                return playlistItem;
-            }
-
-            var transcodingProfile = profile.TranscodingProfiles
-                .FirstOrDefault(i => i.Type == DlnaProfileType.Photo);
-
-            if (transcodingProfile is not null)
-            {
-                playlistItem.StreamInfo.PlayMethod = PlayMethod.Transcode;
-                playlistItem.StreamInfo.Container = "." + transcodingProfile.Container.TrimStart('.');
-            }
-
-            return playlistItem;
-        }
-
-        private static bool IsSupported(DirectPlayProfile profile, Photo item)
-        {
-            var mediaPath = item.Path;
-
-            if (profile.Container.Length > 0)
-            {
-                // Check container type
-                var mediaContainer = (Path.GetExtension(mediaPath) ?? string.Empty).TrimStart('.');
-
-                if (!profile.SupportsContainer(mediaContainer))
-                {
-                    return false;
-                }
-            }
-
-            return true;
-        }
-    }
-}

+ 0 - 181
Emby.Dlna/PlayTo/TransportCommands.cs

@@ -1,181 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Linq;
-using System.Xml.Linq;
-using Emby.Dlna.Common;
-using Emby.Dlna.Ssdp;
-
-namespace Emby.Dlna.PlayTo
-{
-    public class TransportCommands
-    {
-        private const string CommandBase = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" + "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" + "<SOAP-ENV:Body>" + "<m:{0} xmlns:m=\"{1}\">" + "{2}" + "</m:{0}>" + "</SOAP-ENV:Body></SOAP-ENV:Envelope>";
-
-        public List<StateVariable> StateVariables { get; } = new List<StateVariable>();
-
-        public List<ServiceAction> ServiceActions { get; } = new List<ServiceAction>();
-
-        public static TransportCommands Create(XDocument document)
-        {
-            var command = new TransportCommands();
-
-            var actionList = document.Descendants(UPnpNamespaces.Svc + "actionList");
-
-            foreach (var container in actionList.Descendants(UPnpNamespaces.Svc + "action"))
-            {
-                command.ServiceActions.Add(ServiceActionFromXml(container));
-            }
-
-            var stateValues = document.Descendants(UPnpNamespaces.ServiceStateTable).FirstOrDefault();
-
-            if (stateValues is not null)
-            {
-                foreach (var container in stateValues.Elements(UPnpNamespaces.Svc + "stateVariable"))
-                {
-                    command.StateVariables.Add(FromXml(container));
-                }
-            }
-
-            return command;
-        }
-
-        private static ServiceAction ServiceActionFromXml(XElement container)
-        {
-            var serviceAction = new ServiceAction
-            {
-                Name = container.GetValue(UPnpNamespaces.Svc + "name") ?? string.Empty,
-            };
-
-            var argumentList = serviceAction.ArgumentList;
-
-            foreach (var arg in container.Descendants(UPnpNamespaces.Svc + "argument"))
-            {
-                argumentList.Add(ArgumentFromXml(arg));
-            }
-
-            return serviceAction;
-        }
-
-        private static Argument ArgumentFromXml(XElement container)
-        {
-            ArgumentNullException.ThrowIfNull(container);
-
-            return new Argument
-            {
-                Name = container.GetValue(UPnpNamespaces.Svc + "name") ?? string.Empty,
-                Direction = container.GetValue(UPnpNamespaces.Svc + "direction") ?? string.Empty,
-                RelatedStateVariable = container.GetValue(UPnpNamespaces.Svc + "relatedStateVariable") ?? string.Empty
-            };
-        }
-
-        private static StateVariable FromXml(XElement container)
-        {
-            var allowedValues = Array.Empty<string>();
-            var element = container.Descendants(UPnpNamespaces.Svc + "allowedValueList")
-                .FirstOrDefault();
-
-            if (element is not null)
-            {
-                var values = element.Descendants(UPnpNamespaces.Svc + "allowedValue");
-
-                allowedValues = values.Select(child => child.Value).ToArray();
-            }
-
-            return new StateVariable
-            {
-                Name = container.GetValue(UPnpNamespaces.Svc + "name") ?? string.Empty,
-                DataType = container.GetValue(UPnpNamespaces.Svc + "dataType") ?? string.Empty,
-                AllowedValues = allowedValues
-            };
-        }
-
-        public string BuildPost(ServiceAction action, string xmlNamespace)
-        {
-            var stateString = string.Empty;
-
-            foreach (var arg in action.ArgumentList)
-            {
-                if (string.Equals(arg.Direction, "out", StringComparison.Ordinal))
-                {
-                    continue;
-                }
-
-                if (string.Equals(arg.Name, "InstanceID", StringComparison.Ordinal))
-                {
-                    stateString += BuildArgumentXml(arg, "0");
-                }
-                else
-                {
-                    stateString += BuildArgumentXml(arg, null);
-                }
-            }
-
-            return string.Format(CultureInfo.InvariantCulture, CommandBase, action.Name, xmlNamespace, stateString);
-        }
-
-        public string BuildPost(ServiceAction action, string xmlNamespace, object value, string commandParameter = "")
-        {
-            var stateString = string.Empty;
-
-            foreach (var arg in action.ArgumentList)
-            {
-                if (string.Equals(arg.Direction, "out", StringComparison.Ordinal))
-                {
-                    continue;
-                }
-
-                if (string.Equals(arg.Name, "InstanceID", StringComparison.Ordinal))
-                {
-                    stateString += BuildArgumentXml(arg, "0");
-                }
-                else
-                {
-                    stateString += BuildArgumentXml(arg, value.ToString(), commandParameter);
-                }
-            }
-
-            return string.Format(CultureInfo.InvariantCulture, CommandBase, action.Name, xmlNamespace, stateString);
-        }
-
-        public string BuildPost(ServiceAction action, string xmlNamespace, object value, Dictionary<string, string> dictionary)
-        {
-            var stateString = string.Empty;
-
-            foreach (var arg in action.ArgumentList)
-            {
-                if (string.Equals(arg.Name, "InstanceID", StringComparison.Ordinal))
-                {
-                    stateString += BuildArgumentXml(arg, "0");
-                }
-                else if (dictionary.TryGetValue(arg.Name, out var argValue))
-                {
-                    stateString += BuildArgumentXml(arg, argValue);
-                }
-                else
-                {
-                    stateString += BuildArgumentXml(arg, value.ToString());
-                }
-            }
-
-            return string.Format(CultureInfo.InvariantCulture, CommandBase, action.Name, xmlNamespace, stateString);
-        }
-
-        private string BuildArgumentXml(Argument argument, string? value, string commandParameter = "")
-        {
-            var state = StateVariables.FirstOrDefault(a => string.Equals(a.Name, argument.RelatedStateVariable, StringComparison.OrdinalIgnoreCase));
-
-            if (state is not null)
-            {
-                var sendValue = state.AllowedValues.FirstOrDefault(a => string.Equals(a, commandParameter, StringComparison.OrdinalIgnoreCase)) ??
-                    (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}</{0}>", argument.Name, state.DataType, sendValue);
-            }
-
-            return string.Format(CultureInfo.InvariantCulture, "<{0}>{1}</{0}>", argument.Name, value);
-        }
-    }
-}

+ 0 - 16
Emby.Dlna/PlayTo/TransportState.cs

@@ -1,16 +0,0 @@
-#pragma warning disable CS1591
-
-namespace Emby.Dlna.PlayTo
-{
-    /// <summary>
-    /// Core of the AVTransport service. It defines the conceptually top-
-    /// level state of the transport, for example, whether it is playing, recording, etc.
-    /// </summary>
-    public enum TransportState
-    {
-        STOPPED,
-        PLAYING,
-        TRANSITIONING,
-        PAUSED_PLAYBACK
-    }
-}

+ 0 - 25
Emby.Dlna/PlayTo/UpnpContainer.cs

@@ -1,25 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using System.Xml.Linq;
-using Emby.Dlna.Ssdp;
-
-namespace Emby.Dlna.PlayTo
-{
-    public class UpnpContainer : UBaseObject
-    {
-        public static UBaseObject Create(XElement container)
-        {
-            ArgumentNullException.ThrowIfNull(container);
-
-            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.Class)
-            };
-        }
-    }
-}

+ 0 - 63
Emby.Dlna/PlayTo/uBaseObject.cs

@@ -1,63 +0,0 @@
-#nullable disable
-
-#pragma warning disable CS1591
-
-using System;
-using System.Collections.Generic;
-using Jellyfin.Data.Enums;
-
-namespace Emby.Dlna.PlayTo
-{
-    public class UBaseObject
-    {
-        public string Id { get; set; }
-
-        public string ParentId { get; set; }
-
-        public string Title { get; set; }
-
-        public string SecondText { get; set; }
-
-        public string IconUrl { get; set; }
-
-        public string MetaData { get; set; }
-
-        public string Url { get; set; }
-
-        public IReadOnlyList<string> ProtocolInfo { get; set; }
-
-        public string UpnpClass { get; set; }
-
-        public string MediaType
-        {
-            get
-            {
-                var classType = UpnpClass ?? string.Empty;
-
-                if (classType.Contains("Audio", StringComparison.Ordinal))
-                {
-                    return "Audio";
-                }
-
-                if (classType.Contains("Video", StringComparison.Ordinal))
-                {
-                    return "Video";
-                }
-
-                if (classType.Contains("image", StringComparison.Ordinal))
-                {
-                    return "Photo";
-                }
-
-                return null;
-            }
-        }
-
-        public bool Equals(UBaseObject obj)
-        {
-            ArgumentNullException.ThrowIfNull(obj);
-
-            return string.Equals(Id, obj.Id, StringComparison.Ordinal);
-        }
-    }
-}

+ 0 - 67
Emby.Dlna/PlayTo/uPnpNamespaces.cs

@@ -1,67 +0,0 @@
-#pragma warning disable CS1591
-
-using System.Xml.Linq;
-
-namespace Emby.Dlna.PlayTo
-{
-    public static class UPnpNamespaces
-    {
-        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";
-    }
-}

+ 0 - 179
Emby.Dlna/Profiles/DefaultProfile.cs

@@ -1,179 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using System.Globalization;
-using MediaBrowser.Model.Dlna;
-
-namespace Emby.Dlna.Profiles
-{
-    [System.Xml.Serialization.XmlRoot("Profile")]
-    public class DefaultProfile : DeviceProfile
-    {
-        public DefaultProfile()
-        {
-            Id = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
-            Name = "Generic Device";
-
-            ProtocolInfo = "http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*";
-
-            Manufacturer = "Jellyfin";
-            ModelDescription = "UPnP/AV 1.0 Compliant Media Server";
-            ModelName = "Jellyfin Server";
-            ModelNumber = "01";
-            ModelUrl = "https://github.com/jellyfin/jellyfin";
-            ManufacturerUrl = "https://github.com/jellyfin/jellyfin";
-
-            AlbumArtPn = "JPEG_SM";
-
-            MaxAlbumArtHeight = 480;
-            MaxAlbumArtWidth = 480;
-
-            MaxIconWidth = 48;
-            MaxIconHeight = 48;
-
-            MaxStreamingBitrate = 140000000;
-            MaxStaticBitrate = 140000000;
-            MusicStreamingTranscodingBitrate = 192000;
-
-            EnableAlbumArtInDidl = false;
-
-            TranscodingProfiles = new[]
-            {
-                new TranscodingProfile
-                {
-                    Container = "mp3",
-                    AudioCodec = "mp3",
-                    Type = DlnaProfileType.Audio
-                },
-
-                new TranscodingProfile
-                {
-                    Container = "ts",
-                    Type = DlnaProfileType.Video,
-                    AudioCodec = "aac",
-                    VideoCodec = "h264"
-                },
-
-                new TranscodingProfile
-                {
-                    Container = "jpeg",
-                    Type = DlnaProfileType.Photo
-                }
-            };
-
-            DirectPlayProfiles = new[]
-            {
-                new DirectPlayProfile
-                {
-                    // play all
-                    Container = string.Empty,
-                    Type = DlnaProfileType.Video
-                },
-
-                new DirectPlayProfile
-                {
-                    // play all
-                    Container = string.Empty,
-                    Type = DlnaProfileType.Audio
-                }
-            };
-
-            SubtitleProfiles = new[]
-            {
-                new SubtitleProfile
-                {
-                    Format = "srt",
-                    Method = SubtitleDeliveryMethod.External,
-                },
-
-                new SubtitleProfile
-                {
-                    Format = "sub",
-                    Method = SubtitleDeliveryMethod.External,
-                },
-
-                new SubtitleProfile
-                {
-                    Format = "sup",
-                    Method = SubtitleDeliveryMethod.External
-                },
-
-                new SubtitleProfile
-                {
-                    Format = "srt",
-                    Method = SubtitleDeliveryMethod.Embed
-                },
-
-                new SubtitleProfile
-                {
-                    Format = "ass",
-                    Method = SubtitleDeliveryMethod.Embed
-                },
-
-                new SubtitleProfile
-                {
-                    Format = "ssa",
-                    Method = SubtitleDeliveryMethod.Embed
-                },
-
-                new SubtitleProfile
-                {
-                    Format = "smi",
-                    Method = SubtitleDeliveryMethod.Embed
-                },
-
-                new SubtitleProfile
-                {
-                    Format = "dvdsub",
-                    Method = SubtitleDeliveryMethod.Embed
-                },
-
-                new SubtitleProfile
-                {
-                    Format = "pgs",
-                    Method = SubtitleDeliveryMethod.Embed
-                },
-
-                new SubtitleProfile
-                {
-                    Format = "pgssub",
-                    Method = SubtitleDeliveryMethod.Embed
-                },
-
-                new SubtitleProfile
-                {
-                    Format = "sub",
-                    Method = SubtitleDeliveryMethod.Embed
-                },
-
-                new SubtitleProfile
-                {
-                    Format = "sup",
-                    Method = SubtitleDeliveryMethod.Embed
-                },
-
-                new SubtitleProfile
-                {
-                    Format = "subrip",
-                    Method = SubtitleDeliveryMethod.Embed
-                },
-
-                new SubtitleProfile
-                {
-                    Format = "vtt",
-                    Method = SubtitleDeliveryMethod.Embed
-                }
-            };
-
-            ResponseProfiles = new[]
-            {
-                new ResponseProfile
-                {
-                    Container = "m4v",
-                    Type = DlnaProfileType.Video,
-                    MimeType = "video/mp4"
-                }
-            };
-        }
-    }
-}

+ 0 - 61
Emby.Dlna/Profiles/Xml/Default.xml

@@ -1,61 +0,0 @@
-<?xml version="1.0"?>
-<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-  xmlns:xsd="http://www.w3.org/2001/XMLSchema">
-  <Name>Generic Device</Name>
-  <Manufacturer>Jellyfin</Manufacturer>
-  <ManufacturerUrl>https://github.com/jellyfin/jellyfin</ManufacturerUrl>
-  <ModelName>Jellyfin Server</ModelName>
-  <ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
-  <ModelNumber>01</ModelNumber>
-  <ModelUrl>https://github.com/jellyfin/jellyfin</ModelUrl>
-  <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
-  <EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit>
-  <EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit>
-  <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes>
-  <AlbumArtPn>JPEG_SM</AlbumArtPn>
-  <MaxAlbumArtWidth>480</MaxAlbumArtWidth>
-  <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
-  <MaxIconWidth>48</MaxIconWidth>
-  <MaxIconHeight>48</MaxIconHeight>
-  <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
-  <MaxStaticBitrate>140000000</MaxStaticBitrate>
-  <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
-  <MaxStaticMusicBitrate xsi:nil="true" />
-  <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo>
-  <TimelineOffsetSeconds>0</TimelineOffsetSeconds>
-  <RequiresPlainVideoItems>false</RequiresPlainVideoItems>
-  <RequiresPlainFolders>false</RequiresPlainFolders>
-  <EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
-  <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <XmlRootAttributes />
-  <DirectPlayProfiles>
-    <DirectPlayProfile container="" type="Video" />
-    <DirectPlayProfile container="" type="Audio" />
-  </DirectPlayProfiles>
-  <TranscodingProfiles>
-    <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-    <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-    <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-  </TranscodingProfiles>
-  <ContainerProfiles />
-  <CodecProfiles />
-  <ResponseProfiles>
-    <ResponseProfile container="m4v" type="Video" mimeType="video/mp4">
-      <Conditions />
-    </ResponseProfile>
-  </ResponseProfiles>
-  <SubtitleProfiles>
-    <SubtitleProfile format="srt" method="External" />
-    <SubtitleProfile format="sub" method="External" />
-    <SubtitleProfile format="srt" method="Embed" />
-    <SubtitleProfile format="ass" method="Embed" />
-    <SubtitleProfile format="ssa" method="Embed" />
-    <SubtitleProfile format="smi" method="Embed" />
-    <SubtitleProfile format="dvdsub" method="Embed" />
-    <SubtitleProfile format="pgs" method="Embed" />
-    <SubtitleProfile format="pgssub" method="Embed" />
-    <SubtitleProfile format="sub" method="Embed" />
-    <SubtitleProfile format="subrip" method="Embed" />
-    <SubtitleProfile format="vtt" method="Embed" />
-  </SubtitleProfiles>
-</Profile>

+ 0 - 68
Emby.Dlna/Profiles/Xml/Denon AVR.xml

@@ -1,68 +0,0 @@
-<?xml version="1.0"?>
-<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-  xmlns:xsd="http://www.w3.org/2001/XMLSchema">
-  <Name>Denon AVR</Name>
-  <Identification>
-    <FriendlyName>Denon:\[AVR:.*</FriendlyName>
-    <Manufacturer>Denon</Manufacturer>
-    <Headers />
-  </Identification>
-  <Manufacturer>Jellyfin</Manufacturer>
-  <ManufacturerUrl>https://github.com/jellyfin/jellyfin</ManufacturerUrl>
-  <ModelName>Jellyfin Server</ModelName>
-  <ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
-  <ModelNumber>01</ModelNumber>
-  <ModelUrl>https://github.com/jellyfin/jellyfin</ModelUrl>
-  <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
-  <EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit>
-  <EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit>
-  <SupportedMediaTypes>Audio</SupportedMediaTypes>
-  <AlbumArtPn>JPEG_SM</AlbumArtPn>
-  <MaxAlbumArtWidth>480</MaxAlbumArtWidth>
-  <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
-  <MaxIconWidth>48</MaxIconWidth>
-  <MaxIconHeight>48</MaxIconHeight>
-  <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
-  <MaxStaticBitrate>140000000</MaxStaticBitrate>
-  <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
-  <MaxStaticMusicBitrate xsi:nil="true" />
-  <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo>
-  <TimelineOffsetSeconds>0</TimelineOffsetSeconds>
-  <RequiresPlainVideoItems>false</RequiresPlainVideoItems>
-  <RequiresPlainFolders>false</RequiresPlainFolders>
-  <EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
-  <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <XmlRootAttributes />
-  <DirectPlayProfiles>
-    <DirectPlayProfile container="mp3,flac,m4a,wma" type="Audio" />
-  </DirectPlayProfiles>
-  <TranscodingProfiles>
-    <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-    <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-    <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-  </TranscodingProfiles>
-  <ContainerProfiles />
-  <CodecProfiles>
-    <CodecProfile type="Audio" container="flac">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="AudioSampleRate" value="96000" isRequired="true" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-  </CodecProfiles>
-  <ResponseProfiles />
-  <SubtitleProfiles>
-    <SubtitleProfile format="srt" method="External" />
-    <SubtitleProfile format="sub" method="External" />
-    <SubtitleProfile format="srt" method="Embed" />
-    <SubtitleProfile format="ass" method="Embed" />
-    <SubtitleProfile format="ssa" method="Embed" />
-    <SubtitleProfile format="smi" method="Embed" />
-    <SubtitleProfile format="dvdsub" method="Embed" />
-    <SubtitleProfile format="pgs" method="Embed" />
-    <SubtitleProfile format="pgssub" method="Embed" />
-    <SubtitleProfile format="sub" method="Embed" />
-    <SubtitleProfile format="subrip" method="Embed" />
-    <SubtitleProfile format="vtt" method="Embed" />
-  </SubtitleProfiles>
-</Profile>

+ 0 - 67
Emby.Dlna/Profiles/Xml/DirecTV HD-DVR.xml

@@ -1,67 +0,0 @@
-<?xml version="1.0"?>
-<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-  xmlns:xsd="http://www.w3.org/2001/XMLSchema">
-  <Name>DirecTV HD-DVR</Name>
-  <Identification>
-    <FriendlyName>^DIRECTV.*$</FriendlyName>
-    <Headers>
-      <HttpHeaderInfo name="User-Agent" value="DIRECTV" match="Substring" />
-    </Headers>
-  </Identification>
-  <Manufacturer>Jellyfin</Manufacturer>
-  <ManufacturerUrl>https://github.com/jellyfin/jellyfin</ManufacturerUrl>
-  <ModelName>Jellyfin Server</ModelName>
-  <ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
-  <ModelNumber>01</ModelNumber>
-  <ModelUrl>https://github.com/jellyfin/jellyfin</ModelUrl>
-  <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
-  <EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit>
-  <EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit>
-  <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes>
-  <AlbumArtPn>JPEG_SM</AlbumArtPn>
-  <MaxAlbumArtWidth>480</MaxAlbumArtWidth>
-  <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
-  <MaxIconWidth>48</MaxIconWidth>
-  <MaxIconHeight>48</MaxIconHeight>
-  <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
-  <MaxStaticBitrate>140000000</MaxStaticBitrate>
-  <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
-  <MaxStaticMusicBitrate xsi:nil="true" />
-  <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo>
-  <TimelineOffsetSeconds>10</TimelineOffsetSeconds>
-  <RequiresPlainVideoItems>true</RequiresPlainVideoItems>
-  <RequiresPlainFolders>true</RequiresPlainFolders>
-  <EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
-  <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <XmlRootAttributes />
-  <DirectPlayProfiles>
-    <DirectPlayProfile container="mpeg" audioCodec="mp2" videoCodec="mpeg2video" type="Video" />
-    <DirectPlayProfile container="jpeg,jpg" type="Photo" />
-  </DirectPlayProfiles>
-  <TranscodingProfiles>
-    <TranscodingProfile container="mpeg" type="Video" videoCodec="mpeg2video" audioCodec="mp2" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-    <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-  </TranscodingProfiles>
-  <ContainerProfiles />
-  <CodecProfiles>
-    <CodecProfile type="Video" codec="mpeg2video">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="Width" value="1920" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="Height" value="1080" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="VideoFramerate" value="30" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="VideoBitrate" value="8192000" isRequired="true" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-    <CodecProfile type="Audio" codec="mp2">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="AudioChannels" value="2" isRequired="true" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-  </CodecProfiles>
-  <ResponseProfiles />
-  <SubtitleProfiles>
-    <SubtitleProfile format="srt" method="Embed" />
-  </SubtitleProfiles>
-</Profile>

+ 0 - 96
Emby.Dlna/Profiles/Xml/Dish Hopper-Joey.xml

@@ -1,96 +0,0 @@
-<?xml version="1.0"?>
-<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-  xmlns:xsd="http://www.w3.org/2001/XMLSchema">
-  <Name>Dish Hopper-Joey</Name>
-  <Identification>
-    <Manufacturer>Echostar Technologies LLC</Manufacturer>
-    <ManufacturerUrl>http://www.echostar.com</ManufacturerUrl>
-    <Headers>
-      <HttpHeaderInfo name="User-Agent" value="Zip_" match="Substring" />
-    </Headers>
-  </Identification>
-  <Manufacturer>Jellyfin</Manufacturer>
-  <ManufacturerUrl>https://github.com/jellyfin/jellyfin</ManufacturerUrl>
-  <ModelName>Jellyfin Server</ModelName>
-  <ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
-  <ModelNumber>01</ModelNumber>
-  <ModelUrl>https://github.com/jellyfin/jellyfin</ModelUrl>
-  <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
-  <EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit>
-  <EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit>
-  <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes>
-  <AlbumArtPn>JPEG_SM</AlbumArtPn>
-  <MaxAlbumArtWidth>480</MaxAlbumArtWidth>
-  <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
-  <MaxIconWidth>48</MaxIconWidth>
-  <MaxIconHeight>48</MaxIconHeight>
-  <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
-  <MaxStaticBitrate>140000000</MaxStaticBitrate>
-  <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
-  <MaxStaticMusicBitrate xsi:nil="true" />
-  <ProtocolInfo>http-get:*:video/mp2t:http-get:*:video/mpeg:*,http-get:*:video/MP1S:*,http-get:*:video/mpeg2:*,http-get:*:video/mp4:*,http-get:*:video/x-matroska:*,http-get:*:audio/mpeg:*,http-get:*:audio/mpeg3:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/mp4a-latm:*,http-get:*:image/jpeg:*</ProtocolInfo>
-  <TimelineOffsetSeconds>0</TimelineOffsetSeconds>
-  <RequiresPlainVideoItems>false</RequiresPlainVideoItems>
-  <RequiresPlainFolders>false</RequiresPlainFolders>
-  <EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
-  <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <XmlRootAttributes />
-  <DirectPlayProfiles>
-    <DirectPlayProfile container="mp4,mkv,mpeg,ts" audioCodec="mp3,ac3,aac,he-aac,pcm" videoCodec="h264,mpeg2video" type="Video" />
-    <DirectPlayProfile container="mp3,alac,flac" type="Audio" />
-    <DirectPlayProfile container="jpeg" type="Photo" />
-  </DirectPlayProfiles>
-  <TranscodingProfiles>
-    <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-    <TranscodingProfile container="mp4" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-    <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-  </TranscodingProfiles>
-  <ContainerProfiles />
-  <CodecProfiles>
-    <CodecProfile type="Video" codec="h264">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="Width" value="1920" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="Height" value="1080" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="VideoFramerate" value="30" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="VideoBitrate" value="20000000" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="VideoLevel" value="41" isRequired="true" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-    <CodecProfile type="Video">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="Width" value="1920" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="Height" value="1080" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="VideoFramerate" value="30" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="VideoBitrate" value="20000000" isRequired="true" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-    <CodecProfile type="VideoAudio" codec="ac3,he-aac">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="AudioChannels" value="6" isRequired="true" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-    <CodecProfile type="VideoAudio" codec="aac">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="AudioChannels" value="2" isRequired="true" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-    <CodecProfile type="VideoAudio">
-      <Conditions>
-        <ProfileCondition condition="Equals" property="IsSecondaryAudio" value="false" isRequired="true" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-  </CodecProfiles>
-  <ResponseProfiles>
-    <ResponseProfile container="mkv,ts,mpegts" type="Video" mimeType="video/mp4">
-      <Conditions />
-    </ResponseProfile>
-  </ResponseProfiles>
-  <SubtitleProfiles>
-    <SubtitleProfile format="srt" method="Embed" />
-  </SubtitleProfiles>
-</Profile>

+ 0 - 92
Emby.Dlna/Profiles/Xml/LG Smart TV.xml

@@ -1,92 +0,0 @@
-<?xml version="1.0"?>
-<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-  xmlns:xsd="http://www.w3.org/2001/XMLSchema">
-  <Name>LG Smart TV</Name>
-  <Identification>
-    <FriendlyName>LG.*</FriendlyName>
-    <Headers>
-      <HttpHeaderInfo name="User-Agent" value="LG" match="Substring" />
-    </Headers>
-  </Identification>
-  <Manufacturer>Jellyfin</Manufacturer>
-  <ManufacturerUrl>https://github.com/jellyfin/jellyfin</ManufacturerUrl>
-  <ModelName>Jellyfin Server</ModelName>
-  <ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
-  <ModelNumber>01</ModelNumber>
-  <ModelUrl>https://github.com/jellyfin/jellyfin</ModelUrl>
-  <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
-  <EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit>
-  <EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit>
-  <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes>
-  <AlbumArtPn>JPEG_SM</AlbumArtPn>
-  <MaxAlbumArtWidth>480</MaxAlbumArtWidth>
-  <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
-  <MaxIconWidth>48</MaxIconWidth>
-  <MaxIconHeight>48</MaxIconHeight>
-  <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
-  <MaxStaticBitrate>140000000</MaxStaticBitrate>
-  <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
-  <MaxStaticMusicBitrate xsi:nil="true" />
-  <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo>
-  <TimelineOffsetSeconds>10</TimelineOffsetSeconds>
-  <RequiresPlainVideoItems>false</RequiresPlainVideoItems>
-  <RequiresPlainFolders>false</RequiresPlainFolders>
-  <EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
-  <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <XmlRootAttributes />
-  <DirectPlayProfiles>
-    <DirectPlayProfile container="ts,mpegts,avi,mkv,m2ts" audioCodec="aac,ac3,eac3,mp3,dca,dts" videoCodec="h264" type="Video" />
-    <DirectPlayProfile container="mp4,m4v" audioCodec="aac,ac3,eac3,mp3,dca,dts" videoCodec="h264,mpeg4" type="Video" />
-    <DirectPlayProfile container="mp3" type="Audio" />
-    <DirectPlayProfile container="jpeg" type="Photo" />
-  </DirectPlayProfiles>
-  <TranscodingProfiles>
-    <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-    <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3,aac,mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-    <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-  </TranscodingProfiles>
-  <ContainerProfiles>
-    <ContainerProfile type="Photo">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="Width" value="1920" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="Height" value="1080" isRequired="true" />
-      </Conditions>
-    </ContainerProfile>
-  </ContainerProfiles>
-  <CodecProfiles>
-    <CodecProfile type="Video" codec="mpeg4">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="Width" value="1920" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="Height" value="1080" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="VideoFramerate" value="30" isRequired="true" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-    <CodecProfile type="Video" codec="h264">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="Width" value="1920" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="Height" value="1080" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="VideoLevel" value="41" isRequired="true" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-    <CodecProfile type="VideoAudio" codec="ac3,eac3,aac,mp3">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="AudioChannels" value="6" isRequired="true" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-  </CodecProfiles>
-  <ResponseProfiles>
-    <ResponseProfile container="m4v" type="Video" mimeType="video/mp4">
-      <Conditions />
-    </ResponseProfile>
-    <ResponseProfile container="ts,mpegts" type="Video" mimeType="video/mpeg">
-      <Conditions />
-    </ResponseProfile>
-  </ResponseProfiles>
-  <SubtitleProfiles>
-    <SubtitleProfile format="srt" method="Embed" />
-    <SubtitleProfile format="srt" method="External" />
-  </SubtitleProfiles>
-</Profile>

+ 0 - 54
Emby.Dlna/Profiles/Xml/Linksys DMA2100.xml

@@ -1,54 +0,0 @@
-<?xml version="1.0"?>
-<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-  xmlns:xsd="http://www.w3.org/2001/XMLSchema">
-  <Name>Linksys DMA2100</Name>
-  <Identification>
-    <ModelName>DMA2100us</ModelName>
-    <Headers />
-  </Identification>
-  <Manufacturer>Jellyfin</Manufacturer>
-  <ManufacturerUrl>https://github.com/jellyfin/jellyfin</ManufacturerUrl>
-  <ModelName>Jellyfin Server</ModelName>
-  <ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
-  <ModelNumber>01</ModelNumber>
-  <ModelUrl>https://github.com/jellyfin/jellyfin</ModelUrl>
-  <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
-  <EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit>
-  <EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit>
-  <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes>
-  <AlbumArtPn>JPEG_SM</AlbumArtPn>
-  <MaxAlbumArtWidth>480</MaxAlbumArtWidth>
-  <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
-  <MaxIconWidth>48</MaxIconWidth>
-  <MaxIconHeight>48</MaxIconHeight>
-  <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
-  <MaxStaticBitrate>140000000</MaxStaticBitrate>
-  <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
-  <MaxStaticMusicBitrate xsi:nil="true" />
-  <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo>
-  <TimelineOffsetSeconds>0</TimelineOffsetSeconds>
-  <RequiresPlainVideoItems>false</RequiresPlainVideoItems>
-  <RequiresPlainFolders>false</RequiresPlainFolders>
-  <EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
-  <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <XmlRootAttributes />
-  <DirectPlayProfiles>
-    <DirectPlayProfile container="mp3,flac,m4a,wma" type="Audio" />
-    <DirectPlayProfile container="avi,mp4,mkv,ts,mpegts,m4v" type="Video" />
-  </DirectPlayProfiles>
-  <TranscodingProfiles>
-    <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-    <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-    <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-  </TranscodingProfiles>
-  <ContainerProfiles />
-  <CodecProfiles />
-  <ResponseProfiles>
-    <ResponseProfile container="m4v" type="Video" mimeType="video/mp4">
-      <Conditions />
-    </ResponseProfile>
-  </ResponseProfiles>
-  <SubtitleProfiles>
-    <SubtitleProfile format="srt" method="Embed" />
-  </SubtitleProfiles>
-</Profile>

+ 0 - 62
Emby.Dlna/Profiles/Xml/Marantz.xml

@@ -1,62 +0,0 @@
-<?xml version="1.0"?>
-<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-  xmlns:xsd="http://www.w3.org/2001/XMLSchema">
-  <Name>Marantz</Name>
-  <Identification>
-    <Manufacturer>Marantz</Manufacturer>
-    <Headers>
-      <HttpHeaderInfo name="User-Agent" value="Marantz" match="Substring" />
-    </Headers>
-  </Identification>
-  <Manufacturer>Jellyfin</Manufacturer>
-  <ManufacturerUrl>https://github.com/jellyfin/jellyfin</ManufacturerUrl>
-  <ModelName>Jellyfin Server</ModelName>
-  <ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
-  <ModelNumber>01</ModelNumber>
-  <ModelUrl>https://github.com/jellyfin/jellyfin</ModelUrl>
-  <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
-  <EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit>
-  <EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit>
-  <SupportedMediaTypes>Audio</SupportedMediaTypes>
-  <AlbumArtPn>JPEG_SM</AlbumArtPn>
-  <MaxAlbumArtWidth>480</MaxAlbumArtWidth>
-  <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
-  <MaxIconWidth>48</MaxIconWidth>
-  <MaxIconHeight>48</MaxIconHeight>
-  <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
-  <MaxStaticBitrate>140000000</MaxStaticBitrate>
-  <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
-  <MaxStaticMusicBitrate xsi:nil="true" />
-  <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo>
-  <TimelineOffsetSeconds>0</TimelineOffsetSeconds>
-  <RequiresPlainVideoItems>false</RequiresPlainVideoItems>
-  <RequiresPlainFolders>false</RequiresPlainFolders>
-  <EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
-  <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <XmlRootAttributes />
-  <DirectPlayProfiles>
-    <DirectPlayProfile container="aac,mp3,wav,wma,flac" type="Audio" />
-  </DirectPlayProfiles>
-  <TranscodingProfiles>
-    <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-    <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-    <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-  </TranscodingProfiles>
-  <ContainerProfiles />
-  <CodecProfiles />
-  <ResponseProfiles />
-  <SubtitleProfiles>
-    <SubtitleProfile format="srt" method="External" />
-    <SubtitleProfile format="sub" method="External" />
-    <SubtitleProfile format="srt" method="Embed" />
-    <SubtitleProfile format="ass" method="Embed" />
-    <SubtitleProfile format="ssa" method="Embed" />
-    <SubtitleProfile format="smi" method="Embed" />
-    <SubtitleProfile format="dvdsub" method="Embed" />
-    <SubtitleProfile format="pgs" method="Embed" />
-    <SubtitleProfile format="pgssub" method="Embed" />
-    <SubtitleProfile format="sub" method="Embed" />
-    <SubtitleProfile format="subrip" method="Embed" />
-    <SubtitleProfile format="vtt" method="Embed" />
-  </SubtitleProfiles>
-</Profile>

+ 0 - 62
Emby.Dlna/Profiles/Xml/MediaMonkey.xml

@@ -1,62 +0,0 @@
-<?xml version="1.0"?>
-<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-  xmlns:xsd="http://www.w3.org/2001/XMLSchema">
-  <Name>MediaMonkey</Name>
-  <Identification>
-    <FriendlyName>MediaMonkey</FriendlyName>
-    <Headers>
-      <HttpHeaderInfo name="User-Agent" value="MediaMonkey" match="Substring" />
-    </Headers>
-  </Identification>
-  <Manufacturer>Jellyfin</Manufacturer>
-  <ManufacturerUrl>https://github.com/jellyfin/jellyfin</ManufacturerUrl>
-  <ModelName>Jellyfin Server</ModelName>
-  <ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
-  <ModelNumber>01</ModelNumber>
-  <ModelUrl>https://github.com/jellyfin/jellyfin</ModelUrl>
-  <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
-  <EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit>
-  <EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit>
-  <SupportedMediaTypes>Audio</SupportedMediaTypes>
-  <AlbumArtPn>JPEG_SM</AlbumArtPn>
-  <MaxAlbumArtWidth>480</MaxAlbumArtWidth>
-  <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
-  <MaxIconWidth>48</MaxIconWidth>
-  <MaxIconHeight>48</MaxIconHeight>
-  <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
-  <MaxStaticBitrate>140000000</MaxStaticBitrate>
-  <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
-  <MaxStaticMusicBitrate xsi:nil="true" />
-  <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo>
-  <TimelineOffsetSeconds>0</TimelineOffsetSeconds>
-  <RequiresPlainVideoItems>false</RequiresPlainVideoItems>
-  <RequiresPlainFolders>false</RequiresPlainFolders>
-  <EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
-  <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <XmlRootAttributes />
-  <DirectPlayProfiles>
-    <DirectPlayProfile container="aac,mp3,mpa,wav,wma,mp2,ogg,oga,webma,ape,opus,flac,m4a" type="Audio" />
-  </DirectPlayProfiles>
-  <TranscodingProfiles>
-    <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-    <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-    <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-  </TranscodingProfiles>
-  <ContainerProfiles />
-  <CodecProfiles />
-  <ResponseProfiles />
-  <SubtitleProfiles>
-    <SubtitleProfile format="srt" method="External" />
-    <SubtitleProfile format="sub" method="External" />
-    <SubtitleProfile format="srt" method="Embed" />
-    <SubtitleProfile format="ass" method="Embed" />
-    <SubtitleProfile format="ssa" method="Embed" />
-    <SubtitleProfile format="smi" method="Embed" />
-    <SubtitleProfile format="dvdsub" method="Embed" />
-    <SubtitleProfile format="pgs" method="Embed" />
-    <SubtitleProfile format="pgssub" method="Embed" />
-    <SubtitleProfile format="sub" method="Embed" />
-    <SubtitleProfile format="subrip" method="Embed" />
-    <SubtitleProfile format="vtt" method="Embed" />
-  </SubtitleProfiles>
-</Profile>

+ 0 - 87
Emby.Dlna/Profiles/Xml/Panasonic Viera.xml

@@ -1,87 +0,0 @@
-<?xml version="1.0"?>
-<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-  xmlns:xsd="http://www.w3.org/2001/XMLSchema">
-  <Name>Panasonic Viera</Name>
-  <Identification>
-    <FriendlyName>VIERA</FriendlyName>
-    <Manufacturer>Panasonic</Manufacturer>
-    <Headers>
-      <HttpHeaderInfo name="User-Agent" value="Panasonic MIL DLNA" match="Substring" />
-    </Headers>
-  </Identification>
-  <Manufacturer>Jellyfin</Manufacturer>
-  <ManufacturerUrl>https://github.com/jellyfin/jellyfin</ManufacturerUrl>
-  <ModelName>Jellyfin Server</ModelName>
-  <ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
-  <ModelNumber>01</ModelNumber>
-  <ModelUrl>https://github.com/jellyfin/jellyfin</ModelUrl>
-  <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
-  <EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit>
-  <EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit>
-  <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes>
-  <AlbumArtPn>JPEG_SM</AlbumArtPn>
-  <MaxAlbumArtWidth>480</MaxAlbumArtWidth>
-  <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
-  <MaxIconWidth>48</MaxIconWidth>
-  <MaxIconHeight>48</MaxIconHeight>
-  <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
-  <MaxStaticBitrate>140000000</MaxStaticBitrate>
-  <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
-  <MaxStaticMusicBitrate xsi:nil="true" />
-  <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo>
-  <TimelineOffsetSeconds>10</TimelineOffsetSeconds>
-  <RequiresPlainVideoItems>false</RequiresPlainVideoItems>
-  <RequiresPlainFolders>false</RequiresPlainFolders>
-  <EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
-  <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <XmlRootAttributes>
-    <XmlAttribute name="xmlns:pv" value="http://www.pv.com/pvns/" />
-  </XmlRootAttributes>
-  <DirectPlayProfiles>
-    <DirectPlayProfile container="mpeg,mpg" audioCodec="ac3,mp3,pcm_dvd" videoCodec="mpeg2video,mpeg4" type="Video" />
-    <DirectPlayProfile container="mkv" audioCodec="aac,ac3,dca,mp3,mp2,pcm,dts" videoCodec="h264,mpeg2video" type="Video" />
-    <DirectPlayProfile container="ts,mpegts" audioCodec="aac,mp3,mp2" videoCodec="h264,mpeg2video" type="Video" />
-    <DirectPlayProfile container="mp4,m4v" audioCodec="aac,ac3,mp3,pcm" videoCodec="h264" type="Video" />
-    <DirectPlayProfile container="mov" audioCodec="aac,pcm" videoCodec="h264" type="Video" />
-    <DirectPlayProfile container="avi" audioCodec="pcm" videoCodec="mpeg4" type="Video" />
-    <DirectPlayProfile container="flv" audioCodec="aac" videoCodec="h264" type="Video" />
-    <DirectPlayProfile container="mp3" audioCodec="mp3" type="Audio" />
-    <DirectPlayProfile container="mp4" audioCodec="aac" type="Audio" />
-    <DirectPlayProfile container="jpeg" type="Photo" />
-  </DirectPlayProfiles>
-  <TranscodingProfiles>
-    <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-    <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-    <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-  </TranscodingProfiles>
-  <ContainerProfiles>
-    <ContainerProfile type="Photo">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="Width" value="1920" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="Height" value="1080" isRequired="true" />
-      </Conditions>
-    </ContainerProfile>
-  </ContainerProfiles>
-  <CodecProfiles>
-    <CodecProfile type="Video">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="Width" value="1920" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="Height" value="1080" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="VideoBitDepth" value="8" isRequired="false" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-  </CodecProfiles>
-  <ResponseProfiles>
-    <ResponseProfile container="ts,mpegts" type="Video" orgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO" mimeType="video/vnd.dlna.mpeg-tts">
-      <Conditions />
-    </ResponseProfile>
-    <ResponseProfile container="m4v" type="Video" mimeType="video/mp4">
-      <Conditions />
-    </ResponseProfile>
-  </ResponseProfiles>
-  <SubtitleProfiles>
-    <SubtitleProfile format="srt" method="Embed" />
-    <SubtitleProfile format="srt" method="External" />
-  </SubtitleProfiles>
-</Profile>

+ 0 - 92
Emby.Dlna/Profiles/Xml/Popcorn Hour.xml

@@ -1,92 +0,0 @@
-<?xml version="1.0"?>
-<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-  xmlns:xsd="http://www.w3.org/2001/XMLSchema">
-  <Name>Popcorn Hour</Name>
-  <Manufacturer>Jellyfin</Manufacturer>
-  <ManufacturerUrl>https://github.com/jellyfin/jellyfin</ManufacturerUrl>
-  <ModelName>Jellyfin Server</ModelName>
-  <ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
-  <ModelNumber>01</ModelNumber>
-  <ModelUrl>https://github.com/jellyfin/jellyfin</ModelUrl>
-  <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
-  <EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit>
-  <EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit>
-  <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes>
-  <AlbumArtPn>JPEG_SM</AlbumArtPn>
-  <MaxAlbumArtWidth>480</MaxAlbumArtWidth>
-  <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
-  <MaxIconWidth>48</MaxIconWidth>
-  <MaxIconHeight>48</MaxIconHeight>
-  <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
-  <MaxStaticBitrate>140000000</MaxStaticBitrate>
-  <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
-  <MaxStaticMusicBitrate xsi:nil="true" />
-  <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo>
-  <TimelineOffsetSeconds>0</TimelineOffsetSeconds>
-  <RequiresPlainVideoItems>false</RequiresPlainVideoItems>
-  <RequiresPlainFolders>false</RequiresPlainFolders>
-  <EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
-  <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <XmlRootAttributes />
-  <DirectPlayProfiles>
-    <DirectPlayProfile container="mp4,mov,m4v" audioCodec="aac" videoCodec="h264,mpeg4" type="Video" />
-    <DirectPlayProfile container="ts,mpegts" audioCodec="aac,ac3,eac3,mp3,mp2,pcm" videoCodec="h264" type="Video" />
-    <DirectPlayProfile container="asf,wmv" audioCodec="wmav2,wmapro" videoCodec="wmv3,vc1" type="Video" />
-    <DirectPlayProfile container="avi" audioCodec="mp3,ac3,eac3,mp2,pcm" videoCodec="mpeg4,msmpeg4" type="Video" />
-    <DirectPlayProfile container="mkv" audioCodec="aac,mp3,ac3,eac3,mp2,pcm" videoCodec="h264" type="Video" />
-    <DirectPlayProfile container="aac,mp3,flac,ogg,wma,wav" type="Audio" />
-    <DirectPlayProfile container="jpeg,gif,bmp,png" type="Photo" />
-  </DirectPlayProfiles>
-  <TranscodingProfiles>
-    <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-    <TranscodingProfile container="mp4" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-    <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-  </TranscodingProfiles>
-  <ContainerProfiles />
-  <CodecProfiles>
-    <CodecProfile type="Video" codec="h264">
-      <Conditions>
-        <ProfileCondition condition="EqualsAny" property="VideoProfile" value="baseline|constrained baseline" isRequired="false" />
-        <ProfileCondition condition="LessThanEqual" property="Width" value="1920" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="Height" value="1080" isRequired="true" />
-        <ProfileCondition condition="NotEquals" property="IsAnamorphic" value="true" isRequired="false" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-    <CodecProfile type="Video">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="Width" value="1920" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="Height" value="1080" isRequired="true" />
-        <ProfileCondition condition="NotEquals" property="IsAnamorphic" value="true" isRequired="false" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-    <CodecProfile type="VideoAudio" codec="aac">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="AudioChannels" value="2" isRequired="false" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-    <CodecProfile type="Audio" codec="aac">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="AudioChannels" value="2" isRequired="false" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-    <CodecProfile type="Audio" codec="mp3">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="AudioChannels" value="2" isRequired="false" />
-        <ProfileCondition condition="LessThanEqual" property="AudioBitrate" value="320000" isRequired="false" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-  </CodecProfiles>
-  <ResponseProfiles>
-    <ResponseProfile container="m4v" type="Video" mimeType="video/mp4">
-      <Conditions />
-    </ResponseProfile>
-  </ResponseProfiles>
-  <SubtitleProfiles>
-    <SubtitleProfile format="srt" method="Embed" />
-  </SubtitleProfiles>
-</Profile>

+ 0 - 128
Emby.Dlna/Profiles/Xml/Samsung Smart TV.xml

@@ -1,128 +0,0 @@
-<?xml version="1.0"?>
-<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-  xmlns:xsd="http://www.w3.org/2001/XMLSchema">
-  <Name>Samsung Smart TV</Name>
-  <Identification>
-    <ModelUrl>samsung.com</ModelUrl>
-    <Headers>
-      <HttpHeaderInfo name="User-Agent" value="SEC_" match="Substring" />
-    </Headers>
-  </Identification>
-  <Manufacturer>Jellyfin</Manufacturer>
-  <ManufacturerUrl>https://github.com/jellyfin/jellyfin</ManufacturerUrl>
-  <ModelName>Jellyfin Server</ModelName>
-  <ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
-  <ModelNumber>01</ModelNumber>
-  <ModelUrl>https://github.com/jellyfin/jellyfin</ModelUrl>
-  <EnableAlbumArtInDidl>true</EnableAlbumArtInDidl>
-  <EnableSingleAlbumArtLimit>true</EnableSingleAlbumArtLimit>
-  <EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit>
-  <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes>
-  <AlbumArtPn>JPEG_SM</AlbumArtPn>
-  <MaxAlbumArtWidth>480</MaxAlbumArtWidth>
-  <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
-  <MaxIconWidth>48</MaxIconWidth>
-  <MaxIconHeight>48</MaxIconHeight>
-  <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
-  <MaxStaticBitrate>140000000</MaxStaticBitrate>
-  <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
-  <MaxStaticMusicBitrate xsi:nil="true" />
-  <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo>
-  <TimelineOffsetSeconds>0</TimelineOffsetSeconds>
-  <RequiresPlainVideoItems>false</RequiresPlainVideoItems>
-  <RequiresPlainFolders>false</RequiresPlainFolders>
-  <EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
-  <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <XmlRootAttributes>
-    <XmlAttribute name="xmlns:sec" value="http://www.sec.co.kr/" />
-  </XmlRootAttributes>
-  <DirectPlayProfiles>
-    <DirectPlayProfile container="asf" audioCodec="mp3,ac3,wmav2,wmapro,wmavoice" videoCodec="h264,mpeg4,mjpeg" type="Video" />
-    <DirectPlayProfile container="avi" audioCodec="mp3,ac3,dca,dts" videoCodec="h264,mpeg4,mjpeg" type="Video" />
-    <DirectPlayProfile container="mkv" audioCodec="mp3,ac3,dca,aac,dts" videoCodec="h264,mpeg4,mjpeg4" type="Video" />
-    <DirectPlayProfile container="mp4,m4v" audioCodec="mp3,aac" videoCodec="h264,mpeg4" type="Video" />
-    <DirectPlayProfile container="3gp" audioCodec="aac,he-aac" videoCodec="h264,mpeg4" type="Video" />
-    <DirectPlayProfile container="mpg,mpeg" audioCodec="ac3,mp2,mp3,aac" videoCodec="mpeg1video,mpeg2video,h264" type="Video" />
-    <DirectPlayProfile container="vro,vob" audioCodec="ac3,mp2,mp3" videoCodec="mpeg1video,mpeg2video" type="Video" />
-    <DirectPlayProfile container="ts" audioCodec="ac3,aac,mp3,eac3" videoCodec="mpeg2video,h264,vc1" type="Video" />
-    <DirectPlayProfile container="asf" audioCodec="wmav2,wmavoice" videoCodec="wmv2,wmv3" type="Video" />
-    <DirectPlayProfile container="mp3,flac" type="Audio" />
-    <DirectPlayProfile container="jpeg" type="Photo" />
-  </DirectPlayProfiles>
-  <TranscodingProfiles>
-    <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-    <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-    <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-  </TranscodingProfiles>
-  <ContainerProfiles>
-    <ContainerProfile type="Photo">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="Width" value="1920" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="Height" value="1080" isRequired="true" />
-      </Conditions>
-    </ContainerProfile>
-  </ContainerProfiles>
-  <CodecProfiles>
-    <CodecProfile type="Video" codec="mpeg2video">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="Width" value="1920" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="Height" value="1080" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="VideoFramerate" value="30" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="VideoBitrate" value="30720000" isRequired="true" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-    <CodecProfile type="Video" codec="mpeg4">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="Width" value="1920" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="Height" value="1080" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="VideoFramerate" value="30" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="VideoBitrate" value="8192000" isRequired="true" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-    <CodecProfile type="Video" codec="h264">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="Width" value="1920" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="Height" value="1080" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="VideoFramerate" value="30" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="VideoBitrate" value="37500000" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="VideoLevel" value="41" isRequired="true" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-    <CodecProfile type="Video" codec="wmv2,wmv3,vc1">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="Width" value="1920" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="Height" value="1080" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="VideoFramerate" value="30" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="VideoBitrate" value="25600000" isRequired="true" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-    <CodecProfile type="VideoAudio" codec="wmav2,dca,aac,mp3,dts">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="AudioChannels" value="6" isRequired="true" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-  </CodecProfiles>
-  <ResponseProfiles>
-    <ResponseProfile container="avi" type="Video" mimeType="video/x-msvideo">
-      <Conditions />
-    </ResponseProfile>
-    <ResponseProfile container="mkv" type="Video" mimeType="video/x-mkv">
-      <Conditions />
-    </ResponseProfile>
-    <ResponseProfile container="flac" type="Audio" mimeType="audio/x-flac">
-      <Conditions />
-    </ResponseProfile>
-    <ResponseProfile container="m4v" type="Video" mimeType="video/mp4">
-      <Conditions />
-    </ResponseProfile>
-  </ResponseProfiles>
-  <SubtitleProfiles>
-    <SubtitleProfile format="srt" method="Embed" />
-    <SubtitleProfile format="srt" method="External" didlMode="CaptionInfoEx" />
-  </SubtitleProfiles>
-</Profile>

+ 0 - 60
Emby.Dlna/Profiles/Xml/Sharp Smart TV.xml

@@ -1,60 +0,0 @@
-<?xml version="1.0"?>
-<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-  xmlns:xsd="http://www.w3.org/2001/XMLSchema">
-  <Name>Sharp Smart TV</Name>
-  <Identification>
-    <Manufacturer>Sharp</Manufacturer>
-    <Headers>
-      <HttpHeaderInfo name="User-Agent" value="Sharp" match="Substring" />
-    </Headers>
-  </Identification>
-  <Manufacturer>Jellyfin</Manufacturer>
-  <ManufacturerUrl>https://github.com/jellyfin/jellyfin</ManufacturerUrl>
-  <ModelName>Jellyfin Server</ModelName>
-  <ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
-  <ModelNumber>01</ModelNumber>
-  <ModelUrl>https://github.com/jellyfin/jellyfin</ModelUrl>
-  <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
-  <EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit>
-  <EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit>
-  <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes>
-  <AlbumArtPn>JPEG_SM</AlbumArtPn>
-  <MaxAlbumArtWidth>480</MaxAlbumArtWidth>
-  <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
-  <MaxIconWidth>48</MaxIconWidth>
-  <MaxIconHeight>48</MaxIconHeight>
-  <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
-  <MaxStaticBitrate>140000000</MaxStaticBitrate>
-  <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
-  <MaxStaticMusicBitrate xsi:nil="true" />
-  <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo>
-  <TimelineOffsetSeconds>0</TimelineOffsetSeconds>
-  <RequiresPlainVideoItems>true</RequiresPlainVideoItems>
-  <RequiresPlainFolders>true</RequiresPlainFolders>
-  <EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
-  <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <XmlRootAttributes />
-  <DirectPlayProfiles>
-    <DirectPlayProfile container="m4v,mkv,avi,mov,mp4" audioCodec="aac,mp3,ac3,dts,dca" videoCodec="h264,mpeg4" type="Video" />
-    <DirectPlayProfile container="asf,wmv" type="Video" />
-    <DirectPlayProfile container="mpg,mpeg" audioCodec="mp3,aac" videoCodec="mpeg2video" type="Video" />
-    <DirectPlayProfile container="flv" audioCodec="mp3,aac" videoCodec="h264" type="Video" />
-    <DirectPlayProfile container="mp3,wav" type="Audio" />
-  </DirectPlayProfiles>
-  <TranscodingProfiles>
-    <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-    <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3,aac,mp3,dts,dca" estimateContentLength="false" enableMpegtsM2TsMode="true" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-    <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-  </TranscodingProfiles>
-  <ContainerProfiles />
-  <CodecProfiles />
-  <ResponseProfiles>
-    <ResponseProfile container="m4v" type="Video" mimeType="video/mp4">
-      <Conditions />
-    </ResponseProfile>
-  </ResponseProfiles>
-  <SubtitleProfiles>
-    <SubtitleProfile format="srt" method="Embed" />
-    <SubtitleProfile format="srt" method="External" />
-  </SubtitleProfiles>
-</Profile>

File diff ditekan karena terlalu besar
+ 0 - 33
Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml


File diff ditekan karena terlalu besar
+ 0 - 33
Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2014.xml


File diff ditekan karena terlalu besar
+ 0 - 31
Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2015.xml


File diff ditekan karena terlalu besar
+ 0 - 31
Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2016.xml


File diff ditekan karena terlalu besar
+ 0 - 31
Emby.Dlna/Profiles/Xml/Sony Blu-ray Player.xml


+ 0 - 133
Emby.Dlna/Profiles/Xml/Sony Bravia (2010).xml

@@ -1,133 +0,0 @@
-<?xml version="1.0"?>
-<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-  xmlns:xsd="http://www.w3.org/2001/XMLSchema">
-  <Name>Sony Bravia (2010)</Name>
-  <Identification>
-    <FriendlyName>KDL-[0-9]{2}[EHLNPB]X[0-9][01][0-9].*</FriendlyName>
-    <Manufacturer>Sony</Manufacturer>
-    <Headers>
-      <HttpHeaderInfo name="X-AV-Client-Info" value=".*KDL-[0-9]{2}[EHLNPB]X[0-9][01][0-9].*" match="Regex" />
-    </Headers>
-  </Identification>
-  <Manufacturer>Microsoft Corporation</Manufacturer>
-  <ManufacturerUrl>http://www.microsoft.com/</ManufacturerUrl>
-  <ModelName>Windows Media Player Sharing</ModelName>
-  <ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
-  <ModelNumber>3.0</ModelNumber>
-  <ModelUrl>http://www.microsoft.com/</ModelUrl>
-  <EnableAlbumArtInDidl>true</EnableAlbumArtInDidl>
-  <EnableSingleAlbumArtLimit>true</EnableSingleAlbumArtLimit>
-  <EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit>
-  <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes>
-  <AlbumArtPn>JPEG_TN</AlbumArtPn>
-  <MaxAlbumArtWidth>480</MaxAlbumArtWidth>
-  <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
-  <MaxIconWidth>48</MaxIconWidth>
-  <MaxIconHeight>48</MaxIconHeight>
-  <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
-  <MaxStaticBitrate>140000000</MaxStaticBitrate>
-  <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
-  <MaxStaticMusicBitrate xsi:nil="true" />
-  <SonyAggregationFlags>10</SonyAggregationFlags>
-  <ProtocolInfo>http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000</ProtocolInfo>
-  <TimelineOffsetSeconds>0</TimelineOffsetSeconds>
-  <RequiresPlainVideoItems>false</RequiresPlainVideoItems>
-  <RequiresPlainFolders>false</RequiresPlainFolders>
-  <EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
-  <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <XmlRootAttributes>
-    <XmlAttribute name="xmlns:av" value="urn:schemas-sony-com:av" />
-  </XmlRootAttributes>
-  <DirectPlayProfiles>
-    <DirectPlayProfile container="ts,mpegts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" />
-    <DirectPlayProfile container="ts,mpegts" audioCodec="mp3,mp2" videoCodec="mpeg1video,mpeg2video" type="Video" />
-    <DirectPlayProfile container="mpeg" audioCodec="mp3,mp2" videoCodec="mpeg2video,mpeg1video" type="Video" />
-    <DirectPlayProfile container="mp3" audioCodec="mp3" type="Audio" />
-  </DirectPlayProfiles>
-  <TranscodingProfiles>
-    <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-    <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="true" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-    <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-  </TranscodingProfiles>
-  <ContainerProfiles>
-    <ContainerProfile type="Photo">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="Width" value="1920" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="Height" value="1080" isRequired="true" />
-      </Conditions>
-    </ContainerProfile>
-  </ContainerProfiles>
-  <CodecProfiles>
-    <CodecProfile type="Video" codec="h264">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="Width" value="1920" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="Height" value="1080" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="VideoFramerate" value="30" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="VideoBitrate" value="20000000" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="VideoLevel" value="41" isRequired="true" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-    <CodecProfile type="Video" codec="mpeg2video">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="Width" value="1920" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="Height" value="1080" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="VideoFramerate" value="30" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="VideoBitrate" value="20000000" isRequired="true" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-    <CodecProfile type="Video">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="Width" value="1920" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="Height" value="1080" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="VideoFramerate" value="30" isRequired="true" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-    <CodecProfile type="VideoAudio" codec="ac3">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="AudioChannels" value="6" isRequired="true" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-    <CodecProfile type="VideoAudio" codec="aac">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="AudioChannels" value="2" isRequired="true" />
-        <ProfileCondition condition="NotEquals" property="AudioProfile" value="he-aac" isRequired="true" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-    <CodecProfile type="VideoAudio" codec="mp3,mp2">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="AudioChannels" value="2" isRequired="true" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-  </CodecProfiles>
-  <ResponseProfiles>
-    <ResponseProfile container="ts,mpegts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T" mimeType="video/vnd.dlna.mpeg-tts">
-      <Conditions>
-        <ProfileCondition condition="Equals" property="PacketLength" value="192" isRequired="true" />
-        <ProfileCondition condition="Equals" property="VideoTimestamp" value="Valid" isRequired="true" />
-      </Conditions>
-    </ResponseProfile>
-    <ResponseProfile container="ts,mpegts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO" mimeType="video/mpeg">
-      <Conditions>
-        <ProfileCondition condition="Equals" property="PacketLength" value="188" isRequired="true" />
-      </Conditions>
-    </ResponseProfile>
-    <ResponseProfile container="ts,mpegts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU" mimeType="video/vnd.dlna.mpeg-tts">
-      <Conditions />
-    </ResponseProfile>
-    <ResponseProfile container="ts,mpegts" videoCodec="mpeg2video" type="Video" orgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO" mimeType="video/vnd.dlna.mpeg-tts">
-      <Conditions />
-    </ResponseProfile>
-    <ResponseProfile container="mpeg" videoCodec="mpeg1video,mpeg2video" type="Video" orgPn="MPEG_PS_NTSC,MPEG_PS_PAL" mimeType="video/mpeg">
-      <Conditions />
-    </ResponseProfile>
-  </ResponseProfiles>
-  <SubtitleProfiles>
-    <SubtitleProfile format="srt" method="Embed" />
-  </SubtitleProfiles>
-</Profile>

+ 0 - 139
Emby.Dlna/Profiles/Xml/Sony Bravia (2011).xml

@@ -1,139 +0,0 @@
-<?xml version="1.0"?>
-<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-  xmlns:xsd="http://www.w3.org/2001/XMLSchema">
-  <Name>Sony Bravia (2011)</Name>
-  <Identification>
-    <FriendlyName>KDL-[0-9]{2}([A-Z]X[0-9]2[0-9]|CX400).*</FriendlyName>
-    <Manufacturer>Sony</Manufacturer>
-    <Headers>
-      <HttpHeaderInfo name="X-AV-Client-Info" value=".*KDL-[0-9]{2}([A-Z]X[0-9]2[0-9]|CX400).*" match="Regex" />
-    </Headers>
-  </Identification>
-  <Manufacturer>Microsoft Corporation</Manufacturer>
-  <ManufacturerUrl>http://www.microsoft.com/</ManufacturerUrl>
-  <ModelName>Windows Media Player Sharing</ModelName>
-  <ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
-  <ModelNumber>3.0</ModelNumber>
-  <ModelUrl>http://www.microsoft.com/</ModelUrl>
-  <EnableAlbumArtInDidl>true</EnableAlbumArtInDidl>
-  <EnableSingleAlbumArtLimit>true</EnableSingleAlbumArtLimit>
-  <EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit>
-  <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes>
-  <AlbumArtPn>JPEG_TN</AlbumArtPn>
-  <MaxAlbumArtWidth>480</MaxAlbumArtWidth>
-  <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
-  <MaxIconWidth>48</MaxIconWidth>
-  <MaxIconHeight>48</MaxIconHeight>
-  <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
-  <MaxStaticBitrate>140000000</MaxStaticBitrate>
-  <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
-  <MaxStaticMusicBitrate xsi:nil="true" />
-  <SonyAggregationFlags>10</SonyAggregationFlags>
-  <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo>
-  <TimelineOffsetSeconds>0</TimelineOffsetSeconds>
-  <RequiresPlainVideoItems>false</RequiresPlainVideoItems>
-  <RequiresPlainFolders>false</RequiresPlainFolders>
-  <EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
-  <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <XmlRootAttributes>
-    <XmlAttribute name="xmlns:av" value="urn:schemas-sony-com:av" />
-  </XmlRootAttributes>
-  <DirectPlayProfiles>
-    <DirectPlayProfile container="ts,mpegts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" />
-    <DirectPlayProfile container="ts,mpegts" audioCodec="mp3" videoCodec="mpeg2video" type="Video" />
-    <DirectPlayProfile container="mp4,m4v" audioCodec="ac3,aac,mp3" videoCodec="h264,mpeg4" type="Video" />
-    <DirectPlayProfile container="mpeg" audioCodec="mp3" videoCodec="mpeg2video,mpeg1video" type="Video" />
-    <DirectPlayProfile container="asf" audioCodec="wmav2,wmapro,wmavoice" videoCodec="wmv2,wmv3,vc1" type="Video" />
-    <DirectPlayProfile container="mp3" audioCodec="mp3" type="Audio" />
-    <DirectPlayProfile container="asf" audioCodec="wmav2,wmapro,wmavoice" type="Audio" />
-  </DirectPlayProfiles>
-  <TranscodingProfiles>
-    <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-    <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="true" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-    <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-  </TranscodingProfiles>
-  <ContainerProfiles>
-    <ContainerProfile type="Photo">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="Width" value="1920" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="Height" value="1080" isRequired="true" />
-      </Conditions>
-    </ContainerProfile>
-  </ContainerProfiles>
-  <CodecProfiles>
-    <CodecProfile type="Video" codec="h264">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="Width" value="1920" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="Height" value="1080" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="VideoFramerate" value="30" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="VideoBitrate" value="20000000" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="VideoLevel" value="41" isRequired="true" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-    <CodecProfile type="Video" codec="mpeg2video">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="Width" value="1920" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="Height" value="1080" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="VideoFramerate" value="30" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="VideoBitrate" value="20000000" isRequired="true" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-    <CodecProfile type="Video">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="Width" value="1920" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="Height" value="1080" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="VideoFramerate" value="30" isRequired="true" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-    <CodecProfile type="VideoAudio" codec="ac3">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="AudioChannels" value="6" isRequired="true" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-    <CodecProfile type="VideoAudio" codec="aac">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="AudioChannels" value="2" isRequired="true" />
-        <ProfileCondition condition="NotEquals" property="AudioProfile" value="he-aac" isRequired="true" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-    <CodecProfile type="VideoAudio" codec="mp3,mp2">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="AudioChannels" value="2" isRequired="true" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-  </CodecProfiles>
-  <ResponseProfiles>
-    <ResponseProfile container="ts,mpegts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T" mimeType="video/vnd.dlna.mpeg-tts">
-      <Conditions>
-        <ProfileCondition condition="Equals" property="PacketLength" value="192" isRequired="true" />
-        <ProfileCondition condition="Equals" property="VideoTimestamp" value="Valid" isRequired="true" />
-      </Conditions>
-    </ResponseProfile>
-    <ResponseProfile container="ts,mpegts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO" mimeType="video/mpeg">
-      <Conditions>
-        <ProfileCondition condition="Equals" property="PacketLength" value="188" isRequired="true" />
-      </Conditions>
-    </ResponseProfile>
-    <ResponseProfile container="ts,mpegts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU" mimeType="video/vnd.dlna.mpeg-tts">
-      <Conditions />
-    </ResponseProfile>
-    <ResponseProfile container="ts,mpegts" videoCodec="mpeg2video" type="Video" orgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO" mimeType="video/vnd.dlna.mpeg-tts">
-      <Conditions />
-    </ResponseProfile>
-    <ResponseProfile container="mpeg" videoCodec="mpeg1video,mpeg2video" type="Video" orgPn="MPEG_PS_NTSC,MPEG_PS_PAL" mimeType="video/mpeg">
-      <Conditions />
-    </ResponseProfile>
-    <ResponseProfile container="m4v" type="Video" mimeType="video/mp4">
-      <Conditions />
-    </ResponseProfile>
-  </ResponseProfiles>
-  <SubtitleProfiles>
-    <SubtitleProfile format="srt" method="Embed" />
-  </SubtitleProfiles>
-</Profile>

+ 0 - 115
Emby.Dlna/Profiles/Xml/Sony Bravia (2012).xml

@@ -1,115 +0,0 @@
-<?xml version="1.0"?>
-<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-  xmlns:xsd="http://www.w3.org/2001/XMLSchema">
-  <Name>Sony Bravia (2012)</Name>
-  <Identification>
-    <FriendlyName>KDL-[0-9]{2}[A-Z]X[0-9]5([0-9]|G).*</FriendlyName>
-    <Manufacturer>Sony</Manufacturer>
-    <Headers>
-      <HttpHeaderInfo name="X-AV-Client-Info" value=".*KDL-[0-9]{2}[A-Z]X[0-9]5([0-9]|G).*" match="Regex" />
-    </Headers>
-  </Identification>
-  <Manufacturer>Microsoft Corporation</Manufacturer>
-  <ManufacturerUrl>http://www.microsoft.com/</ManufacturerUrl>
-  <ModelName>Windows Media Player Sharing</ModelName>
-  <ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
-  <ModelNumber>3.0</ModelNumber>
-  <ModelUrl>http://www.microsoft.com/</ModelUrl>
-  <EnableAlbumArtInDidl>true</EnableAlbumArtInDidl>
-  <EnableSingleAlbumArtLimit>true</EnableSingleAlbumArtLimit>
-  <EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit>
-  <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes>
-  <AlbumArtPn>JPEG_TN</AlbumArtPn>
-  <MaxAlbumArtWidth>480</MaxAlbumArtWidth>
-  <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
-  <MaxIconWidth>48</MaxIconWidth>
-  <MaxIconHeight>48</MaxIconHeight>
-  <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
-  <MaxStaticBitrate>140000000</MaxStaticBitrate>
-  <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
-  <MaxStaticMusicBitrate xsi:nil="true" />
-  <SonyAggregationFlags>10</SonyAggregationFlags>
-  <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo>
-  <TimelineOffsetSeconds>0</TimelineOffsetSeconds>
-  <RequiresPlainVideoItems>false</RequiresPlainVideoItems>
-  <RequiresPlainFolders>false</RequiresPlainFolders>
-  <EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
-  <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <XmlRootAttributes>
-    <XmlAttribute name="xmlns:av" value="urn:schemas-sony-com:av" />
-  </XmlRootAttributes>
-  <DirectPlayProfiles>
-    <DirectPlayProfile container="ts,mpegts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" />
-    <DirectPlayProfile container="ts,mpegts" audioCodec="mp3,mp2" videoCodec="mpeg2video" type="Video" />
-    <DirectPlayProfile container="mp4,m4v" audioCodec="ac3,aac,mp3,mp2" videoCodec="h264,mpeg4" type="Video" />
-    <DirectPlayProfile container="avi" audioCodec="ac3,mp3" videoCodec="mpeg4" type="Video" />
-    <DirectPlayProfile container="mpeg" audioCodec="mp3,mp2" videoCodec="mpeg2video,mpeg1video" type="Video" />
-    <DirectPlayProfile container="asf" audioCodec="wmav2,wmapro,wmavoice" videoCodec="wmv2,wmv3,vc1" type="Video" />
-    <DirectPlayProfile container="mp3" audioCodec="mp3" type="Audio" />
-    <DirectPlayProfile container="asf" audioCodec="wmav2,wmapro,wmavoice" type="Audio" />
-    <DirectPlayProfile container="jpeg" type="Photo" />
-  </DirectPlayProfiles>
-  <TranscodingProfiles>
-    <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-    <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="true" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-    <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-  </TranscodingProfiles>
-  <ContainerProfiles>
-    <ContainerProfile type="Photo">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="Width" value="1920" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="Height" value="1080" isRequired="true" />
-      </Conditions>
-    </ContainerProfile>
-  </ContainerProfiles>
-  <CodecProfiles>
-    <CodecProfile type="Video">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="Width" value="1920" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="Height" value="1080" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="VideoFramerate" value="30" isRequired="true" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-    <CodecProfile type="VideoAudio" codec="ac3">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="AudioChannels" value="6" isRequired="true" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-    <CodecProfile type="VideoAudio" codec="mp3,mp2">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="AudioChannels" value="2" isRequired="true" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-  </CodecProfiles>
-  <ResponseProfiles>
-    <ResponseProfile container="ts,mpegts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T" mimeType="video/vnd.dlna.mpeg-tts">
-      <Conditions>
-        <ProfileCondition condition="Equals" property="PacketLength" value="192" isRequired="true" />
-        <ProfileCondition condition="Equals" property="VideoTimestamp" value="Valid" isRequired="true" />
-      </Conditions>
-    </ResponseProfile>
-    <ResponseProfile container="ts,mpegts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO" mimeType="video/mpeg">
-      <Conditions>
-        <ProfileCondition condition="Equals" property="PacketLength" value="188" isRequired="true" />
-      </Conditions>
-    </ResponseProfile>
-    <ResponseProfile container="ts,mpegts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU" mimeType="video/vnd.dlna.mpeg-tts">
-      <Conditions />
-    </ResponseProfile>
-    <ResponseProfile container="ts,mpegts" videoCodec="mpeg2video" type="Video" orgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO" mimeType="video/vnd.dlna.mpeg-tts">
-      <Conditions />
-    </ResponseProfile>
-    <ResponseProfile container="mpeg" videoCodec="mpeg1video,mpeg2video" type="Video" orgPn="MPEG_PS_NTSC,MPEG_PS_PAL" mimeType="video/mpeg">
-      <Conditions />
-    </ResponseProfile>
-    <ResponseProfile container="m4v" type="Video" mimeType="video/mp4">
-      <Conditions />
-    </ResponseProfile>
-  </ResponseProfiles>
-  <SubtitleProfiles>
-    <SubtitleProfile format="srt" method="Embed" />
-  </SubtitleProfiles>
-</Profile>

+ 0 - 114
Emby.Dlna/Profiles/Xml/Sony Bravia (2013).xml

@@ -1,114 +0,0 @@
-<?xml version="1.0"?>
-<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-  xmlns:xsd="http://www.w3.org/2001/XMLSchema">
-  <Name>Sony Bravia (2013)</Name>
-  <Identification>
-    <FriendlyName>KDL-[0-9]{2}[WR][5689][0-9]{2}A.*</FriendlyName>
-    <Manufacturer>Sony</Manufacturer>
-    <Headers>
-      <HttpHeaderInfo name="X-AV-Client-Info" value=".*KDL-[0-9]{2}[WR][5689][0-9]{2}A.*" match="Regex" />
-    </Headers>
-  </Identification>
-  <Manufacturer>Microsoft Corporation</Manufacturer>
-  <ManufacturerUrl>http://www.microsoft.com/</ManufacturerUrl>
-  <ModelName>Windows Media Player Sharing</ModelName>
-  <ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
-  <ModelNumber>3.0</ModelNumber>
-  <ModelUrl>http://www.microsoft.com/</ModelUrl>
-  <EnableAlbumArtInDidl>true</EnableAlbumArtInDidl>
-  <EnableSingleAlbumArtLimit>true</EnableSingleAlbumArtLimit>
-  <EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit>
-  <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes>
-  <AlbumArtPn>JPEG_TN</AlbumArtPn>
-  <MaxAlbumArtWidth>480</MaxAlbumArtWidth>
-  <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
-  <MaxIconWidth>48</MaxIconWidth>
-  <MaxIconHeight>48</MaxIconHeight>
-  <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
-  <MaxStaticBitrate>140000000</MaxStaticBitrate>
-  <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
-  <MaxStaticMusicBitrate xsi:nil="true" />
-  <SonyAggregationFlags>10</SonyAggregationFlags>
-  <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo>
-  <TimelineOffsetSeconds>0</TimelineOffsetSeconds>
-  <RequiresPlainVideoItems>false</RequiresPlainVideoItems>
-  <RequiresPlainFolders>false</RequiresPlainFolders>
-  <EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
-  <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <XmlRootAttributes>
-    <XmlAttribute name="xmlns:av" value="urn:schemas-sony-com:av" />
-  </XmlRootAttributes>
-  <DirectPlayProfiles>
-    <DirectPlayProfile container="ts,mpegts" audioCodec="ac3,eac3,aac,mp3" videoCodec="h264" type="Video" />
-    <DirectPlayProfile container="ts,mpegts" audioCodec="mp3,mp2" videoCodec="mpeg2video" type="Video" />
-    <DirectPlayProfile container="mp4,m4v" audioCodec="ac3,eac3,aac,mp3,mp2" videoCodec="h264,mpeg4" type="Video" />
-    <DirectPlayProfile container="mov" audioCodec="ac3,eac3,aac,mp3,mp2" videoCodec="h264,mpeg4,mjpeg" type="Video" />
-    <DirectPlayProfile container="mkv" audioCodec="ac3,eac3,aac,mp3,mp2,pcm,vorbis" videoCodec="h264,mpeg4,vp8" type="Video" />
-    <DirectPlayProfile container="avi" audioCodec="ac3,eac3,mp3" videoCodec="mpeg4" type="Video" />
-    <DirectPlayProfile container="avi" audioCodec="pcm" videoCodec="mjpeg" type="Video" />
-    <DirectPlayProfile container="mpeg" audioCodec="mp3,mp2" videoCodec="mpeg2video,mpeg1video" type="Video" />
-    <DirectPlayProfile container="asf" audioCodec="wmav2,wmapro,wmavoice" videoCodec="wmv2,wmv3,vc1" type="Video" />
-    <DirectPlayProfile container="mp3" audioCodec="mp3" type="Audio" />
-    <DirectPlayProfile container="mp4" audioCodec="aac" type="Audio" />
-    <DirectPlayProfile container="wav" audioCodec="pcm" type="Audio" />
-    <DirectPlayProfile container="asf" audioCodec="wmav2,wmapro,wmavoice" type="Audio" />
-    <DirectPlayProfile container="jpeg" type="Photo" />
-  </DirectPlayProfiles>
-  <TranscodingProfiles>
-    <TranscodingProfile container="mp3" type="Audio" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-    <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="true" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-    <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-  </TranscodingProfiles>
-  <ContainerProfiles>
-    <ContainerProfile type="Photo">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="Width" value="1920" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="Height" value="1080" isRequired="true" />
-      </Conditions>
-    </ContainerProfile>
-  </ContainerProfiles>
-  <CodecProfiles>
-    <CodecProfile type="Video">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="Width" value="1920" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="Height" value="1080" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="VideoFramerate" value="30" isRequired="true" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-    <CodecProfile type="VideoAudio" codec="mp3,mp2">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="AudioChannels" value="2" isRequired="true" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-  </CodecProfiles>
-  <ResponseProfiles>
-    <ResponseProfile container="ts,mpegts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T" mimeType="video/vnd.dlna.mpeg-tts">
-      <Conditions>
-        <ProfileCondition condition="Equals" property="PacketLength" value="192" isRequired="true" />
-        <ProfileCondition condition="Equals" property="VideoTimestamp" value="Valid" isRequired="true" />
-      </Conditions>
-    </ResponseProfile>
-    <ResponseProfile container="ts,mpegts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO" mimeType="video/mpeg">
-      <Conditions>
-        <ProfileCondition condition="Equals" property="PacketLength" value="188" isRequired="true" />
-      </Conditions>
-    </ResponseProfile>
-    <ResponseProfile container="ts,mpegts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU" mimeType="video/vnd.dlna.mpeg-tts">
-      <Conditions />
-    </ResponseProfile>
-    <ResponseProfile container="ts,mpegts" videoCodec="mpeg2video" type="Video" orgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO" mimeType="video/vnd.dlna.mpeg-tts">
-      <Conditions />
-    </ResponseProfile>
-    <ResponseProfile container="mpeg" videoCodec="mpeg1video,mpeg2video" type="Video" orgPn="MPEG_PS_NTSC,MPEG_PS_PAL" mimeType="video/mpeg">
-      <Conditions />
-    </ResponseProfile>
-    <ResponseProfile container="m4v" type="Video" mimeType="video/mp4">
-      <Conditions />
-    </ResponseProfile>
-  </ResponseProfiles>
-  <SubtitleProfiles>
-    <SubtitleProfile format="srt" method="Embed" />
-  </SubtitleProfiles>
-</Profile>

+ 0 - 114
Emby.Dlna/Profiles/Xml/Sony Bravia (2014).xml

@@ -1,114 +0,0 @@
-<?xml version="1.0"?>
-<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-  xmlns:xsd="http://www.w3.org/2001/XMLSchema">
-  <Name>Sony Bravia (2014)</Name>
-  <Identification>
-    <FriendlyName>(KDL-[0-9]{2}W[5-9][0-9]{2}B|KDL-[0-9]{2}R480|XBR-[0-9]{2}X[89][0-9]{2}B|KD-[0-9]{2}[SX][89][0-9]{3}B).*</FriendlyName>
-    <Manufacturer>Sony</Manufacturer>
-    <Headers>
-      <HttpHeaderInfo name="X-AV-Client-Info" value=".*(KDL-[0-9]{2}W[5-9][0-9]{2}B|KDL-[0-9]{2}R480|XBR-[0-9]{2}X[89][0-9]{2}B|KD-[0-9]{2}[SX][89][0-9]{3}B).*" match="Regex" />
-    </Headers>
-  </Identification>
-  <Manufacturer>Microsoft Corporation</Manufacturer>
-  <ManufacturerUrl>http://www.microsoft.com/</ManufacturerUrl>
-  <ModelName>Windows Media Player Sharing</ModelName>
-  <ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
-  <ModelNumber>3.0</ModelNumber>
-  <ModelUrl>http://www.microsoft.com/</ModelUrl>
-  <EnableAlbumArtInDidl>true</EnableAlbumArtInDidl>
-  <EnableSingleAlbumArtLimit>true</EnableSingleAlbumArtLimit>
-  <EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit>
-  <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes>
-  <AlbumArtPn>JPEG_TN</AlbumArtPn>
-  <MaxAlbumArtWidth>480</MaxAlbumArtWidth>
-  <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
-  <MaxIconWidth>48</MaxIconWidth>
-  <MaxIconHeight>48</MaxIconHeight>
-  <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
-  <MaxStaticBitrate>140000000</MaxStaticBitrate>
-  <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
-  <MaxStaticMusicBitrate xsi:nil="true" />
-  <SonyAggregationFlags>10</SonyAggregationFlags>
-  <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo>
-  <TimelineOffsetSeconds>0</TimelineOffsetSeconds>
-  <RequiresPlainVideoItems>false</RequiresPlainVideoItems>
-  <RequiresPlainFolders>false</RequiresPlainFolders>
-  <EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
-  <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <XmlRootAttributes>
-    <XmlAttribute name="xmlns:av" value="urn:schemas-sony-com:av" />
-  </XmlRootAttributes>
-  <DirectPlayProfiles>
-    <DirectPlayProfile container="ts,mpegts" audioCodec="ac3,eac3,aac,mp3" videoCodec="h264" type="Video" />
-    <DirectPlayProfile container="ts,mpegts" audioCodec="mp3,mp2" videoCodec="mpeg2video" type="Video" />
-    <DirectPlayProfile container="mp4,m4v" audioCodec="ac3,eac3,aac,mp3,mp2" videoCodec="h264,mpeg4" type="Video" />
-    <DirectPlayProfile container="mov" audioCodec="ac3,eac3,aac,mp3,mp2" videoCodec="h264,mpeg4,mjpeg" type="Video" />
-    <DirectPlayProfile container="mkv" audioCodec="ac3,eac3,aac,mp3,mp2,pcm,vorbis" videoCodec="h264,mpeg4,vp8" type="Video" />
-    <DirectPlayProfile container="avi" audioCodec="ac3,eac3,mp3" videoCodec="mpeg4" type="Video" />
-    <DirectPlayProfile container="avi" audioCodec="pcm" videoCodec="mjpeg" type="Video" />
-    <DirectPlayProfile container="mpeg" audioCodec="mp3,mp2" videoCodec="mpeg2video,mpeg1video" type="Video" />
-    <DirectPlayProfile container="asf" audioCodec="wmav2,wmapro,wmavoice" videoCodec="wmv2,wmv3,vc1" type="Video" />
-    <DirectPlayProfile container="mp3" audioCodec="mp3" type="Audio" />
-    <DirectPlayProfile container="mp4" audioCodec="aac" type="Audio" />
-    <DirectPlayProfile container="wav" audioCodec="pcm" type="Audio" />
-    <DirectPlayProfile container="asf" audioCodec="wmav2,wmapro,wmavoice" type="Audio" />
-    <DirectPlayProfile container="jpeg" type="Photo" />
-  </DirectPlayProfiles>
-  <TranscodingProfiles>
-    <TranscodingProfile container="mp3" type="Audio" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-    <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="ac3" estimateContentLength="false" enableMpegtsM2TsMode="true" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-    <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-  </TranscodingProfiles>
-  <ContainerProfiles>
-    <ContainerProfile type="Photo">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="Width" value="1920" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="Height" value="1080" isRequired="true" />
-      </Conditions>
-    </ContainerProfile>
-  </ContainerProfiles>
-  <CodecProfiles>
-    <CodecProfile type="Video">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="Width" value="1920" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="Height" value="1080" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="VideoFramerate" value="30" isRequired="true" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-    <CodecProfile type="VideoAudio" codec="mp3,mp2">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="AudioChannels" value="2" isRequired="true" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-  </CodecProfiles>
-  <ResponseProfiles>
-    <ResponseProfile container="ts,mpegts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T" mimeType="video/vnd.dlna.mpeg-tts">
-      <Conditions>
-        <ProfileCondition condition="Equals" property="PacketLength" value="192" isRequired="true" />
-        <ProfileCondition condition="Equals" property="VideoTimestamp" value="Valid" isRequired="true" />
-      </Conditions>
-    </ResponseProfile>
-    <ResponseProfile container="ts,mpegts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO" mimeType="video/mpeg">
-      <Conditions>
-        <ProfileCondition condition="Equals" property="PacketLength" value="188" isRequired="true" />
-      </Conditions>
-    </ResponseProfile>
-    <ResponseProfile container="ts,mpegts" audioCodec="ac3,aac,mp3" videoCodec="h264" type="Video" orgPn="AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU" mimeType="video/vnd.dlna.mpeg-tts">
-      <Conditions />
-    </ResponseProfile>
-    <ResponseProfile container="ts,mpegts" videoCodec="mpeg2video" type="Video" orgPn="MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO" mimeType="video/vnd.dlna.mpeg-tts">
-      <Conditions />
-    </ResponseProfile>
-    <ResponseProfile container="mpeg" videoCodec="mpeg1video,mpeg2video" type="Video" orgPn="MPEG_PS_NTSC,MPEG_PS_PAL" mimeType="video/mpeg">
-      <Conditions />
-    </ResponseProfile>
-    <ResponseProfile container="m4v" type="Video" mimeType="video/mp4">
-      <Conditions />
-    </ResponseProfile>
-  </ResponseProfiles>
-  <SubtitleProfiles>
-    <SubtitleProfile format="srt" method="Embed" />
-  </SubtitleProfiles>
-</Profile>

+ 0 - 105
Emby.Dlna/Profiles/Xml/Sony PlayStation 3.xml

@@ -1,105 +0,0 @@
-<?xml version="1.0"?>
-<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-  xmlns:xsd="http://www.w3.org/2001/XMLSchema">
-  <Name>Sony PlayStation 3</Name>
-  <Identification>
-    <FriendlyName>PLAYSTATION 3</FriendlyName>
-    <Headers>
-      <HttpHeaderInfo name="User-Agent" value="PLAYSTATION 3" match="Substring" />
-      <HttpHeaderInfo name="X-AV-Client-Info" value="PLAYSTATION 3" match="Substring" />
-    </Headers>
-  </Identification>
-  <Manufacturer>Jellyfin</Manufacturer>
-  <ManufacturerUrl>https://github.com/jellyfin/jellyfin</ManufacturerUrl>
-  <ModelName>Jellyfin Server</ModelName>
-  <ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
-  <ModelNumber>01</ModelNumber>
-  <ModelUrl>https://github.com/jellyfin/jellyfin</ModelUrl>
-  <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
-  <EnableSingleAlbumArtLimit>true</EnableSingleAlbumArtLimit>
-  <EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit>
-  <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes>
-  <AlbumArtPn>JPEG_TN</AlbumArtPn>
-  <MaxAlbumArtWidth>480</MaxAlbumArtWidth>
-  <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
-  <MaxIconWidth>48</MaxIconWidth>
-  <MaxIconHeight>48</MaxIconHeight>
-  <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
-  <MaxStaticBitrate>140000000</MaxStaticBitrate>
-  <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
-  <MaxStaticMusicBitrate xsi:nil="true" />
-  <SonyAggregationFlags>10</SonyAggregationFlags>
-  <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo>
-  <TimelineOffsetSeconds>0</TimelineOffsetSeconds>
-  <RequiresPlainVideoItems>false</RequiresPlainVideoItems>
-  <RequiresPlainFolders>false</RequiresPlainFolders>
-  <EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
-  <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <XmlRootAttributes />
-  <DirectPlayProfiles>
-    <DirectPlayProfile container="avi" audioCodec="mp2,mp3" videoCodec="mpeg4" type="Video" />
-    <DirectPlayProfile container="ts,mpegts" audioCodec="aac,ac3,mp2" videoCodec="mpeg1video,mpeg2video,h264" type="Video" />
-    <DirectPlayProfile container="mpeg" audioCodec="mp2" videoCodec="mpeg1video,mpeg2video" type="Video" />
-    <DirectPlayProfile container="mp4" audioCodec="aac,ac3" videoCodec="h264,mpeg4" type="Video" />
-    <DirectPlayProfile container="aac,mp3,wav" type="Audio" />
-    <DirectPlayProfile container="jpeg,png,gif,bmp,tiff" type="Photo" />
-  </DirectPlayProfiles>
-  <TranscodingProfiles>
-    <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-    <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac,ac3,mp2" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-    <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-  </TranscodingProfiles>
-  <ContainerProfiles>
-    <ContainerProfile type="Photo">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="Width" value="1920" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="Height" value="1080" isRequired="true" />
-      </Conditions>
-    </ContainerProfile>
-  </ContainerProfiles>
-  <CodecProfiles>
-    <CodecProfile type="Video" codec="h264">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="Width" value="1920" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="Height" value="1080" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="VideoFramerate" value="30" isRequired="false" />
-        <ProfileCondition condition="LessThanEqual" property="VideoBitrate" value="15360000" isRequired="false" />
-        <ProfileCondition condition="LessThanEqual" property="VideoLevel" value="41" isRequired="false" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-    <CodecProfile type="VideoAudio" codec="ac3">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="AudioChannels" value="6" isRequired="false" />
-        <ProfileCondition condition="LessThanEqual" property="AudioBitrate" value="640000" isRequired="false" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-    <CodecProfile type="VideoAudio" codec="wmapro">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="AudioChannels" value="2" isRequired="true" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-    <CodecProfile type="VideoAudio" codec="aac">
-      <Conditions>
-        <ProfileCondition condition="NotEquals" property="AudioProfile" value="he-aac" isRequired="false" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-  </CodecProfiles>
-  <ResponseProfiles>
-    <ResponseProfile container="mp4,mov" audioCodec="aac" type="Video" mimeType="video/mp4">
-      <Conditions />
-    </ResponseProfile>
-    <ResponseProfile container="avi" type="Video" orgPn="AVI" mimeType="video/divx">
-      <Conditions />
-    </ResponseProfile>
-    <ResponseProfile container="wav" type="Audio" mimeType="audio/wav">
-      <Conditions />
-    </ResponseProfile>
-  </ResponseProfiles>
-  <SubtitleProfiles>
-    <SubtitleProfile format="srt" method="Embed" />
-  </SubtitleProfiles>
-</Profile>

+ 0 - 108
Emby.Dlna/Profiles/Xml/Sony PlayStation 4.xml

@@ -1,108 +0,0 @@
-<?xml version="1.0"?>
-<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-  xmlns:xsd="http://www.w3.org/2001/XMLSchema">
-  <Name>Sony PlayStation 4</Name>
-  <Identification>
-    <FriendlyName>PLAYSTATION 4</FriendlyName>
-    <Headers>
-      <HttpHeaderInfo name="User-Agent" value="PLAYSTATION 4" match="Substring" />
-      <HttpHeaderInfo name="X-AV-Client-Info" value="PLAYSTATION 4" match="Substring" />
-    </Headers>
-  </Identification>
-  <Manufacturer>Jellyfin</Manufacturer>
-  <ManufacturerUrl>https://github.com/jellyfin/jellyfin</ManufacturerUrl>
-  <ModelName>Jellyfin Server</ModelName>
-  <ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
-  <ModelNumber>01</ModelNumber>
-  <ModelUrl>https://github.com/jellyfin/jellyfin</ModelUrl>
-  <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
-  <EnableSingleAlbumArtLimit>true</EnableSingleAlbumArtLimit>
-  <EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit>
-  <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes>
-  <AlbumArtPn>JPEG_TN</AlbumArtPn>
-  <MaxAlbumArtWidth>480</MaxAlbumArtWidth>
-  <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
-  <MaxIconWidth>48</MaxIconWidth>
-  <MaxIconHeight>48</MaxIconHeight>
-  <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
-  <MaxStaticBitrate>140000000</MaxStaticBitrate>
-  <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
-  <MaxStaticMusicBitrate xsi:nil="true" />
-  <SonyAggregationFlags>10</SonyAggregationFlags>
-  <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo>
-  <TimelineOffsetSeconds>0</TimelineOffsetSeconds>
-  <RequiresPlainVideoItems>false</RequiresPlainVideoItems>
-  <RequiresPlainFolders>false</RequiresPlainFolders>
-  <EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
-  <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <XmlRootAttributes />
-  <DirectPlayProfiles>
-    <DirectPlayProfile container="avi" audioCodec="mp2,mp3" videoCodec="mpeg4" type="Video" />
-    <DirectPlayProfile container="ts,mpegts" audioCodec="aac,ac3,mp2" videoCodec="mpeg1video,mpeg2video,h264" type="Video" />
-    <DirectPlayProfile container="mpeg" audioCodec="mp2" videoCodec="mpeg1video,mpeg2video" type="Video" />
-    <DirectPlayProfile container="mp4,mkv,m4v" audioCodec="aac,ac3" videoCodec="h264,mpeg4" type="Video" />
-    <DirectPlayProfile container="aac,mp3,wav" type="Audio" />
-    <DirectPlayProfile container="jpeg,png,gif,bmp,tiff" type="Photo" />
-  </DirectPlayProfiles>
-  <TranscodingProfiles>
-    <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Bytes" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-    <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac,ac3,mp2" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-    <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-  </TranscodingProfiles>
-  <ContainerProfiles>
-    <ContainerProfile type="Photo">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="Width" value="1920" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="Height" value="1080" isRequired="true" />
-      </Conditions>
-    </ContainerProfile>
-  </ContainerProfiles>
-  <CodecProfiles>
-    <CodecProfile type="Video" codec="h264">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="Width" value="1920" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="Height" value="1080" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="VideoFramerate" value="30" isRequired="false" />
-        <ProfileCondition condition="LessThanEqual" property="VideoBitrate" value="15360000" isRequired="false" />
-        <ProfileCondition condition="LessThanEqual" property="VideoLevel" value="41" isRequired="false" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-    <CodecProfile type="VideoAudio" codec="ac3">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="AudioChannels" value="6" isRequired="false" />
-        <ProfileCondition condition="LessThanEqual" property="AudioBitrate" value="640000" isRequired="false" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-    <CodecProfile type="VideoAudio" codec="wmapro">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="AudioChannels" value="2" isRequired="true" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-    <CodecProfile type="VideoAudio" codec="aac">
-      <Conditions>
-        <ProfileCondition condition="NotEquals" property="AudioProfile" value="he-aac" isRequired="false" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-  </CodecProfiles>
-  <ResponseProfiles>
-    <ResponseProfile container="mp4,mov" audioCodec="aac" type="Video" mimeType="video/mp4">
-      <Conditions />
-    </ResponseProfile>
-    <ResponseProfile container="avi" type="Video" orgPn="AVI" mimeType="video/divx">
-      <Conditions />
-    </ResponseProfile>
-    <ResponseProfile container="wav" type="Audio" mimeType="audio/wav">
-      <Conditions />
-    </ResponseProfile>
-    <ResponseProfile container="m4v" type="Video" mimeType="video/mp4">
-      <Conditions />
-    </ResponseProfile>
-  </ResponseProfiles>
-  <SubtitleProfiles>
-    <SubtitleProfile format="srt" method="Embed" />
-  </SubtitleProfiles>
-</Profile>

+ 0 - 94
Emby.Dlna/Profiles/Xml/WDTV Live.xml

@@ -1,94 +0,0 @@
-<?xml version="1.0"?>
-<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-  xmlns:xsd="http://www.w3.org/2001/XMLSchema">
-  <Name>WDTV Live</Name>
-  <Identification>
-    <ModelName>WD TV</ModelName>
-    <Headers>
-      <HttpHeaderInfo name="User-Agent" value="alphanetworks" match="Substring" />
-      <HttpHeaderInfo name="User-Agent" value="ALPHA Networks" match="Substring" />
-    </Headers>
-  </Identification>
-  <Manufacturer>Jellyfin</Manufacturer>
-  <ManufacturerUrl>https://github.com/jellyfin/jellyfin</ManufacturerUrl>
-  <ModelName>Jellyfin Server</ModelName>
-  <ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
-  <ModelNumber>01</ModelNumber>
-  <ModelUrl>https://github.com/jellyfin/jellyfin</ModelUrl>
-  <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
-  <EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit>
-  <EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit>
-  <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes>
-  <AlbumArtPn>JPEG_SM</AlbumArtPn>
-  <MaxAlbumArtWidth>480</MaxAlbumArtWidth>
-  <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
-  <MaxIconWidth>48</MaxIconWidth>
-  <MaxIconHeight>48</MaxIconHeight>
-  <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
-  <MaxStaticBitrate>140000000</MaxStaticBitrate>
-  <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
-  <MaxStaticMusicBitrate xsi:nil="true" />
-  <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo>
-  <TimelineOffsetSeconds>5</TimelineOffsetSeconds>
-  <RequiresPlainVideoItems>false</RequiresPlainVideoItems>
-  <RequiresPlainFolders>false</RequiresPlainFolders>
-  <EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
-  <IgnoreTranscodeByteRangeRequests>true</IgnoreTranscodeByteRangeRequests>
-  <XmlRootAttributes />
-  <DirectPlayProfiles>
-    <DirectPlayProfile container="avi" audioCodec="ac3,eac3,dca,mp2,mp3,pcm,dts" videoCodec="mpeg1video,mpeg2video,mpeg4,h264,vc1" type="Video" />
-    <DirectPlayProfile container="mpeg" audioCodec="ac3,eac3,dca,mp2,mp3,pcm,dts" videoCodec="mpeg1video,mpeg2video" type="Video" />
-    <DirectPlayProfile container="mkv" audioCodec="ac3,eac3,dca,aac,mp2,mp3,pcm,dts" videoCodec="mpeg1video,mpeg2video,mpeg4,h264,vc1" type="Video" />
-    <DirectPlayProfile container="ts,m2ts,mpegts" audioCodec="ac3,eac3,dca,mp2,mp3,aac,dts" videoCodec="mpeg1video,mpeg2video,h264,vc1" type="Video" />
-    <DirectPlayProfile container="mp4,mov,m4v" audioCodec="ac3,eac3,aac,mp2,mp3,dca,dts" videoCodec="h264,mpeg4" type="Video" />
-    <DirectPlayProfile container="asf" audioCodec="wmav2,wmapro" videoCodec="vc1" type="Video" />
-    <DirectPlayProfile container="asf" audioCodec="mp2,ac3" videoCodec="mpeg2video" type="Video" />
-    <DirectPlayProfile container="mp3" audioCodec="mp2,mp3" type="Audio" />
-    <DirectPlayProfile container="mp4" audioCodec="mp4" type="Audio" />
-    <DirectPlayProfile container="flac" type="Audio" />
-    <DirectPlayProfile container="asf" audioCodec="wmav2,wmapro,wmavoice" type="Audio" />
-    <DirectPlayProfile container="ogg" audioCodec="vorbis" type="Audio" />
-    <DirectPlayProfile container="jpeg,png,gif,bmp,tiff" type="Photo" />
-  </DirectPlayProfiles>
-  <TranscodingProfiles>
-    <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-    <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-    <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-  </TranscodingProfiles>
-  <ContainerProfiles>
-    <ContainerProfile type="Photo">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="Width" value="1920" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="Height" value="1080" isRequired="true" />
-      </Conditions>
-    </ContainerProfile>
-  </ContainerProfiles>
-  <CodecProfiles>
-    <CodecProfile type="Video" codec="h264">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="Width" value="1920" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="Height" value="1080" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="VideoLevel" value="41" isRequired="true" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-    <CodecProfile type="VideoAudio" codec="aac">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="AudioChannels" value="2" isRequired="true" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-  </CodecProfiles>
-  <ResponseProfiles>
-    <ResponseProfile container="ts,mpegts" type="Video" orgPn="MPEG_TS_SD_NA">
-      <Conditions />
-    </ResponseProfile>
-  </ResponseProfiles>
-  <SubtitleProfiles>
-    <SubtitleProfile format="srt" method="External" />
-    <SubtitleProfile format="srt" method="Embed" />
-    <SubtitleProfile format="sub" method="Embed" />
-    <SubtitleProfile format="subrip" method="Embed" />
-    <SubtitleProfile format="idx" method="Embed" />
-  </SubtitleProfiles>
-</Profile>

+ 0 - 126
Emby.Dlna/Profiles/Xml/Xbox One.xml

@@ -1,126 +0,0 @@
-<?xml version="1.0"?>
-<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-  xmlns:xsd="http://www.w3.org/2001/XMLSchema">
-  <Name>Xbox One</Name>
-  <Identification>
-    <ModelName>Xbox One</ModelName>
-    <Headers>
-      <HttpHeaderInfo name="FriendlyName.DLNA.ORG" value="XboxOne" match="Substring" />
-      <HttpHeaderInfo name="User-Agent" value="NSPlayer/12" match="Substring" />
-    </Headers>
-  </Identification>
-  <Manufacturer>Jellyfin</Manufacturer>
-  <ManufacturerUrl>https://github.com/jellyfin/jellyfin</ManufacturerUrl>
-  <ModelName>Jellyfin Server</ModelName>
-  <ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
-  <ModelNumber>01</ModelNumber>
-  <ModelUrl>https://github.com/jellyfin/jellyfin</ModelUrl>
-  <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
-  <EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit>
-  <EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit>
-  <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes>
-  <AlbumArtPn>JPEG_SM</AlbumArtPn>
-  <MaxAlbumArtWidth>480</MaxAlbumArtWidth>
-  <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
-  <MaxIconWidth>48</MaxIconWidth>
-  <MaxIconHeight>48</MaxIconHeight>
-  <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
-  <MaxStaticBitrate>140000000</MaxStaticBitrate>
-  <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
-  <MaxStaticMusicBitrate xsi:nil="true" />
-  <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo>
-  <TimelineOffsetSeconds>40</TimelineOffsetSeconds>
-  <RequiresPlainVideoItems>false</RequiresPlainVideoItems>
-  <RequiresPlainFolders>false</RequiresPlainFolders>
-  <EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
-  <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <XmlRootAttributes />
-  <DirectPlayProfiles>
-    <DirectPlayProfile container="ts,mpegts" audioCodec="ac3,aac,mp3" videoCodec="h264,mpeg2video,hevc" type="Video" />
-    <DirectPlayProfile container="avi" audioCodec="ac3,mp3" videoCodec="mpeg4" type="Video" />
-    <DirectPlayProfile container="avi" audioCodec="aac" videoCodec="h264" type="Video" />
-    <DirectPlayProfile container="mp4,mov,mkv,m4v" audioCodec="aac,ac3" videoCodec="h264,mpeg4,mpeg2video,hevc" type="Video" />
-    <DirectPlayProfile container="asf" audioCodec="wmav2,wmapro" videoCodec="wmv2,wmv3,vc1" type="Video" />
-    <DirectPlayProfile container="asf" audioCodec="wmav2,wmapro,wmavoice" type="Audio" />
-    <DirectPlayProfile container="mp3" audioCodec="mp3" type="Audio" />
-    <DirectPlayProfile container="jpeg" type="Photo" />
-  </DirectPlayProfiles>
-  <TranscodingProfiles>
-    <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-    <TranscodingProfile container="jpeg" type="Photo" videoCodec="jpeg" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-    <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-  </TranscodingProfiles>
-  <ContainerProfiles>
-    <ContainerProfile type="Video" container="mp4,mov">
-      <Conditions>
-        <ProfileCondition condition="Equals" property="Has64BitOffsets" value="false" isRequired="false" />
-      </Conditions>
-    </ContainerProfile>
-  </ContainerProfiles>
-  <CodecProfiles>
-    <CodecProfile type="Video" codec="mpeg4">
-      <Conditions>
-        <ProfileCondition condition="NotEquals" property="IsAnamorphic" value="true" isRequired="false" />
-        <ProfileCondition condition="LessThanEqual" property="VideoBitDepth" value="8" isRequired="false" />
-        <ProfileCondition condition="LessThanEqual" property="Width" value="1920" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="Height" value="1080" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="VideoFramerate" value="30" isRequired="false" />
-        <ProfileCondition condition="LessThanEqual" property="VideoBitrate" value="5120000" isRequired="false" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-    <CodecProfile type="Video" codec="h264">
-      <Conditions>
-        <ProfileCondition condition="NotEquals" property="IsAnamorphic" value="true" isRequired="false" />
-        <ProfileCondition condition="LessThanEqual" property="VideoBitDepth" value="8" isRequired="false" />
-        <ProfileCondition condition="LessThanEqual" property="Width" value="1920" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="Height" value="1080" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="VideoLevel" value="41" isRequired="false" />
-        <ProfileCondition condition="EqualsAny" property="VideoProfile" value="high|main|baseline|constrained baseline" isRequired="false" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-    <CodecProfile type="Video" codec="wmv2,wmv3,vc1">
-      <Conditions>
-        <ProfileCondition condition="NotEquals" property="IsAnamorphic" value="true" isRequired="false" />
-        <ProfileCondition condition="LessThanEqual" property="VideoBitDepth" value="8" isRequired="false" />
-        <ProfileCondition condition="LessThanEqual" property="Width" value="1920" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="Height" value="1080" isRequired="true" />
-        <ProfileCondition condition="LessThanEqual" property="VideoFramerate" value="30" isRequired="false" />
-        <ProfileCondition condition="LessThanEqual" property="VideoBitrate" value="15360000" isRequired="false" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-    <CodecProfile type="Video">
-      <Conditions>
-        <ProfileCondition condition="NotEquals" property="IsAnamorphic" value="true" isRequired="false" />
-        <ProfileCondition condition="LessThanEqual" property="VideoBitDepth" value="8" isRequired="false" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-    <CodecProfile type="VideoAudio" codec="ac3,wmav2,wmapro">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="AudioChannels" value="6" isRequired="false" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-    <CodecProfile type="VideoAudio" codec="aac">
-      <Conditions>
-        <ProfileCondition condition="LessThanEqual" property="AudioChannels" value="2" isRequired="false" />
-        <ProfileCondition condition="Equals" property="AudioProfile" value="lc" isRequired="false" />
-      </Conditions>
-      <ApplyConditions />
-    </CodecProfile>
-  </CodecProfiles>
-  <ResponseProfiles>
-    <ResponseProfile container="avi" type="Video" mimeType="video/avi">
-      <Conditions />
-    </ResponseProfile>
-    <ResponseProfile container="m4v" type="Video" mimeType="video/mp4">
-      <Conditions />
-    </ResponseProfile>
-  </ResponseProfiles>
-  <SubtitleProfiles>
-    <SubtitleProfile format="srt" method="Embed" />
-  </SubtitleProfiles>
-</Profile>

+ 0 - 67
Emby.Dlna/Profiles/Xml/foobar2000.xml

@@ -1,67 +0,0 @@
-<?xml version="1.0"?>
-<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-  xmlns:xsd="http://www.w3.org/2001/XMLSchema">
-  <Name>foobar2000</Name>
-  <Identification>
-    <FriendlyName>foobar</FriendlyName>
-    <Headers>
-      <HttpHeaderInfo name="User-Agent" value="foobar" match="Substring" />
-    </Headers>
-  </Identification>
-  <Manufacturer>Jellyfin</Manufacturer>
-  <ManufacturerUrl>https://github.com/jellyfin/jellyfin</ManufacturerUrl>
-  <ModelName>Jellyfin Server</ModelName>
-  <ModelDescription>UPnP/AV 1.0 Compliant Media Server</ModelDescription>
-  <ModelNumber>01</ModelNumber>
-  <ModelUrl>https://github.com/jellyfin/jellyfin</ModelUrl>
-  <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl>
-  <EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit>
-  <EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit>
-  <SupportedMediaTypes>Audio</SupportedMediaTypes>
-  <AlbumArtPn>JPEG_SM</AlbumArtPn>
-  <MaxAlbumArtWidth>480</MaxAlbumArtWidth>
-  <MaxAlbumArtHeight>480</MaxAlbumArtHeight>
-  <MaxIconWidth>48</MaxIconWidth>
-  <MaxIconHeight>48</MaxIconHeight>
-  <MaxStreamingBitrate>140000000</MaxStreamingBitrate>
-  <MaxStaticBitrate>140000000</MaxStaticBitrate>
-  <MusicStreamingTranscodingBitrate>192000</MusicStreamingTranscodingBitrate>
-  <MaxStaticMusicBitrate xsi:nil="true" />
-  <ProtocolInfo>http-get:*:video/mpeg:*,http-get:*:video/mp4:*,http-get:*:video/vnd.dlna.mpeg-tts:*,http-get:*:video/avi:*,http-get:*:video/x-matroska:*,http-get:*:video/x-ms-wmv:*,http-get:*:video/wtv:*,http-get:*:audio/mpeg:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/x-ms-wma:*,http-get:*:audio/wav:*,http-get:*:audio/L16:*,http-get:*:image/jpeg:*,http-get:*:image/png:*,http-get:*:image/gif:*,http-get:*:image/tiff:*</ProtocolInfo>
-  <TimelineOffsetSeconds>0</TimelineOffsetSeconds>
-  <RequiresPlainVideoItems>false</RequiresPlainVideoItems>
-  <RequiresPlainFolders>false</RequiresPlainFolders>
-  <EnableMSMediaReceiverRegistrar>false</EnableMSMediaReceiverRegistrar>
-  <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
-  <XmlRootAttributes />
-  <DirectPlayProfiles>
-    <DirectPlayProfile container="mp3" audioCodec="mp2,mp3" type="Audio" />
-    <DirectPlayProfile container="mp4" audioCodec="mp4" type="Audio" />
-    <DirectPlayProfile container="aac,wav" type="Audio" />
-    <DirectPlayProfile container="flac" audioCodec="flac" type="Audio" />
-    <DirectPlayProfile container="asf" audioCodec="wmav2,wmapro,wmavoice" type="Audio" />
-    <DirectPlayProfile container="ogg" audioCodec="vorbis" type="Audio" />
-  </DirectPlayProfiles>
-  <TranscodingProfiles>
-    <TranscodingProfile container="mp3" type="Audio" audioCodec="mp3" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-    <TranscodingProfile container="ts" type="Video" videoCodec="h264" audioCodec="aac" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-    <TranscodingProfile container="jpeg" type="Photo" estimateContentLength="false" enableMpegtsM2TsMode="false" transcodeSeekInfo="Auto" copyTimestamps="false" context="Streaming" enableSubtitlesInManifest="false" minSegments="0" segmentLength="0" breakOnNonKeyFrames="false" />
-  </TranscodingProfiles>
-  <ContainerProfiles />
-  <CodecProfiles />
-  <ResponseProfiles />
-  <SubtitleProfiles>
-    <SubtitleProfile format="srt" method="External" />
-    <SubtitleProfile format="sub" method="External" />
-    <SubtitleProfile format="srt" method="Embed" />
-    <SubtitleProfile format="ass" method="Embed" />
-    <SubtitleProfile format="ssa" method="Embed" />
-    <SubtitleProfile format="smi" method="Embed" />
-    <SubtitleProfile format="dvdsub" method="Embed" />
-    <SubtitleProfile format="pgs" method="Embed" />
-    <SubtitleProfile format="pgssub" method="Embed" />
-    <SubtitleProfile format="sub" method="Embed" />
-    <SubtitleProfile format="subrip" method="Embed" />
-    <SubtitleProfile format="vtt" method="Embed" />
-  </SubtitleProfiles>
-</Profile>

+ 0 - 28
Emby.Dlna/Properties/AssemblyInfo.cs

@@ -1,28 +0,0 @@
-using System.Reflection;
-using System.Resources;
-using System.Runtime.CompilerServices;
-
-// General Information about an assembly is controlled through the following
-// set of attributes. Change these attribute values to modify the information
-// associated with an assembly.
-[assembly: AssemblyTitle("Emby.Dlna")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("Jellyfin Project")]
-[assembly: AssemblyProduct("Jellyfin Server")]
-[assembly: AssemblyCopyright("Copyright ©  2019 Jellyfin Contributors. Code released under the GNU General Public License")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-[assembly: NeutralResourcesLanguage("en")]
-[assembly: InternalsVisibleTo("Jellyfin.Dlna.Tests")]
-
-// Version information for an assembly consists of the following four values:
-//
-//      Major Version
-//      Minor Version
-//      Build Number
-//      Revision
-//
-// You can specify all the values or you can default the Build and Revision Numbers
-// by using the '*' as shown below:
-// [assembly: AssemblyVersion("1.0.*")]

+ 0 - 358
Emby.Dlna/Server/DescriptionXmlBuilder.cs

@@ -1,358 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Linq;
-using System.Security;
-using System.Text;
-using Emby.Dlna.Common;
-using MediaBrowser.Model.Dlna;
-
-namespace Emby.Dlna.Server
-{
-    public class DescriptionXmlBuilder
-    {
-        private readonly DeviceProfile _profile;
-
-        private readonly string _serverUdn;
-        private readonly string _serverAddress;
-        private readonly string _serverName;
-        private readonly string _serverId;
-
-        public DescriptionXmlBuilder(DeviceProfile profile, string serverUdn, string serverAddress, string serverName, string serverId)
-        {
-            ArgumentException.ThrowIfNullOrEmpty(serverUdn);
-            ArgumentException.ThrowIfNullOrEmpty(serverAddress);
-
-            _profile = profile;
-            _serverUdn = serverUdn;
-            _serverAddress = serverAddress;
-            _serverName = serverName;
-            _serverId = serverId;
-        }
-
-        public string GetXml()
-        {
-            var builder = new StringBuilder();
-
-            builder.Append("<?xml version=\"1.0\"?>");
-
-            builder.Append("<root");
-
-            var attributes = _profile.XmlRootAttributes.ToList();
-
-            attributes.Insert(0, new XmlAttribute
-            {
-                Name = "xmlns:dlna",
-                Value = "urn:schemas-dlna-org:device-1-0"
-            });
-            attributes.Insert(0, new XmlAttribute
-            {
-                Name = "xmlns",
-                Value = "urn:schemas-upnp-org:device-1-0"
-            });
-
-            foreach (var att in attributes)
-            {
-                builder.AppendFormat(CultureInfo.InvariantCulture, " {0}=\"{1}\"", att.Name, att.Value);
-            }
-
-            builder.Append('>');
-
-            builder.Append("<specVersion>");
-            builder.Append("<major>1</major>");
-            builder.Append("<minor>0</minor>");
-            builder.Append("</specVersion>");
-
-            AppendDeviceInfo(builder);
-
-            builder.Append("</root>");
-
-            return builder.ToString();
-        }
-
-        private void AppendDeviceInfo(StringBuilder builder)
-        {
-            builder.Append("<device>");
-            AppendDeviceProperties(builder);
-
-            AppendIconList(builder);
-
-            builder.Append("<presentationURL>")
-                .Append(SecurityElement.Escape(_serverAddress))
-                .Append("/web/index.html</presentationURL>");
-
-            AppendServiceList(builder);
-            builder.Append("</device>");
-        }
-
-        private void AppendDeviceProperties(StringBuilder builder)
-        {
-            builder.Append("<dlna:X_DLNACAP/>");
-
-            builder.Append("<dlna:X_DLNADOC xmlns:dlna=\"urn:schemas-dlna-org:device-1-0\">DMS-1.50</dlna:X_DLNADOC>");
-            builder.Append("<dlna:X_DLNADOC xmlns:dlna=\"urn:schemas-dlna-org:device-1-0\">M-DMS-1.50</dlna:X_DLNADOC>");
-
-            builder.Append("<deviceType>urn:schemas-upnp-org:device:MediaServer:1</deviceType>");
-
-            builder.Append("<friendlyName>")
-                .Append(SecurityElement.Escape(GetFriendlyName()))
-                .Append("</friendlyName>");
-            builder.Append("<manufacturer>")
-                .Append(SecurityElement.Escape(_profile.Manufacturer ?? string.Empty))
-                .Append("</manufacturer>");
-            builder.Append("<manufacturerURL>")
-                .Append(SecurityElement.Escape(_profile.ManufacturerUrl ?? string.Empty))
-                .Append("</manufacturerURL>");
-
-            builder.Append("<modelDescription>")
-                .Append(SecurityElement.Escape(_profile.ModelDescription ?? string.Empty))
-                .Append("</modelDescription>");
-            builder.Append("<modelName>")
-                .Append(SecurityElement.Escape(_profile.ModelName ?? string.Empty))
-                .Append("</modelName>");
-
-            builder.Append("<modelNumber>")
-                .Append(SecurityElement.Escape(_profile.ModelNumber ?? string.Empty))
-                .Append("</modelNumber>");
-            builder.Append("<modelURL>")
-                .Append(SecurityElement.Escape(_profile.ModelUrl ?? string.Empty))
-                .Append("</modelURL>");
-
-            if (string.IsNullOrEmpty(_profile.SerialNumber))
-            {
-                builder.Append("<serialNumber>")
-                    .Append(SecurityElement.Escape(_serverId))
-                    .Append("</serialNumber>");
-            }
-            else
-            {
-                builder.Append("<serialNumber>")
-                    .Append(SecurityElement.Escape(_profile.SerialNumber))
-                    .Append("</serialNumber>");
-            }
-
-            builder.Append("<UPC/>");
-
-            builder.Append("<UDN>uuid:")
-                .Append(SecurityElement.Escape(_serverUdn))
-                .Append("</UDN>");
-
-            if (!string.IsNullOrEmpty(_profile.SonyAggregationFlags))
-            {
-                builder.Append("<av:aggregationFlags xmlns:av=\"urn:schemas-sony-com:av\">")
-                    .Append(SecurityElement.Escape(_profile.SonyAggregationFlags))
-                    .Append("</av:aggregationFlags>");
-            }
-        }
-
-        internal string GetFriendlyName()
-        {
-            if (string.IsNullOrEmpty(_profile.FriendlyName))
-            {
-                return _serverName;
-            }
-
-            if (!_profile.FriendlyName.Contains("${HostName}", StringComparison.OrdinalIgnoreCase))
-            {
-                return _profile.FriendlyName;
-            }
-
-            var characterList = new List<char>();
-
-            foreach (var c in _serverName)
-            {
-                if (char.IsLetterOrDigit(c) || c == '-')
-                {
-                    characterList.Add(c);
-                }
-            }
-
-            var serverName = string.Create(
-                characterList.Count,
-                characterList,
-                (dest, source) =>
-                {
-                    for (int i = 0; i < dest.Length; i++)
-                    {
-                        dest[i] = source[i];
-                    }
-                });
-
-            return _profile.FriendlyName.Replace("${HostName}", serverName, StringComparison.OrdinalIgnoreCase);
-        }
-
-        private void AppendIconList(StringBuilder builder)
-        {
-            builder.Append("<iconList>");
-
-            foreach (var icon in GetIcons())
-            {
-                builder.Append("<icon>");
-
-                builder.Append("<mimetype>")
-                    .Append(SecurityElement.Escape(icon.MimeType))
-                    .Append("</mimetype>");
-                builder.Append("<width>")
-                    .Append(SecurityElement.Escape(icon.Width.ToString(CultureInfo.InvariantCulture)))
-                    .Append("</width>");
-                builder.Append("<height>")
-                    .Append(SecurityElement.Escape(icon.Height.ToString(CultureInfo.InvariantCulture)))
-                    .Append("</height>");
-                builder.Append("<depth>")
-                    .Append(SecurityElement.Escape(icon.Depth))
-                    .Append("</depth>");
-                builder.Append("<url>")
-                    .Append(BuildUrl(icon.Url))
-                    .Append("</url>");
-
-                builder.Append("</icon>");
-            }
-
-            builder.Append("</iconList>");
-        }
-
-        private void AppendServiceList(StringBuilder builder)
-        {
-            builder.Append("<serviceList>");
-
-            foreach (var service in GetServices())
-            {
-                builder.Append("<service>");
-
-                builder.Append("<serviceType>")
-                    .Append(SecurityElement.Escape(service.ServiceType))
-                    .Append("</serviceType>");
-                builder.Append("<serviceId>")
-                    .Append(SecurityElement.Escape(service.ServiceId))
-                    .Append("</serviceId>");
-                builder.Append("<SCPDURL>")
-                    .Append(BuildUrl(service.ScpdUrl))
-                    .Append("</SCPDURL>");
-                builder.Append("<controlURL>")
-                    .Append(BuildUrl(service.ControlUrl))
-                    .Append("</controlURL>");
-                builder.Append("<eventSubURL>")
-                    .Append(BuildUrl(service.EventSubUrl))
-                    .Append("</eventSubURL>");
-
-                builder.Append("</service>");
-            }
-
-            builder.Append("</serviceList>");
-        }
-
-        private string BuildUrl(string url)
-        {
-            if (string.IsNullOrEmpty(url))
-            {
-                return string.Empty;
-            }
-
-            url = _serverAddress.TrimEnd('/') + "/dlna/" + _serverUdn + "/" + url.TrimStart('/');
-
-            return SecurityElement.Escape(url);
-        }
-
-        private IEnumerable<DeviceIcon> GetIcons()
-            => new[]
-            {
-                new DeviceIcon
-                {
-                    MimeType = "image/png",
-                    Depth = "24",
-                    Width = 240,
-                    Height = 240,
-                    Url = "icons/logo240.png"
-                },
-
-                new DeviceIcon
-                {
-                    MimeType = "image/jpeg",
-                    Depth = "24",
-                    Width = 240,
-                    Height = 240,
-                    Url = "icons/logo240.jpg"
-                },
-
-                new DeviceIcon
-                {
-                    MimeType = "image/png",
-                    Depth = "24",
-                    Width = 120,
-                    Height = 120,
-                    Url = "icons/logo120.png"
-                },
-
-                new DeviceIcon
-                {
-                    MimeType = "image/jpeg",
-                    Depth = "24",
-                    Width = 120,
-                    Height = 120,
-                    Url = "icons/logo120.jpg"
-                },
-
-                new DeviceIcon
-                {
-                    MimeType = "image/png",
-                    Depth = "24",
-                    Width = 48,
-                    Height = 48,
-                    Url = "icons/logo48.png"
-                },
-
-                new DeviceIcon
-                {
-                    MimeType = "image/jpeg",
-                    Depth = "24",
-                    Width = 48,
-                    Height = 48,
-                    Url = "icons/logo48.jpg"
-                }
-            };
-
-        private IEnumerable<DeviceService> GetServices()
-        {
-            var list = new List<DeviceService>();
-
-            list.Add(new DeviceService
-            {
-                ServiceType = "urn:schemas-upnp-org:service:ContentDirectory:1",
-                ServiceId = "urn:upnp-org:serviceId:ContentDirectory",
-                ScpdUrl = "contentdirectory/contentdirectory.xml",
-                ControlUrl = "contentdirectory/control",
-                EventSubUrl = "contentdirectory/events"
-            });
-
-            list.Add(new DeviceService
-            {
-                ServiceType = "urn:schemas-upnp-org:service:ConnectionManager:1",
-                ServiceId = "urn:upnp-org:serviceId:ConnectionManager",
-                ScpdUrl = "connectionmanager/connectionmanager.xml",
-                ControlUrl = "connectionmanager/control",
-                EventSubUrl = "connectionmanager/events"
-            });
-
-            if (_profile.EnableMSMediaReceiverRegistrar)
-            {
-                list.Add(new DeviceService
-                {
-                    ServiceType = "urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1",
-                    ServiceId = "urn:microsoft.com:serviceId:X_MS_MediaReceiverRegistrar",
-                    ScpdUrl = "mediareceiverregistrar/mediareceiverregistrar.xml",
-                    ControlUrl = "mediareceiverregistrar/control",
-                    EventSubUrl = "mediareceiverregistrar/events"
-                });
-            }
-
-            return list;
-        }
-
-        public override string ToString()
-        {
-            return GetXml();
-        }
-    }
-}

+ 0 - 242
Emby.Dlna/Service/BaseControlHandler.cs

@@ -1,242 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Text;
-using System.Threading.Tasks;
-using System.Xml;
-using Emby.Dlna.Didl;
-using Jellyfin.Extensions;
-using MediaBrowser.Controller.Configuration;
-using Microsoft.Extensions.Logging;
-
-namespace Emby.Dlna.Service
-{
-    public abstract class BaseControlHandler
-    {
-        private const string NsSoapEnv = "http://schemas.xmlsoap.org/soap/envelope/";
-
-        protected BaseControlHandler(IServerConfigurationManager config, ILogger logger)
-        {
-            Config = config;
-            Logger = logger;
-        }
-
-        protected IServerConfigurationManager Config { get; }
-
-        protected ILogger Logger { get; }
-
-        public async Task<ControlResponse> ProcessControlRequestAsync(ControlRequest request)
-        {
-            try
-            {
-                LogRequest(request);
-
-                var response = await ProcessControlRequestInternalAsync(request).ConfigureAwait(false);
-                LogResponse(response);
-                return response;
-            }
-            catch (Exception ex)
-            {
-                Logger.LogError(ex, "Error processing control request");
-
-                return ControlErrorHandler.GetResponse(ex);
-            }
-        }
-
-        private async Task<ControlResponse> ProcessControlRequestInternalAsync(ControlRequest request)
-        {
-            ControlRequestInfo requestInfo;
-
-            using (var streamReader = new StreamReader(request.InputXml, Encoding.UTF8))
-            {
-                var readerSettings = new XmlReaderSettings()
-                {
-                    ValidationType = ValidationType.None,
-                    CheckCharacters = false,
-                    IgnoreProcessingInstructions = true,
-                    IgnoreComments = true,
-                    Async = true
-                };
-
-                using var reader = XmlReader.Create(streamReader, readerSettings);
-                requestInfo = await ParseRequestAsync(reader).ConfigureAwait(false);
-            }
-
-            Logger.LogDebug("Received control request {LocalName}, params: {@Headers}", requestInfo.LocalName, requestInfo.Headers);
-
-            return CreateControlResponse(requestInfo);
-        }
-
-        private ControlResponse CreateControlResponse(ControlRequestInfo requestInfo)
-        {
-            var settings = new XmlWriterSettings
-            {
-                Encoding = Encoding.UTF8,
-                CloseOutput = false
-            };
-
-            StringWriter builder = new StringWriterWithEncoding(Encoding.UTF8);
-
-            using (var writer = XmlWriter.Create(builder, settings))
-            {
-                writer.WriteStartDocument(true);
-
-                writer.WriteStartElement("SOAP-ENV", "Envelope", NsSoapEnv);
-                writer.WriteAttributeString(string.Empty, "encodingStyle", NsSoapEnv, "http://schemas.xmlsoap.org/soap/encoding/");
-
-                writer.WriteStartElement("SOAP-ENV", "Body", NsSoapEnv);
-                writer.WriteStartElement("u", requestInfo.LocalName + "Response", requestInfo.NamespaceURI);
-
-                WriteResult(requestInfo.LocalName, requestInfo.Headers, writer);
-
-                writer.WriteFullEndElement();
-                writer.WriteFullEndElement();
-
-                writer.WriteFullEndElement();
-                writer.WriteEndDocument();
-            }
-
-            var xml = builder.ToString().Replace("xmlns:m=", "xmlns:u=", StringComparison.Ordinal);
-
-            var controlResponse = new ControlResponse(xml, true);
-
-            controlResponse.Headers.Add("EXT", string.Empty);
-
-            return controlResponse;
-        }
-
-        private async Task<ControlRequestInfo> ParseRequestAsync(XmlReader reader)
-        {
-            await reader.MoveToContentAsync().ConfigureAwait(false);
-            await reader.ReadAsync().ConfigureAwait(false);
-
-            // Loop through each element
-            while (!reader.EOF && reader.ReadState == ReadState.Interactive)
-            {
-                if (reader.NodeType == XmlNodeType.Element)
-                {
-                    if (string.Equals(reader.LocalName, "Body", StringComparison.Ordinal))
-                    {
-                        if (reader.IsEmptyElement)
-                        {
-                            await reader.ReadAsync().ConfigureAwait(false);
-                            continue;
-                        }
-
-                        using var subReader = reader.ReadSubtree();
-                        return await ParseBodyTagAsync(subReader).ConfigureAwait(false);
-                    }
-
-                    await reader.SkipAsync().ConfigureAwait(false);
-                }
-                else
-                {
-                    await reader.ReadAsync().ConfigureAwait(false);
-                }
-            }
-
-            throw new EndOfStreamException("Stream ended but no body tag found.");
-        }
-
-        private async Task<ControlRequestInfo> ParseBodyTagAsync(XmlReader reader)
-        {
-            string? namespaceURI = null, localName = null;
-
-            await reader.MoveToContentAsync().ConfigureAwait(false);
-            await reader.ReadAsync().ConfigureAwait(false);
-
-            // Loop through each element
-            while (!reader.EOF && reader.ReadState == ReadState.Interactive)
-            {
-                if (reader.NodeType == XmlNodeType.Element)
-                {
-                    localName = reader.LocalName;
-                    namespaceURI = reader.NamespaceURI;
-
-                    if (reader.IsEmptyElement)
-                    {
-                        await reader.ReadAsync().ConfigureAwait(false);
-                    }
-                    else
-                    {
-                        var result = new ControlRequestInfo(localName, namespaceURI);
-                        using var subReader = reader.ReadSubtree();
-                        await ParseFirstBodyChildAsync(subReader, result.Headers).ConfigureAwait(false);
-                        return result;
-                    }
-                }
-                else
-                {
-                    await reader.ReadAsync().ConfigureAwait(false);
-                }
-            }
-
-            if (localName is not null && namespaceURI is not null)
-            {
-                return new ControlRequestInfo(localName, namespaceURI);
-            }
-
-            throw new EndOfStreamException("Stream ended but no control found.");
-        }
-
-        private async Task ParseFirstBodyChildAsync(XmlReader reader, IDictionary<string, string> headers)
-        {
-            await reader.MoveToContentAsync().ConfigureAwait(false);
-            await reader.ReadAsync().ConfigureAwait(false);
-
-            // Loop through each element
-            while (!reader.EOF && reader.ReadState == ReadState.Interactive)
-            {
-                if (reader.NodeType == XmlNodeType.Element)
-                {
-                    // TODO: Should we be doing this here, or should it be handled earlier when decoding the request?
-                    headers[reader.LocalName.RemoveDiacritics()] = await reader.ReadElementContentAsStringAsync().ConfigureAwait(false);
-                }
-                else
-                {
-                    await reader.ReadAsync().ConfigureAwait(false);
-                }
-            }
-        }
-
-        protected abstract void WriteResult(string methodName, IReadOnlyDictionary<string, string> methodParams, XmlWriter xmlWriter);
-
-        private void LogRequest(ControlRequest request)
-        {
-            if (!Config.GetDlnaConfiguration().EnableDebugLog)
-            {
-                return;
-            }
-
-            Logger.LogDebug("Control request. Headers: {@Headers}", request.Headers);
-        }
-
-        private void LogResponse(ControlResponse response)
-        {
-            if (!Config.GetDlnaConfiguration().EnableDebugLog)
-            {
-                return;
-            }
-
-            Logger.LogDebug("Control response. Headers: {@Headers}\n{Xml}", response.Headers, response.Xml);
-        }
-
-        private class ControlRequestInfo
-        {
-            public ControlRequestInfo(string localName, string namespaceUri)
-            {
-                LocalName = localName;
-                NamespaceURI = namespaceUri;
-                Headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
-            }
-
-            public string LocalName { get; set; }
-
-            public string NamespaceURI { get; set; }
-
-            public Dictionary<string, string> Headers { get; }
-        }
-    }
-}

+ 0 - 37
Emby.Dlna/Service/BaseService.cs

@@ -1,37 +0,0 @@
-#nullable disable
-#pragma warning disable CS1591
-
-using System.Net.Http;
-using Emby.Dlna.Eventing;
-using Microsoft.Extensions.Logging;
-
-namespace Emby.Dlna.Service
-{
-    public class BaseService : IDlnaEventManager
-    {
-        protected BaseService(ILogger<BaseService> logger, IHttpClientFactory httpClientFactory)
-        {
-            Logger = logger;
-            EventManager = new DlnaEventManager(logger, httpClientFactory);
-        }
-
-        protected IDlnaEventManager EventManager { get; }
-
-        protected ILogger Logger { get; }
-
-        public EventSubscriptionResponse CancelEventSubscription(string subscriptionId)
-        {
-            return EventManager.CancelEventSubscription(subscriptionId);
-        }
-
-        public EventSubscriptionResponse RenewEventSubscription(string subscriptionId, string notificationType, string requestedTimeoutString, string callbackUrl)
-        {
-            return EventManager.RenewEventSubscription(subscriptionId, notificationType, requestedTimeoutString, callbackUrl);
-        }
-
-        public EventSubscriptionResponse CreateEventSubscription(string notificationType, string requestedTimeoutString, string callbackUrl)
-        {
-            return EventManager.CreateEventSubscription(notificationType, requestedTimeoutString, callbackUrl);
-        }
-    }
-}

+ 0 - 52
Emby.Dlna/Service/ControlErrorHandler.cs

@@ -1,52 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using System.IO;
-using System.Text;
-using System.Xml;
-using Emby.Dlna.Didl;
-
-namespace Emby.Dlna.Service
-{
-    public static class ControlErrorHandler
-    {
-        private const string NsSoapEnv = "http://schemas.xmlsoap.org/soap/envelope/";
-
-        public static ControlResponse GetResponse(Exception ex)
-        {
-            var settings = new XmlWriterSettings
-            {
-                Encoding = Encoding.UTF8,
-                CloseOutput = false
-            };
-
-            StringWriter builder = new StringWriterWithEncoding(Encoding.UTF8);
-
-            using (var writer = XmlWriter.Create(builder, settings))
-            {
-                writer.WriteStartDocument(true);
-
-                writer.WriteStartElement("SOAP-ENV", "Envelope", NsSoapEnv);
-                writer.WriteAttributeString(string.Empty, "encodingStyle", NsSoapEnv, "http://schemas.xmlsoap.org/soap/encoding/");
-
-                writer.WriteStartElement("SOAP-ENV", "Body", NsSoapEnv);
-                writer.WriteStartElement("SOAP-ENV", "Fault", NsSoapEnv);
-
-                writer.WriteElementString("faultcode", "500");
-                writer.WriteElementString("faultstring", ex.Message);
-
-                writer.WriteStartElement("detail");
-                writer.WriteRaw("<UPnPError xmlns=\"urn:schemas-upnp-org:control-1-0\"><errorCode>401</errorCode><errorDescription>Invalid Action</errorDescription></UPnPError>");
-                writer.WriteFullEndElement();
-
-                writer.WriteFullEndElement();
-                writer.WriteFullEndElement();
-
-                writer.WriteFullEndElement();
-                writer.WriteEndDocument();
-            }
-
-            return new ControlResponse(builder.ToString(), false);
-        }
-    }
-}

+ 0 - 109
Emby.Dlna/Service/ServiceXmlBuilder.cs

@@ -1,109 +0,0 @@
-#pragma warning disable CS1591
-
-using System.Collections.Generic;
-using System.Security;
-using System.Text;
-using Emby.Dlna.Common;
-
-namespace Emby.Dlna.Service
-{
-    public class ServiceXmlBuilder
-    {
-        public string GetXml(IEnumerable<ServiceAction> actions, IEnumerable<StateVariable> stateVariables)
-        {
-            var builder = new StringBuilder();
-
-            builder.Append("<?xml version=\"1.0\"?>");
-            builder.Append("<scpd xmlns=\"urn:schemas-upnp-org:service-1-0\">");
-
-            builder.Append("<specVersion>");
-            builder.Append("<major>1</major>");
-            builder.Append("<minor>0</minor>");
-            builder.Append("</specVersion>");
-
-            AppendActionList(builder, actions);
-            AppendServiceStateTable(builder, stateVariables);
-
-            builder.Append("</scpd>");
-
-            return builder.ToString();
-        }
-
-        private static void AppendActionList(StringBuilder builder, IEnumerable<ServiceAction> actions)
-        {
-            builder.Append("<actionList>");
-
-            foreach (var item in actions)
-            {
-                builder.Append("<action>");
-
-                builder.Append("<name>")
-                    .Append(SecurityElement.Escape(item.Name))
-                    .Append("</name>");
-
-                builder.Append("<argumentList>");
-
-                foreach (var argument in item.ArgumentList)
-                {
-                    builder.Append("<argument>");
-
-                    builder.Append("<name>")
-                        .Append(SecurityElement.Escape(argument.Name))
-                        .Append("</name>");
-                    builder.Append("<direction>")
-                        .Append(SecurityElement.Escape(argument.Direction))
-                        .Append("</direction>");
-                    builder.Append("<relatedStateVariable>")
-                        .Append(SecurityElement.Escape(argument.RelatedStateVariable))
-                        .Append("</relatedStateVariable>");
-
-                    builder.Append("</argument>");
-                }
-
-                builder.Append("</argumentList>");
-
-                builder.Append("</action>");
-            }
-
-            builder.Append("</actionList>");
-        }
-
-        private static void AppendServiceStateTable(StringBuilder builder, IEnumerable<StateVariable> stateVariables)
-        {
-            builder.Append("<serviceStateTable>");
-
-            foreach (var item in stateVariables)
-            {
-                var sendEvents = item.SendsEvents ? "yes" : "no";
-
-                builder.Append("<stateVariable sendEvents=\"")
-                    .Append(sendEvents)
-                    .Append("\">");
-
-                builder.Append("<name>")
-                    .Append(SecurityElement.Escape(item.Name))
-                    .Append("</name>");
-                builder.Append("<dataType>")
-                    .Append(SecurityElement.Escape(item.DataType))
-                    .Append("</dataType>");
-
-                if (item.AllowedValues.Count > 0)
-                {
-                    builder.Append("<allowedValueList>");
-                    foreach (var allowedValue in item.AllowedValues)
-                    {
-                        builder.Append("<allowedValue>")
-                            .Append(SecurityElement.Escape(allowedValue))
-                            .Append("</allowedValue>");
-                    }
-
-                    builder.Append("</allowedValueList>");
-                }
-
-                builder.Append("</stateVariable>");
-            }
-
-            builder.Append("</serviceStateTable>");
-        }
-    }
-}

+ 0 - 151
Emby.Dlna/Ssdp/DeviceDiscovery.cs

@@ -1,151 +0,0 @@
-#nullable disable
-
-#pragma warning disable CS1591
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using Jellyfin.Data.Events;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Model.Dlna;
-using Rssdp;
-using Rssdp.Infrastructure;
-
-namespace Emby.Dlna.Ssdp
-{
-    public sealed class DeviceDiscovery : IDeviceDiscovery, IDisposable
-    {
-        private readonly object _syncLock = new object();
-
-        private readonly IServerConfigurationManager _config;
-
-        private SsdpDeviceLocator _deviceLocator;
-        private ISsdpCommunicationsServer _commsServer;
-
-        private int _listenerCount;
-        private bool _disposed;
-
-        public DeviceDiscovery(IServerConfigurationManager config)
-        {
-            _config = config;
-        }
-
-        private event EventHandler<GenericEventArgs<UpnpDeviceInfo>> DeviceDiscoveredInternal;
-
-        /// <inheritdoc />
-        public event EventHandler<GenericEventArgs<UpnpDeviceInfo>> DeviceDiscovered
-        {
-            add
-            {
-                lock (_syncLock)
-                {
-                    _listenerCount++;
-                    DeviceDiscoveredInternal += value;
-                }
-
-                StartInternal();
-            }
-
-            remove
-            {
-                lock (_syncLock)
-                {
-                    _listenerCount--;
-                    DeviceDiscoveredInternal -= value;
-                }
-            }
-        }
-
-        /// <inheritdoc />
-        public event EventHandler<GenericEventArgs<UpnpDeviceInfo>> DeviceLeft;
-
-        // Call this method from somewhere in your code to start the search.
-        public void Start(ISsdpCommunicationsServer communicationsServer)
-        {
-            _commsServer = communicationsServer;
-
-            StartInternal();
-        }
-
-        private void StartInternal()
-        {
-            lock (_syncLock)
-            {
-                if (_listenerCount > 0 && _deviceLocator is null && _commsServer is not null)
-                {
-                    _deviceLocator = new SsdpDeviceLocator(
-                        _commsServer,
-                        Environment.OSVersion.Platform.ToString(),
-                        // Can not use VersionString here since that includes OS and version
-                        Environment.OSVersion.Version.ToString());
-
-                    // (Optional) Set the filter so we only see notifications for devices we care about
-                    // (can be any search target value i.e device type, uuid value etc - any value that appears in the
-                    // DiscoverdSsdpDevice.NotificationType property or that is used with the searchTarget parameter of the Search method).
-                    // _DeviceLocator.NotificationFilter = "upnp:rootdevice";
-
-                    // Connect our event handler so we process devices as they are found
-                    _deviceLocator.DeviceAvailable += OnDeviceLocatorDeviceAvailable;
-                    _deviceLocator.DeviceUnavailable += OnDeviceLocatorDeviceUnavailable;
-
-                    var dueTime = TimeSpan.FromSeconds(5);
-                    var interval = TimeSpan.FromSeconds(_config.GetDlnaConfiguration().ClientDiscoveryIntervalSeconds);
-
-                    _deviceLocator.RestartBroadcastTimer(dueTime, interval);
-                }
-            }
-        }
-
-        // Process each found device in the event handler
-        private void OnDeviceLocatorDeviceAvailable(object sender, DeviceAvailableEventArgs e)
-        {
-            var originalHeaders = e.DiscoveredDevice.ResponseHeaders;
-
-            var headerDict = originalHeaders is null ? new Dictionary<string, KeyValuePair<string, IEnumerable<string>>>() : originalHeaders.ToDictionary(i => i.Key, StringComparer.OrdinalIgnoreCase);
-
-            var headers = headerDict.ToDictionary(i => i.Key, i => i.Value.Value.FirstOrDefault(), StringComparer.OrdinalIgnoreCase);
-
-            var args = new GenericEventArgs<UpnpDeviceInfo>(
-                new UpnpDeviceInfo
-                {
-                    Location = e.DiscoveredDevice.DescriptionLocation,
-                    Headers = headers,
-                    RemoteIPAddress = e.RemoteIPAddress
-                });
-
-            DeviceDiscoveredInternal?.Invoke(this, args);
-        }
-
-        private void OnDeviceLocatorDeviceUnavailable(object sender, DeviceUnavailableEventArgs e)
-        {
-            var originalHeaders = e.DiscoveredDevice.ResponseHeaders;
-
-            var headerDict = originalHeaders is null ? new Dictionary<string, KeyValuePair<string, IEnumerable<string>>>() : originalHeaders.ToDictionary(i => i.Key, StringComparer.OrdinalIgnoreCase);
-
-            var headers = headerDict.ToDictionary(i => i.Key, i => i.Value.Value.FirstOrDefault(), StringComparer.OrdinalIgnoreCase);
-
-            var args = new GenericEventArgs<UpnpDeviceInfo>(
-                new UpnpDeviceInfo
-                {
-                    Location = e.DiscoveredDevice.DescriptionLocation,
-                    Headers = headers
-                });
-
-            DeviceLeft?.Invoke(this, args);
-        }
-
-        /// <inheritdoc />
-        public void Dispose()
-        {
-            if (!_disposed)
-            {
-                _disposed = true;
-                if (_deviceLocator is not null)
-                {
-                    _deviceLocator.Dispose();
-                    _deviceLocator = null;
-                }
-            }
-        }
-    }
-}

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini