diff options
23 files changed, 779 insertions, 281 deletions
diff --git a/MediaBrowser.Api/Dlna/DlnaServerService.cs b/MediaBrowser.Api/Dlna/DlnaServerService.cs index 05d41c18b..8e4e559df 100644 --- a/MediaBrowser.Api/Dlna/DlnaServerService.cs +++ b/MediaBrowser.Api/Dlna/DlnaServerService.cs @@ -25,8 +25,23 @@ namespace MediaBrowser.Api.Dlna { } + [Route("/Dlna/connectionmanager/connectionmanager.xml", "GET", Summary = "Gets dlna connection manager xml")] + [Route("/Dlna/connectionmanager/connectionmanager", "GET", Summary = "Gets dlna connection manager xml")] + public class GetConnnectionManager + { + } + [Route("/Dlna/contentdirectory/{UuId}/control", "POST", Summary = "Processes a control request")] - public class ProcessControlRequest : IRequiresRequestStream + public class ProcessContentDirectoryControlRequest : IRequiresRequestStream + { + [ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")] + public string UuId { get; set; } + + public Stream RequestStream { get; set; } + } + + [Route("/Dlna/connectionmanager/{UuId}/control", "POST", Summary = "Processes a control request")] + public class ProcessConnectionManagerControlRequest : IRequiresRequestStream { [ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")] public string UuId { get; set; } @@ -35,6 +50,7 @@ namespace MediaBrowser.Api.Dlna } [Route("/Dlna/contentdirectory/{UuId}/events", Summary = "Processes an event subscription request")] + [Route("/Dlna/connectionmanager/{UuId}/events", Summary = "Processes an event subscription request")] public class ProcessEventRequest { [ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")] @@ -53,12 +69,14 @@ namespace MediaBrowser.Api.Dlna private readonly IDlnaManager _dlnaManager; private readonly IContentDirectory _contentDirectory; private readonly IEventManager _eventManager; + private readonly IConnectionManager _connectionManager; - public DlnaServerService(IDlnaManager dlnaManager, IContentDirectory contentDirectory, IEventManager eventManager) + public DlnaServerService(IDlnaManager dlnaManager, IContentDirectory contentDirectory, IEventManager eventManager, IConnectionManager connectionManager) { _dlnaManager = dlnaManager; _contentDirectory = contentDirectory; _eventManager = eventManager; + _connectionManager = connectionManager; } public object Get(GetDescriptionXml request) @@ -70,26 +88,40 @@ namespace MediaBrowser.Api.Dlna public object Get(GetContentDirectory request) { - var xml = _contentDirectory.GetContentDirectoryXml(GetRequestHeaders()); + var xml = _contentDirectory.GetServiceXml(GetRequestHeaders()); return ResultFactory.GetResult(xml, "text/xml"); } - public object Post(ProcessControlRequest request) + public object Get(GetConnnectionManager request) + { + var xml = _connectionManager.GetServiceXml(GetRequestHeaders()); + + return ResultFactory.GetResult(xml, "text/xml"); + } + + public object Post(ProcessContentDirectoryControlRequest request) + { + var response = PostAsync(request.RequestStream, _contentDirectory).Result; + + return ResultFactory.GetResult(response.Xml, "text/xml"); + } + + public object Post(ProcessConnectionManagerControlRequest request) { - var response = PostAsync(request).Result; + var response = PostAsync(request.RequestStream, _connectionManager).Result; return ResultFactory.GetResult(response.Xml, "text/xml"); } - private async Task<ControlResponse> PostAsync(ProcessControlRequest request) + private async Task<ControlResponse> PostAsync(Stream requestStream, IUpnpService service) { var pathInfo = PathInfo.Parse(Request.PathInfo); var id = pathInfo.GetArgumentValue<string>(2); - using (var reader = new StreamReader(request.RequestStream)) + using (var reader = new StreamReader(requestStream)) { - return _contentDirectory.ProcessControlRequest(new ControlRequest + return service.ProcessControlRequest(new ControlRequest { Headers = GetRequestHeaders(), InputXml = await reader.ReadToEndAsync().ConfigureAwait(false), diff --git a/MediaBrowser.Controller/Dlna/IConnectionManager.cs b/MediaBrowser.Controller/Dlna/IConnectionManager.cs new file mode 100644 index 000000000..942e523e8 --- /dev/null +++ b/MediaBrowser.Controller/Dlna/IConnectionManager.cs @@ -0,0 +1,7 @@ + +namespace MediaBrowser.Controller.Dlna +{ + public interface IConnectionManager : IUpnpService + { + } +} diff --git a/MediaBrowser.Controller/Dlna/IContentDirectory.cs b/MediaBrowser.Controller/Dlna/IContentDirectory.cs index e48d498df..00c236813 100644 --- a/MediaBrowser.Controller/Dlna/IContentDirectory.cs +++ b/MediaBrowser.Controller/Dlna/IContentDirectory.cs @@ -1,21 +1,7 @@ -using System.Collections.Generic; - + namespace MediaBrowser.Controller.Dlna { - public interface IContentDirectory + public interface IContentDirectory : IUpnpService { - /// <summary> - /// Gets the content directory XML. - /// </summary> - /// <param name="headers">The headers.</param> - /// <returns>System.String.</returns> - string GetContentDirectoryXml(IDictionary<string, string> headers); - - /// <summary> - /// Processes the control request. - /// </summary> - /// <param name="request">The request.</param> - /// <returns>ControlResponse.</returns> - ControlResponse ProcessControlRequest(ControlRequest request); } } diff --git a/MediaBrowser.Controller/Dlna/IUpnpService.cs b/MediaBrowser.Controller/Dlna/IUpnpService.cs new file mode 100644 index 000000000..3511740ad --- /dev/null +++ b/MediaBrowser.Controller/Dlna/IUpnpService.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; + +namespace MediaBrowser.Controller.Dlna +{ + public interface IUpnpService + { + /// <summary> + /// Gets the content directory XML. + /// </summary> + /// <param name="headers">The headers.</param> + /// <returns>System.String.</returns> + string GetServiceXml(IDictionary<string, string> headers); + + /// <summary> + /// Processes the control request. + /// </summary> + /// <param name="request">The request.</param> + /// <returns>ControlResponse.</returns> + ControlResponse ProcessControlRequest(ControlRequest request); + } +} diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index bab550e45..fa7842a68 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -92,9 +92,11 @@ <Compile Include="Dlna\ControlResponse.cs" /> <Compile Include="Dlna\DlnaIconResponse.cs" /> <Compile Include="Dlna\EventSubscriptionResponse.cs" /> + <Compile Include="Dlna\IConnectionManager.cs" /> <Compile Include="Dlna\IContentDirectory.cs" /> <Compile Include="Dlna\IDlnaManager.cs" /> <Compile Include="Dlna\IEventManager.cs" /> + <Compile Include="Dlna\IUpnpService.cs" /> <Compile Include="Drawing\IImageProcessor.cs" /> <Compile Include="Drawing\ImageFormat.cs" /> <Compile Include="Drawing\ImageProcessingOptions.cs" /> diff --git a/MediaBrowser.Dlna/ConnectionManager/ConnectionManager.cs b/MediaBrowser.Dlna/ConnectionManager/ConnectionManager.cs new file mode 100644 index 000000000..3270fca72 --- /dev/null +++ b/MediaBrowser.Dlna/ConnectionManager/ConnectionManager.cs @@ -0,0 +1,34 @@ +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Dlna; +using MediaBrowser.Model.Logging; +using System.Collections.Generic; + +namespace MediaBrowser.Dlna.ConnectionManager +{ + public class ConnectionManager : IConnectionManager + { + private readonly IDlnaManager _dlna; + private readonly ILogger _logger; + private readonly IServerConfigurationManager _config; + + public ConnectionManager(IDlnaManager dlna, ILogManager logManager, IServerConfigurationManager config) + { + _dlna = dlna; + _config = config; + _logger = logManager.GetLogger("UpnpConnectionManager"); + } + + public string GetServiceXml(IDictionary<string, string> headers) + { + return new ConnectionManagerXmlBuilder().GetXml(); + } + + public ControlResponse ProcessControlRequest(ControlRequest request) + { + var profile = _dlna.GetProfile(request.Headers) ?? + _dlna.GetDefaultProfile(); + + return new ControlHandler(_logger, profile, _config).ProcessControlRequest(request); + } + } +} diff --git a/MediaBrowser.Dlna/ConnectionManager/ConnectionManagerXmlBuilder.cs b/MediaBrowser.Dlna/ConnectionManager/ConnectionManagerXmlBuilder.cs new file mode 100644 index 000000000..4efa11159 --- /dev/null +++ b/MediaBrowser.Dlna/ConnectionManager/ConnectionManagerXmlBuilder.cs @@ -0,0 +1,106 @@ +using MediaBrowser.Dlna.Common; +using MediaBrowser.Dlna.Service; +using System.Collections.Generic; + +namespace MediaBrowser.Dlna.ConnectionManager +{ + public class ConnectionManagerXmlBuilder + { + public string GetXml() + { + return new ServiceXmlBuilder().GetXml(new ServiceActionListBuilder().GetActions(), GetStateVariables()); + } + + private IEnumerable<StateVariable> GetStateVariables() + { + var list = new List<StateVariable>(); + + list.Add(new StateVariable + { + Name = "SourceProtocolInfo", + DataType = "string", + SendsEvents = true + }); + + list.Add(new StateVariable + { + Name = "SinkProtocolInfo", + DataType = "string", + SendsEvents = true + }); + + list.Add(new StateVariable + { + Name = "CurrentConnectionIDs", + DataType = "string", + SendsEvents = true + }); + + list.Add(new StateVariable + { + Name = "A_ARG_TYPE_ConnectionStatus", + DataType = "string", + SendsEvents = false, + + AllowedValues = new List<string> + { + "OK", + "ContentFormatMismatch", + "InsufficientBandwidth", + "UnreliableChannel", + "Unknown" + } + }); + + list.Add(new StateVariable + { + Name = "A_ARG_TYPE_ConnectionManager", + DataType = "string", + SendsEvents = false + }); + + list.Add(new StateVariable + { + Name = "A_ARG_TYPE_Direction", + DataType = "string", + SendsEvents = false, + + AllowedValues = new List<string> + { + "Output", + "Input" + } + }); + + list.Add(new StateVariable + { + Name = "A_ARG_TYPE_ProtocolInfo", + DataType = "string", + SendsEvents = false + }); + + list.Add(new StateVariable + { + Name = "A_ARG_TYPE_ConnectionID", + DataType = "ui4", + SendsEvents = false + }); + + list.Add(new StateVariable + { + Name = "A_ARG_TYPE_AVTransportID", + DataType = "ui4", + SendsEvents = false + }); + + list.Add(new StateVariable + { + Name = "A_ARG_TYPE_RcsID", + DataType = "ui4", + SendsEvents = false + }); + + return list; + } + } +} diff --git a/MediaBrowser.Dlna/ConnectionManager/ControlHandler.cs b/MediaBrowser.Dlna/ConnectionManager/ControlHandler.cs new file mode 100644 index 000000000..842e9380a --- /dev/null +++ b/MediaBrowser.Dlna/ConnectionManager/ControlHandler.cs @@ -0,0 +1,30 @@ +using MediaBrowser.Common.Extensions; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Dlna.Server; +using MediaBrowser.Dlna.Service; +using MediaBrowser.Model.Dlna; +using MediaBrowser.Model.Logging; +using System.Collections.Generic; +using System.Globalization; + +namespace MediaBrowser.Dlna.ConnectionManager +{ + public class ControlHandler : BaseControlHandler + { + private readonly CultureInfo _usCulture = new CultureInfo("en-US"); + private readonly DeviceProfile _profile; + + public ControlHandler(ILogger logger, DeviceProfile profile, IServerConfigurationManager config) + : base(config, logger) + { + _profile = profile; + } + + protected override IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, Headers methodParams) + { + var deviceId = "test"; + + throw new ResourceNotFoundException("Unexpected control request name: " + methodName); + } + } +} diff --git a/MediaBrowser.Dlna/ConnectionManager/ServiceActionListBuilder.cs b/MediaBrowser.Dlna/ConnectionManager/ServiceActionListBuilder.cs new file mode 100644 index 000000000..9dbd4e0e2 --- /dev/null +++ b/MediaBrowser.Dlna/ConnectionManager/ServiceActionListBuilder.cs @@ -0,0 +1,205 @@ +using MediaBrowser.Dlna.Common; +using System.Collections.Generic; + +namespace MediaBrowser.Dlna.ConnectionManager +{ + public class ServiceActionListBuilder + { + public IEnumerable<ServiceAction> GetActions() + { + var list = new List<ServiceAction> + { + GetCurrentConnectionInfo(), + GetProtocolInfo(), + GetCurrentConnectionIDs(), + ConnectionComplete(), + PrepareForConnection() + }; + + return list; + } + + private 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; + } + + private 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; + } + + private 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; + } + + private ServiceAction GetCurrentConnectionIDs() + { + var action = new ServiceAction + { + Name = "GetCurrentConnectionIDs" + }; + + action.ArgumentList.Add(new Argument + { + Name = "ConnectionIDs", + Direction = "out", + RelatedStateVariable = "CurrentConnectionIDs" + }); + + return action; + } + + private 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/MediaBrowser.Dlna/Server/ContentDirectory.cs b/MediaBrowser.Dlna/ContentDirectory/ContentDirectory.cs index e2f53aeaf..d1b29502d 100644 --- a/MediaBrowser.Dlna/Server/ContentDirectory.cs +++ b/MediaBrowser.Dlna/ContentDirectory/ContentDirectory.cs @@ -10,7 +10,7 @@ using System; using System.Collections.Generic; using System.Linq; -namespace MediaBrowser.Dlna.Server +namespace MediaBrowser.Dlna.ContentDirectory { public class ContentDirectory : IContentDirectory, IDisposable { @@ -43,7 +43,7 @@ namespace MediaBrowser.Dlna.Server _config = config; _userManager = userManager; _eventManager = eventManager; - _logger = logManager.GetLogger("DlnaContentDirectory"); + _logger = logManager.GetLogger("UpnpContentDirectory"); } private int SystemUpdateId @@ -56,12 +56,9 @@ namespace MediaBrowser.Dlna.Server } } - public string GetContentDirectoryXml(IDictionary<string, string> headers) + public string GetServiceXml(IDictionary<string, string> headers) { - var profile = _dlna.GetProfile(headers) ?? - _dlna.GetDefaultProfile(); - - return new ContentDirectoryXmlBuilder(profile).GetXml(); + return new ContentDirectoryXmlBuilder().GetXml(); } public ControlResponse ProcessControlRequest(ControlRequest request) diff --git a/MediaBrowser.Dlna/Server/ContentDirectoryXmlBuilder.cs b/MediaBrowser.Dlna/ContentDirectory/ContentDirectoryXmlBuilder.cs index 58ccf49bd..0e5a2671c 100644 --- a/MediaBrowser.Dlna/Server/ContentDirectoryXmlBuilder.cs +++ b/MediaBrowser.Dlna/ContentDirectory/ContentDirectoryXmlBuilder.cs @@ -1,98 +1,15 @@ using MediaBrowser.Dlna.Common; -using MediaBrowser.Model.Dlna; +using MediaBrowser.Dlna.Service; using System.Collections.Generic; -using System.Security; -using System.Text; -namespace MediaBrowser.Dlna.Server +namespace MediaBrowser.Dlna.ContentDirectory { public class ContentDirectoryXmlBuilder { - private readonly DeviceProfile _profile; - - public ContentDirectoryXmlBuilder(DeviceProfile profile) - { - _profile = profile; - } - public string GetXml() { - var builder = new StringBuilder(); - - builder.Append("<?xml version=\"1.0\"?>"); - builder.Append("<scpd xmlns=\"urn:schemas-upnp-org:service-1-0\">"); - - builder.Append("<specVersion>"); - builder.Append("<major>1</major>"); - builder.Append("<minor>0</minor>"); - builder.Append("</specVersion>"); - - AppendActionList(builder); - AppendServiceStateTable(builder); - - builder.Append("</scpd>"); - - return builder.ToString(); - } - - private void AppendActionList(StringBuilder builder) - { - builder.Append("<actionList>"); - - foreach (var item in new ServiceActionListBuilder().GetActions()) - { - builder.Append("<action>"); - - builder.Append("<name>" + SecurityElement.Escape(item.Name ?? string.Empty) + "</name>"); - - builder.Append("<argumentList>"); - - foreach (var argument in item.ArgumentList) - { - builder.Append("<argument>"); - - builder.Append("<name>" + SecurityElement.Escape(argument.Name ?? string.Empty) + "</name>"); - builder.Append("<direction>" + SecurityElement.Escape(argument.Direction ?? string.Empty) + "</direction>"); - builder.Append("<relatedStateVariable>" + SecurityElement.Escape(argument.RelatedStateVariable ?? string.Empty) + "</relatedStateVariable>"); - - builder.Append("</argument>"); - } - - builder.Append("</argumentList>"); - - builder.Append("</action>"); - } - - builder.Append("</actionList>"); - } - - private void AppendServiceStateTable(StringBuilder builder) - { - builder.Append("<serviceStateTable>"); - - foreach (var item in GetStateVariables()) - { - var sendEvents = item.SendsEvents ? "yes" : "no"; - - builder.Append("<stateVariable sendEvents=\"" + sendEvents + "\">"); - - builder.Append("<name>" + SecurityElement.Escape(item.Name ?? string.Empty) + "</name>"); - builder.Append("<dataType>" + SecurityElement.Escape(item.DataType ?? string.Empty) + "</dataType>"); - - if (item.AllowedValues.Count > 0) - { - builder.Append("<allowedValueList>"); - foreach (var allowedValue in item.AllowedValues) - { - builder.Append("<allowedValue>" + SecurityElement.Escape(allowedValue) + "</allowedValue>"); - } - builder.Append("</allowedValueList>"); - } - - builder.Append("</stateVariable>"); - } - - builder.Append("</serviceStateTable>"); + return new ServiceXmlBuilder().GetXml(new ServiceActionListBuilder().GetActions(), + GetStateVariables()); } private IEnumerable<StateVariable> GetStateVariables() @@ -223,13 +140,8 @@ namespace MediaBrowser.Dlna.Server DataType = "string", SendsEvents = false }); - - return list; - } - public override string ToString() - { - return GetXml(); + return list; } } } diff --git a/MediaBrowser.Dlna/Server/ControlHandler.cs b/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs index a3eb7f055..0e4f0d8f9 100644 --- a/MediaBrowser.Dlna/Server/ControlHandler.cs +++ b/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs @@ -1,15 +1,16 @@ using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Drawing; +using MediaBrowser.Controller.Dto; +using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Dlna.Didl; +using MediaBrowser.Dlna.Server; +using MediaBrowser.Dlna.Service; using MediaBrowser.Model.Dlna; -using MediaBrowser.Controller.Dto; -using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Querying; @@ -21,20 +22,17 @@ using System.Text; using System.Threading; using System.Xml; -namespace MediaBrowser.Dlna.Server +namespace MediaBrowser.Dlna.ContentDirectory { - public class ControlHandler + public class ControlHandler : BaseControlHandler { - private readonly ILogger _logger; private readonly ILibraryManager _libraryManager; private readonly IUserDataManager _userDataManager; - private readonly IServerConfigurationManager _config; private readonly User _user; private const string NS_DC = "http://purl.org/dc/elements/1.1/"; private const string NS_DIDL = "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"; private const string NS_DLNA = "urn:schemas-dlna-org:metadata-1-0/"; - private const string NS_SOAPENV = "http://schemas.xmlsoap.org/soap/envelope/"; private const string NS_UPNP = "urn:schemas-upnp-org:metadata-1-0/upnp/"; private readonly int _systemUpdateId; @@ -45,151 +43,45 @@ namespace MediaBrowser.Dlna.Server private readonly DeviceProfile _profile; public ControlHandler(ILogger logger, ILibraryManager libraryManager, DeviceProfile profile, string serverAddress, IDtoService dtoService, IImageProcessor imageProcessor, IUserDataManager userDataManager, User user, int systemUpdateId, IServerConfigurationManager config) + : base(config, logger) { - _logger = logger; _libraryManager = libraryManager; _userDataManager = userDataManager; _user = user; _systemUpdateId = systemUpdateId; - _config = config; _profile = profile; _didlBuilder = new DidlBuilder(profile, imageProcessor, serverAddress, dtoService); } - public ControlResponse ProcessControlRequest(ControlRequest request) - { - try - { - if (_config.Configuration.DlnaOptions.EnableDebugLogging) - { - LogRequest(request); - } - - return ProcessControlRequestInternal(request); - } - catch (Exception ex) - { - _logger.ErrorException("Error processing control request", ex); - - return GetErrorResponse(ex); - } - } - - private void LogRequest(ControlRequest request) - { - var builder = new StringBuilder(); - - var headers = string.Join(", ", request.Headers.Select(i => string.Format("{0}={1}", i.Key, i.Value)).ToArray()); - builder.AppendFormat("Headers: {0}", headers); - builder.AppendLine(); - builder.Append(request.InputXml); - - _logger.LogMultiline("Control request", LogSeverity.Debug, builder); - } - - private ControlResponse ProcessControlRequestInternal(ControlRequest request) + protected override IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, Headers methodParams) { - var soap = new XmlDocument(); - soap.LoadXml(request.InputXml); - var sparams = new Headers(); - var body = soap.GetElementsByTagName("Body", NS_SOAPENV).Item(0); - - var method = body.FirstChild; - - foreach (var p in method.ChildNodes) - { - var e = p as XmlElement; - if (e == null) - { - continue; - } - sparams.Add(e.LocalName, e.InnerText.Trim()); - } - var deviceId = "test"; - IEnumerable<KeyValuePair<string, string>> result; - - _logger.Debug("Received control request {0}", method.Name); - var user = _user; - if (string.Equals(method.LocalName, "GetSearchCapabilities", StringComparison.OrdinalIgnoreCase)) - result = HandleGetSearchCapabilities(); - else if (string.Equals(method.LocalName, "GetSortCapabilities", StringComparison.OrdinalIgnoreCase)) - result = HandleGetSortCapabilities(); - else if (string.Equals(method.LocalName, "GetSystemUpdateID", StringComparison.OrdinalIgnoreCase)) - result = HandleGetSystemUpdateID(); - else if (string.Equals(method.LocalName, "Browse", StringComparison.OrdinalIgnoreCase)) - result = HandleBrowse(sparams, user, deviceId); - else if (string.Equals(method.LocalName, "X_GetFeatureList", StringComparison.OrdinalIgnoreCase)) - result = HandleXGetFeatureList(); - else if (string.Equals(method.LocalName, "X_SetBookmark", StringComparison.OrdinalIgnoreCase)) - result = HandleXSetBookmark(sparams, user); - else if (string.Equals(method.LocalName, "Search", StringComparison.OrdinalIgnoreCase)) - result = HandleSearch(sparams, user, deviceId); - else - throw new ResourceNotFoundException("Unexpected control request name: " + method.LocalName); + if (string.Equals(methodName, "GetSearchCapabilities", StringComparison.OrdinalIgnoreCase)) + return HandleGetSearchCapabilities(); - var env = new XmlDocument(); - env.AppendChild(env.CreateXmlDeclaration("1.0", "utf-8", string.Empty)); - var envelope = env.CreateElement("SOAP-ENV", "Envelope", NS_SOAPENV); - env.AppendChild(envelope); - envelope.SetAttribute("encodingStyle", NS_SOAPENV, "http://schemas.xmlsoap.org/soap/encoding/"); + if (string.Equals(methodName, "GetSortCapabilities", StringComparison.OrdinalIgnoreCase)) + return HandleGetSortCapabilities(); - var rbody = env.CreateElement("SOAP-ENV:Body", NS_SOAPENV); - env.DocumentElement.AppendChild(rbody); + if (string.Equals(methodName, "GetSystemUpdateID", StringComparison.OrdinalIgnoreCase)) + return HandleGetSystemUpdateID(); - var response = env.CreateElement(String.Format("u:{0}Response", method.LocalName), method.NamespaceURI); - rbody.AppendChild(response); + if (string.Equals(methodName, "Browse", StringComparison.OrdinalIgnoreCase)) + return HandleBrowse(methodParams, user, deviceId); - foreach (var i in result) - { - var ri = env.CreateElement(i.Key); - ri.InnerText = i.Value; - response.AppendChild(ri); - } - - var controlResponse = new ControlResponse - { - Xml = env.OuterXml, - IsSuccessful = true - }; + if (string.Equals(methodName, "X_GetFeatureList", StringComparison.OrdinalIgnoreCase)) + return HandleXGetFeatureList(); - controlResponse.Headers.Add("EXT", string.Empty); + if (string.Equals(methodName, "X_SetBookmark", StringComparison.OrdinalIgnoreCase)) + return HandleXSetBookmark(methodParams, user); - return controlResponse; - } + if (string.Equals(methodName, "Search", StringComparison.OrdinalIgnoreCase)) + return HandleSearch(methodParams, user, deviceId); - private ControlResponse GetErrorResponse(Exception ex) - { - var env = new XmlDocument(); - env.AppendChild(env.CreateXmlDeclaration("1.0", "utf-8", "yes")); - var envelope = env.CreateElement("SOAP-ENV", "Envelope", NS_SOAPENV); - env.AppendChild(envelope); - envelope.SetAttribute("encodingStyle", NS_SOAPENV, "http://schemas.xmlsoap.org/soap/encoding/"); - - var rbody = env.CreateElement("SOAP-ENV:Body", NS_SOAPENV); - env.DocumentElement.AppendChild(rbody); - - var fault = env.CreateElement("SOAP-ENV", "Fault", NS_SOAPENV); - var faultCode = env.CreateElement("faultcode"); - faultCode.InnerText = "500"; - fault.AppendChild(faultCode); - var faultString = env.CreateElement("faultstring"); - faultString.InnerText = ex.ToString(); - fault.AppendChild(faultString); - var detail = env.CreateDocumentFragment(); - detail.InnerXml = "<detail><UPnPError xmlns=\"urn:schemas-upnp-org:control-1-0\"><errorCode>401</errorCode><errorDescription>Invalid Action</errorDescription></UPnPError></detail>"; - fault.AppendChild(detail); - rbody.AppendChild(fault); - - return new ControlResponse - { - Xml = env.OuterXml, - IsSuccessful = false - }; + throw new ResourceNotFoundException("Unexpected control request name: " + methodName); } private IEnumerable<KeyValuePair<string, string>> HandleXSetBookmark(IDictionary<string, string> sparams, User user) @@ -224,7 +116,7 @@ namespace MediaBrowser.Dlna.Server { var headers = new Headers(true); headers.Add("Id", _systemUpdateId.ToString(_usCulture)); - return headers; + return headers; } private IEnumerable<KeyValuePair<string, string>> HandleXGetFeatureList() diff --git a/MediaBrowser.Dlna/Server/ServiceActionListBuilder.cs b/MediaBrowser.Dlna/ContentDirectory/ServiceActionListBuilder.cs index 9ccfaf603..cd8119048 100644 --- a/MediaBrowser.Dlna/Server/ServiceActionListBuilder.cs +++ b/MediaBrowser.Dlna/ContentDirectory/ServiceActionListBuilder.cs @@ -1,7 +1,7 @@ using MediaBrowser.Dlna.Common; using System.Collections.Generic; -namespace MediaBrowser.Dlna.Server +namespace MediaBrowser.Dlna.ContentDirectory { public class ServiceActionListBuilder { diff --git a/MediaBrowser.Dlna/Didl/DidlBuilder.cs b/MediaBrowser.Dlna/Didl/DidlBuilder.cs index 1cd4a4cbd..c60b3a49a 100644 --- a/MediaBrowser.Dlna/Didl/DidlBuilder.cs +++ b/MediaBrowser.Dlna/Didl/DidlBuilder.cs @@ -345,7 +345,9 @@ namespace MediaBrowser.Dlna.Didl /// <param name="filter">The filter.</param> private void AddCommonFields(BaseItem item, XmlElement element, Filter filter) { - if (filter.Contains("dc:title")) + // 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(element, "dc", "title", item.Name, NS_DC); } @@ -360,9 +362,12 @@ namespace MediaBrowser.Dlna.Didl } } - foreach (var genre in item.Genres) + if (filter.Contains("upnp:genre")) { - AddValue(element, "upnp", "genre", genre, NS_UPNP); + foreach (var genre in item.Genres) + { + AddValue(element, "upnp", "genre", genre, NS_UPNP); + } } foreach (var studio in item.Studios) diff --git a/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj b/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj index d39bcbefe..caef50d36 100644 --- a/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj +++ b/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj @@ -51,6 +51,10 @@ <Compile Include="..\SharedVersion.cs"> <Link>Properties\SharedVersion.cs</Link> </Compile> + <Compile Include="ConnectionManager\ConnectionManager.cs" /> + <Compile Include="ConnectionManager\ConnectionManagerXmlBuilder.cs" /> + <Compile Include="ConnectionManager\ControlHandler.cs" /> + <Compile Include="ConnectionManager\ServiceActionListBuilder.cs" /> <Compile Include="DlnaManager.cs" /> <Compile Include="Common\Argument.cs" /> <Compile Include="Eventing\EventManager.cs" /> @@ -79,10 +83,13 @@ <Compile Include="Profiles\Windows81Profile.cs" /> <Compile Include="Profiles\WindowsMediaCenterProfile.cs" /> <Compile Include="Profiles\WindowsPhoneProfile.cs" /> - <Compile Include="Server\ContentDirectory.cs" /> - <Compile Include="Server\ControlHandler.cs" /> - <Compile Include="Server\ServiceActionListBuilder.cs" /> - <Compile Include="Server\ContentDirectoryXmlBuilder.cs" /> + <Compile Include="ContentDirectory\ContentDirectory.cs" /> + <Compile Include="ContentDirectory\ControlHandler.cs" /> + <Compile Include="ContentDirectory\ServiceActionListBuilder.cs" /> + <Compile Include="ContentDirectory\ContentDirectoryXmlBuilder.cs" /> + <Compile Include="Service\BaseControlHandler.cs" /> + <Compile Include="Service\ControlErrorHandler.cs" /> + <Compile Include="Service\ServiceXmlBuilder.cs" /> <Compile Include="Ssdp\Datagram.cs" /> <Compile Include="Server\DescriptionXmlBuilder.cs" /> <Compile Include="Ssdp\SsdpHelper.cs" /> @@ -159,6 +166,7 @@ <EmbeddedResource Include="Images\logo48.jpg" /> <EmbeddedResource Include="Images\logo48.png" /> </ItemGroup> + <ItemGroup /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <!-- To modify your build process, add your task inside one of the targets below and uncomment it. Other similar extension points exist, see Microsoft.Common.targets. diff --git a/MediaBrowser.Dlna/Server/DescriptionXmlBuilder.cs b/MediaBrowser.Dlna/Server/DescriptionXmlBuilder.cs index 446f975f3..885c66474 100644 --- a/MediaBrowser.Dlna/Server/DescriptionXmlBuilder.cs +++ b/MediaBrowser.Dlna/Server/DescriptionXmlBuilder.cs @@ -185,6 +185,15 @@ namespace MediaBrowser.Dlna.Server EventSubUrl = "/mediabrowser/dlna/contentdirectory/" + _serverUdn + "/events" }); + list.Add(new DeviceService + { + ServiceType = "urn:schemas-upnp-org:service:ConnectionManager:1", + ServiceId = "urn:upnp-org:serviceId:ConnectionManager", + ScpdUrl = "/mediabrowser/dlna/connectionmanager/connectionmanager.xml", + ControlUrl = "/mediabrowser/dlna/connectionmanager/" + _serverUdn + "/control", + EventSubUrl = "/mediabrowser/dlna/connectionmanager/" + _serverUdn + "/events" + }); + return list; } diff --git a/MediaBrowser.Dlna/Service/BaseControlHandler.cs b/MediaBrowser.Dlna/Service/BaseControlHandler.cs new file mode 100644 index 000000000..2185d51a5 --- /dev/null +++ b/MediaBrowser.Dlna/Service/BaseControlHandler.cs @@ -0,0 +1,112 @@ +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Dlna; +using MediaBrowser.Dlna.Server; +using MediaBrowser.Model.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml; + +namespace MediaBrowser.Dlna.Service +{ + public abstract class BaseControlHandler + { + private const string NS_SOAPENV = "http://schemas.xmlsoap.org/soap/envelope/"; + + protected readonly IServerConfigurationManager Config; + protected readonly ILogger Logger; + + protected BaseControlHandler(IServerConfigurationManager config, ILogger logger) + { + Config = config; + Logger = logger; + } + + public ControlResponse ProcessControlRequest(ControlRequest request) + { + try + { + if (Config.Configuration.DlnaOptions.EnableDebugLogging) + { + LogRequest(request); + } + + return ProcessControlRequestInternal(request); + } + catch (Exception ex) + { + Logger.ErrorException("Error processing control request", ex); + + return new ControlErrorHandler().GetResponse(ex); + } + } + + private ControlResponse ProcessControlRequestInternal(ControlRequest request) + { + var soap = new XmlDocument(); + soap.LoadXml(request.InputXml); + var sparams = new Headers(); + var body = soap.GetElementsByTagName("Body", NS_SOAPENV).Item(0); + + var method = body.FirstChild; + + foreach (var p in method.ChildNodes) + { + var e = p as XmlElement; + if (e == null) + { + continue; + } + sparams.Add(e.LocalName, e.InnerText.Trim()); + } + + Logger.Debug("Received control request {0}", method.LocalName); + + IEnumerable<KeyValuePair<string, string>> result = GetResult(method.LocalName, sparams); + + var env = new XmlDocument(); + env.AppendChild(env.CreateXmlDeclaration("1.0", "utf-8", string.Empty)); + var envelope = env.CreateElement("SOAP-ENV", "Envelope", NS_SOAPENV); + env.AppendChild(envelope); + envelope.SetAttribute("encodingStyle", NS_SOAPENV, "http://schemas.xmlsoap.org/soap/encoding/"); + + var rbody = env.CreateElement("SOAP-ENV:Body", NS_SOAPENV); + env.DocumentElement.AppendChild(rbody); + + var response = env.CreateElement(String.Format("u:{0}Response", method.LocalName), method.NamespaceURI); + rbody.AppendChild(response); + + foreach (var i in result) + { + var ri = env.CreateElement(i.Key); + ri.InnerText = i.Value; + response.AppendChild(ri); + } + + var controlResponse = new ControlResponse + { + Xml = env.OuterXml, + IsSuccessful = true + }; + + controlResponse.Headers.Add("EXT", string.Empty); + + return controlResponse; + } + + protected abstract IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, Headers methodParams); + + private void LogRequest(ControlRequest request) + { + var builder = new StringBuilder(); + + var headers = string.Join(", ", request.Headers.Select(i => string.Format("{0}={1}", i.Key, i.Value)).ToArray()); + builder.AppendFormat("Headers: {0}", headers); + builder.AppendLine(); + builder.Append(request.InputXml); + + Logger.LogMultiline("Control request", LogSeverity.Debug, builder); + } + } +} diff --git a/MediaBrowser.Dlna/Service/ControlErrorHandler.cs b/MediaBrowser.Dlna/Service/ControlErrorHandler.cs new file mode 100644 index 000000000..42b1fcbc9 --- /dev/null +++ b/MediaBrowser.Dlna/Service/ControlErrorHandler.cs @@ -0,0 +1,41 @@ +using MediaBrowser.Controller.Dlna; +using System; +using System.Xml; + +namespace MediaBrowser.Dlna.Service +{ + public class ControlErrorHandler + { + private const string NS_SOAPENV = "http://schemas.xmlsoap.org/soap/envelope/"; + + public ControlResponse GetResponse(Exception ex) + { + var env = new XmlDocument(); + env.AppendChild(env.CreateXmlDeclaration("1.0", "utf-8", "yes")); + var envelope = env.CreateElement("SOAP-ENV", "Envelope", NS_SOAPENV); + env.AppendChild(envelope); + envelope.SetAttribute("encodingStyle", NS_SOAPENV, "http://schemas.xmlsoap.org/soap/encoding/"); + + var rbody = env.CreateElement("SOAP-ENV:Body", NS_SOAPENV); + env.DocumentElement.AppendChild(rbody); + + var fault = env.CreateElement("SOAP-ENV", "Fault", NS_SOAPENV); + var faultCode = env.CreateElement("faultcode"); + faultCode.InnerText = "500"; + fault.AppendChild(faultCode); + var faultString = env.CreateElement("faultstring"); + faultString.InnerText = ex.ToString(); + fault.AppendChild(faultString); + var detail = env.CreateDocumentFragment(); + detail.InnerXml = "<detail><UPnPError xmlns=\"urn:schemas-upnp-org:control-1-0\"><errorCode>401</errorCode><errorDescription>Invalid Action</errorDescription></UPnPError></detail>"; + fault.AppendChild(detail); + rbody.AppendChild(fault); + + return new ControlResponse + { + Xml = env.OuterXml, + IsSuccessful = false + }; + } + } +} diff --git a/MediaBrowser.Dlna/Service/ServiceXmlBuilder.cs b/MediaBrowser.Dlna/Service/ServiceXmlBuilder.cs new file mode 100644 index 000000000..fae604f85 --- /dev/null +++ b/MediaBrowser.Dlna/Service/ServiceXmlBuilder.cs @@ -0,0 +1,90 @@ +using MediaBrowser.Dlna.Common; +using System.Collections.Generic; +using System.Security; +using System.Text; + +namespace MediaBrowser.Dlna.Service +{ + public class ServiceXmlBuilder + { + public string GetXml(IEnumerable<ServiceAction> actions, IEnumerable<StateVariable> stateVariables) + { + var builder = new StringBuilder(); + + builder.Append("<?xml version=\"1.0\"?>"); + builder.Append("<scpd xmlns=\"urn:schemas-upnp-org:service-1-0\">"); + + builder.Append("<specVersion>"); + builder.Append("<major>1</major>"); + builder.Append("<minor>0</minor>"); + builder.Append("</specVersion>"); + + AppendActionList(builder, actions); + AppendServiceStateTable(builder, stateVariables); + + builder.Append("</scpd>"); + + return builder.ToString(); + } + + private void AppendActionList(StringBuilder builder, IEnumerable<ServiceAction> actions) + { + builder.Append("<actionList>"); + + foreach (var item in actions) + { + builder.Append("<action>"); + + builder.Append("<name>" + SecurityElement.Escape(item.Name ?? string.Empty) + "</name>"); + + builder.Append("<argumentList>"); + + foreach (var argument in item.ArgumentList) + { + builder.Append("<argument>"); + + builder.Append("<name>" + SecurityElement.Escape(argument.Name ?? string.Empty) + "</name>"); + builder.Append("<direction>" + SecurityElement.Escape(argument.Direction ?? string.Empty) + "</direction>"); + builder.Append("<relatedStateVariable>" + SecurityElement.Escape(argument.RelatedStateVariable ?? string.Empty) + "</relatedStateVariable>"); + + builder.Append("</argument>"); + } + + builder.Append("</argumentList>"); + + builder.Append("</action>"); + } + + builder.Append("</actionList>"); + } + + private void AppendServiceStateTable(StringBuilder builder, IEnumerable<StateVariable> stateVariables) + { + builder.Append("<serviceStateTable>"); + + foreach (var item in stateVariables) + { + var sendEvents = item.SendsEvents ? "yes" : "no"; + + builder.Append("<stateVariable sendEvents=\"" + sendEvents + "\">"); + + builder.Append("<name>" + SecurityElement.Escape(item.Name ?? string.Empty) + "</name>"); + builder.Append("<dataType>" + SecurityElement.Escape(item.DataType ?? string.Empty) + "</dataType>"); + + if (item.AllowedValues.Count > 0) + { + builder.Append("<allowedValueList>"); + foreach (var allowedValue in item.AllowedValues) + { + builder.Append("<allowedValue>" + SecurityElement.Escape(allowedValue) + "</allowedValue>"); + } + builder.Append("</allowedValueList>"); + } + + builder.Append("</stateVariable>"); + } + + builder.Append("</serviceStateTable>"); + } + } +} diff --git a/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs b/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs index 2b2c3d291..06408d525 100644 --- a/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs +++ b/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs @@ -267,7 +267,7 @@ namespace MediaBrowser.Server.Implementations.Channels { providerStartIndex = query.StartIndex; - if (!query.Limit.HasValue || query.Limit.Value > channelInfo.MaxPageSize.Value) + if (query.Limit.HasValue && query.Limit.Value > channelInfo.MaxPageSize.Value) { throw new ArgumentException(string.Format("Channel {0} only supports a maximum of {1} records at a time.", channel.Name, channelInfo.MaxPageSize.Value)); } diff --git a/MediaBrowser.Server.Implementations/Session/HttpSessionController.cs b/MediaBrowser.Server.Implementations/Session/HttpSessionController.cs index 0b0f21e2c..110f66476 100644 --- a/MediaBrowser.Server.Implementations/Session/HttpSessionController.cs +++ b/MediaBrowser.Server.Implementations/Session/HttpSessionController.cs @@ -63,17 +63,18 @@ namespace MediaBrowser.Server.Implementations.Session private Task SendMessage(string name, CancellationToken cancellationToken) { - return SendMessage(name, new NameValueCollection(), cancellationToken); + return SendMessage(name, new Dictionary<string, string>(), cancellationToken); } - private Task SendMessage(string name, NameValueCollection args, CancellationToken cancellationToken) + private Task SendMessage(string name, Dictionary<string, string> args, CancellationToken cancellationToken) { - return SendMessage(new WebSocketMessage<string> - { - MessageType = name, - Data = string.Empty + var url = _postUrl + "/" + name + ToQueryString(args); - }, cancellationToken); + return _httpClient.Post(new HttpRequestOptions + { + Url = url, + CancellationToken = cancellationToken + }); } public Task SendSessionEndedNotification(SessionInfoDto sessionInfo, CancellationToken cancellationToken) @@ -141,12 +142,7 @@ namespace MediaBrowser.Server.Implementations.Session public Task SendGeneralCommand(GeneralCommand command, CancellationToken cancellationToken) { - return SendMessage(new WebSocketMessage<GeneralCommand> - { - MessageType = "GeneralCommand", - Data = command - - }, cancellationToken); + return SendMessage(command.Name, command.Arguments, cancellationToken); } private string ToQueryString(Dictionary<string, string> nvc) @@ -154,7 +150,15 @@ namespace MediaBrowser.Server.Implementations.Session var array = (from item in nvc select string.Format("{0}={1}", WebUtility.UrlEncode(item.Key), WebUtility.UrlEncode(item.Value))) .ToArray(); - return "?" + string.Join("&", array); + + var args = string.Join("&", array); + + if (string.IsNullOrEmpty(args)) + { + return args; + } + + return "?" + args; } } } diff --git a/MediaBrowser.Server.Implementations/WebSocket/AlchemyServer.cs b/MediaBrowser.Server.Implementations/WebSocket/AlchemyServer.cs index e46dab23e..454dff4b9 100644 --- a/MediaBrowser.Server.Implementations/WebSocket/AlchemyServer.cs +++ b/MediaBrowser.Server.Implementations/WebSocket/AlchemyServer.cs @@ -154,9 +154,9 @@ namespace MediaBrowser.Server.Implementations.WebSocket { if (WebSocketServer != null) { - // Calling dispose will also call stop _logger.Debug("Disposing alchemy server"); - WebSocketServer.Stop(); + + WebSocketServer.Dispose(); WebSocketServer = null; } } diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs index 7ccd92f04..dcb69d49d 100644 --- a/MediaBrowser.ServerApplication/ApplicationHost.cs +++ b/MediaBrowser.ServerApplication/ApplicationHost.cs @@ -35,6 +35,8 @@ using MediaBrowser.Controller.Sorting; using MediaBrowser.Controller.Subtitles; using MediaBrowser.Controller.Themes; using MediaBrowser.Dlna; +using MediaBrowser.Dlna.ConnectionManager; +using MediaBrowser.Dlna.ContentDirectory; using MediaBrowser.Dlna.Eventing; using MediaBrowser.Dlna.Main; using MediaBrowser.Dlna.Server; @@ -526,6 +528,9 @@ namespace MediaBrowser.ServerApplication var contentDirectory = new ContentDirectory(dlnaManager, UserDataManager, ImageProcessor, DtoService, LibraryManager, LogManager, ServerConfigurationManager, UserManager, dlnaEventManager); RegisterSingleInstance<IContentDirectory>(contentDirectory); + var connectionManager = new ConnectionManager(dlnaManager, LogManager, ServerConfigurationManager); + RegisterSingleInstance<IConnectionManager>(connectionManager); + var collectionManager = new CollectionManager(LibraryManager, FileSystemManager, LibraryMonitor); RegisterSingleInstance<ICollectionManager>(collectionManager); |
