added upnp ConnectionManager.cs

This commit is contained in:
Luke Pulverenti 2014-05-20 20:56:24 -04:00
parent ad3c30c145
commit 1774e5b1ac
23 changed files with 779 additions and 281 deletions

View File

@ -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")] [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")] [ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")]
public string UuId { get; set; } 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/contentdirectory/{UuId}/events", Summary = "Processes an event subscription request")]
[Route("/Dlna/connectionmanager/{UuId}/events", Summary = "Processes an event subscription request")]
public class ProcessEventRequest public class ProcessEventRequest
{ {
[ApiMember(Name = "UuId", Description = "Server UuId", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")] [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 IDlnaManager _dlnaManager;
private readonly IContentDirectory _contentDirectory; private readonly IContentDirectory _contentDirectory;
private readonly IEventManager _eventManager; 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; _dlnaManager = dlnaManager;
_contentDirectory = contentDirectory; _contentDirectory = contentDirectory;
_eventManager = eventManager; _eventManager = eventManager;
_connectionManager = connectionManager;
} }
public object Get(GetDescriptionXml request) public object Get(GetDescriptionXml request)
@ -70,26 +88,40 @@ namespace MediaBrowser.Api.Dlna
public object Get(GetContentDirectory request) public object Get(GetContentDirectory request)
{ {
var xml = _contentDirectory.GetContentDirectoryXml(GetRequestHeaders()); var xml = _contentDirectory.GetServiceXml(GetRequestHeaders());
return ResultFactory.GetResult(xml, "text/xml"); return ResultFactory.GetResult(xml, "text/xml");
} }
public object Post(ProcessControlRequest request) public object Get(GetConnnectionManager request)
{ {
var response = PostAsync(request).Result; 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"); return ResultFactory.GetResult(response.Xml, "text/xml");
} }
private async Task<ControlResponse> PostAsync(ProcessControlRequest request) public object Post(ProcessConnectionManagerControlRequest request)
{
var response = PostAsync(request.RequestStream, _connectionManager).Result;
return ResultFactory.GetResult(response.Xml, "text/xml");
}
private async Task<ControlResponse> PostAsync(Stream requestStream, IUpnpService service)
{ {
var pathInfo = PathInfo.Parse(Request.PathInfo); var pathInfo = PathInfo.Parse(Request.PathInfo);
var id = pathInfo.GetArgumentValue<string>(2); 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(), Headers = GetRequestHeaders(),
InputXml = await reader.ReadToEndAsync().ConfigureAwait(false), InputXml = await reader.ReadToEndAsync().ConfigureAwait(false),

View File

@ -0,0 +1,7 @@

namespace MediaBrowser.Controller.Dlna
{
public interface IConnectionManager : IUpnpService
{
}
}

View File

@ -1,21 +1,7 @@
using System.Collections.Generic; 
namespace MediaBrowser.Controller.Dlna 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);
} }
} }

View File

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

View File

@ -92,9 +92,11 @@
<Compile Include="Dlna\ControlResponse.cs" /> <Compile Include="Dlna\ControlResponse.cs" />
<Compile Include="Dlna\DlnaIconResponse.cs" /> <Compile Include="Dlna\DlnaIconResponse.cs" />
<Compile Include="Dlna\EventSubscriptionResponse.cs" /> <Compile Include="Dlna\EventSubscriptionResponse.cs" />
<Compile Include="Dlna\IConnectionManager.cs" />
<Compile Include="Dlna\IContentDirectory.cs" /> <Compile Include="Dlna\IContentDirectory.cs" />
<Compile Include="Dlna\IDlnaManager.cs" /> <Compile Include="Dlna\IDlnaManager.cs" />
<Compile Include="Dlna\IEventManager.cs" /> <Compile Include="Dlna\IEventManager.cs" />
<Compile Include="Dlna\IUpnpService.cs" />
<Compile Include="Drawing\IImageProcessor.cs" /> <Compile Include="Drawing\IImageProcessor.cs" />
<Compile Include="Drawing\ImageFormat.cs" /> <Compile Include="Drawing\ImageFormat.cs" />
<Compile Include="Drawing\ImageProcessingOptions.cs" /> <Compile Include="Drawing\ImageProcessingOptions.cs" />

View File

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

View File

@ -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;
}
}
}

View File

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

View File

@ -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;
}
}
}

View File

@ -10,7 +10,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
namespace MediaBrowser.Dlna.Server namespace MediaBrowser.Dlna.ContentDirectory
{ {
public class ContentDirectory : IContentDirectory, IDisposable public class ContentDirectory : IContentDirectory, IDisposable
{ {
@ -43,7 +43,7 @@ namespace MediaBrowser.Dlna.Server
_config = config; _config = config;
_userManager = userManager; _userManager = userManager;
_eventManager = eventManager; _eventManager = eventManager;
_logger = logManager.GetLogger("DlnaContentDirectory"); _logger = logManager.GetLogger("UpnpContentDirectory");
} }
private int SystemUpdateId 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) ?? return new ContentDirectoryXmlBuilder().GetXml();
_dlna.GetDefaultProfile();
return new ContentDirectoryXmlBuilder(profile).GetXml();
} }
public ControlResponse ProcessControlRequest(ControlRequest request) public ControlResponse ProcessControlRequest(ControlRequest request)

View File

@ -1,98 +1,15 @@
using MediaBrowser.Dlna.Common; using MediaBrowser.Dlna.Common;
using MediaBrowser.Model.Dlna; using MediaBrowser.Dlna.Service;
using System.Collections.Generic; using System.Collections.Generic;
using System.Security;
using System.Text;
namespace MediaBrowser.Dlna.Server namespace MediaBrowser.Dlna.ContentDirectory
{ {
public class ContentDirectoryXmlBuilder public class ContentDirectoryXmlBuilder
{ {
private readonly DeviceProfile _profile;
public ContentDirectoryXmlBuilder(DeviceProfile profile)
{
_profile = profile;
}
public string GetXml() public string GetXml()
{ {
var builder = new StringBuilder(); return new ServiceXmlBuilder().GetXml(new ServiceActionListBuilder().GetActions(),
GetStateVariables());
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>");
} }
private IEnumerable<StateVariable> GetStateVariables() private IEnumerable<StateVariable> GetStateVariables()
@ -223,13 +140,8 @@ namespace MediaBrowser.Dlna.Server
DataType = "string", DataType = "string",
SendsEvents = false SendsEvents = false
}); });
return list;
}
public override string ToString() return list;
{
return GetXml();
} }
} }
} }

View File

@ -1,15 +1,16 @@
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Dlna.Didl; using MediaBrowser.Dlna.Didl;
using MediaBrowser.Dlna.Server;
using MediaBrowser.Dlna.Service;
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Querying; using MediaBrowser.Model.Querying;
@ -21,20 +22,17 @@ using System.Text;
using System.Threading; using System.Threading;
using System.Xml; 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 ILibraryManager _libraryManager;
private readonly IUserDataManager _userDataManager; private readonly IUserDataManager _userDataManager;
private readonly IServerConfigurationManager _config;
private readonly User _user; private readonly User _user;
private const string NS_DC = "http://purl.org/dc/elements/1.1/"; 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_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_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 const string NS_UPNP = "urn:schemas-upnp-org:metadata-1-0/upnp/";
private readonly int _systemUpdateId; private readonly int _systemUpdateId;
@ -45,151 +43,45 @@ namespace MediaBrowser.Dlna.Server
private readonly DeviceProfile _profile; 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) 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; _libraryManager = libraryManager;
_userDataManager = userDataManager; _userDataManager = userDataManager;
_user = user; _user = user;
_systemUpdateId = systemUpdateId; _systemUpdateId = systemUpdateId;
_config = config;
_profile = profile; _profile = profile;
_didlBuilder = new DidlBuilder(profile, imageProcessor, serverAddress, dtoService); _didlBuilder = new DidlBuilder(profile, imageProcessor, serverAddress, dtoService);
} }
public ControlResponse ProcessControlRequest(ControlRequest request) protected override IEnumerable<KeyValuePair<string, string>> GetResult(string methodName, Headers methodParams)
{ {
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)
{
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"; var deviceId = "test";
IEnumerable<KeyValuePair<string, string>> result;
_logger.Debug("Received control request {0}", method.Name);
var user = _user; var user = _user;
if (string.Equals(method.LocalName, "GetSearchCapabilities", StringComparison.OrdinalIgnoreCase)) if (string.Equals(methodName, "GetSearchCapabilities", StringComparison.OrdinalIgnoreCase))
result = HandleGetSearchCapabilities(); return 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);
var env = new XmlDocument(); if (string.Equals(methodName, "GetSortCapabilities", StringComparison.OrdinalIgnoreCase))
env.AppendChild(env.CreateXmlDeclaration("1.0", "utf-8", string.Empty)); return HandleGetSortCapabilities();
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); if (string.Equals(methodName, "GetSystemUpdateID", StringComparison.OrdinalIgnoreCase))
env.DocumentElement.AppendChild(rbody); return HandleGetSystemUpdateID();
var response = env.CreateElement(String.Format("u:{0}Response", method.LocalName), method.NamespaceURI); if (string.Equals(methodName, "Browse", StringComparison.OrdinalIgnoreCase))
rbody.AppendChild(response); return HandleBrowse(methodParams, user, deviceId);
foreach (var i in result) if (string.Equals(methodName, "X_GetFeatureList", StringComparison.OrdinalIgnoreCase))
{ return HandleXGetFeatureList();
var ri = env.CreateElement(i.Key);
ri.InnerText = i.Value;
response.AppendChild(ri);
}
var controlResponse = new ControlResponse if (string.Equals(methodName, "X_SetBookmark", StringComparison.OrdinalIgnoreCase))
{ return HandleXSetBookmark(methodParams, user);
Xml = env.OuterXml,
IsSuccessful = true
};
controlResponse.Headers.Add("EXT", string.Empty); if (string.Equals(methodName, "Search", StringComparison.OrdinalIgnoreCase))
return HandleSearch(methodParams, user, deviceId);
return controlResponse; throw new ResourceNotFoundException("Unexpected control request name: " + methodName);
}
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
};
} }
private IEnumerable<KeyValuePair<string, string>> HandleXSetBookmark(IDictionary<string, string> sparams, User user) 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); var headers = new Headers(true);
headers.Add("Id", _systemUpdateId.ToString(_usCulture)); headers.Add("Id", _systemUpdateId.ToString(_usCulture));
return headers; return headers;
} }
private IEnumerable<KeyValuePair<string, string>> HandleXGetFeatureList() private IEnumerable<KeyValuePair<string, string>> HandleXGetFeatureList()

View File

@ -1,7 +1,7 @@
using MediaBrowser.Dlna.Common; using MediaBrowser.Dlna.Common;
using System.Collections.Generic; using System.Collections.Generic;
namespace MediaBrowser.Dlna.Server namespace MediaBrowser.Dlna.ContentDirectory
{ {
public class ServiceActionListBuilder public class ServiceActionListBuilder
{ {

View File

@ -345,7 +345,9 @@ namespace MediaBrowser.Dlna.Didl
/// <param name="filter">The filter.</param> /// <param name="filter">The filter.</param>
private void AddCommonFields(BaseItem item, XmlElement element, Filter filter) 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); 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) foreach (var studio in item.Studios)

View File

@ -51,6 +51,10 @@
<Compile Include="..\SharedVersion.cs"> <Compile Include="..\SharedVersion.cs">
<Link>Properties\SharedVersion.cs</Link> <Link>Properties\SharedVersion.cs</Link>
</Compile> </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="DlnaManager.cs" />
<Compile Include="Common\Argument.cs" /> <Compile Include="Common\Argument.cs" />
<Compile Include="Eventing\EventManager.cs" /> <Compile Include="Eventing\EventManager.cs" />
@ -79,10 +83,13 @@
<Compile Include="Profiles\Windows81Profile.cs" /> <Compile Include="Profiles\Windows81Profile.cs" />
<Compile Include="Profiles\WindowsMediaCenterProfile.cs" /> <Compile Include="Profiles\WindowsMediaCenterProfile.cs" />
<Compile Include="Profiles\WindowsPhoneProfile.cs" /> <Compile Include="Profiles\WindowsPhoneProfile.cs" />
<Compile Include="Server\ContentDirectory.cs" /> <Compile Include="ContentDirectory\ContentDirectory.cs" />
<Compile Include="Server\ControlHandler.cs" /> <Compile Include="ContentDirectory\ControlHandler.cs" />
<Compile Include="Server\ServiceActionListBuilder.cs" /> <Compile Include="ContentDirectory\ServiceActionListBuilder.cs" />
<Compile Include="Server\ContentDirectoryXmlBuilder.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="Ssdp\Datagram.cs" />
<Compile Include="Server\DescriptionXmlBuilder.cs" /> <Compile Include="Server\DescriptionXmlBuilder.cs" />
<Compile Include="Ssdp\SsdpHelper.cs" /> <Compile Include="Ssdp\SsdpHelper.cs" />
@ -159,6 +166,7 @@
<EmbeddedResource Include="Images\logo48.jpg" /> <EmbeddedResource Include="Images\logo48.jpg" />
<EmbeddedResource Include="Images\logo48.png" /> <EmbeddedResource Include="Images\logo48.png" />
</ItemGroup> </ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- 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. Other similar extension points exist, see Microsoft.Common.targets.

View File

@ -185,6 +185,15 @@ namespace MediaBrowser.Dlna.Server
EventSubUrl = "/mediabrowser/dlna/contentdirectory/" + _serverUdn + "/events" 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; return list;
} }

View File

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

View File

@ -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
};
}
}
}

View File

@ -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>");
}
}
}

View File

@ -267,7 +267,7 @@ namespace MediaBrowser.Server.Implementations.Channels
{ {
providerStartIndex = query.StartIndex; 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)); throw new ArgumentException(string.Format("Channel {0} only supports a maximum of {1} records at a time.", channel.Name, channelInfo.MaxPageSize.Value));
} }

View File

@ -63,17 +63,18 @@ namespace MediaBrowser.Server.Implementations.Session
private Task SendMessage(string name, CancellationToken cancellationToken) 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> var url = _postUrl + "/" + name + ToQueryString(args);
{
MessageType = name,
Data = string.Empty
}, cancellationToken); return _httpClient.Post(new HttpRequestOptions
{
Url = url,
CancellationToken = cancellationToken
});
} }
public Task SendSessionEndedNotification(SessionInfoDto sessionInfo, 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) public Task SendGeneralCommand(GeneralCommand command, CancellationToken cancellationToken)
{ {
return SendMessage(new WebSocketMessage<GeneralCommand> return SendMessage(command.Name, command.Arguments, cancellationToken);
{
MessageType = "GeneralCommand",
Data = command
}, cancellationToken);
} }
private string ToQueryString(Dictionary<string, string> nvc) private string ToQueryString(Dictionary<string, string> nvc)
@ -154,7 +150,15 @@ namespace MediaBrowser.Server.Implementations.Session
var array = (from item in nvc var array = (from item in nvc
select string.Format("{0}={1}", WebUtility.UrlEncode(item.Key), WebUtility.UrlEncode(item.Value))) select string.Format("{0}={1}", WebUtility.UrlEncode(item.Key), WebUtility.UrlEncode(item.Value)))
.ToArray(); .ToArray();
return "?" + string.Join("&", array);
var args = string.Join("&", array);
if (string.IsNullOrEmpty(args))
{
return args;
}
return "?" + args;
} }
} }
} }

View File

@ -154,9 +154,9 @@ namespace MediaBrowser.Server.Implementations.WebSocket
{ {
if (WebSocketServer != null) if (WebSocketServer != null)
{ {
// Calling dispose will also call stop
_logger.Debug("Disposing alchemy server"); _logger.Debug("Disposing alchemy server");
WebSocketServer.Stop();
WebSocketServer.Dispose();
WebSocketServer = null; WebSocketServer = null;
} }
} }

View File

@ -35,6 +35,8 @@ using MediaBrowser.Controller.Sorting;
using MediaBrowser.Controller.Subtitles; using MediaBrowser.Controller.Subtitles;
using MediaBrowser.Controller.Themes; using MediaBrowser.Controller.Themes;
using MediaBrowser.Dlna; using MediaBrowser.Dlna;
using MediaBrowser.Dlna.ConnectionManager;
using MediaBrowser.Dlna.ContentDirectory;
using MediaBrowser.Dlna.Eventing; using MediaBrowser.Dlna.Eventing;
using MediaBrowser.Dlna.Main; using MediaBrowser.Dlna.Main;
using MediaBrowser.Dlna.Server; using MediaBrowser.Dlna.Server;
@ -526,6 +528,9 @@ namespace MediaBrowser.ServerApplication
var contentDirectory = new ContentDirectory(dlnaManager, UserDataManager, ImageProcessor, DtoService, LibraryManager, LogManager, ServerConfigurationManager, UserManager, dlnaEventManager); var contentDirectory = new ContentDirectory(dlnaManager, UserDataManager, ImageProcessor, DtoService, LibraryManager, LogManager, ServerConfigurationManager, UserManager, dlnaEventManager);
RegisterSingleInstance<IContentDirectory>(contentDirectory); RegisterSingleInstance<IContentDirectory>(contentDirectory);
var connectionManager = new ConnectionManager(dlnaManager, LogManager, ServerConfigurationManager);
RegisterSingleInstance<IConnectionManager>(connectionManager);
var collectionManager = new CollectionManager(LibraryManager, FileSystemManager, LibraryMonitor); var collectionManager = new CollectionManager(LibraryManager, FileSystemManager, LibraryMonitor);
RegisterSingleInstance<ICollectionManager>(collectionManager); RegisterSingleInstance<ICollectionManager>(collectionManager);