diff --git a/Emby.Dlna/Common/Argument.cs b/Emby.Dlna/Common/Argument.cs
deleted file mode 100644
index e4e9c55e0..000000000
--- a/Emby.Dlna/Common/Argument.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-namespace Emby.Dlna.Common
-{
- ///
- /// DLNA Query parameter type, used when querying DLNA devices via SOAP.
- ///
- public class Argument
- {
- ///
- /// Gets or sets name of the DLNA argument.
- ///
- public string Name { get; set; } = string.Empty;
-
- ///
- /// Gets or sets the direction of the parameter.
- ///
- public string Direction { get; set; } = string.Empty;
-
- ///
- /// Gets or sets the related DLNA state variable for this argument.
- ///
- public string RelatedStateVariable { get; set; } = string.Empty;
- }
-}
diff --git a/Emby.Dlna/Common/DeviceIcon.cs b/Emby.Dlna/Common/DeviceIcon.cs
deleted file mode 100644
index f9fd1dcec..000000000
--- a/Emby.Dlna/Common/DeviceIcon.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-using System.Globalization;
-
-namespace Emby.Dlna.Common
-{
- ///
- /// Defines the .
- ///
- public class DeviceIcon
- {
- ///
- /// Gets or sets the Url.
- ///
- public string Url { get; set; } = string.Empty;
-
- ///
- /// Gets or sets the MimeType.
- ///
- public string MimeType { get; set; } = string.Empty;
-
- ///
- /// Gets or sets the Width.
- ///
- public int Width { get; set; }
-
- ///
- /// Gets or sets the Height.
- ///
- public int Height { get; set; }
-
- ///
- /// Gets or sets the Depth.
- ///
- public string Depth { get; set; } = string.Empty;
-
- ///
- public override string ToString()
- {
- return string.Format(CultureInfo.InvariantCulture, "{0}x{1}", Height, Width);
- }
- }
-}
diff --git a/Emby.Dlna/Common/DeviceService.cs b/Emby.Dlna/Common/DeviceService.cs
deleted file mode 100644
index c1369558e..000000000
--- a/Emby.Dlna/Common/DeviceService.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-namespace Emby.Dlna.Common
-{
- ///
- /// Defines the .
- ///
- public class DeviceService
- {
- ///
- /// Gets or sets the Service Type.
- ///
- public string ServiceType { get; set; } = string.Empty;
-
- ///
- /// Gets or sets the Service Id.
- ///
- public string ServiceId { get; set; } = string.Empty;
-
- ///
- /// Gets or sets the Scpd Url.
- ///
- public string ScpdUrl { get; set; } = string.Empty;
-
- ///
- /// Gets or sets the Control Url.
- ///
- public string ControlUrl { get; set; } = string.Empty;
-
- ///
- /// Gets or sets the EventSubUrl.
- ///
- public string EventSubUrl { get; set; } = string.Empty;
-
- ///
- public override string ToString() => ServiceId;
- }
-}
diff --git a/Emby.Dlna/Common/ServiceAction.cs b/Emby.Dlna/Common/ServiceAction.cs
deleted file mode 100644
index 02b81a0aa..000000000
--- a/Emby.Dlna/Common/ServiceAction.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-using System.Collections.Generic;
-
-namespace Emby.Dlna.Common
-{
- ///
- /// Defines the .
- ///
- public class ServiceAction
- {
- ///
- /// Initializes a new instance of the class.
- ///
- public ServiceAction()
- {
- ArgumentList = new List();
- }
-
- ///
- /// Gets or sets the name of the action.
- ///
- public string Name { get; set; } = string.Empty;
-
- ///
- /// Gets the ArgumentList.
- ///
- public List ArgumentList { get; }
-
- ///
- public override string ToString() => Name;
- }
-}
diff --git a/Emby.Dlna/Common/StateVariable.cs b/Emby.Dlna/Common/StateVariable.cs
deleted file mode 100644
index fd733e085..000000000
--- a/Emby.Dlna/Common/StateVariable.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace Emby.Dlna.Common
-{
- ///
- /// Defines the .
- ///
- public class StateVariable
- {
- ///
- /// Gets or sets the name of the state variable.
- ///
- public string Name { get; set; } = string.Empty;
-
- ///
- /// Gets or sets the data type of the state variable.
- ///
- public string DataType { get; set; } = string.Empty;
-
- ///
- /// Gets or sets a value indicating whether it sends events.
- ///
- public bool SendsEvents { get; set; }
-
- ///
- /// Gets or sets the allowed values range.
- ///
- public IReadOnlyList AllowedValues { get; set; } = Array.Empty();
-
- ///
- public override string ToString() => Name;
- }
-}
diff --git a/Emby.Dlna/Configuration/DlnaOptions.cs b/Emby.Dlna/Configuration/DlnaOptions.cs
deleted file mode 100644
index f233468de..000000000
--- a/Emby.Dlna/Configuration/DlnaOptions.cs
+++ /dev/null
@@ -1,92 +0,0 @@
-#pragma warning disable CS1591
-
-namespace Emby.Dlna.Configuration
-{
- ///
- /// The DlnaOptions class contains the user definable parameters for the dlna subsystems.
- ///
- public class DlnaOptions
- {
- ///
- /// Initializes a new instance of the class.
- ///
- public DlnaOptions()
- {
- EnablePlayTo = true;
- EnableServer = false;
- BlastAliveMessages = true;
- SendOnlyMatchedHost = true;
- ClientDiscoveryIntervalSeconds = 60;
- AliveMessageIntervalSeconds = 180;
- }
-
- ///
- /// Gets or sets a value indicating whether gets or sets a value to indicate the status of the dlna playTo subsystem.
- ///
- public bool EnablePlayTo { get; set; }
-
- ///
- /// Gets or sets a value indicating whether gets or sets a value to indicate the status of the dlna server subsystem.
- ///
- public bool EnableServer { get; set; }
-
- ///
- /// 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.
- ///
- public bool EnableDebugLog { get; set; }
-
- ///
- /// 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.
- ///
- public bool EnablePlayToTracing { get; set; }
-
- ///
- /// 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.
- ///
- public int ClientDiscoveryIntervalSeconds { get; set; }
-
- ///
- /// Gets or sets the frequency at which ssdp alive notifications are transmitted.
- ///
- public int AliveMessageIntervalSeconds { get; set; }
-
- ///
- /// Gets or sets the frequency at which ssdp alive notifications are transmitted. MIGRATING - TO BE REMOVED ONCE WEB HAS BEEN ALTERED.
- ///
- public int BlastAliveMessageIntervalSeconds
- {
- get
- {
- return AliveMessageIntervalSeconds;
- }
-
- set
- {
- AliveMessageIntervalSeconds = value;
- }
- }
-
- ///
- /// Gets or sets the default user account that the dlna server uses.
- ///
- public string? DefaultUserId { get; set; }
-
- ///
- /// Gets or sets a value indicating whether playTo device profiles should be created.
- ///
- public bool AutoCreatePlayToProfiles { get; set; }
-
- ///
- /// Gets or sets a value indicating whether to blast alive messages.
- ///
- public bool BlastAliveMessages { get; set; } = true;
-
- ///
- /// gets or sets a value indicating whether to send only matched host.
- ///
- public bool SendOnlyMatchedHost { get; set; } = true;
- }
-}
diff --git a/Emby.Dlna/ConfigurationExtension.cs b/Emby.Dlna/ConfigurationExtension.cs
deleted file mode 100644
index 3ca43052a..000000000
--- a/Emby.Dlna/ConfigurationExtension.cs
+++ /dev/null
@@ -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("dlna");
- }
- }
-}
diff --git a/Emby.Dlna/ConnectionManager/ConnectionManagerService.cs b/Emby.Dlna/ConnectionManager/ConnectionManagerService.cs
deleted file mode 100644
index 916044a0c..000000000
--- a/Emby.Dlna/ConnectionManager/ConnectionManagerService.cs
+++ /dev/null
@@ -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
-{
- ///
- /// Defines the .
- ///
- public class ConnectionManagerService : BaseService, IConnectionManager
- {
- private readonly IDlnaManager _dlna;
- private readonly IServerConfigurationManager _config;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The for use with the instance.
- /// The for use with the instance.
- /// The for use with the instance..
- /// The for use with the instance..
- public ConnectionManagerService(
- IDlnaManager dlna,
- IServerConfigurationManager config,
- ILogger logger,
- IHttpClientFactory httpClientFactory)
- : base(logger, httpClientFactory)
- {
- _dlna = dlna;
- _config = config;
- }
-
- ///
- public string GetServiceXml()
- {
- return ConnectionManagerXmlBuilder.GetXml();
- }
-
- ///
- public Task ProcessControlRequestAsync(ControlRequest request)
- {
- var profile = _dlna.GetProfile(request.Headers) ??
- _dlna.GetDefaultProfile();
-
- return new ControlHandler(_config, Logger, profile).ProcessControlRequestAsync(request);
- }
- }
-}
diff --git a/Emby.Dlna/ConnectionManager/ConnectionManagerXmlBuilder.cs b/Emby.Dlna/ConnectionManager/ConnectionManagerXmlBuilder.cs
deleted file mode 100644
index db1190ae7..000000000
--- a/Emby.Dlna/ConnectionManager/ConnectionManagerXmlBuilder.cs
+++ /dev/null
@@ -1,119 +0,0 @@
-#pragma warning disable CS1591
-
-using System.Collections.Generic;
-using Emby.Dlna.Common;
-using Emby.Dlna.Service;
-
-namespace Emby.Dlna.ConnectionManager
-{
- ///
- /// Defines the .
- ///
- public static class ConnectionManagerXmlBuilder
- {
- ///
- /// Gets the ConnectionManager:1 service template.
- /// See http://upnp.org/specs/av/UPnP-av-ConnectionManager-v1-Service.pdf.
- ///
- /// An XML description of this service.
- public static string GetXml()
- {
- return new ServiceXmlBuilder().GetXml(ServiceActionListBuilder.GetActions(), GetStateVariables());
- }
-
- ///
- /// Get the list of state variables for this invocation.
- ///
- /// The .
- private static IEnumerable 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
- }
- };
- }
- }
-}
diff --git a/Emby.Dlna/ConnectionManager/ControlHandler.cs b/Emby.Dlna/ConnectionManager/ControlHandler.cs
deleted file mode 100644
index 1a1790ee6..000000000
--- a/Emby.Dlna/ConnectionManager/ControlHandler.cs
+++ /dev/null
@@ -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
-{
- ///
- /// Defines the .
- ///
- public class ControlHandler : BaseControlHandler
- {
- private readonly DeviceProfile _profile;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The for use with the instance.
- /// The for use with the instance.
- /// The for use with the instance.
- public ControlHandler(IServerConfigurationManager config, ILogger logger, DeviceProfile profile)
- : base(config, logger)
- {
- _profile = profile;
- }
-
- ///
- protected override void WriteResult(string methodName, IReadOnlyDictionary methodParams, XmlWriter xmlWriter)
- {
- if (string.Equals(methodName, "GetProtocolInfo", StringComparison.OrdinalIgnoreCase))
- {
- HandleGetProtocolInfo(xmlWriter);
- return;
- }
-
- throw new ResourceNotFoundException("Unexpected control request name: " + methodName);
- }
-
- ///
- /// Builds the response to the GetProtocolInfo request.
- ///
- /// The .
- private void HandleGetProtocolInfo(XmlWriter xmlWriter)
- {
- xmlWriter.WriteElementString("Source", _profile.ProtocolInfo);
- xmlWriter.WriteElementString("Sink", string.Empty);
- }
- }
-}
diff --git a/Emby.Dlna/ConnectionManager/ServiceActionListBuilder.cs b/Emby.Dlna/ConnectionManager/ServiceActionListBuilder.cs
deleted file mode 100644
index 542c7bfb4..000000000
--- a/Emby.Dlna/ConnectionManager/ServiceActionListBuilder.cs
+++ /dev/null
@@ -1,234 +0,0 @@
-#pragma warning disable CS1591
-
-using System.Collections.Generic;
-using Emby.Dlna.Common;
-
-namespace Emby.Dlna.ConnectionManager
-{
- ///
- /// Defines the .
- ///
- public static class ServiceActionListBuilder
- {
- ///
- /// Returns an enumerable of the ConnectionManagar:1 DLNA actions.
- ///
- /// An .
- public static IEnumerable GetActions()
- {
- var list = new List
- {
- GetCurrentConnectionInfo(),
- GetProtocolInfo(),
- GetCurrentConnectionIDs(),
- ConnectionComplete(),
- PrepareForConnection()
- };
-
- return list;
- }
-
- ///
- /// Returns the action details for "PrepareForConnection".
- ///
- /// The .
- 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;
- }
-
- ///
- /// Returns the action details for "GetCurrentConnectionInfo".
- ///
- /// The .
- 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;
- }
-
- ///
- /// Returns the action details for "GetProtocolInfo".
- ///
- /// The .
- 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;
- }
-
- ///
- /// Returns the action details for "GetCurrentConnectionIDs".
- ///
- /// The .
- private static ServiceAction GetCurrentConnectionIDs()
- {
- var action = new ServiceAction
- {
- Name = "GetCurrentConnectionIDs"
- };
-
- action.ArgumentList.Add(new Argument
- {
- Name = "ConnectionIDs",
- Direction = "out",
- RelatedStateVariable = "CurrentConnectionIDs"
- });
-
- return action;
- }
-
- ///
- /// Returns the action details for "ConnectionComplete".
- ///
- /// The .
- 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;
- }
- }
-}
diff --git a/Emby.Dlna/ContentDirectory/ContentDirectoryService.cs b/Emby.Dlna/ContentDirectory/ContentDirectoryService.cs
deleted file mode 100644
index 389e971a6..000000000
--- a/Emby.Dlna/ContentDirectory/ContentDirectoryService.cs
+++ /dev/null
@@ -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
-{
- ///
- /// Defines the .
- ///
- 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;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The to use in the instance.
- /// The to use in the instance.
- /// The to use in the instance.
- /// The to use in the instance.
- /// The to use in the instance.
- /// The to use in the instance.
- /// The to use in the instance.
- /// The to use in the instance.
- /// The to use in the instance.
- /// The to use in the instance.
- /// The to use in the instance.
- /// The to use in the instance.
- /// The to use in the instance.
- public ContentDirectoryService(
- IDlnaManager dlna,
- IUserDataManager userDataManager,
- IImageProcessor imageProcessor,
- ILibraryManager libraryManager,
- IServerConfigurationManager config,
- IUserManager userManager,
- ILogger 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;
- }
-
- ///
- /// Gets the system id. (A unique id which changes on when our definition changes.)
- ///
- private static int SystemUpdateId
- {
- get
- {
- var now = DateTime.UtcNow;
-
- return now.Year + now.DayOfYear + now.Hour;
- }
- }
-
- ///
- public string GetServiceXml()
- {
- return ContentDirectoryXmlBuilder.GetXml();
- }
-
- ///
- public Task 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);
- }
-
- ///
- /// Get the user stored in the device profile.
- ///
- /// The .
- /// The .
- 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();
- }
- }
-}
diff --git a/Emby.Dlna/ContentDirectory/ContentDirectoryXmlBuilder.cs b/Emby.Dlna/ContentDirectory/ContentDirectoryXmlBuilder.cs
deleted file mode 100644
index 9af28aa7c..000000000
--- a/Emby.Dlna/ContentDirectory/ContentDirectoryXmlBuilder.cs
+++ /dev/null
@@ -1,159 +0,0 @@
-#pragma warning disable CS1591
-
-using System.Collections.Generic;
-using Emby.Dlna.Common;
-using Emby.Dlna.Service;
-
-namespace Emby.Dlna.ContentDirectory
-{
- ///
- /// Defines the .
- ///
- public static class ContentDirectoryXmlBuilder
- {
- ///
- /// Gets the ContentDirectory:1 service template.
- /// See http://upnp.org/specs/av/UPnP-av-ContentDirectory-v1-Service.pdf.
- ///
- /// An XML description of this service.
- public static string GetXml()
- {
- return new ServiceXmlBuilder().GetXml(ServiceActionListBuilder.GetActions(), GetStateVariables());
- }
-
- ///
- /// Get the list of state variables for this invocation.
- ///
- /// The .
- private static IEnumerable 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
- }
- };
- }
- }
-}
diff --git a/Emby.Dlna/ContentDirectory/ControlHandler.cs b/Emby.Dlna/ContentDirectory/ControlHandler.cs
deleted file mode 100644
index 99068826d..000000000
--- a/Emby.Dlna/ContentDirectory/ControlHandler.cs
+++ /dev/null
@@ -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
-{
- ///
- /// Defines the .
- ///
- 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;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The for use with the instance.
- /// The for use with the instance.
- /// The for use with the instance.
- /// The server address to use in this instance> for use with the instance.
- /// The for use with the instance.
- /// The for use with the instance.
- /// The for use with the instance.
- /// The for use with the instance.
- /// The system id for use with the instance.
- /// The for use with the instance.
- /// The for use with the instance.
- /// The for use with the instance.
- /// The for use with the instance.
- /// The for use with the instance.
- /// The for use with the instance.
- 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);
- }
-
- ///
- protected override void WriteResult(string methodName, IReadOnlyDictionary 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);
- }
-
- ///
- /// Adds a "XSetBookmark" element to the xml document.
- ///
- /// The method parameters.
- private void HandleXSetBookmark(IReadOnlyDictionary 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);
- }
-
- ///
- /// Adds the "SearchCaps" element to the xml document.
- ///
- /// The .
- 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");
- }
-
- ///
- /// Adds the "SortCaps" element to the xml document.
- ///
- /// The .
- 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");
- }
-
- ///
- /// Adds the "SortExtensionCaps" element to the xml document.
- ///
- /// The .
- 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");
- }
-
- ///
- /// Adds the "Id" element to the xml document.
- ///
- /// The .
- private void HandleGetSystemUpdateID(XmlWriter xmlWriter)
- {
- xmlWriter.WriteElementString("Id", _systemUpdateId.ToString(CultureInfo.InvariantCulture));
- }
-
- ///
- /// Adds the "FeatureList" element to the xml document.
- ///
- /// The .
- private static void HandleGetFeatureList(XmlWriter xmlWriter)
- {
- xmlWriter.WriteElementString("FeatureList", WriteFeatureListXml());
- }
-
- ///
- /// Adds the "FeatureList" element to the xml document.
- ///
- /// The .
- private static void HandleXGetFeatureList(XmlWriter xmlWriter)
- => HandleGetFeatureList(xmlWriter);
-
- ///
- /// Builds a static feature list.
- ///
- /// The xml feature list.
- private static string WriteFeatureListXml()
- {
- return ""
- + ""
- + ""
- + ""
- + ""
- + ""
- + ""
- + "";
- }
-
- ///
- /// Builds the "Browse" xml response.
- ///
- /// The .
- /// The method parameters.
- /// The device Id to use.
- private void HandleBrowse(XmlWriter xmlWriter, IReadOnlyDictionary 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));
- }
-
- ///
- /// Builds the response to the "X_BrowseByLetter request.
- ///
- /// The .
- /// The method parameters.
- /// The device id.
- private void HandleXBrowseByLetter(XmlWriter xmlWriter, IReadOnlyDictionary sparams, string deviceId)
- {
- // TODO: Implement this method
- HandleSearch(xmlWriter, sparams, deviceId);
- }
-
- ///
- /// Builds a response to the "Search" request.
- ///
- /// The xmlWriter.
- /// The method parameters.
- /// The deviceId.
- private void HandleSearch(XmlWriter xmlWriter, IReadOnlyDictionary 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 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));
- }
-
- ///
- /// Returns the child items meeting the criteria.
- ///
- /// The .
- /// The .
- /// The .
- /// The .
- /// The start index.
- /// The maximum number to return.
- /// The .
- private static QueryResult GetChildrenSorted(BaseItem item, User user, SearchCriteria search, SortCriteria sort, int? startIndex, int? limit)
- {
- var folder = (Folder)item;
-
- MediaType[] mediaTypes = Array.Empty();
- 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()
- });
- }
-
- ///
- /// Returns a new DtoOptions object.
- ///
- /// The .
- private static DtoOptions GetDtoOptions()
- {
- return new DtoOptions(true);
- }
-
- ///
- /// Returns the User items meeting the criteria.
- ///
- /// The .
- /// The .
- /// The .
- /// The .
- /// The start index.
- /// The maximum number to return.
- /// The .
- private QueryResult 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();
- }
-
- 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);
- }
-
- ///
- /// Returns the Live Tv Channels meeting the criteria.
- ///
- /// The .
- /// The .
- /// The start index.
- /// The maximum number to return.
- /// The .
- private QueryResult 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);
- }
-
- ///
- /// Returns the music folders meeting the criteria.
- ///
- /// The .
- /// The .
- /// The .
- /// The .
- /// The start index.
- /// The maximum number to return.
- /// The .
- private QueryResult 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(
- startIndex,
- serverItems.Length,
- serverItems);
- }
-
- ///
- /// Returns the movie folders meeting the criteria.
- ///
- /// The .
- /// The .
- /// The .
- /// The .
- /// The start index.
- /// The maximum number to return.
- /// The .
- private QueryResult 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(
- startIndex,
- array.Length,
- array);
- }
-
- ///
- /// Returns the folders meeting the criteria.
- ///
- /// The .
- /// The start index.
- /// The maximum number to return.
- /// The .
- private QueryResult 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(
- startIndex,
- totalRecordCount,
- items);
- }
-
- ///
- /// Returns the TV folders meeting the criteria.
- ///
- /// The .
- /// The .
- /// The .
- /// The .
- /// The start index.
- /// The maximum number to return.
- /// The .
- private QueryResult 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(
- startIndex,
- serverItems.Length,
- serverItems);
- }
-
- ///
- /// Returns the Movies that are part watched that meet the criteria.
- ///
- /// The .
- /// The .
- /// The .
- private QueryResult 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);
- }
-
- ///
- /// Returns the Movie collections meeting the criteria.
- ///
- /// The see cref="InternalItemsQuery"/>.
- /// The .
- private QueryResult GetMovieCollections(InternalItemsQuery query)
- {
- query.Recursive = true;
- query.IncludeItemTypes = new[] { BaseItemKind.BoxSet };
-
- var result = _libraryManager.GetItemsResult(query);
-
- return ToResult(query.StartIndex, result);
- }
-
- ///
- /// Returns the children that meet the criteria.
- ///
- /// The .
- /// The .
- /// The item type.
- /// A value indicating whether to only fetch favorite items.
- /// The .
- private QueryResult 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);
- }
-
- ///
- /// Returns the genres meeting the criteria.
- /// The GetGenres.
- ///
- /// The .
- /// The .
- /// The .
- private QueryResult 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);
- }
-
- ///
- /// Returns the music genres meeting the criteria.
- ///
- /// The .
- /// The .
- /// The .
- private QueryResult 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);
- }
-
- ///
- /// Returns the music albums by artist that meet the criteria.
- ///
- /// The .
- /// The .
- /// The .
- private QueryResult 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);
- }
-
- ///
- /// Returns the music artists meeting the criteria.
- ///
- /// The .
- /// The .
- /// The .
- private QueryResult 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);
- }
-
- ///
- /// Returns the artists tagged as favourite that meet the criteria.
- ///
- /// The .
- /// The .
- /// The .
- private QueryResult 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);
- }
-
- ///
- /// Returns the music playlists meeting the criteria.
- ///
- /// The query.
- /// The .
- private QueryResult GetMusicPlaylists(InternalItemsQuery query)
- {
- query.Parent = null;
- query.IncludeItemTypes = new[] { BaseItemKind.Playlist };
- query.Recursive = true;
-
- var result = _libraryManager.GetItemsResult(query);
-
- return ToResult(query.StartIndex, result);
- }
-
- ///
- /// Returns the next up item meeting the criteria.
- ///
- /// The .
- /// The .
- /// The .
- private QueryResult 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);
- }
-
- ///
- /// Returns the latest items of [itemType] meeting the criteria.
- ///
- /// The .
- /// The .
- /// The item type.
- /// The .
- private QueryResult 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);
- }
-
- ///
- /// Returns music artist items that meet the criteria.
- ///
- /// The .
- /// The .
- /// The .
- /// The start index.
- /// The maximum number to return.
- /// The .
- private QueryResult 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);
- }
-
- ///
- /// Returns the genre items meeting the criteria.
- ///
- /// The .
- /// The .
- /// The .
- /// The start index.
- /// The maximum number to return.
- /// The .
- private QueryResult 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);
- }
-
- ///
- /// Returns the music genre items meeting the criteria.
- ///
- /// The .
- /// The .
- /// The .
- /// The start index.
- /// The maximum number to return.
- /// The .
- private QueryResult 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);
- }
-
- ///
- /// Converts into a .
- ///
- /// The start index.
- /// An array of .
- /// A .
- private static QueryResult ToResult(int? startIndex, IReadOnlyCollection result)
- {
- var serverItems = result
- .Select(i => new ServerItem(i, null))
- .ToArray();
-
- return new QueryResult(
- startIndex,
- result.Count,
- serverItems);
- }
-
- ///
- /// Converts a to a .
- ///
- /// The index the result started at.
- /// A .
- /// The .
- private static QueryResult ToResult(int? startIndex, QueryResult 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(
- startIndex,
- result.TotalRecordCount,
- serverItems);
- }
-
- ///
- /// Converts a query result to a .
- ///
- /// The start index.
- /// A .
- /// The .
- private static QueryResult 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(
- startIndex,
- result.TotalRecordCount,
- serverItems);
- }
-
- ///
- /// Gets the sorting method on a query.
- ///
- /// The .
- /// True if pre-sorted.
- private static (ItemSortBy SortName, SortOrder SortOrder)[] GetOrderBy(SortCriteria sort, bool isPreSorted)
- {
- return isPreSorted ? Array.Empty<(ItemSortBy, SortOrder)>() : new[] { (ItemSortBy.SortName, sort.SortOrder) };
- }
-
- ///
- /// Retrieves the ServerItem id.
- ///
- /// The id.
- /// The .
- private ServerItem GetItemFromObjectId(string id)
- {
- return DidlBuilder.IsIdRoot(id)
- ? new ServerItem(_libraryManager.GetUserRootFolder(), null)
- : ParseItemId(id);
- }
-
- ///
- /// Parses the item id into a .
- ///
- /// The .
- /// The corresponding .
- 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(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);
- }
- }
-}
diff --git a/Emby.Dlna/ContentDirectory/ServerItem.cs b/Emby.Dlna/ContentDirectory/ServerItem.cs
deleted file mode 100644
index df05fa966..000000000
--- a/Emby.Dlna/ContentDirectory/ServerItem.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-using MediaBrowser.Controller.Entities;
-
-namespace Emby.Dlna.ContentDirectory
-{
- ///
- /// Defines the .
- ///
- internal class ServerItem
- {
- ///
- /// Initializes a new instance of the class.
- ///
- /// The .
- /// The stub type.
- 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;
- }
- }
-
- ///
- /// Gets the underlying base item.
- ///
- public BaseItem Item { get; }
-
- ///
- /// Gets the DLNA item type.
- ///
- public StubType? StubType { get; }
- }
-}
diff --git a/Emby.Dlna/ContentDirectory/ServiceActionListBuilder.cs b/Emby.Dlna/ContentDirectory/ServiceActionListBuilder.cs
deleted file mode 100644
index 7e3db4651..000000000
--- a/Emby.Dlna/ContentDirectory/ServiceActionListBuilder.cs
+++ /dev/null
@@ -1,415 +0,0 @@
-using System.Collections.Generic;
-using Emby.Dlna.Common;
-
-namespace Emby.Dlna.ContentDirectory
-{
- ///
- /// Defines the .
- ///
- public static class ServiceActionListBuilder
- {
- ///
- /// Returns a list of services that this instance provides.
- ///
- /// An .
- public static IEnumerable GetActions()
- {
- return new[]
- {
- GetSearchCapabilitiesAction(),
- GetSortCapabilitiesAction(),
- GetGetSystemUpdateIDAction(),
- GetBrowseAction(),
- GetSearchAction(),
- GetX_GetFeatureListAction(),
- GetXSetBookmarkAction(),
- GetBrowseByLetterAction()
- };
- }
-
- ///
- /// Returns the action details for "GetSystemUpdateID".
- ///
- /// The .
- private static ServiceAction GetGetSystemUpdateIDAction()
- {
- var action = new ServiceAction
- {
- Name = "GetSystemUpdateID"
- };
-
- action.ArgumentList.Add(new Argument
- {
- Name = "Id",
- Direction = "out",
- RelatedStateVariable = "SystemUpdateID"
- });
-
- return action;
- }
-
- ///
- /// Returns the action details for "GetSearchCapabilities".
- ///
- /// The .
- private static ServiceAction GetSearchCapabilitiesAction()
- {
- var action = new ServiceAction
- {
- Name = "GetSearchCapabilities"
- };
-
- action.ArgumentList.Add(new Argument
- {
- Name = "SearchCaps",
- Direction = "out",
- RelatedStateVariable = "SearchCapabilities"
- });
-
- return action;
- }
-
- ///
- /// Returns the action details for "GetSortCapabilities".
- ///
- /// The .
- private static ServiceAction GetSortCapabilitiesAction()
- {
- var action = new ServiceAction
- {
- Name = "GetSortCapabilities"
- };
-
- action.ArgumentList.Add(new Argument
- {
- Name = "SortCaps",
- Direction = "out",
- RelatedStateVariable = "SortCapabilities"
- });
-
- return action;
- }
-
- ///
- /// Returns the action details for "X_GetFeatureList".
- ///
- /// The .
- 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;
- }
-
- ///
- /// Returns the action details for "Search".
- ///
- /// The .
- 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;
- }
-
- ///
- /// Returns the action details for "Browse".
- ///
- /// The .
- 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;
- }
-
- ///
- /// Returns the action details for "X_BrowseByLetter".
- ///
- /// The .
- 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;
- }
-
- ///
- /// Returns the action details for "X_SetBookmark".
- ///
- /// The .
- 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;
- }
- }
-}
diff --git a/Emby.Dlna/ContentDirectory/StubType.cs b/Emby.Dlna/ContentDirectory/StubType.cs
deleted file mode 100644
index 187dc1d75..000000000
--- a/Emby.Dlna/ContentDirectory/StubType.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-#pragma warning disable CS1591
-
-namespace Emby.Dlna.ContentDirectory
-{
- ///
- /// Defines the DLNA item types.
- ///
- public enum StubType
- {
- Folder = 0,
- Latest = 2,
- Playlists = 3,
- Albums = 4,
- AlbumArtists = 5,
- Artists = 6,
- Songs = 7,
- Genres = 8,
- FavoriteSongs = 9,
- FavoriteArtists = 10,
- FavoriteAlbums = 11,
- ContinueWatching = 12,
- Movies = 13,
- Collections = 14,
- Favorites = 15,
- NextUp = 16,
- Series = 17,
- FavoriteSeries = 18,
- FavoriteEpisodes = 19
- }
-}
diff --git a/Emby.Dlna/ControlRequest.cs b/Emby.Dlna/ControlRequest.cs
deleted file mode 100644
index 8ee6325e9..000000000
--- a/Emby.Dlna/ControlRequest.cs
+++ /dev/null
@@ -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; }
- }
-}
diff --git a/Emby.Dlna/ControlResponse.cs b/Emby.Dlna/ControlResponse.cs
deleted file mode 100644
index 8b0958842..000000000
--- a/Emby.Dlna/ControlResponse.cs
+++ /dev/null
@@ -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();
- Xml = xml;
- IsSuccessful = isSuccessful;
- }
-
- public IDictionary Headers { get; }
-
- public string Xml { get; set; }
-
- public bool IsSuccessful { get; set; }
-
- ///
- public override string ToString()
- {
- return Xml;
- }
- }
-}
diff --git a/Emby.Dlna/Didl/DidlBuilder.cs b/Emby.Dlna/Didl/DidlBuilder.cs
deleted file mode 100644
index 9f152df13..000000000
--- a/Emby.Dlna/Didl/DidlBuilder.cs
+++ /dev/null
@@ -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))
- {
- // http://192.168.1.3:9999/video.srt
- // http://192.168.1.3:9999/video.srt
-
- 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;
- }
-
- ///
- /// Gets episode display name appropriate for the given context.
- ///
- ///
- /// 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.
- ///
- /// The episode.
- /// Current context.
- /// Formatted name of the episode.
- 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));
- }
-
- ///
- /// Gets complete episode number.
- ///
- /// The episode.
- /// For single episodes returns just the number. For double episodes - current and ending numbers.
- 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;
- }
-
- ///
- /// Gets episode number formatted as 'S##E##'.
- ///
- /// The episode.
- /// Formatted episode number.
- 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);
- }
- }
-
- ///
- /// Adds fields used by both items and folders.
- ///
- 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; }
- }
- }
-}
diff --git a/Emby.Dlna/Didl/Filter.cs b/Emby.Dlna/Didl/Filter.cs
deleted file mode 100644
index 6db6f3ae3..000000000
--- a/Emby.Dlna/Didl/Filter.cs
+++ /dev/null
@@ -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));
- }
- }
-}
diff --git a/Emby.Dlna/Didl/StringWriterWithEncoding.cs b/Emby.Dlna/Didl/StringWriterWithEncoding.cs
deleted file mode 100644
index b66f53ece..000000000
--- a/Emby.Dlna/Didl/StringWriterWithEncoding.cs
+++ /dev/null
@@ -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;
- }
-}
diff --git a/Emby.Dlna/DlnaConfigurationFactory.cs b/Emby.Dlna/DlnaConfigurationFactory.cs
deleted file mode 100644
index 6cc6b73a0..000000000
--- a/Emby.Dlna/DlnaConfigurationFactory.cs
+++ /dev/null
@@ -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 GetConfigurations()
- {
- return new[]
- {
- new ConfigurationStore
- {
- Key = "dlna",
- ConfigurationType = typeof(DlnaOptions)
- }
- };
- }
- }
-}
diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs
deleted file mode 100644
index d67cb67b5..000000000
--- a/Emby.Dlna/DlnaManager.cs
+++ /dev/null
@@ -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 _logger;
- private readonly IServerApplicationHost _appHost;
- private static readonly Assembly _assembly = typeof(DlnaManager).Assembly;
- private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options;
-
- private readonly Dictionary> _profiles = new Dictionary>(StringComparer.Ordinal);
-
- public DlnaManager(
- IXmlSerializer xmlSerializer,
- IFileSystem fileSystem,
- IApplicationPaths appPaths,
- ILoggerFactory loggerFactory,
- IServerApplicationHost appHost)
- {
- _xmlSerializer = xmlSerializer;
- _fileSystem = fileSystem;
- _appPaths = appPaths;
- _logger = loggerFactory.CreateLogger();
- _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 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();
- }
- }
-
- ///
- public DeviceProfile GetDefaultProfile()
- {
- return new DefaultProfile();
- }
-
- ///
- 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;
- }
-
- ///
- /// 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.
- ///
- /// The of the device.
- /// The of the profile.
- /// True if they match.
- 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;
- }
- }
-
- ///
- 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 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();
- }
- }
-
- private DeviceProfile? ParseProfileFile(string path, DeviceProfileType type)
- {
- lock (_profiles)
- {
- if (_profiles.TryGetValue(path, out Tuple? 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(GetInternalProfileInfo(_fileSystem.GetFileInfo(path), type), profile);
-
- return profile;
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Error parsing profile file: {Path}", path);
-
- return null;
- }
- }
- }
-
- ///
- 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 GetProfileInfosInternal()
- {
- lock (_profiles)
- {
- return _profiles.Values
- .Select(i => i.Item1)
- .OrderBy(i => i.Info.Type == DeviceProfileType.User ? 0 : 1)
- .ThenBy(i => i.Info.Name);
- }
- }
-
- ///
- public IEnumerable 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);
- }
- }
- }
- }
-
- ///
- 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);
- }
- }
-
- ///
- 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);
- }
-
- ///
- 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(GetInternalProfileInfo(_fileSystem.GetFileInfo(path), type), profile);
- }
-
- SerializeToXml(profile, path);
- }
-
- internal void SerializeToXml(DeviceProfile profile, string path)
- {
- _xmlSerializer.SerializeToFile(profile, path);
- }
-
- ///
- /// 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).
- ///
- /// The device profile.
- /// The re-serialized device profile.
- 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(json, _jsonOptions)!;
- }
-
- ///
- 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();
- }
-
- ///
- 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; }
- }
- }
-}
diff --git a/Emby.Dlna/Emby.Dlna.csproj b/Emby.Dlna/Emby.Dlna.csproj
deleted file mode 100644
index 7336482e5..000000000
--- a/Emby.Dlna/Emby.Dlna.csproj
+++ /dev/null
@@ -1,90 +0,0 @@
-
-
-
-
- {805844AB-E92F-45E6-9D99-4F6D48D129A5}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- net8.0
- false
- true
-
-
-
- false
-
-
-
-
-
- all
- runtime; build; native; contentfiles; analyzers
-
-
- all
- runtime; build; native; contentfiles; analyzers
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Emby.Dlna/EventSubscriptionResponse.cs b/Emby.Dlna/EventSubscriptionResponse.cs
deleted file mode 100644
index 635d2c47a..000000000
--- a/Emby.Dlna/EventSubscriptionResponse.cs
+++ /dev/null
@@ -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();
- }
-
- public string Content { get; set; }
-
- public string ContentType { get; set; }
-
- public Dictionary Headers { get; }
- }
-}
diff --git a/Emby.Dlna/Eventing/DlnaEventManager.cs b/Emby.Dlna/Eventing/DlnaEventManager.cs
deleted file mode 100644
index ecbbdf9df..000000000
--- a/Emby.Dlna/Eventing/DlnaEventManager.cs
+++ /dev/null
@@ -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 _subscriptions =
- new ConcurrentDictionary(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 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 stateVariables)
- {
- var builder = new StringBuilder();
-
- builder.Append("");
- builder.Append("");
- foreach (var key in stateVariables.Keys)
- {
- builder.Append("")
- .Append('<')
- .Append(key)
- .Append('>')
- .Append(stateVariables[key])
- .Append("")
- .Append(key)
- .Append('>')
- .Append("");
- }
-
- builder.Append("");
-
- 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();
- }
- }
- }
-}
diff --git a/Emby.Dlna/Eventing/EventSubscription.cs b/Emby.Dlna/Eventing/EventSubscription.cs
deleted file mode 100644
index 4fd7f8169..000000000
--- a/Emby.Dlna/Eventing/EventSubscription.cs
+++ /dev/null
@@ -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++;
- }
- }
-}
diff --git a/Emby.Dlna/Extensions/DlnaServiceCollectionExtensions.cs b/Emby.Dlna/Extensions/DlnaServiceCollectionExtensions.cs
deleted file mode 100644
index 82c80070a..000000000
--- a/Emby.Dlna/Extensions/DlnaServiceCollectionExtensions.cs
+++ /dev/null
@@ -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;
-
-///
-/// Extension methods for adding DLNA services.
-///
-public static class DlnaServiceCollectionExtensions
-{
- ///
- /// Adds DLNA services to the provided .
- ///
- /// The .
- /// The .
- 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();
- services.AddSingleton();
- services.AddSingleton();
- services.AddSingleton();
- services.AddSingleton();
-
- services.AddSingleton(provider => new SsdpCommunicationsServer(
- provider.GetRequiredService(),
- provider.GetRequiredService(),
- provider.GetRequiredService>())
- {
- IsShared = true
- });
-
- services.AddHostedService();
- }
-}
diff --git a/Emby.Dlna/IConnectionManager.cs b/Emby.Dlna/IConnectionManager.cs
deleted file mode 100644
index 9f643a9e6..000000000
--- a/Emby.Dlna/IConnectionManager.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-#pragma warning disable CS1591
-
-namespace Emby.Dlna
-{
- public interface IConnectionManager : IDlnaEventManager, IUpnpService
- {
- }
-}
diff --git a/Emby.Dlna/IContentDirectory.cs b/Emby.Dlna/IContentDirectory.cs
deleted file mode 100644
index 10f4d6386..000000000
--- a/Emby.Dlna/IContentDirectory.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-#pragma warning disable CS1591
-
-namespace Emby.Dlna
-{
- public interface IContentDirectory : IDlnaEventManager, IUpnpService
- {
- }
-}
diff --git a/Emby.Dlna/IDlnaEventManager.cs b/Emby.Dlna/IDlnaEventManager.cs
deleted file mode 100644
index bb1eeb963..000000000
--- a/Emby.Dlna/IDlnaEventManager.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-#nullable disable
-#pragma warning disable CS1591
-
-namespace Emby.Dlna
-{
- public interface IDlnaEventManager
- {
- ///
- /// Cancels the event subscription.
- ///
- /// The subscription identifier.
- /// The response.
- EventSubscriptionResponse CancelEventSubscription(string subscriptionId);
-
- ///
- /// Renews the event subscription.
- ///
- /// The subscription identifier.
- /// The notification type.
- /// The requested timeout as a string.
- /// The callback url.
- /// The response.
- EventSubscriptionResponse RenewEventSubscription(string subscriptionId, string notificationType, string requestedTimeoutString, string callbackUrl);
-
- ///
- /// Creates the event subscription.
- ///
- /// The notification type.
- /// The requested timeout as a string.
- /// The callback url.
- /// The response.
- EventSubscriptionResponse CreateEventSubscription(string notificationType, string requestedTimeoutString, string callbackUrl);
- }
-}
diff --git a/Emby.Dlna/IMediaReceiverRegistrar.cs b/Emby.Dlna/IMediaReceiverRegistrar.cs
deleted file mode 100644
index 43e934b53..000000000
--- a/Emby.Dlna/IMediaReceiverRegistrar.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-#pragma warning disable CS1591
-
-namespace Emby.Dlna
-{
- public interface IMediaReceiverRegistrar : IDlnaEventManager, IUpnpService
- {
- }
-}
diff --git a/Emby.Dlna/IUpnpService.cs b/Emby.Dlna/IUpnpService.cs
deleted file mode 100644
index 9e7859567..000000000
--- a/Emby.Dlna/IUpnpService.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-#pragma warning disable CS1591
-
-using System.Threading.Tasks;
-
-namespace Emby.Dlna
-{
- public interface IUpnpService
- {
- ///
- /// Gets the content directory XML.
- ///
- /// System.String.
- string GetServiceXml();
-
- ///
- /// Processes the control request.
- ///
- /// The request.
- /// ControlResponse.
- Task ProcessControlRequestAsync(ControlRequest request);
- }
-}
diff --git a/Emby.Dlna/Images/logo120.jpg b/Emby.Dlna/Images/logo120.jpg
deleted file mode 100644
index c70f4db0d..000000000
Binary files a/Emby.Dlna/Images/logo120.jpg and /dev/null differ
diff --git a/Emby.Dlna/Images/logo120.png b/Emby.Dlna/Images/logo120.png
deleted file mode 100644
index 14f6c8d5f..000000000
Binary files a/Emby.Dlna/Images/logo120.png and /dev/null differ
diff --git a/Emby.Dlna/Images/logo240.jpg b/Emby.Dlna/Images/logo240.jpg
deleted file mode 100644
index 78a27f1b5..000000000
Binary files a/Emby.Dlna/Images/logo240.jpg and /dev/null differ
diff --git a/Emby.Dlna/Images/logo240.png b/Emby.Dlna/Images/logo240.png
deleted file mode 100644
index ff50314d4..000000000
Binary files a/Emby.Dlna/Images/logo240.png and /dev/null differ
diff --git a/Emby.Dlna/Images/logo48.jpg b/Emby.Dlna/Images/logo48.jpg
deleted file mode 100644
index 269bcf589..000000000
Binary files a/Emby.Dlna/Images/logo48.jpg and /dev/null differ
diff --git a/Emby.Dlna/Images/logo48.png b/Emby.Dlna/Images/logo48.png
deleted file mode 100644
index d6b5fd1df..000000000
Binary files a/Emby.Dlna/Images/logo48.png and /dev/null differ
diff --git a/Emby.Dlna/Images/people48.jpg b/Emby.Dlna/Images/people48.jpg
deleted file mode 100644
index 3ed287062..000000000
Binary files a/Emby.Dlna/Images/people48.jpg and /dev/null differ
diff --git a/Emby.Dlna/Images/people48.png b/Emby.Dlna/Images/people48.png
deleted file mode 100644
index dae5f6057..000000000
Binary files a/Emby.Dlna/Images/people48.png and /dev/null differ
diff --git a/Emby.Dlna/Images/people480.jpg b/Emby.Dlna/Images/people480.jpg
deleted file mode 100644
index 01a316206..000000000
Binary files a/Emby.Dlna/Images/people480.jpg and /dev/null differ
diff --git a/Emby.Dlna/Images/people480.png b/Emby.Dlna/Images/people480.png
deleted file mode 100644
index 800a3d804..000000000
Binary files a/Emby.Dlna/Images/people480.png and /dev/null differ
diff --git a/Emby.Dlna/Main/DlnaHost.cs b/Emby.Dlna/Main/DlnaHost.cs
deleted file mode 100644
index 58db7c26f..000000000
--- a/Emby.Dlna/Main/DlnaHost.cs
+++ /dev/null
@@ -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;
-
-///
-/// A that manages a DLNA server.
-///
-public sealed class DlnaHost : IHostedService, IDisposable
-{
- private readonly ILogger _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;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The .
- /// The .
- /// The .
- /// The .
- /// The .
- /// The .
- /// The .
- /// The .
- /// The .
- /// The .
- /// The .
- /// The .
- /// The .
- /// The .
- /// The .
- /// The .
- 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();
- }
-
- ///
- public async Task StartAsync(CancellationToken cancellationToken)
- {
- var netConfig = _config.GetConfiguration(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;
- }
-
- ///
- public Task StopAsync(CancellationToken cancellationToken)
- {
- Stop();
-
- return Task.CompletedTask;
- }
-
- ///
- 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();
- }
-}
diff --git a/Emby.Dlna/MediaReceiverRegistrar/ControlHandler.cs b/Emby.Dlna/MediaReceiverRegistrar/ControlHandler.cs
deleted file mode 100644
index d8fb12742..000000000
--- a/Emby.Dlna/MediaReceiverRegistrar/ControlHandler.cs
+++ /dev/null
@@ -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
-{
- ///
- /// Defines the .
- ///
- public class ControlHandler : BaseControlHandler
- {
- ///
- /// Initializes a new instance of the class.
- ///
- /// The for use with the instance.
- /// The for use with the instance.
- public ControlHandler(IServerConfigurationManager config, ILogger logger)
- : base(config, logger)
- {
- }
-
- ///
- protected override void WriteResult(string methodName, IReadOnlyDictionary 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);
- }
-
- ///
- /// Records that the handle is authorized in the xml stream.
- ///
- /// The .
- private static void HandleIsAuthorized(XmlWriter xmlWriter)
- => xmlWriter.WriteElementString("Result", "1");
-
- ///
- /// Records that the handle is validated in the xml stream.
- ///
- /// The .
- private static void HandleIsValidated(XmlWriter xmlWriter)
- => xmlWriter.WriteElementString("Result", "1");
- }
-}
diff --git a/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarService.cs b/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarService.cs
deleted file mode 100644
index a5aae515c..000000000
--- a/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarService.cs
+++ /dev/null
@@ -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
-{
- ///
- /// Defines the .
- ///
- public class MediaReceiverRegistrarService : BaseService, IMediaReceiverRegistrar
- {
- private readonly IServerConfigurationManager _config;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The for use with the instance.
- /// The for use with the instance.
- /// The for use with the instance.
- public MediaReceiverRegistrarService(
- ILogger logger,
- IHttpClientFactory httpClientFactory,
- IServerConfigurationManager config)
- : base(logger, httpClientFactory)
- {
- _config = config;
- }
-
- ///
- public string GetServiceXml()
- {
- return MediaReceiverRegistrarXmlBuilder.GetXml();
- }
-
- ///
- public Task ProcessControlRequestAsync(ControlRequest request)
- {
- return new ControlHandler(
- _config,
- Logger)
- .ProcessControlRequestAsync(request);
- }
- }
-}
diff --git a/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarXmlBuilder.cs b/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarXmlBuilder.cs
deleted file mode 100644
index f3789a791..000000000
--- a/Emby.Dlna/MediaReceiverRegistrar/MediaReceiverRegistrarXmlBuilder.cs
+++ /dev/null
@@ -1,90 +0,0 @@
-using System.Collections.Generic;
-using Emby.Dlna.Common;
-using Emby.Dlna.Service;
-
-namespace Emby.Dlna.MediaReceiverRegistrar
-{
- ///
- /// Defines the .
- /// See https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-drmnd/5d37515e-7a63-4709-8258-8fd4e0ed4482.
- ///
- public static class MediaReceiverRegistrarXmlBuilder
- {
- ///
- /// Retrieves an XML description of the X_MS_MediaReceiverRegistrar.
- ///
- /// An XML representation of this service.
- public static string GetXml()
- {
- return new ServiceXmlBuilder().GetXml(ServiceActionListBuilder.GetActions(), GetStateVariables());
- }
-
- ///
- /// The a list of all the state variables for this invocation.
- ///
- /// The .
- private static IEnumerable GetStateVariables()
- {
- var list = new List
- {
- 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;
- }
- }
-}
diff --git a/Emby.Dlna/MediaReceiverRegistrar/ServiceActionListBuilder.cs b/Emby.Dlna/MediaReceiverRegistrar/ServiceActionListBuilder.cs
deleted file mode 100644
index 56788ae22..000000000
--- a/Emby.Dlna/MediaReceiverRegistrar/ServiceActionListBuilder.cs
+++ /dev/null
@@ -1,187 +0,0 @@
-using System.Collections.Generic;
-using Emby.Dlna.Common;
-
-namespace Emby.Dlna.MediaReceiverRegistrar
-{
- ///
- /// Defines the .
- ///
- public static class ServiceActionListBuilder
- {
- ///
- /// Returns a list of services that this instance provides.
- ///
- /// An .
- public static IEnumerable GetActions()
- {
- return new[]
- {
- GetIsValidated(),
- GetIsAuthorized(),
- GetRegisterDevice(),
- GetGetAuthorizationDeniedUpdateID(),
- GetGetAuthorizationGrantedUpdateID(),
- GetGetValidationRevokedUpdateID(),
- GetGetValidationSucceededUpdateID()
- };
- }
-
- ///
- /// Returns the action details for "IsValidated".
- ///
- /// The .
- 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;
- }
-
- ///
- /// Returns the action details for "IsAuthorized".
- ///
- /// The .
- 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;
- }
-
- ///
- /// Returns the action details for "RegisterDevice".
- ///
- /// The .
- 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;
- }
-
- ///
- /// Returns the action details for "GetValidationSucceededUpdateID".
- ///
- /// The .
- private static ServiceAction GetGetValidationSucceededUpdateID()
- {
- var action = new ServiceAction
- {
- Name = "GetValidationSucceededUpdateID"
- };
-
- action.ArgumentList.Add(new Argument
- {
- Name = "ValidationSucceededUpdateID",
- Direction = "out"
- });
-
- return action;
- }
-
- ///
- /// Returns the action details for "GetGetAuthorizationDeniedUpdateID".
- ///
- /// The .
- private static ServiceAction GetGetAuthorizationDeniedUpdateID()
- {
- var action = new ServiceAction
- {
- Name = "GetAuthorizationDeniedUpdateID"
- };
-
- action.ArgumentList.Add(new Argument
- {
- Name = "AuthorizationDeniedUpdateID",
- Direction = "out"
- });
-
- return action;
- }
-
- ///
- /// Returns the action details for "GetValidationRevokedUpdateID".
- ///
- /// The .
- private static ServiceAction GetGetValidationRevokedUpdateID()
- {
- var action = new ServiceAction
- {
- Name = "GetValidationRevokedUpdateID"
- };
-
- action.ArgumentList.Add(new Argument
- {
- Name = "ValidationRevokedUpdateID",
- Direction = "out"
- });
-
- return action;
- }
-
- ///
- /// Returns the action details for "GetAuthorizationGrantedUpdateID".
- ///
- /// The .
- private static ServiceAction GetGetAuthorizationGrantedUpdateID()
- {
- var action = new ServiceAction
- {
- Name = "GetAuthorizationGrantedUpdateID"
- };
-
- action.ArgumentList.Add(new Argument
- {
- Name = "AuthorizationGrantedUpdateID",
- Direction = "out"
- });
-
- return action;
- }
- }
-}
diff --git a/Emby.Dlna/PlayTo/Device.cs b/Emby.Dlna/PlayTo/Device.cs
deleted file mode 100644
index 18fa19650..000000000
--- a/Emby.Dlna/PlayTo/Device.cs
+++ /dev/null
@@ -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? PlaybackStart;
-
- public event EventHandler? PlaybackProgress;
-
- public event EventHandler? PlaybackStopped;
-
- public event EventHandler? 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);
- }
- }
-
- ///
- /// Restarts the timer in inactive mode.
- ///
- 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 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;
- }
-
- ///
- /// Sets volume on a scale of 0-100.
- ///
- /// The volume on a scale of 0-100.
- /// The cancellation token to cancel operation.
- /// A representing the asynchronous operation.
- public async Task SetVolume(int value, CancellationToken cancellationToken)
- {
- var rendererCommands = await GetRenderingProtocolAsync(cancellationToken).ConfigureAwait(false);
-
- 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("&", "&", 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
- {
- { "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("&", "&", 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
- {
- { "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 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 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("" + xml + "")
- .Descendants()
- .First();
- }
- catch (XmlException)
- {
- }
-
- // some devices send back invalid xml
- try
- {
- return XElement.Parse(xml.Replace("&", "&", 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 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 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 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();
-
- 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));
- }
-
- ///
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- ///
- /// Releases unmanaged and optionally managed resources.
- ///
- /// true to release both managed and unmanaged resources; false to release only unmanaged resources.
- protected virtual void Dispose(bool disposing)
- {
- if (_disposed)
- {
- return;
- }
-
- if (disposing)
- {
- _timer?.Dispose();
- _timer = null;
- Properties = null!;
- }
-
- _disposed = true;
- }
-
- ///
- public override string ToString()
- {
- return string.Format(CultureInfo.InvariantCulture, "{0} - {1}", Properties.Name, Properties.BaseUrl);
- }
- }
-}
diff --git a/Emby.Dlna/PlayTo/DeviceInfo.cs b/Emby.Dlna/PlayTo/DeviceInfo.cs
deleted file mode 100644
index 2acfff4eb..000000000
--- a/Emby.Dlna/PlayTo/DeviceInfo.cs
+++ /dev/null
@@ -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 _services = new List();
- 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 Services => _services;
-
- public DeviceIdentification ToDeviceIdentification()
- {
- return new DeviceIdentification
- {
- Manufacturer = Manufacturer,
- ModelName = ModelName,
- ModelNumber = ModelNumber,
- FriendlyName = Name,
- ManufacturerUrl = ManufacturerUrl,
- ModelUrl = ModelUrl,
- ModelDescription = ModelDescription,
- SerialNumber = SerialNumber
- };
- }
- }
-}
diff --git a/Emby.Dlna/PlayTo/DlnaHttpClient.cs b/Emby.Dlna/PlayTo/DlnaHttpClient.cs
deleted file mode 100644
index 255c51f19..000000000
--- a/Emby.Dlna/PlayTo/DlnaHttpClient.cs
+++ /dev/null
@@ -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
-{
- ///
- /// Http client for Dlna PlayTo function.
- ///
- 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 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, "&");
-
- 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 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 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);
- }
- }
-}
diff --git a/Emby.Dlna/PlayTo/MediaChangedEventArgs.cs b/Emby.Dlna/PlayTo/MediaChangedEventArgs.cs
deleted file mode 100644
index 0f7a524d6..000000000
--- a/Emby.Dlna/PlayTo/MediaChangedEventArgs.cs
+++ /dev/null
@@ -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; }
- }
-}
diff --git a/Emby.Dlna/PlayTo/PlayToController.cs b/Emby.Dlna/PlayTo/PlayToController.cs
deleted file mode 100644
index f70ebf3eb..000000000
--- a/Emby.Dlna/PlayTo/PlayToController.cs
+++ /dev/null
@@ -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 _playlist = new List();
- 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 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();
- 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 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();
-
- 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.");
- }
-
- ///
- /// Plays the items.
- ///
- /// The items.
- /// The cancellation token.
- /// true on success.
- private async Task PlayItems(IEnumerable 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);
- }
- }
-
- ///
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- ///
- /// Releases unmanaged and optionally managed resources.
- ///
- /// true to release both managed and unmanaged resources; false to release only unmanaged resources.
- protected virtual void Dispose(bool disposing)
- {
- if (_disposed)
- {
- return;
- }
-
- if (disposing)
- {
- _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 values, string name)
- {
- var value = values.GetValueOrDefault(name);
-
- if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
- {
- return result;
- }
-
- return null;
- }
-
- private static long GetLongValue(IReadOnlyDictionary values, string name)
- {
- var value = values.GetValueOrDefault(name);
-
- if (long.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
- {
- return result;
- }
-
- return 0;
- }
-
- ///
- public Task SendMessage(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 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 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;
- }
- }
- }
-}
diff --git a/Emby.Dlna/PlayTo/PlayToManager.cs b/Emby.Dlna/PlayTo/PlayToManager.cs
deleted file mode 100644
index b05e0a095..000000000
--- a/Emby.Dlna/PlayTo/PlayToManager.cs
+++ /dev/null
@@ -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 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 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().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);
- }
- }
-
- ///
- 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;
- }
- }
-}
diff --git a/Emby.Dlna/PlayTo/PlaybackProgressEventArgs.cs b/Emby.Dlna/PlayTo/PlaybackProgressEventArgs.cs
deleted file mode 100644
index c95d8b1e8..000000000
--- a/Emby.Dlna/PlayTo/PlaybackProgressEventArgs.cs
+++ /dev/null
@@ -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; }
- }
-}
diff --git a/Emby.Dlna/PlayTo/PlaybackStartEventArgs.cs b/Emby.Dlna/PlayTo/PlaybackStartEventArgs.cs
deleted file mode 100644
index 619c861ed..000000000
--- a/Emby.Dlna/PlayTo/PlaybackStartEventArgs.cs
+++ /dev/null
@@ -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; }
- }
-}
diff --git a/Emby.Dlna/PlayTo/PlaybackStoppedEventArgs.cs b/Emby.Dlna/PlayTo/PlaybackStoppedEventArgs.cs
deleted file mode 100644
index d0ec25059..000000000
--- a/Emby.Dlna/PlayTo/PlaybackStoppedEventArgs.cs
+++ /dev/null
@@ -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; }
- }
-}
diff --git a/Emby.Dlna/PlayTo/PlaylistItem.cs b/Emby.Dlna/PlayTo/PlaylistItem.cs
deleted file mode 100644
index 5056e69ae..000000000
--- a/Emby.Dlna/PlayTo/PlaylistItem.cs
+++ /dev/null
@@ -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; }
- }
-}
diff --git a/Emby.Dlna/PlayTo/PlaylistItemFactory.cs b/Emby.Dlna/PlayTo/PlaylistItemFactory.cs
deleted file mode 100644
index 53cd05cfd..000000000
--- a/Emby.Dlna/PlayTo/PlaylistItemFactory.cs
+++ /dev/null
@@ -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;
- }
- }
-}
diff --git a/Emby.Dlna/PlayTo/TransportCommands.cs b/Emby.Dlna/PlayTo/TransportCommands.cs
deleted file mode 100644
index 6b2096d9d..000000000
--- a/Emby.Dlna/PlayTo/TransportCommands.cs
+++ /dev/null
@@ -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 = "\r\n" + "" + "" + "" + "{2}" + "" + "";
-
- public List StateVariables { get; } = new List();
-
- public List ServiceActions { get; } = new List();
-
- 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();
- 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 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);
- }
- }
-}
diff --git a/Emby.Dlna/PlayTo/TransportState.cs b/Emby.Dlna/PlayTo/TransportState.cs
deleted file mode 100644
index 0d6a78438..000000000
--- a/Emby.Dlna/PlayTo/TransportState.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-#pragma warning disable CS1591
-
-namespace Emby.Dlna.PlayTo
-{
- ///
- /// Core of the AVTransport service. It defines the conceptually top-
- /// level state of the transport, for example, whether it is playing, recording, etc.
- ///
- public enum TransportState
- {
- STOPPED,
- PLAYING,
- TRANSITIONING,
- PAUSED_PLAYBACK
- }
-}
diff --git a/Emby.Dlna/PlayTo/UpnpContainer.cs b/Emby.Dlna/PlayTo/UpnpContainer.cs
deleted file mode 100644
index 017d51e60..000000000
--- a/Emby.Dlna/PlayTo/UpnpContainer.cs
+++ /dev/null
@@ -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)
- };
- }
- }
-}
diff --git a/Emby.Dlna/PlayTo/uBaseObject.cs b/Emby.Dlna/PlayTo/uBaseObject.cs
deleted file mode 100644
index a8f451405..000000000
--- a/Emby.Dlna/PlayTo/uBaseObject.cs
+++ /dev/null
@@ -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 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);
- }
- }
-}
diff --git a/Emby.Dlna/PlayTo/uPnpNamespaces.cs b/Emby.Dlna/PlayTo/uPnpNamespaces.cs
deleted file mode 100644
index 5042d4493..000000000
--- a/Emby.Dlna/PlayTo/uPnpNamespaces.cs
+++ /dev/null
@@ -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";
- }
-}
diff --git a/Emby.Dlna/Profiles/DefaultProfile.cs b/Emby.Dlna/Profiles/DefaultProfile.cs
deleted file mode 100644
index 54a0a87a8..000000000
--- a/Emby.Dlna/Profiles/DefaultProfile.cs
+++ /dev/null
@@ -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"
- }
- };
- }
- }
-}
diff --git a/Emby.Dlna/Profiles/Xml/Default.xml b/Emby.Dlna/Profiles/Xml/Default.xml
deleted file mode 100644
index 9460f9d5a..000000000
--- a/Emby.Dlna/Profiles/Xml/Default.xml
+++ /dev/null
@@ -1,61 +0,0 @@
-
-
- Generic Device
- Jellyfin
- https://github.com/jellyfin/jellyfin
- Jellyfin Server
- UPnP/AV 1.0 Compliant Media Server
- 01
- https://github.com/jellyfin/jellyfin
- false
- false
- false
- Audio,Photo,Video
- JPEG_SM
- 480
- 480
- 48
- 48
- 140000000
- 140000000
- 192000
-
- 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:*
- 0
- false
- false
- false
- false
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Emby.Dlna/Profiles/Xml/Denon AVR.xml b/Emby.Dlna/Profiles/Xml/Denon AVR.xml
deleted file mode 100644
index 571786906..000000000
--- a/Emby.Dlna/Profiles/Xml/Denon AVR.xml
+++ /dev/null
@@ -1,68 +0,0 @@
-
-
- Denon AVR
-
- Denon:\[AVR:.*
- Denon
-
-
- Jellyfin
- https://github.com/jellyfin/jellyfin
- Jellyfin Server
- UPnP/AV 1.0 Compliant Media Server
- 01
- https://github.com/jellyfin/jellyfin
- false
- false
- false
- Audio
- JPEG_SM
- 480
- 480
- 48
- 48
- 140000000
- 140000000
- 192000
-
- 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:*
- 0
- false
- false
- false
- false
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Emby.Dlna/Profiles/Xml/DirecTV HD-DVR.xml b/Emby.Dlna/Profiles/Xml/DirecTV HD-DVR.xml
deleted file mode 100644
index eea0febfd..000000000
--- a/Emby.Dlna/Profiles/Xml/DirecTV HD-DVR.xml
+++ /dev/null
@@ -1,67 +0,0 @@
-
-
- DirecTV HD-DVR
-
- ^DIRECTV.*$
-
-
-
-
- Jellyfin
- https://github.com/jellyfin/jellyfin
- Jellyfin Server
- UPnP/AV 1.0 Compliant Media Server
- 01
- https://github.com/jellyfin/jellyfin
- false
- false
- false
- Audio,Photo,Video
- JPEG_SM
- 480
- 480
- 48
- 48
- 140000000
- 140000000
- 192000
-
- 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:*
- 10
- true
- true
- false
- false
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Emby.Dlna/Profiles/Xml/Dish Hopper-Joey.xml b/Emby.Dlna/Profiles/Xml/Dish Hopper-Joey.xml
deleted file mode 100644
index 5b299577e..000000000
--- a/Emby.Dlna/Profiles/Xml/Dish Hopper-Joey.xml
+++ /dev/null
@@ -1,96 +0,0 @@
-
-
- Dish Hopper-Joey
-
- Echostar Technologies LLC
- http://www.echostar.com
-
-
-
-
- Jellyfin
- https://github.com/jellyfin/jellyfin
- Jellyfin Server
- UPnP/AV 1.0 Compliant Media Server
- 01
- https://github.com/jellyfin/jellyfin
- false
- false
- false
- Audio,Photo,Video
- JPEG_SM
- 480
- 480
- 48
- 48
- 140000000
- 140000000
- 192000
-
- 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:*
- 0
- false
- false
- false
- false
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Emby.Dlna/Profiles/Xml/LG Smart TV.xml b/Emby.Dlna/Profiles/Xml/LG Smart TV.xml
deleted file mode 100644
index 20f5ba79b..000000000
--- a/Emby.Dlna/Profiles/Xml/LG Smart TV.xml
+++ /dev/null
@@ -1,92 +0,0 @@
-
-
- LG Smart TV
-
- LG.*
-
-
-
-
- Jellyfin
- https://github.com/jellyfin/jellyfin
- Jellyfin Server
- UPnP/AV 1.0 Compliant Media Server
- 01
- https://github.com/jellyfin/jellyfin
- false
- false
- false
- Audio,Photo,Video
- JPEG_SM
- 480
- 480
- 48
- 48
- 140000000
- 140000000
- 192000
-
- 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:*
- 10
- false
- false
- false
- false
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Emby.Dlna/Profiles/Xml/Linksys DMA2100.xml b/Emby.Dlna/Profiles/Xml/Linksys DMA2100.xml
deleted file mode 100644
index d01e3a145..000000000
--- a/Emby.Dlna/Profiles/Xml/Linksys DMA2100.xml
+++ /dev/null
@@ -1,54 +0,0 @@
-
-
- Linksys DMA2100
-
- DMA2100us
-
-
- Jellyfin
- https://github.com/jellyfin/jellyfin
- Jellyfin Server
- UPnP/AV 1.0 Compliant Media Server
- 01
- https://github.com/jellyfin/jellyfin
- false
- false
- false
- Audio,Photo,Video
- JPEG_SM
- 480
- 480
- 48
- 48
- 140000000
- 140000000
- 192000
-
- 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:*
- 0
- false
- false
- false
- false
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Emby.Dlna/Profiles/Xml/Marantz.xml b/Emby.Dlna/Profiles/Xml/Marantz.xml
deleted file mode 100644
index 0cc9c86e8..000000000
--- a/Emby.Dlna/Profiles/Xml/Marantz.xml
+++ /dev/null
@@ -1,62 +0,0 @@
-
-
- Marantz
-
- Marantz
-
-
-
-
- Jellyfin
- https://github.com/jellyfin/jellyfin
- Jellyfin Server
- UPnP/AV 1.0 Compliant Media Server
- 01
- https://github.com/jellyfin/jellyfin
- false
- false
- false
- Audio
- JPEG_SM
- 480
- 480
- 48
- 48
- 140000000
- 140000000
- 192000
-
- 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:*
- 0
- false
- false
- false
- false
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Emby.Dlna/Profiles/Xml/MediaMonkey.xml b/Emby.Dlna/Profiles/Xml/MediaMonkey.xml
deleted file mode 100644
index 9d5ddc3d1..000000000
--- a/Emby.Dlna/Profiles/Xml/MediaMonkey.xml
+++ /dev/null
@@ -1,62 +0,0 @@
-
-
- MediaMonkey
-
- MediaMonkey
-
-
-
-
- Jellyfin
- https://github.com/jellyfin/jellyfin
- Jellyfin Server
- UPnP/AV 1.0 Compliant Media Server
- 01
- https://github.com/jellyfin/jellyfin
- false
- false
- false
- Audio
- JPEG_SM
- 480
- 480
- 48
- 48
- 140000000
- 140000000
- 192000
-
- 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:*
- 0
- false
- false
- false
- false
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Emby.Dlna/Profiles/Xml/Panasonic Viera.xml b/Emby.Dlna/Profiles/Xml/Panasonic Viera.xml
deleted file mode 100644
index 8f766853b..000000000
--- a/Emby.Dlna/Profiles/Xml/Panasonic Viera.xml
+++ /dev/null
@@ -1,87 +0,0 @@
-
-
- Panasonic Viera
-
- VIERA
- Panasonic
-
-
-
-
- Jellyfin
- https://github.com/jellyfin/jellyfin
- Jellyfin Server
- UPnP/AV 1.0 Compliant Media Server
- 01
- https://github.com/jellyfin/jellyfin
- false
- false
- false
- Audio,Photo,Video
- JPEG_SM
- 480
- 480
- 48
- 48
- 140000000
- 140000000
- 192000
-
- 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:*
- 10
- false
- false
- false
- false
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Emby.Dlna/Profiles/Xml/Popcorn Hour.xml b/Emby.Dlna/Profiles/Xml/Popcorn Hour.xml
deleted file mode 100644
index aa881d014..000000000
--- a/Emby.Dlna/Profiles/Xml/Popcorn Hour.xml
+++ /dev/null
@@ -1,92 +0,0 @@
-
-
- Popcorn Hour
- Jellyfin
- https://github.com/jellyfin/jellyfin
- Jellyfin Server
- UPnP/AV 1.0 Compliant Media Server
- 01
- https://github.com/jellyfin/jellyfin
- false
- false
- false
- Audio,Photo,Video
- JPEG_SM
- 480
- 480
- 48
- 48
- 140000000
- 140000000
- 192000
-
- 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:*
- 0
- false
- false
- false
- false
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Emby.Dlna/Profiles/Xml/Samsung Smart TV.xml b/Emby.Dlna/Profiles/Xml/Samsung Smart TV.xml
deleted file mode 100644
index 7160a9c2e..000000000
--- a/Emby.Dlna/Profiles/Xml/Samsung Smart TV.xml
+++ /dev/null
@@ -1,128 +0,0 @@
-
-
- Samsung Smart TV
-
- samsung.com
-
-
-
-
- Jellyfin
- https://github.com/jellyfin/jellyfin
- Jellyfin Server
- UPnP/AV 1.0 Compliant Media Server
- 01
- https://github.com/jellyfin/jellyfin
- true
- true
- false
- Audio,Photo,Video
- JPEG_SM
- 480
- 480
- 48
- 48
- 140000000
- 140000000
- 192000
-
- 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:*
- 0
- false
- false
- false
- false
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Emby.Dlna/Profiles/Xml/Sharp Smart TV.xml b/Emby.Dlna/Profiles/Xml/Sharp Smart TV.xml
deleted file mode 100644
index c9b907e58..000000000
--- a/Emby.Dlna/Profiles/Xml/Sharp Smart TV.xml
+++ /dev/null
@@ -1,60 +0,0 @@
-
-
- Sharp Smart TV
-
- Sharp
-
-
-
-
- Jellyfin
- https://github.com/jellyfin/jellyfin
- Jellyfin Server
- UPnP/AV 1.0 Compliant Media Server
- 01
- https://github.com/jellyfin/jellyfin
- false
- false
- false
- Audio,Photo,Video
- JPEG_SM
- 480
- 480
- 48
- 48
- 140000000
- 140000000
- 192000
-
- 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:*
- 0
- true
- true
- false
- false
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml b/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml
deleted file mode 100644
index 2c5614883..000000000
--- a/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml
+++ /dev/null
@@ -1,87 +0,0 @@
-
-
- Sony Blu-ray Player 2013
-
- BDP-2013
-
-
-
-
-
-
-
-
- Microsoft Corporation
- https://github.com/jellyfin/jellyfin
- Windows Media Player Sharing
- UPnP/AV 1.0 Compliant Media Server
- 3.0
- https://github.com/jellyfin/jellyfin
- false
- false
- false
- Audio,Photo,Video
- JPEG_SM
- 480
- 480
- 48
- 48
- 140000000
- 140000000
- 192000
-
- http-get:*:video/divx:DLNA.ORG_PN=MATROSKA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMABASE;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMAFULL;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/mp4:DLNA.ORG_PN=AAC_ISO;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/mp4:DLNA.ORG_PN=AAC_ISO_320;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/vnd.dlna.adts:DLNA.ORG_PN=AAC_ADTS;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/vnd.dlna.adts:DLNA.ORG_PN=AAC_ADTS_320;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/flac:DLNA.ORG_PN=FLAC;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/ogg:DLNA.ORG_PN=OGG;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:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/png:DLNA.ORG_PN=PNG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/png:DLNA.ORG_PN=PNG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/gif:DLNA.ORG_PN=GIF_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG1;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_EU_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_NA_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_KO_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_JP_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-msvideo:DLNA.ORG_PN=AVI;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-flv:DLNA.ORG_PN=FLV;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-dvr:DLNA.ORG_PN=DVR_MS;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/wtv:DLNA.ORG_PN=WTV;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/ogg:DLNA.ORG_PN=OGV;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.rn-realvideo:DLNA.ORG_PN=REAL_VIDEO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_BASE;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L1_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L2_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L3_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_P2_3GPP_SP_L0B_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_P2_3GPP_SP_L0B_AMR;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_H263_3GPP_P0_L10_AMR;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_H263_MP4_P0_L10_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000
- 0
- false
- false
- false
- false
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2014.xml b/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2014.xml
deleted file mode 100644
index 44f9821b3..000000000
--- a/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2014.xml
+++ /dev/null
@@ -1,87 +0,0 @@
-
-
- Sony Blu-ray Player 2014
-
- BDP-2014
-
-
-
-
-
-
-
-
- Microsoft Corporation
- https://github.com/jellyfin/jellyfin
- Windows Media Player Sharing
- UPnP/AV 1.0 Compliant Media Server
- 3.0
- https://github.com/jellyfin/jellyfin
- false
- false
- false
- Audio,Photo,Video
- JPEG_SM
- 480
- 480
- 48
- 48
- 140000000
- 140000000
- 192000
-
- http-get:*:video/divx:DLNA.ORG_PN=MATROSKA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMABASE;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMAFULL;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/mp4:DLNA.ORG_PN=AAC_ISO;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/mp4:DLNA.ORG_PN=AAC_ISO_320;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/vnd.dlna.adts:DLNA.ORG_PN=AAC_ADTS;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/vnd.dlna.adts:DLNA.ORG_PN=AAC_ADTS_320;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/flac:DLNA.ORG_PN=FLAC;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/ogg:DLNA.ORG_PN=OGG;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:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/png:DLNA.ORG_PN=PNG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/png:DLNA.ORG_PN=PNG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/gif:DLNA.ORG_PN=GIF_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG1;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_EU_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_NA_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_KO_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_JP_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-msvideo:DLNA.ORG_PN=AVI;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-flv:DLNA.ORG_PN=FLV;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-dvr:DLNA.ORG_PN=DVR_MS;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/wtv:DLNA.ORG_PN=WTV;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/ogg:DLNA.ORG_PN=OGV;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.rn-realvideo:DLNA.ORG_PN=REAL_VIDEO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_BASE;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L1_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L2_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L3_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_P2_3GPP_SP_L0B_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_P2_3GPP_SP_L0B_AMR;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_H263_3GPP_P0_L10_AMR;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_H263_MP4_P0_L10_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000
- 0
- false
- false
- false
- false
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2015.xml b/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2015.xml
deleted file mode 100644
index a7d17c1a0..000000000
--- a/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2015.xml
+++ /dev/null
@@ -1,85 +0,0 @@
-
-
- Sony Blu-ray Player 2015
-
- BDP-2015
-
-
-
-
-
-
- Microsoft Corporation
- https://github.com/jellyfin/jellyfin
- Windows Media Player Sharing
- UPnP/AV 1.0 Compliant Media Server
- 3.0
- https://github.com/jellyfin/jellyfin
- false
- false
- false
- Audio,Photo,Video
- JPEG_SM
- 480
- 480
- 48
- 48
- 140000000
- 140000000
- 192000
-
- http-get:*:video/divx:DLNA.ORG_PN=MATROSKA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMABASE;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMAFULL;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/mp4:DLNA.ORG_PN=AAC_ISO;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/mp4:DLNA.ORG_PN=AAC_ISO_320;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/vnd.dlna.adts:DLNA.ORG_PN=AAC_ADTS;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/vnd.dlna.adts:DLNA.ORG_PN=AAC_ADTS_320;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/flac:DLNA.ORG_PN=FLAC;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/ogg:DLNA.ORG_PN=OGG;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:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/png:DLNA.ORG_PN=PNG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/png:DLNA.ORG_PN=PNG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/gif:DLNA.ORG_PN=GIF_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG1;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_EU_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_NA_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_KO_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_JP_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-msvideo:DLNA.ORG_PN=AVI;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-flv:DLNA.ORG_PN=FLV;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-dvr:DLNA.ORG_PN=DVR_MS;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/wtv:DLNA.ORG_PN=WTV;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/ogg:DLNA.ORG_PN=OGV;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.rn-realvideo:DLNA.ORG_PN=REAL_VIDEO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_BASE;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L1_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L2_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L3_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_P2_3GPP_SP_L0B_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_P2_3GPP_SP_L0B_AMR;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_H263_3GPP_P0_L10_AMR;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_H263_MP4_P0_L10_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000
- 0
- false
- false
- false
- false
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2016.xml b/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2016.xml
deleted file mode 100644
index b42b1e84f..000000000
--- a/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player 2016.xml
+++ /dev/null
@@ -1,85 +0,0 @@
-
-
- Sony Blu-ray Player 2016
-
- BDP-2016
-
-
-
-
-
-
- Microsoft Corporation
- https://github.com/jellyfin/jellyfin
- Windows Media Player Sharing
- UPnP/AV 1.0 Compliant Media Server
- 3.0
- https://github.com/jellyfin/jellyfin
- false
- false
- false
- Audio,Photo,Video
- JPEG_SM
- 480
- 480
- 48
- 48
- 140000000
- 140000000
- 192000
-
- http-get:*:video/divx:DLNA.ORG_PN=MATROSKA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMABASE;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMAFULL;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/mp4:DLNA.ORG_PN=AAC_ISO;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/mp4:DLNA.ORG_PN=AAC_ISO_320;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/vnd.dlna.adts:DLNA.ORG_PN=AAC_ADTS;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/vnd.dlna.adts:DLNA.ORG_PN=AAC_ADTS_320;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/flac:DLNA.ORG_PN=FLAC;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/ogg:DLNA.ORG_PN=OGG;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:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/png:DLNA.ORG_PN=PNG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/png:DLNA.ORG_PN=PNG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/gif:DLNA.ORG_PN=GIF_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG1;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_EU_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_NA_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_KO_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_JP_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-msvideo:DLNA.ORG_PN=AVI;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-flv:DLNA.ORG_PN=FLV;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-dvr:DLNA.ORG_PN=DVR_MS;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/wtv:DLNA.ORG_PN=WTV;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/ogg:DLNA.ORG_PN=OGV;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.rn-realvideo:DLNA.ORG_PN=REAL_VIDEO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_BASE;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L1_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L2_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L3_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_P2_3GPP_SP_L0B_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_P2_3GPP_SP_L0B_AMR;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_H263_3GPP_P0_L10_AMR;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_H263_MP4_P0_L10_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000
- 0
- false
- false
- false
- false
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player.xml b/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player.xml
deleted file mode 100644
index 46857caf0..000000000
--- a/Emby.Dlna/Profiles/Xml/Sony Blu-ray Player.xml
+++ /dev/null
@@ -1,115 +0,0 @@
-
-
- Sony Blu-ray Player
-
- Blu-ray Disc Player
- Sony
-
-
-
-
-
- Microsoft Corporation
- https://github.com/jellyfin/jellyfin
- Windows Media Player Sharing
- UPnP/AV 1.0 Compliant Media Server
- 3.0
- https://github.com/jellyfin/jellyfin
- false
- false
- false
- Audio,Photo,Video
- JPEG_SM
- 480
- 480
- 48
- 48
- 140000000
- 140000000
- 192000
-
- http-get:*:video/divx:DLNA.ORG_PN=MATROSKA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMABASE;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMAFULL;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/mp4:DLNA.ORG_PN=AAC_ISO;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/mp4:DLNA.ORG_PN=AAC_ISO_320;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/vnd.dlna.adts:DLNA.ORG_PN=AAC_ADTS;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/vnd.dlna.adts:DLNA.ORG_PN=AAC_ADTS_320;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/flac:DLNA.ORG_PN=FLAC;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/ogg:DLNA.ORG_PN=OGG;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:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/png:DLNA.ORG_PN=PNG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/png:DLNA.ORG_PN=PNG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/gif:DLNA.ORG_PN=GIF_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG1;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_EU_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_NA_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_KO_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_JP_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-msvideo:DLNA.ORG_PN=AVI;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-flv:DLNA.ORG_PN=FLV;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-dvr:DLNA.ORG_PN=DVR_MS;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/wtv:DLNA.ORG_PN=WTV;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/ogg:DLNA.ORG_PN=OGV;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.rn-realvideo:DLNA.ORG_PN=REAL_VIDEO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_BASE;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L1_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L2_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L3_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_P2_3GPP_SP_L0B_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_P2_3GPP_SP_L0B_AMR;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_H263_3GPP_P0_L10_AMR;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_H263_MP4_P0_L10_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000
- 0
- false
- false
- false
- false
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Emby.Dlna/Profiles/Xml/Sony Bravia (2010).xml b/Emby.Dlna/Profiles/Xml/Sony Bravia (2010).xml
deleted file mode 100644
index 1461db311..000000000
--- a/Emby.Dlna/Profiles/Xml/Sony Bravia (2010).xml
+++ /dev/null
@@ -1,133 +0,0 @@
-
-
- Sony Bravia (2010)
-
- KDL-[0-9]{2}[EHLNPB]X[0-9][01][0-9].*
- Sony
-
-
-
-
- Microsoft Corporation
- http://www.microsoft.com/
- Windows Media Player Sharing
- UPnP/AV 1.0 Compliant Media Server
- 3.0
- http://www.microsoft.com/
- true
- true
- false
- Audio,Photo,Video
- JPEG_TN
- 480
- 480
- 48
- 48
- 140000000
- 140000000
- 192000
-
- 10
- 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
- 0
- false
- false
- false
- false
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Emby.Dlna/Profiles/Xml/Sony Bravia (2011).xml b/Emby.Dlna/Profiles/Xml/Sony Bravia (2011).xml
deleted file mode 100644
index 7c5f2b181..000000000
--- a/Emby.Dlna/Profiles/Xml/Sony Bravia (2011).xml
+++ /dev/null
@@ -1,139 +0,0 @@
-
-
- Sony Bravia (2011)
-
- KDL-[0-9]{2}([A-Z]X[0-9]2[0-9]|CX400).*
- Sony
-
-
-
-
- Microsoft Corporation
- http://www.microsoft.com/
- Windows Media Player Sharing
- UPnP/AV 1.0 Compliant Media Server
- 3.0
- http://www.microsoft.com/
- true
- true
- false
- Audio,Photo,Video
- JPEG_TN
- 480
- 480
- 48
- 48
- 140000000
- 140000000
- 192000
-
- 10
- 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:*
- 0
- false
- false
- false
- false
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Emby.Dlna/Profiles/Xml/Sony Bravia (2012).xml b/Emby.Dlna/Profiles/Xml/Sony Bravia (2012).xml
deleted file mode 100644
index 842a8fba3..000000000
--- a/Emby.Dlna/Profiles/Xml/Sony Bravia (2012).xml
+++ /dev/null
@@ -1,115 +0,0 @@
-
-
- Sony Bravia (2012)
-
- KDL-[0-9]{2}[A-Z]X[0-9]5([0-9]|G).*
- Sony
-
-
-
-
- Microsoft Corporation
- http://www.microsoft.com/
- Windows Media Player Sharing
- UPnP/AV 1.0 Compliant Media Server
- 3.0
- http://www.microsoft.com/
- true
- true
- false
- Audio,Photo,Video
- JPEG_TN
- 480
- 480
- 48
- 48
- 140000000
- 140000000
- 192000
-
- 10
- 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:*
- 0
- false
- false
- false
- false
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Emby.Dlna/Profiles/Xml/Sony Bravia (2013).xml b/Emby.Dlna/Profiles/Xml/Sony Bravia (2013).xml
deleted file mode 100644
index f1135c3fe..000000000
--- a/Emby.Dlna/Profiles/Xml/Sony Bravia (2013).xml
+++ /dev/null
@@ -1,114 +0,0 @@
-
-
- Sony Bravia (2013)
-
- KDL-[0-9]{2}[WR][5689][0-9]{2}A.*
- Sony
-
-
-
-
- Microsoft Corporation
- http://www.microsoft.com/
- Windows Media Player Sharing
- UPnP/AV 1.0 Compliant Media Server
- 3.0
- http://www.microsoft.com/
- true
- true
- false
- Audio,Photo,Video
- JPEG_TN
- 480
- 480
- 48
- 48
- 140000000
- 140000000
- 192000
-
- 10
- 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:*
- 0
- false
- false
- false
- false
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Emby.Dlna/Profiles/Xml/Sony Bravia (2014).xml b/Emby.Dlna/Profiles/Xml/Sony Bravia (2014).xml
deleted file mode 100644
index 85c7868c6..000000000
--- a/Emby.Dlna/Profiles/Xml/Sony Bravia (2014).xml
+++ /dev/null
@@ -1,114 +0,0 @@
-
-
- Sony Bravia (2014)
-
- (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).*
- Sony
-
-
-
-
- Microsoft Corporation
- http://www.microsoft.com/
- Windows Media Player Sharing
- UPnP/AV 1.0 Compliant Media Server
- 3.0
- http://www.microsoft.com/
- true
- true
- false
- Audio,Photo,Video
- JPEG_TN
- 480
- 480
- 48
- 48
- 140000000
- 140000000
- 192000
-
- 10
- 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:*
- 0
- false
- false
- false
- false
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Emby.Dlna/Profiles/Xml/Sony PlayStation 3.xml b/Emby.Dlna/Profiles/Xml/Sony PlayStation 3.xml
deleted file mode 100644
index 129b188e2..000000000
--- a/Emby.Dlna/Profiles/Xml/Sony PlayStation 3.xml
+++ /dev/null
@@ -1,105 +0,0 @@
-
-
- Sony PlayStation 3
-
- PLAYSTATION 3
-
-
-
-
-
- Jellyfin
- https://github.com/jellyfin/jellyfin
- Jellyfin Server
- UPnP/AV 1.0 Compliant Media Server
- 01
- https://github.com/jellyfin/jellyfin
- false
- true
- false
- Audio,Photo,Video
- JPEG_TN
- 480
- 480
- 48
- 48
- 140000000
- 140000000
- 192000
-
- 10
- 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:*
- 0
- false
- false
- false
- false
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Emby.Dlna/Profiles/Xml/Sony PlayStation 4.xml b/Emby.Dlna/Profiles/Xml/Sony PlayStation 4.xml
deleted file mode 100644
index 592119305..000000000
--- a/Emby.Dlna/Profiles/Xml/Sony PlayStation 4.xml
+++ /dev/null
@@ -1,108 +0,0 @@
-
-
- Sony PlayStation 4
-
- PLAYSTATION 4
-
-
-
-
-
- Jellyfin
- https://github.com/jellyfin/jellyfin
- Jellyfin Server
- UPnP/AV 1.0 Compliant Media Server
- 01
- https://github.com/jellyfin/jellyfin
- false
- true
- false
- Audio,Photo,Video
- JPEG_TN
- 480
- 480
- 48
- 48
- 140000000
- 140000000
- 192000
-
- 10
- 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:*
- 0
- false
- false
- false
- false
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Emby.Dlna/Profiles/Xml/WDTV Live.xml b/Emby.Dlna/Profiles/Xml/WDTV Live.xml
deleted file mode 100644
index ccb74ee64..000000000
--- a/Emby.Dlna/Profiles/Xml/WDTV Live.xml
+++ /dev/null
@@ -1,94 +0,0 @@
-
-
- WDTV Live
-
- WD TV
-
-
-
-
-
- Jellyfin
- https://github.com/jellyfin/jellyfin
- Jellyfin Server
- UPnP/AV 1.0 Compliant Media Server
- 01
- https://github.com/jellyfin/jellyfin
- false
- false
- false
- Audio,Photo,Video
- JPEG_SM
- 480
- 480
- 48
- 48
- 140000000
- 140000000
- 192000
-
- 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:*
- 5
- false
- false
- false
- true
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Emby.Dlna/Profiles/Xml/Xbox One.xml b/Emby.Dlna/Profiles/Xml/Xbox One.xml
deleted file mode 100644
index 26a65bbd4..000000000
--- a/Emby.Dlna/Profiles/Xml/Xbox One.xml
+++ /dev/null
@@ -1,126 +0,0 @@
-
-
- Xbox One
-
- Xbox One
-
-
-
-
-
- Jellyfin
- https://github.com/jellyfin/jellyfin
- Jellyfin Server
- UPnP/AV 1.0 Compliant Media Server
- 01
- https://github.com/jellyfin/jellyfin
- false
- false
- false
- Audio,Photo,Video
- JPEG_SM
- 480
- 480
- 48
- 48
- 140000000
- 140000000
- 192000
-
- 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:*
- 40
- false
- false
- false
- false
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Emby.Dlna/Profiles/Xml/foobar2000.xml b/Emby.Dlna/Profiles/Xml/foobar2000.xml
deleted file mode 100644
index 5ce75ace5..000000000
--- a/Emby.Dlna/Profiles/Xml/foobar2000.xml
+++ /dev/null
@@ -1,67 +0,0 @@
-
-
- foobar2000
-
- foobar
-
-
-
-
- Jellyfin
- https://github.com/jellyfin/jellyfin
- Jellyfin Server
- UPnP/AV 1.0 Compliant Media Server
- 01
- https://github.com/jellyfin/jellyfin
- false
- false
- false
- Audio
- JPEG_SM
- 480
- 480
- 48
- 48
- 140000000
- 140000000
- 192000
-
- 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:*
- 0
- false
- false
- false
- false
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Emby.Dlna/Properties/AssemblyInfo.cs b/Emby.Dlna/Properties/AssemblyInfo.cs
deleted file mode 100644
index 606ffcf4f..000000000
--- a/Emby.Dlna/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -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.*")]
diff --git a/Emby.Dlna/Server/DescriptionXmlBuilder.cs b/Emby.Dlna/Server/DescriptionXmlBuilder.cs
deleted file mode 100644
index 69ef6f645..000000000
--- a/Emby.Dlna/Server/DescriptionXmlBuilder.cs
+++ /dev/null
@@ -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("");
-
- builder.Append("');
-
- builder.Append("");
- builder.Append("1");
- builder.Append("0");
- builder.Append("");
-
- AppendDeviceInfo(builder);
-
- builder.Append("");
-
- return builder.ToString();
- }
-
- private void AppendDeviceInfo(StringBuilder builder)
- {
- builder.Append("");
- AppendDeviceProperties(builder);
-
- AppendIconList(builder);
-
- builder.Append("")
- .Append(SecurityElement.Escape(_serverAddress))
- .Append("/web/index.html");
-
- AppendServiceList(builder);
- builder.Append("");
- }
-
- private void AppendDeviceProperties(StringBuilder builder)
- {
- builder.Append("");
-
- builder.Append("DMS-1.50");
- builder.Append("M-DMS-1.50");
-
- builder.Append("urn:schemas-upnp-org:device:MediaServer:1");
-
- builder.Append("")
- .Append(SecurityElement.Escape(GetFriendlyName()))
- .Append("");
- builder.Append("")
- .Append(SecurityElement.Escape(_profile.Manufacturer ?? string.Empty))
- .Append("");
- builder.Append("")
- .Append(SecurityElement.Escape(_profile.ManufacturerUrl ?? string.Empty))
- .Append("");
-
- builder.Append("")
- .Append(SecurityElement.Escape(_profile.ModelDescription ?? string.Empty))
- .Append("");
- builder.Append("")
- .Append(SecurityElement.Escape(_profile.ModelName ?? string.Empty))
- .Append("");
-
- builder.Append("")
- .Append(SecurityElement.Escape(_profile.ModelNumber ?? string.Empty))
- .Append("");
- builder.Append("")
- .Append(SecurityElement.Escape(_profile.ModelUrl ?? string.Empty))
- .Append("");
-
- if (string.IsNullOrEmpty(_profile.SerialNumber))
- {
- builder.Append("")
- .Append(SecurityElement.Escape(_serverId))
- .Append("");
- }
- else
- {
- builder.Append("")
- .Append(SecurityElement.Escape(_profile.SerialNumber))
- .Append("");
- }
-
- builder.Append("");
-
- builder.Append("uuid:")
- .Append(SecurityElement.Escape(_serverUdn))
- .Append("");
-
- if (!string.IsNullOrEmpty(_profile.SonyAggregationFlags))
- {
- builder.Append("")
- .Append(SecurityElement.Escape(_profile.SonyAggregationFlags))
- .Append("");
- }
- }
-
- internal string GetFriendlyName()
- {
- if (string.IsNullOrEmpty(_profile.FriendlyName))
- {
- return _serverName;
- }
-
- if (!_profile.FriendlyName.Contains("${HostName}", StringComparison.OrdinalIgnoreCase))
- {
- return _profile.FriendlyName;
- }
-
- var characterList = new List();
-
- 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("");
-
- foreach (var icon in GetIcons())
- {
- builder.Append("");
-
- builder.Append("")
- .Append(SecurityElement.Escape(icon.MimeType))
- .Append("");
- builder.Append("")
- .Append(SecurityElement.Escape(icon.Width.ToString(CultureInfo.InvariantCulture)))
- .Append("");
- builder.Append("")
- .Append(SecurityElement.Escape(icon.Height.ToString(CultureInfo.InvariantCulture)))
- .Append("");
- builder.Append("")
- .Append(SecurityElement.Escape(icon.Depth))
- .Append("");
- builder.Append("")
- .Append(BuildUrl(icon.Url))
- .Append("");
-
- builder.Append("");
- }
-
- builder.Append("");
- }
-
- private void AppendServiceList(StringBuilder builder)
- {
- builder.Append("");
-
- foreach (var service in GetServices())
- {
- builder.Append("");
-
- builder.Append("")
- .Append(SecurityElement.Escape(service.ServiceType))
- .Append("");
- builder.Append("")
- .Append(SecurityElement.Escape(service.ServiceId))
- .Append("");
- builder.Append("")
- .Append(BuildUrl(service.ScpdUrl))
- .Append("");
- builder.Append("")
- .Append(BuildUrl(service.ControlUrl))
- .Append("");
- builder.Append("")
- .Append(BuildUrl(service.EventSubUrl))
- .Append("");
-
- builder.Append("");
- }
-
- builder.Append("");
- }
-
- 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 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 GetServices()
- {
- var list = new List();
-
- 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();
- }
- }
-}
diff --git a/Emby.Dlna/Service/BaseControlHandler.cs b/Emby.Dlna/Service/BaseControlHandler.cs
deleted file mode 100644
index bff5307a4..000000000
--- a/Emby.Dlna/Service/BaseControlHandler.cs
+++ /dev/null
@@ -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 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 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 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 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 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 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(StringComparer.OrdinalIgnoreCase);
- }
-
- public string LocalName { get; set; }
-
- public string NamespaceURI { get; set; }
-
- public Dictionary Headers { get; }
- }
- }
-}
diff --git a/Emby.Dlna/Service/BaseService.cs b/Emby.Dlna/Service/BaseService.cs
deleted file mode 100644
index 67e7bf6a6..000000000
--- a/Emby.Dlna/Service/BaseService.cs
+++ /dev/null
@@ -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 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);
- }
- }
-}
diff --git a/Emby.Dlna/Service/ControlErrorHandler.cs b/Emby.Dlna/Service/ControlErrorHandler.cs
deleted file mode 100644
index 3e2cd6d2e..000000000
--- a/Emby.Dlna/Service/ControlErrorHandler.cs
+++ /dev/null
@@ -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("401Invalid Action");
- writer.WriteFullEndElement();
-
- writer.WriteFullEndElement();
- writer.WriteFullEndElement();
-
- writer.WriteFullEndElement();
- writer.WriteEndDocument();
- }
-
- return new ControlResponse(builder.ToString(), false);
- }
- }
-}
diff --git a/Emby.Dlna/Service/ServiceXmlBuilder.cs b/Emby.Dlna/Service/ServiceXmlBuilder.cs
deleted file mode 100644
index 6e0bc6ad8..000000000
--- a/Emby.Dlna/Service/ServiceXmlBuilder.cs
+++ /dev/null
@@ -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 actions, IEnumerable stateVariables)
- {
- var builder = new StringBuilder();
-
- builder.Append("");
- builder.Append("");
-
- builder.Append("");
- builder.Append("1");
- builder.Append("0");
- builder.Append("");
-
- AppendActionList(builder, actions);
- AppendServiceStateTable(builder, stateVariables);
-
- builder.Append("");
-
- return builder.ToString();
- }
-
- private static void AppendActionList(StringBuilder builder, IEnumerable actions)
- {
- builder.Append("");
-
- foreach (var item in actions)
- {
- builder.Append("");
-
- builder.Append("")
- .Append(SecurityElement.Escape(item.Name))
- .Append("");
-
- builder.Append("");
-
- foreach (var argument in item.ArgumentList)
- {
- builder.Append("");
-
- builder.Append("")
- .Append(SecurityElement.Escape(argument.Name))
- .Append("");
- builder.Append("")
- .Append(SecurityElement.Escape(argument.Direction))
- .Append("");
- builder.Append("")
- .Append(SecurityElement.Escape(argument.RelatedStateVariable))
- .Append("");
-
- builder.Append("");
- }
-
- builder.Append("");
-
- builder.Append("");
- }
-
- builder.Append("");
- }
-
- private static void AppendServiceStateTable(StringBuilder builder, IEnumerable stateVariables)
- {
- builder.Append("");
-
- foreach (var item in stateVariables)
- {
- var sendEvents = item.SendsEvents ? "yes" : "no";
-
- builder.Append("");
-
- builder.Append("")
- .Append(SecurityElement.Escape(item.Name))
- .Append("");
- builder.Append("")
- .Append(SecurityElement.Escape(item.DataType))
- .Append("");
-
- if (item.AllowedValues.Count > 0)
- {
- builder.Append("");
- foreach (var allowedValue in item.AllowedValues)
- {
- builder.Append("")
- .Append(SecurityElement.Escape(allowedValue))
- .Append("");
- }
-
- builder.Append("");
- }
-
- builder.Append("");
- }
-
- builder.Append("");
- }
- }
-}
diff --git a/Emby.Dlna/Ssdp/DeviceDiscovery.cs b/Emby.Dlna/Ssdp/DeviceDiscovery.cs
deleted file mode 100644
index 4fbbc3885..000000000
--- a/Emby.Dlna/Ssdp/DeviceDiscovery.cs
+++ /dev/null
@@ -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> DeviceDiscoveredInternal;
-
- ///
- public event EventHandler> DeviceDiscovered
- {
- add
- {
- lock (_syncLock)
- {
- _listenerCount++;
- DeviceDiscoveredInternal += value;
- }
-
- StartInternal();
- }
-
- remove
- {
- lock (_syncLock)
- {
- _listenerCount--;
- DeviceDiscoveredInternal -= value;
- }
- }
- }
-
- ///
- public event EventHandler> 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>>() : 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(
- 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>>() : 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(
- new UpnpDeviceInfo
- {
- Location = e.DiscoveredDevice.DescriptionLocation,
- Headers = headers
- });
-
- DeviceLeft?.Invoke(this, args);
- }
-
- ///
- public void Dispose()
- {
- if (!_disposed)
- {
- _disposed = true;
- if (_deviceLocator is not null)
- {
- _deviceLocator.Dispose();
- _deviceLocator = null;
- }
- }
- }
- }
-}
diff --git a/Emby.Dlna/Ssdp/SsdpExtensions.cs b/Emby.Dlna/Ssdp/SsdpExtensions.cs
deleted file mode 100644
index d00eb02b4..000000000
--- a/Emby.Dlna/Ssdp/SsdpExtensions.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-#pragma warning disable CS1591
-
-using System.Linq;
-using System.Xml.Linq;
-
-namespace Emby.Dlna.Ssdp
-{
- public static class SsdpExtensions
- {
- public static string? GetValue(this XElement container, XName name)
- {
- var node = container.Element(name);
-
- return node?.Value;
- }
-
- public static string? GetAttributeValue(this XElement container, XName name)
- {
- var node = container.Attribute(name);
-
- return node?.Value;
- }
-
- public static string? GetDescendantValue(this XElement container, XName name)
- => container.Descendants(name).FirstOrDefault()?.Value;
- }
-}
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index 4540ab205..895542409 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -13,7 +13,6 @@ using System.Net;
using System.Reflection;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
-using Emby.Dlna.Main;
using Emby.Naming.Common;
using Emby.Photos;
using Emby.Server.Implementations.Channels;
@@ -867,9 +866,6 @@ namespace Emby.Server.Implementations
// MediaEncoding
yield return typeof(MediaBrowser.MediaEncoding.Encoder.MediaEncoder).Assembly;
- // Dlna
- yield return typeof(DlnaHost).Assembly;
-
// Local metadata
yield return typeof(BoxSetXmlSaver).Assembly;
diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
index 905f36e43..abe387181 100644
--- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj
+++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
@@ -14,7 +14,6 @@
-
diff --git a/Emby.Server.Implementations/SystemManager.cs b/Emby.Server.Implementations/SystemManager.cs
index 2c477218f..c4552474c 100644
--- a/Emby.Server.Implementations/SystemManager.cs
+++ b/Emby.Server.Implementations/SystemManager.cs
@@ -1,4 +1,3 @@
-using System;
using System.Linq;
using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
diff --git a/Jellyfin.Server/Startup.cs b/Jellyfin.Server/Startup.cs
index 49f5bf232..aa7be9109 100644
--- a/Jellyfin.Server/Startup.cs
+++ b/Jellyfin.Server/Startup.cs
@@ -4,7 +4,6 @@ using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Mime;
using System.Text;
-using Emby.Dlna.Extensions;
using Jellyfin.Api.Middleware;
using Jellyfin.MediaEncoding.Hls.Extensions;
using Jellyfin.Networking.HappyEyeballs;
@@ -122,7 +121,6 @@ namespace Jellyfin.Server
.AddCheck>(nameof(JellyfinDbContext));
services.AddHlsPlaylistGenerator();
- services.AddDlnaServices(_serverApplicationHost);
}
///
diff --git a/Jellyfin.sln b/Jellyfin.sln
index 6f2312454..cf656bcba 100644
--- a/Jellyfin.sln
+++ b/Jellyfin.sln
@@ -25,8 +25,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emby.Server.Implementations
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RSSDP", "RSSDP\RSSDP.csproj", "{21002819-C39A-4D3E-BE83-2A276A77FB1F}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emby.Dlna", "Emby.Dlna\Emby.Dlna.csproj", "{805844AB-E92F-45E6-9D99-4F6D48D129A5}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Emby.Naming", "Emby.Naming\Emby.Naming.csproj", "{E5AF7B26-2239-4CE0-B477-0AA2018EDAA2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MediaBrowser.MediaEncoding", "MediaBrowser.MediaEncoding\MediaBrowser.MediaEncoding.csproj", "{960295EE-4AF4-4440-A525-B4C295B01A61}"
@@ -141,10 +139,6 @@ Global
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{21002819-C39A-4D3E-BE83-2A276A77FB1F}.Release|Any CPU.Build.0 = Release|Any CPU
- {805844AB-E92F-45E6-9D99-4F6D48D129A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {805844AB-E92F-45E6-9D99-4F6D48D129A5}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {805844AB-E92F-45E6-9D99-4F6D48D129A5}.Release|Any CPU.Build.0 = Release|Any CPU
{E5AF7B26-2239-4CE0-B477-0AA2018EDAA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E5AF7B26-2239-4CE0-B477-0AA2018EDAA2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E5AF7B26-2239-4CE0-B477-0AA2018EDAA2}.Release|Any CPU.ActiveCfg = Release|Any CPU